get info changes
This commit is contained in:
parent
77411e94a4
commit
96f05cd518
6 changed files with 226 additions and 126 deletions
src/main
java/stirling/software/SPDF/controller/api/security
resources
|
@ -73,7 +73,7 @@ import java.util.Set;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
@RestController
|
@RestController
|
||||||
@Tag(name = "Security", description = "Security APIs")
|
@Tag(name = "Security", description = "Security APIs")
|
||||||
public class PDFExtractor {
|
public class GetInfoOnPDF {
|
||||||
|
|
||||||
static ObjectMapper objectMapper = new ObjectMapper();
|
static ObjectMapper objectMapper = new ObjectMapper();
|
||||||
|
|
||||||
|
@ -94,6 +94,13 @@ public class PDFExtractor {
|
||||||
// Metadata using PDFBox
|
// Metadata using PDFBox
|
||||||
PDDocumentInformation info = pdfBoxDoc.getDocumentInformation();
|
PDDocumentInformation info = pdfBoxDoc.getDocumentInformation();
|
||||||
ObjectNode metadata = objectMapper.createObjectNode();
|
ObjectNode metadata = objectMapper.createObjectNode();
|
||||||
|
ObjectNode basicInfo = objectMapper.createObjectNode();
|
||||||
|
ObjectNode docInfoNode = objectMapper.createObjectNode();
|
||||||
|
ObjectNode compliancy = objectMapper.createObjectNode();
|
||||||
|
ObjectNode encryption = objectMapper.createObjectNode();
|
||||||
|
ObjectNode other = objectMapper.createObjectNode();
|
||||||
|
|
||||||
|
|
||||||
metadata.put("Title", info.getTitle());
|
metadata.put("Title", info.getTitle());
|
||||||
metadata.put("Author", info.getAuthor());
|
metadata.put("Author", info.getAuthor());
|
||||||
metadata.put("Subject", info.getSubject());
|
metadata.put("Subject", info.getSubject());
|
||||||
|
@ -102,25 +109,25 @@ public class PDFExtractor {
|
||||||
metadata.put("Creator", info.getCreator());
|
metadata.put("Creator", info.getCreator());
|
||||||
metadata.put("CreationDate", formatDate(info.getCreationDate()));
|
metadata.put("CreationDate", formatDate(info.getCreationDate()));
|
||||||
metadata.put("ModificationDate", formatDate(info.getModificationDate()));
|
metadata.put("ModificationDate", formatDate(info.getModificationDate()));
|
||||||
metadata.put("Trapped", info.getTrapped());
|
|
||||||
jsonOutput.set("Metadata", metadata);
|
jsonOutput.set("Metadata", metadata);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Total file size of the PDF
|
// Total file size of the PDF
|
||||||
long fileSizeInBytes = inputFile.getSize();
|
long fileSizeInBytes = inputFile.getSize();
|
||||||
jsonOutput.put("FileSizeInBytes", fileSizeInBytes);
|
basicInfo.put("FileSizeInBytes", fileSizeInBytes);
|
||||||
|
|
||||||
// Number of words, paragraphs, and images in the entire document
|
// Number of words, paragraphs, and images in the entire document
|
||||||
String fullText = new PDFTextStripper().getText(pdfBoxDoc);
|
String fullText = new PDFTextStripper().getText(pdfBoxDoc);
|
||||||
String[] words = fullText.split("\\s+");
|
String[] words = fullText.split("\\s+");
|
||||||
int wordCount = words.length;
|
int wordCount = words.length;
|
||||||
int paragraphCount = fullText.split("\r\n|\r|\n").length;
|
int paragraphCount = fullText.split("\r\n|\r|\n").length;
|
||||||
jsonOutput.put("WordCount", wordCount);
|
basicInfo.put("WordCount", wordCount);
|
||||||
jsonOutput.put("ParagraphCount", paragraphCount);
|
basicInfo.put("ParagraphCount", paragraphCount);
|
||||||
// Number of characters in the entire document (including spaces and special characters)
|
// Number of characters in the entire document (including spaces and special characters)
|
||||||
int charCount = fullText.length();
|
int charCount = fullText.length();
|
||||||
jsonOutput.put("CharacterCount", charCount);
|
basicInfo.put("CharacterCount", charCount);
|
||||||
|
|
||||||
|
|
||||||
// Initialize the flags and types
|
// Initialize the flags and types
|
||||||
|
@ -142,22 +149,24 @@ public class PDFExtractor {
|
||||||
hasCompression = true;
|
hasCompression = true;
|
||||||
compressionType = "Compressed Xref or Rebuilt Xref";
|
compressionType = "Compressed Xref or Rebuilt Xref";
|
||||||
}
|
}
|
||||||
jsonOutput.put("Compression", hasCompression);
|
basicInfo.put("Compression", hasCompression);
|
||||||
if(hasCompression)
|
if(hasCompression)
|
||||||
jsonOutput.put("CompressionType", compressionType);
|
basicInfo.put("CompressionType", compressionType);
|
||||||
|
|
||||||
String language = pdfBoxDoc.getDocumentCatalog().getLanguage();
|
String language = pdfBoxDoc.getDocumentCatalog().getLanguage();
|
||||||
jsonOutput.put("Language", language);
|
basicInfo.put("Language", language);
|
||||||
|
basicInfo.put("Number of pages", pdfBoxDoc.getNumberOfPages());
|
||||||
|
|
||||||
// Document Information using PDFBox
|
|
||||||
ObjectNode docInfoNode = objectMapper.createObjectNode();
|
|
||||||
docInfoNode.put("Number of pages", pdfBoxDoc.getNumberOfPages());
|
|
||||||
docInfoNode.put("PDF version", pdfBoxDoc.getVersion());
|
|
||||||
|
|
||||||
|
|
||||||
// Page Mode using iText7
|
// Page Mode using iText7
|
||||||
PdfCatalog catalog = itextDoc.getCatalog();
|
PdfCatalog catalog = itextDoc.getCatalog();
|
||||||
PdfName pageMode = catalog.getPdfObject().getAsName(PdfName.PageMode);
|
PdfName pageMode = catalog.getPdfObject().getAsName(PdfName.PageMode);
|
||||||
|
|
||||||
|
// Document Information using PDFBox
|
||||||
|
docInfoNode.put("PDF version", pdfBoxDoc.getVersion());
|
||||||
|
docInfoNode.put("Trapped", info.getTrapped());
|
||||||
|
docInfoNode.put("Page Mode", getPageModeDescription(pageMode));;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -193,7 +202,7 @@ public class PDFExtractor {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
jsonOutput.set("EmbeddedFiles", embeddedFilesArray);
|
other.set("EmbeddedFiles", embeddedFilesArray);
|
||||||
|
|
||||||
//attachments TODO size
|
//attachments TODO size
|
||||||
ArrayNode attachmentsArray = objectMapper.createArrayNode();
|
ArrayNode attachmentsArray = objectMapper.createArrayNode();
|
||||||
|
@ -207,7 +216,7 @@ public class PDFExtractor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
jsonOutput.set("Attachments", attachmentsArray);
|
other.set("Attachments", attachmentsArray);
|
||||||
|
|
||||||
//Javascript
|
//Javascript
|
||||||
PdfDictionary namesDict = itextDoc.getCatalog().getPdfObject().getAsDictionary(PdfName.Names);
|
PdfDictionary namesDict = itextDoc.getCatalog().getPdfObject().getAsDictionary(PdfName.Names);
|
||||||
|
@ -226,7 +235,7 @@ public class PDFExtractor {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
jsonOutput.set("JavaScript", javascriptArray);
|
other.set("JavaScript", javascriptArray);
|
||||||
|
|
||||||
//TODO size
|
//TODO size
|
||||||
PdfOCProperties ocProperties = itextDoc.getCatalog().getOCProperties(false);
|
PdfOCProperties ocProperties = itextDoc.getCatalog().getOCProperties(false);
|
||||||
|
@ -240,7 +249,7 @@ public class PDFExtractor {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
jsonOutput.set("Layers", layersArray);
|
other.set("Layers", layersArray);
|
||||||
|
|
||||||
//TODO Security
|
//TODO Security
|
||||||
|
|
||||||
|
@ -267,7 +276,7 @@ public class PDFExtractor {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
jsonOutput.set("FormFields", formFieldsArray2);
|
jsonOutput.set("FormFields2", formFieldsArray2);
|
||||||
|
|
||||||
|
|
||||||
PDStructureTreeRoot structureTreeRoot = pdfBoxDoc.getDocumentCatalog().getStructureTreeRoot();
|
PDStructureTreeRoot structureTreeRoot = pdfBoxDoc.getDocumentCatalog().getStructureTreeRoot();
|
||||||
|
@ -275,19 +284,13 @@ public class PDFExtractor {
|
||||||
try {
|
try {
|
||||||
if(structureTreeRoot != null) {
|
if(structureTreeRoot != null) {
|
||||||
structureTreeArray = exploreStructureTree(structureTreeRoot.getKids());
|
structureTreeArray = exploreStructureTree(structureTreeRoot.getKids());
|
||||||
jsonOutput.set("StructureTree", structureTreeArray);
|
other.set("StructureTree", structureTreeArray);
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// TODO Auto-generated catch block
|
// TODO Auto-generated catch block
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
boolean isPdfACompliant = checkOutputIntent(itextDoc, "PDF/A");
|
boolean isPdfACompliant = checkOutputIntent(itextDoc, "PDF/A");
|
||||||
boolean isPdfXCompliant = checkOutputIntent(itextDoc, "PDF/X");
|
boolean isPdfXCompliant = checkOutputIntent(itextDoc, "PDF/X");
|
||||||
|
@ -297,7 +300,6 @@ public class PDFExtractor {
|
||||||
boolean isPdfBCompliant = checkForStandard(itextDoc, "PDF/B"); // If you want to check for PDF/Broadcast, though this isn't an official ISO standard.
|
boolean isPdfBCompliant = checkForStandard(itextDoc, "PDF/B"); // If you want to check for PDF/Broadcast, though this isn't an official ISO standard.
|
||||||
boolean isPdfSECCompliant = checkForStandard(itextDoc, "PDF/SEC"); // This might not be effective since PDF/SEC was under development in 2021.
|
boolean isPdfSECCompliant = checkForStandard(itextDoc, "PDF/SEC"); // This might not be effective since PDF/SEC was under development in 2021.
|
||||||
|
|
||||||
ObjectNode compliancy = objectMapper.createObjectNode();
|
|
||||||
compliancy.put("IsPDF/ACompliant", isPdfACompliant);
|
compliancy.put("IsPDF/ACompliant", isPdfACompliant);
|
||||||
compliancy.put("IsPDF/XCompliant", isPdfXCompliant);
|
compliancy.put("IsPDF/XCompliant", isPdfXCompliant);
|
||||||
compliancy.put("IsPDF/ECompliant", isPdfECompliant);
|
compliancy.put("IsPDF/ECompliant", isPdfECompliant);
|
||||||
|
@ -306,7 +308,6 @@ public class PDFExtractor {
|
||||||
compliancy.put("IsPDF/BCompliant", isPdfBCompliant);
|
compliancy.put("IsPDF/BCompliant", isPdfBCompliant);
|
||||||
compliancy.put("IsPDF/SECCompliant", isPdfSECCompliant);
|
compliancy.put("IsPDF/SECCompliant", isPdfSECCompliant);
|
||||||
|
|
||||||
jsonOutput.set("Compliancy", compliancy);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -318,7 +319,7 @@ public class PDFExtractor {
|
||||||
addOutlinesToArray(child, bookmarksArray);
|
addOutlinesToArray(child, bookmarksArray);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
jsonOutput.set("Bookmarks/Outline/TOC", bookmarksArray);
|
other.set("Bookmarks/Outline/TOC", bookmarksArray);
|
||||||
|
|
||||||
String xmpString = null;
|
String xmpString = null;
|
||||||
try {
|
try {
|
||||||
|
@ -331,29 +332,27 @@ public class PDFExtractor {
|
||||||
} catch (XMPException e) {
|
} catch (XMPException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
jsonOutput.put("XMPMetadata", xmpString);
|
other.put("XMPMetadata", xmpString);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
ObjectNode encryptionNode = objectMapper.createObjectNode();
|
|
||||||
if (pdfBoxDoc.isEncrypted()) {
|
if (pdfBoxDoc.isEncrypted()) {
|
||||||
encryptionNode.put("IsEncrypted", true);
|
encryption.put("IsEncrypted", true);
|
||||||
|
|
||||||
// Retrieve encryption details using getEncryption()
|
// Retrieve encryption details using getEncryption()
|
||||||
PDEncryption encryption = pdfBoxDoc.getEncryption();
|
PDEncryption pdfEncryption = pdfBoxDoc.getEncryption();
|
||||||
encryptionNode.put("EncryptionAlgorithm", encryption.getFilter());
|
encryption.put("EncryptionAlgorithm", pdfEncryption.getFilter());
|
||||||
encryptionNode.put("KeyLength", encryption.getLength());
|
encryption.put("KeyLength", pdfEncryption.getLength());
|
||||||
encryptionNode.put("Permissions", pdfBoxDoc.getCurrentAccessPermission().toString());
|
encryption.put("Permissions", pdfBoxDoc.getCurrentAccessPermission().toString());
|
||||||
|
|
||||||
// Add other encryption-related properties as needed
|
// Add other encryption-related properties as needed
|
||||||
} else {
|
} else {
|
||||||
encryptionNode.put("IsEncrypted", false);
|
encryption.put("IsEncrypted", false);
|
||||||
}
|
}
|
||||||
jsonOutput.set("Encryption", encryptionNode);
|
|
||||||
|
|
||||||
docInfoNode.put("Page Mode", getPageModeDescription(pageMode));;
|
|
||||||
|
|
||||||
|
|
||||||
jsonOutput.set("Document Information", docInfoNode);
|
|
||||||
ObjectNode pageInfoParent = objectMapper.createObjectNode();
|
ObjectNode pageInfoParent = objectMapper.createObjectNode();
|
||||||
for (int pageNum = 1; pageNum <= itextDoc.getNumberOfPages(); pageNum++) {
|
for (int pageNum = 1; pageNum <= itextDoc.getNumberOfPages(); pageNum++) {
|
||||||
ObjectNode pageInfo = objectMapper.createObjectNode();
|
ObjectNode pageInfo = objectMapper.createObjectNode();
|
||||||
|
@ -382,7 +381,6 @@ public class PDFExtractor {
|
||||||
pageInfo.put("Text Characters Count", pageText.length()); //
|
pageInfo.put("Text Characters Count", pageText.length()); //
|
||||||
|
|
||||||
// Annotations
|
// Annotations
|
||||||
ArrayNode annotationsArray = objectMapper.createArrayNode();
|
|
||||||
List<PdfAnnotation> annotations = itextDoc.getPage(pageNum).getAnnotations();
|
List<PdfAnnotation> annotations = itextDoc.getPage(pageNum).getAnnotations();
|
||||||
|
|
||||||
int subtypeCount = 0;
|
int subtypeCount = 0;
|
||||||
|
@ -447,61 +445,57 @@ public class PDFExtractor {
|
||||||
}
|
}
|
||||||
pageInfo.set("Links", linksArray);
|
pageInfo.set("Links", linksArray);
|
||||||
|
|
||||||
//Fonts
|
// Fonts
|
||||||
ArrayNode fontsArray = objectMapper.createArrayNode();
|
ArrayNode fontsArray = objectMapper.createArrayNode();
|
||||||
PdfDictionary fontDicts = resources.getResource(PdfName.Font);
|
PdfDictionary fontDicts = resources.getResource(PdfName.Font);
|
||||||
Set<String> uniqueSubtypes = new HashSet<>(); // To store unique subtypes
|
Set<String> uniqueSubtypes = new HashSet<>(); // To store unique subtypes
|
||||||
|
|
||||||
|
// Map to store unique fonts and their counts
|
||||||
|
Map<String, ObjectNode> uniqueFontsMap = new HashMap<>();
|
||||||
|
|
||||||
|
|
||||||
if (fontDicts != null) {
|
if (fontDicts != null) {
|
||||||
for (PdfName key : fontDicts.keySet()) {
|
for (PdfName key : fontDicts.keySet()) {
|
||||||
ObjectNode fontNode = objectMapper.createObjectNode(); // Create a new font node for each font
|
ObjectNode fontNode = objectMapper.createObjectNode(); // Create a new font node for each font
|
||||||
PdfDictionary font = fontDicts.getAsDictionary(key);
|
PdfDictionary font = fontDicts.getAsDictionary(key);
|
||||||
|
|
||||||
boolean isEmbedded = font.containsKey(PdfName.FontFile) ||
|
boolean isEmbedded = font.containsKey(PdfName.FontFile) ||
|
||||||
font.containsKey(PdfName.FontFile2) ||
|
font.containsKey(PdfName.FontFile2) ||
|
||||||
font.containsKey(PdfName.FontFile3);
|
font.containsKey(PdfName.FontFile3);
|
||||||
fontNode.put("IsEmbedded", isEmbedded);
|
fontNode.put("IsEmbedded", isEmbedded);
|
||||||
|
|
||||||
|
if (font.containsKey(PdfName.Encoding)) {
|
||||||
if (font.containsKey(PdfName.Encoding)) {
|
String encoding = font.getAsName(PdfName.Encoding).toString();
|
||||||
String encoding = font.getAsName(PdfName.Encoding).toString();
|
fontNode.put("Encoding", encoding);
|
||||||
fontNode.put("Encoding", encoding);
|
}
|
||||||
}
|
|
||||||
|
if (font.getAsString(PdfName.BaseFont) != null) {
|
||||||
|
|
||||||
|
|
||||||
if(font.getAsString(PdfName.BaseFont) != null)
|
|
||||||
fontNode.put("Name", font.getAsString(PdfName.BaseFont).toString());
|
fontNode.put("Name", font.getAsString(PdfName.BaseFont).toString());
|
||||||
|
}
|
||||||
|
|
||||||
String subtype = null;
|
String subtype = null;
|
||||||
// Font Subtype (e.g., Type1, TrueType)
|
|
||||||
if (font.containsKey(PdfName.Subtype)) {
|
if (font.containsKey(PdfName.Subtype)) {
|
||||||
subtype = font.getAsName(PdfName.Subtype).toString();
|
subtype = font.getAsName(PdfName.Subtype).toString();
|
||||||
uniqueSubtypes.add(subtype); // Add to set to ensure uniqueness
|
uniqueSubtypes.add(subtype); // Add to set to ensure uniqueness
|
||||||
}
|
}
|
||||||
fontNode.put("Subtype", subtype);
|
fontNode.put("Subtype", subtype);
|
||||||
|
|
||||||
// Font Descriptor
|
|
||||||
PdfDictionary fontDescriptor = font.getAsDictionary(PdfName.FontDescriptor);
|
PdfDictionary fontDescriptor = font.getAsDictionary(PdfName.FontDescriptor);
|
||||||
if (fontDescriptor != null) {
|
if (fontDescriptor != null) {
|
||||||
// Italic Angle
|
|
||||||
if (fontDescriptor.containsKey(PdfName.ItalicAngle)) {
|
if (fontDescriptor.containsKey(PdfName.ItalicAngle)) {
|
||||||
fontNode.put("ItalicAngle", fontDescriptor.getAsNumber(PdfName.ItalicAngle).floatValue());
|
fontNode.put("ItalicAngle", fontDescriptor.getAsNumber(PdfName.ItalicAngle).floatValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Flags (e.g., italic, bold)
|
|
||||||
if (fontDescriptor.containsKey(PdfName.Flags)) {
|
if (fontDescriptor.containsKey(PdfName.Flags)) {
|
||||||
int flags = fontDescriptor.getAsNumber(PdfName.Flags).intValue();
|
int flags = fontDescriptor.getAsNumber(PdfName.Flags).intValue();
|
||||||
fontNode.put("IsItalic", (flags & 64) != 0); // Existing italic flag
|
fontNode.put("IsItalic", (flags & 64) != 0);
|
||||||
fontNode.put("IsBold", (flags & 1 << 16) != 0); // Existing bold flag
|
fontNode.put("IsBold", (flags & 1 << 16) != 0);
|
||||||
fontNode.put("IsFixedPitch", (flags & 1) != 0);
|
fontNode.put("IsFixedPitch", (flags & 1) != 0);
|
||||||
fontNode.put("IsSerif", (flags & 2) != 0);
|
fontNode.put("IsSerif", (flags & 2) != 0);
|
||||||
fontNode.put("IsSymbolic", (flags & 4) != 0);
|
fontNode.put("IsSymbolic", (flags & 4) != 0);
|
||||||
fontNode.put("IsScript", (flags & 8) != 0);
|
fontNode.put("IsScript", (flags & 8) != 0);
|
||||||
fontNode.put("IsNonsymbolic", (flags & 16) != 0);
|
fontNode.put("IsNonsymbolic", (flags & 16) != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fontDescriptor.containsKey(PdfName.FontFamily)) {
|
if (fontDescriptor.containsKey(PdfName.FontFamily)) {
|
||||||
String fontFamily = fontDescriptor.getAsString(PdfName.FontFamily).toString();
|
String fontFamily = fontDescriptor.getAsString(PdfName.FontFamily).toString();
|
||||||
fontNode.put("FontFamily", fontFamily);
|
fontNode.put("FontFamily", fontFamily);
|
||||||
|
@ -511,34 +505,43 @@ public class PDFExtractor {
|
||||||
String fontStretch = fontDescriptor.getAsName(PdfName.FontStretch).toString();
|
String fontStretch = fontDescriptor.getAsName(PdfName.FontStretch).toString();
|
||||||
fontNode.put("FontStretch", fontStretch);
|
fontNode.put("FontStretch", fontStretch);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fontDescriptor != null && fontDescriptor.containsKey(PdfName.FontBBox)) {
|
if (fontDescriptor.containsKey(PdfName.FontBBox)) {
|
||||||
PdfArray bbox = fontDescriptor.getAsArray(PdfName.FontBBox);
|
PdfArray bbox = fontDescriptor.getAsArray(PdfName.FontBBox);
|
||||||
fontNode.put("FontBoundingBox", bbox.toString());
|
fontNode.put("FontBoundingBox", bbox.toString());
|
||||||
}
|
}
|
||||||
if (fontDescriptor != null && fontDescriptor.containsKey(PdfName.FontWeight)) {
|
|
||||||
|
if (fontDescriptor.containsKey(PdfName.FontWeight)) {
|
||||||
float fontWeight = fontDescriptor.getAsNumber(PdfName.FontWeight).floatValue();
|
float fontWeight = fontDescriptor.getAsNumber(PdfName.FontWeight).floatValue();
|
||||||
fontNode.put("FontWeight", fontWeight);
|
fontNode.put("FontWeight", fontWeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (font.containsKey(PdfName.ToUnicode)) {
|
if (font.containsKey(PdfName.ToUnicode)) {
|
||||||
PdfStream toUnicodeStream = font.getAsStream(PdfName.ToUnicode);
|
|
||||||
// Handle the stream as needed, maybe extract some details or just note its existence
|
|
||||||
fontNode.put("HasToUnicodeMap", true);
|
fontNode.put("HasToUnicodeMap", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fontNode.size() > 0) {
|
if (fontNode.size() > 0) {
|
||||||
fontsArray.add(fontNode); // Add each font node to fontsArray
|
// Create a unique key for this font node based on its attributes
|
||||||
|
String uniqueKey = fontNode.toString();
|
||||||
|
|
||||||
|
// Increment count if this font exists, or initialize it if new
|
||||||
|
if (uniqueFontsMap.containsKey(uniqueKey)) {
|
||||||
|
ObjectNode existingFontNode = uniqueFontsMap.get(uniqueKey);
|
||||||
|
int count = existingFontNode.get("Count").asInt() + 1;
|
||||||
|
existingFontNode.put("Count", count);
|
||||||
|
} else {
|
||||||
|
fontNode.put("Count", 1);
|
||||||
|
uniqueFontsMap.put(uniqueKey, fontNode);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add unique subtypes to fontsArray
|
// Add unique font entries to fontsArray
|
||||||
ArrayNode subtypesArray = objectMapper.createArrayNode();
|
for (ObjectNode uniqueFontNode : uniqueFontsMap.values()) {
|
||||||
for (String subtype : uniqueSubtypes) {
|
fontsArray.add(uniqueFontNode);
|
||||||
subtypesArray.add(subtype);
|
|
||||||
}
|
}
|
||||||
pageInfo.set("FontSubtypes", subtypesArray); // Changed from Fonts to FontSubtypes
|
|
||||||
|
|
||||||
pageInfo.set("Fonts", fontsArray);
|
pageInfo.set("Fonts", fontsArray);
|
||||||
|
|
||||||
|
@ -605,8 +608,14 @@ public class PDFExtractor {
|
||||||
|
|
||||||
pageInfoParent.set("Page " + pageNum, pageInfo);
|
pageInfoParent.set("Page " + pageNum, pageInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
jsonOutput.set("Per Page Info", pageInfoParent);
|
jsonOutput.set("BasicInfo", basicInfo);
|
||||||
|
jsonOutput.set("DocumentInfo", docInfoNode);
|
||||||
|
jsonOutput.set("Compliancy", compliancy);
|
||||||
|
jsonOutput.set("Encryption", encryption);
|
||||||
|
jsonOutput.set("Other", other);
|
||||||
|
jsonOutput.set("PerPageInfo", pageInfoParent);
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -237,7 +237,7 @@ HTMLToPDF.tags=markup,web-content,transformation,convert
|
||||||
|
|
||||||
|
|
||||||
home.MarkdownToPDF.title=Markdown to PDF
|
home.MarkdownToPDF.title=Markdown to PDF
|
||||||
home.MarkdownToPDF.desc=Converts any Markdown fileto PDF
|
home.MarkdownToPDF.desc=Converts any Markdown file to PDF
|
||||||
MarkdownToPDF.tags=markup,web-content,transformation,convert
|
MarkdownToPDF.tags=markup,web-content,transformation,convert
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,18 @@
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
document.querySelectorAll('.custom-file-chooser').forEach(setupFileInput);
|
||||||
|
});
|
||||||
|
|
||||||
|
function setupFileInput(chooser) {
|
||||||
|
const elementId = chooser.getAttribute('data-element-id');
|
||||||
|
const filesSelected = chooser.getAttribute('data-files-selected');
|
||||||
|
const pdfPrompt = chooser.getAttribute('data-pdf-prompt');
|
||||||
|
|
||||||
let overlay;
|
let overlay;
|
||||||
let dragCounter = 0;
|
let dragCounter = 0;
|
||||||
|
|
||||||
const dragenterListener = function() {
|
const dragenterListener = function() {
|
||||||
dragCounter++;
|
dragCounter++;
|
||||||
if (!overlay) {
|
if (!overlay) {
|
||||||
// Create and show the overlay
|
|
||||||
overlay = document.createElement('div');
|
overlay = document.createElement('div');
|
||||||
overlay.style.position = 'fixed';
|
overlay.style.position = 'fixed';
|
||||||
overlay.style.top = 0;
|
overlay.style.top = 0;
|
||||||
|
@ -28,7 +34,6 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||||
const dragleaveListener = function() {
|
const dragleaveListener = function() {
|
||||||
dragCounter--;
|
dragCounter--;
|
||||||
if (dragCounter === 0) {
|
if (dragCounter === 0) {
|
||||||
// Hide and remove the overlay
|
|
||||||
if (overlay) {
|
if (overlay) {
|
||||||
overlay.remove();
|
overlay.remove();
|
||||||
overlay = null;
|
overlay = null;
|
||||||
|
@ -40,24 +45,19 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||||
const dt = e.dataTransfer;
|
const dt = e.dataTransfer;
|
||||||
const files = dt.files;
|
const files = dt.files;
|
||||||
|
|
||||||
// Access the file input element and assign dropped files
|
const fileInput = document.getElementById(elementId);
|
||||||
const fileInput = document.getElementById(elementID);
|
|
||||||
fileInput.files = files;
|
fileInput.files = files;
|
||||||
|
|
||||||
// Hide and remove the overlay
|
|
||||||
if (overlay) {
|
if (overlay) {
|
||||||
overlay.remove();
|
overlay.remove();
|
||||||
overlay = null;
|
overlay = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset drag counter
|
|
||||||
dragCounter = 0;
|
dragCounter = 0;
|
||||||
|
|
||||||
//handleFileInputChange(fileInput);
|
|
||||||
fileInput.dispatchEvent(new Event('change', { bubbles: true }));
|
fileInput.dispatchEvent(new Event('change', { bubbles: true }));
|
||||||
};
|
};
|
||||||
|
|
||||||
// Prevent default behavior for drag events
|
|
||||||
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
|
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
|
||||||
document.body.addEventListener(eventName, preventDefaults, false);
|
document.body.addEventListener(eventName, preventDefaults, false);
|
||||||
});
|
});
|
||||||
|
@ -69,29 +69,26 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
|
||||||
document.body.addEventListener('dragenter', dragenterListener);
|
document.body.addEventListener('dragenter', dragenterListener);
|
||||||
document.body.addEventListener('dragleave', dragleaveListener);
|
document.body.addEventListener('dragleave', dragleaveListener);
|
||||||
// Add drop event listener
|
|
||||||
document.body.addEventListener('drop', dropListener);
|
document.body.addEventListener('drop', dropListener);
|
||||||
|
|
||||||
});
|
$("#" + elementId).on("change", function() {
|
||||||
|
handleFileInputChange(this);
|
||||||
|
});
|
||||||
|
|
||||||
$("#"+elementID).on("change", function() {
|
function handleFileInputChange(inputElement) {
|
||||||
handleFileInputChange(this);
|
const files = $(inputElement).get(0).files;
|
||||||
});
|
const fileNames = Array.from(files).map(f => f.name);
|
||||||
|
const selectedFilesContainer = $(inputElement).siblings(".selected-files");
|
||||||
|
selectedFilesContainer.empty();
|
||||||
function handleFileInputChange(inputElement) {
|
fileNames.forEach(fileName => {
|
||||||
const files = $(inputElement).get(0).files;
|
selectedFilesContainer.append("<div>" + fileName + "</div>");
|
||||||
const fileNames = Array.from(files).map(f => f.name);
|
});
|
||||||
const selectedFilesContainer = $(inputElement).siblings(".selected-files");
|
if (fileNames.length === 1) {
|
||||||
selectedFilesContainer.empty();
|
$(inputElement).siblings(".custom-file-label").addClass("selected").html(fileNames[0]);
|
||||||
fileNames.forEach(fileName => {
|
} else if (fileNames.length > 1) {
|
||||||
selectedFilesContainer.append("<div>" + fileName + "</div>");
|
$(inputElement).siblings(".custom-file-label").addClass("selected").html(fileNames.length + " " + filesSelected);
|
||||||
});
|
} else {
|
||||||
if (fileNames.length === 1) {
|
$(inputElement).siblings(".custom-file-label").addClass("selected").html(pdfPrompt);
|
||||||
$(inputElement).siblings(".custom-file-label").addClass("selected").html(fileNames[0]);
|
}
|
||||||
} else if (fileNames.length > 1) {
|
}
|
||||||
$(inputElement).siblings(".custom-file-label").addClass("selected").html(fileNames.length + " " + filesSelected);
|
}
|
||||||
} else {
|
|
||||||
$(inputElement).siblings(".custom-file-label").addClass("selected").html(pdfPrompt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -98,7 +98,10 @@
|
||||||
</script>
|
</script>
|
||||||
<script src="js/downloader.js"></script>
|
<script src="js/downloader.js"></script>
|
||||||
|
|
||||||
<div class="custom-file-chooser">
|
<div class="custom-file-chooser" th:attr="data-unique-id=${name},
|
||||||
|
data-element-id=${name+'-input'},
|
||||||
|
data-files-selected=#{filesSelected},
|
||||||
|
data-pdf-prompt=#{pdfPrompt}">
|
||||||
<div class="custom-file">
|
<div class="custom-file">
|
||||||
<input type="file" class="custom-file-input" th:name="${name}" th:id="${name}+'-input'" th:accept="${accept}" multiple th:classappend="${notRequired ? '' : 'required'}">
|
<input type="file" class="custom-file-input" th:name="${name}" th:id="${name}+'-input'" th:accept="${accept}" multiple th:classappend="${notRequired ? '' : 'required'}">
|
||||||
<label class="custom-file-label" th:for="${name}+'-input'" th:text="${inputText}"></label>
|
<label class="custom-file-label" th:for="${name}+'-input'" th:text="${inputText}"></label>
|
||||||
|
@ -114,12 +117,7 @@
|
||||||
</div>
|
</div>
|
||||||
<button type="button" class="btn btn-primary" id="show-game-btn" style="display:none;">Bored waiting?</button>
|
<button type="button" class="btn btn-primary" id="show-game-btn" style="display:none;">Bored waiting?</button>
|
||||||
|
|
||||||
|
|
||||||
<script th:inline="javascript">
|
|
||||||
const elementID = /*[[${name+"-input"}]]*/ '';
|
|
||||||
const filesSelected = /*[[#{filesSelected}]]*/ '';
|
|
||||||
const pdfPrompt = /*[[#{pdfPrompt}]]*/ '';
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script src="js/fileInput.js"></script>
|
<script src="js/fileInput.js"></script>
|
||||||
<link rel="stylesheet" href="css/fileSelect.css">
|
<link rel="stylesheet" href="css/fileSelect.css">
|
||||||
|
|
|
@ -15,8 +15,8 @@
|
||||||
<div class="col-md-9">
|
<div class="col-md-9">
|
||||||
<h2 th:text="#{compare.header}"></h2>
|
<h2 th:text="#{compare.header}"></h2>
|
||||||
|
|
||||||
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
|
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf', remoteCall='false')}"></div>
|
||||||
<div th:replace="~{fragments/common :: fileSelector(name='fileInput2', multiple=false, accept='application/pdf')}"></div>
|
<div th:replace="~{fragments/common :: fileSelector(name='fileInput2', multiple=false, accept='application/pdf', remoteCall='false')}"></div>
|
||||||
<button class="btn btn-primary" onclick="comparePDFs()" th:text="#{compare.submit}"></button>
|
<button class="btn btn-primary" onclick="comparePDFs()" th:text="#{compare.submit}"></button>
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -15,13 +15,109 @@
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<h2 th:text="#{getPdfInfo.header}"></h2>
|
<h2 th:text="#{getPdfInfo.header}"></h2>
|
||||||
<p th:text="#{processTimeWarning}">
|
<p th:text="#{processTimeWarning}">
|
||||||
<form method="post" enctype="multipart/form-data" th:action="@{get-info-on-pdf}">
|
<form id="pdfInfoForm" method="post" enctype="multipart/form-data" th:action="@{get-info-on-pdf}">
|
||||||
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false)}"></div>
|
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, remoteCall='false')}"></div>
|
||||||
<br>
|
<br>
|
||||||
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{getPdfInfo.submit}"></button>
|
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{getPdfInfo.submit}"></button>
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
|
<div class="container mt-5">
|
||||||
|
<!-- Iterate over each main section in the JSON -->
|
||||||
|
<div id="json-content">
|
||||||
|
<!-- JavaScript will populate this section -->
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Button to download the JSON -->
|
||||||
|
<a href="#" id="downloadJson" class="btn btn-primary mt-3">Download JSON</a>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
|
||||||
|
|
||||||
|
// Prevent the form from submitting the traditional way
|
||||||
|
document.getElementById("pdfInfoForm").addEventListener("submit", function(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
// Fetch the formData
|
||||||
|
const formData = new FormData(event.target);
|
||||||
|
|
||||||
|
fetch('get-info-on-pdf', {
|
||||||
|
method: 'POST',
|
||||||
|
body: formData
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
displayJsonData(data); // Display the data
|
||||||
|
setDownloadLink(data); // Set download link
|
||||||
|
})
|
||||||
|
.catch(error => console.error('Error:', error));
|
||||||
|
});
|
||||||
|
|
||||||
|
function displayJsonData(jsonData) {
|
||||||
|
let content = '';
|
||||||
|
for (const key in jsonData) {
|
||||||
|
content += renderJsonSection(key, jsonData[key]);
|
||||||
|
}
|
||||||
|
document.getElementById('json-content').innerHTML = content;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setDownloadLink(jsonData) {
|
||||||
|
const downloadLink = document.getElementById('downloadJson');
|
||||||
|
const dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(jsonData, null, 4));
|
||||||
|
downloadLink.setAttribute("href", dataStr);
|
||||||
|
downloadLink.setAttribute("download", "data.json");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function renderJsonSection(key, value, depth = 0) {
|
||||||
|
let output = `<div class="card mb-3">
|
||||||
|
<div class="card-header" id="${key}-heading-${depth}">
|
||||||
|
<h5 class="mb-0">`;
|
||||||
|
|
||||||
|
// Check if the value is an object and has children
|
||||||
|
if (value && typeof value === 'object' && Object.keys(value).length) {
|
||||||
|
output += `
|
||||||
|
<button class="btn btn-link" type="button" data-toggle="collapse" data-target="#${key}-content-${depth}" aria-expanded="true" aria-controls="${key}-content-${depth}">
|
||||||
|
${key}
|
||||||
|
</button>`;
|
||||||
|
} else {
|
||||||
|
// Display both key and value for simple entries
|
||||||
|
output += `${key}: ${value}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
output += `
|
||||||
|
</h5>
|
||||||
|
</div>
|
||||||
|
<div id="${key}-content-${depth}" class="collapse" aria-labelledby="${key}-heading-${depth}">`;
|
||||||
|
|
||||||
|
// Check if the value is a nested object
|
||||||
|
if (typeof value === 'object' && !Array.isArray(value)) {
|
||||||
|
output += '<div class="card-body">';
|
||||||
|
for (const subKey in value) {
|
||||||
|
output += renderJsonSection(subKey, value[subKey], depth + 1);
|
||||||
|
}
|
||||||
|
output += '</div>';
|
||||||
|
} else if (typeof value === 'object' && Array.isArray(value) && value.length) { // Array values
|
||||||
|
value.forEach((val, index) => {
|
||||||
|
output += renderJsonSection(index, val, depth + 1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
output += '</div></div>';
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Reference in a new issue