Merge branch 'main' into proprietary_module

This commit is contained in:
Dario Ghunney Ware 2025-06-03 18:04:00 +01:00 committed by GitHub
commit 85e92c65bc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 659 additions and 140 deletions

1
.gitignore vendored
View File

@ -13,6 +13,7 @@ local.properties
.recommenders .recommenders
.classpath .classpath
.project .project
*.local.json
version.properties version.properties
#### Stirling-PDF Files ### #### Stirling-PDF Files ###

View File

@ -128,13 +128,13 @@ Stirling-PDF currently supports 40 languages!
| English (English) (en_GB) | ![100%](https://geps.dev/progress/100) | | English (English) (en_GB) | ![100%](https://geps.dev/progress/100) |
| English (US) (en_US) | ![100%](https://geps.dev/progress/100) | | English (US) (en_US) | ![100%](https://geps.dev/progress/100) |
| French (Français) (fr_FR) | ![82%](https://geps.dev/progress/82) | | French (Français) (fr_FR) | ![82%](https://geps.dev/progress/82) |
| German (Deutsch) (de_DE) | ![89%](https://geps.dev/progress/89) | | German (Deutsch) (de_DE) | ![99%](https://geps.dev/progress/99) |
| Greek (Ελληνικά) (el_GR) | ![80%](https://geps.dev/progress/80) | | Greek (Ελληνικά) (el_GR) | ![80%](https://geps.dev/progress/80) |
| Hindi (हिंदी) (hi_IN) | ![80%](https://geps.dev/progress/80) | | Hindi (हिंदी) (hi_IN) | ![80%](https://geps.dev/progress/80) |
| Hungarian (Magyar) (hu_HU) | ![87%](https://geps.dev/progress/87) | | Hungarian (Magyar) (hu_HU) | ![87%](https://geps.dev/progress/87) |
| Indonesian (Bahasa Indonesia) (id_ID) | ![74%](https://geps.dev/progress/74) | | Indonesian (Bahasa Indonesia) (id_ID) | ![74%](https://geps.dev/progress/74) |
| Irish (Gaeilge) (ga_IE) | ![81%](https://geps.dev/progress/81) | | Irish (Gaeilge) (ga_IE) | ![81%](https://geps.dev/progress/81) |
| Italian (Italiano) (it_IT) | ![95%](https://geps.dev/progress/95) | | Italian (Italiano) (it_IT) | ![98%](https://geps.dev/progress/98) |
| Japanese (日本語) (ja_JP) | ![82%](https://geps.dev/progress/82) | | Japanese (日本語) (ja_JP) | ![82%](https://geps.dev/progress/82) |
| Korean (한국어) (ko_KR) | ![80%](https://geps.dev/progress/80) | | Korean (한국어) (ko_KR) | ![80%](https://geps.dev/progress/80) |
| Norwegian (Norsk) (no_NB) | ![78%](https://geps.dev/progress/78) | | Norwegian (Norsk) (no_NB) | ![78%](https://geps.dev/progress/78) |

View File

@ -131,7 +131,7 @@ public class SplitPdfByChaptersController {
Integer bookmarkLevel = Integer bookmarkLevel =
request.getBookmarkLevel(); // levels start from 0 (top most bookmarks) request.getBookmarkLevel(); // levels start from 0 (top most bookmarks)
if (bookmarkLevel < 0) { if (bookmarkLevel < 0) {
return ResponseEntity.badRequest().body("Invalid bookmark level".getBytes()); throw new IllegalArgumentException("Invalid bookmark level");
} }
sourceDocument = pdfDocumentFactory.load(file); sourceDocument = pdfDocumentFactory.load(file);
@ -139,7 +139,7 @@ public class SplitPdfByChaptersController {
if (outline == null) { if (outline == null) {
log.warn("No outline found for {}", file.getOriginalFilename()); log.warn("No outline found for {}", file.getOriginalFilename());
return ResponseEntity.badRequest().body("No outline found".getBytes()); throw new IllegalArgumentException("No outline found");
} }
List<Bookmark> bookmarks = new ArrayList<>(); List<Bookmark> bookmarks = new ArrayList<>();
try { try {

View File

@ -91,6 +91,59 @@ public class GetInfoOnPDF {
} }
} }
/**
* Generates structured summary data about the PDF highlighting its unique characteristics such
* as encryption status, permission restrictions, and standards compliance.
*
* @param document The PDF document to analyze
* @return An ObjectNode containing structured summary data
*/
private ObjectNode generatePDFSummaryData(PDDocument document) {
ObjectNode summaryData = objectMapper.createObjectNode();
// Check if encrypted
if (document.isEncrypted()) {
summaryData.put("encrypted", true);
}
// Check permissions
AccessPermission ap = document.getCurrentAccessPermission();
ArrayNode restrictedPermissions = objectMapper.createArrayNode();
if (!ap.canAssembleDocument()) restrictedPermissions.add("document assembly");
if (!ap.canExtractContent()) restrictedPermissions.add("content extraction");
if (!ap.canExtractForAccessibility()) restrictedPermissions.add("accessibility extraction");
if (!ap.canFillInForm()) restrictedPermissions.add("form filling");
if (!ap.canModify()) restrictedPermissions.add("modification");
if (!ap.canModifyAnnotations()) restrictedPermissions.add("annotation modification");
if (!ap.canPrint()) restrictedPermissions.add("printing");
if (restrictedPermissions.size() > 0) {
summaryData.set("restrictedPermissions", restrictedPermissions);
summaryData.put("restrictedPermissionsCount", restrictedPermissions.size());
}
// Check standard compliance
if (checkForStandard(document, "PDF/A")) {
summaryData.put("standardCompliance", "PDF/A");
summaryData.put("standardPurpose", "long-term archiving");
} else if (checkForStandard(document, "PDF/X")) {
summaryData.put("standardCompliance", "PDF/X");
summaryData.put("standardPurpose", "graphic exchange");
} else if (checkForStandard(document, "PDF/UA")) {
summaryData.put("standardCompliance", "PDF/UA");
summaryData.put("standardPurpose", "universal accessibility");
} else if (checkForStandard(document, "PDF/E")) {
summaryData.put("standardCompliance", "PDF/E");
summaryData.put("standardPurpose", "engineering workflows");
} else if (checkForStandard(document, "PDF/VT")) {
summaryData.put("standardCompliance", "PDF/VT");
summaryData.put("standardPurpose", "variable and transactional printing");
}
return summaryData;
}
public static boolean checkForStandard(PDDocument document, String standardKeyword) { public static boolean checkForStandard(PDDocument document, String standardKeyword) {
// Check XMP Metadata // Check XMP Metadata
try { try {
@ -191,6 +244,12 @@ public class GetInfoOnPDF {
} }
jsonOutput.set("FormFields", formFieldsNode); jsonOutput.set("FormFields", formFieldsNode);
// Generate structured summary data about PDF characteristics
ObjectNode summaryData = generatePDFSummaryData(pdfBoxDoc);
if (summaryData != null && summaryData.size() > 0) {
jsonOutput.set("SummaryData", summaryData);
}
// embeed files TODO size // embeed files TODO size
if (catalog.getNames() != null) { if (catalog.getNames() != null) {
PDEmbeddedFilesNameTreeNode efTree = catalog.getNames().getEmbeddedFiles(); PDEmbeddedFilesNameTreeNode efTree = catalog.getNames().getEmbeddedFiles();

View File

@ -6,133 +6,133 @@ language.direction=ltr
# Language names for reuse throughout the application # Language names for reuse throughout the application
lang.afr=Afrikaans lang.afr=Afrikaans
lang.amh=Amharic lang.amh=Amharisch
lang.ara=Arabic lang.ara=Arabisch
lang.asm=Assamese lang.asm=Assamesisch
lang.aze=Azerbaijani lang.aze=Aserbaidschanisch
lang.aze_cyrl=Azerbaijani (Cyrillic) lang.aze_cyrl=Aserbaidschanisch (Kyrillisch)
lang.bel=Belarusian lang.bel=Weißrussisch
lang.ben=Bengali lang.ben=Bengalisch
lang.bod=Tibetan lang.bod=Tibetisch
lang.bos=Bosnian lang.bos=Bosnisch
lang.bre=Breton lang.bre=Bretonisch
lang.bul=Bulgarian lang.bul=Bulgarisch
lang.cat=Catalan lang.cat=Katalanisch
lang.ceb=Cebuano lang.ceb=Cebuano
lang.ces=Czech lang.ces=Tschechisch
lang.chi_sim=Chinese (Simplified) lang.chi_sim=Chinesisch (vereinfacht)
lang.chi_sim_vert=Chinese (Simplified, Vertical) lang.chi_sim_vert=Chinesisch (vereinfacht, vertikal)
lang.chi_tra=Chinese (Traditional) lang.chi_tra=Chinesisch (traditionell)
lang.chi_tra_vert=Chinese (Traditional, Vertical) lang.chi_tra_vert=Chinesisch (traditionell, vertikal)
lang.chr=Cherokee lang.chr=Cherokee
lang.cos=Corsican lang.cos=Korsisch
lang.cym=Welsh lang.cym=Walisisch
lang.dan=Danish lang.dan=Dänisch
lang.dan_frak=Danish (Fraktur) lang.dan_frak=Dänisch (Fraktur)
lang.deu=German lang.deu=Deutsch
lang.deu_frak=German (Fraktur) lang.deu_frak=Deutsch (Fraktur)
lang.div=Divehi lang.div=Divehi
lang.dzo=Dzongkha lang.dzo=Dzongkha
lang.ell=Greek lang.ell=Griechisch
lang.eng=English lang.eng=Englisch
lang.enm=English, Middle (1100-1500) lang.enm=Englisch, Mittelenglisch (1100-1500)
lang.epo=Esperanto lang.epo=Esperanto
lang.equ=Math / equation detection module lang.equ=Mathe-/Gleichungserkennungsmodul
lang.est=Estonian lang.est=Estnisch
lang.eus=Basque lang.eus=Baskisch
lang.fao=Faroese lang.fao=Färöisch
lang.fas=Persian lang.fas=Persisch
lang.fil=Filipino lang.fil=Philippinisch
lang.fin=Finnish lang.fin=Finnisch
lang.fra=French lang.fra=Französisch
lang.frk=Frankish lang.frk=Fränkisch
lang.frm=French, Middle (ca.1400-1600) lang.frm=Französisch, Mittelfranzösisch (ca. 1400-1600)
lang.fry=Western Frisian lang.fry=Westfriesisch
lang.gla=Scottish Gaelic lang.gla=Schottisch-Gälisch
lang.gle=Irish lang.gle=Irisch
lang.glg=Galician lang.glg=Galizisch
lang.grc=Ancient Greek lang.grc=Altgriechisch
lang.guj=Gujarati lang.guj=Gujarati
lang.hat=Haitian, Haitian Creole lang.hat=Haitianisch, haitianisches Kreol
lang.heb=Hebrew lang.heb=Hebräisch
lang.hin=Hindi lang.hin=Hindi
lang.hrv=Croatian lang.hrv=Kroatisch
lang.hun=Hungarian lang.hun=Ungarisch
lang.hye=Armenian lang.hye=Armenisch
lang.iku=Inuktitut lang.iku=Inuktitut
lang.ind=Indonesian lang.ind=Indonesisch
lang.isl=Icelandic lang.isl=Isländisch
lang.ita=Italian lang.ita=Italienisch
lang.ita_old=Italian (Old) lang.ita_old=Italienisch (Alt)
lang.jav=Javanese lang.jav=Javanesisch
lang.jpn=Japanese lang.jpn=Japanisch
lang.jpn_vert=Japanese (Vertical) lang.jpn_vert=Japanisch (vertikal)
lang.kan=Kannada lang.kan=Kannada
lang.kat=Georgian lang.kat=Georgisch
lang.kat_old=Georgian (Old) lang.kat_old=Georgisch (Alt)
lang.kaz=Kazakh lang.kaz=Kasachisch
lang.khm=Central Khmer lang.khm=Zentral Khmer
lang.kir=Kirghiz, Kyrgyz lang.kir=Kirgisisch
lang.kmr=Northern Kurdish lang.kmr=Nordkurdisch
lang.kor=Korean lang.kor=Koreanisch
lang.kor_vert=Korean (Vertical) lang.kor_vert=Koreanisch (vertikal)
lang.lao=Lao lang.lao=Laotisch
lang.lat=Latin lang.lat=Latein
lang.lav=Latvian lang.lav=Lettisch
lang.lit=Lithuanian lang.lit=Litauisch
lang.ltz=Luxembourgish lang.ltz=Luxemburgisch
lang.mal=Malayalam lang.mal=Malayalam
lang.mar=Marathi lang.mar=Marathi
lang.mkd=Macedonian lang.mkd=Mazedonisch
lang.mlt=Maltese lang.mlt=Maltesisch
lang.mon=Mongolian lang.mon=Mongolisch
lang.mri=Maori lang.mri=Maori
lang.msa=Malay lang.msa=Malaiisch
lang.mya=Burmese lang.mya=Burmesisch
lang.nep=Nepali lang.nep=Nepali
lang.nld=Dutch; Flemish lang.nld=Niederländisch; Flämisch
lang.nor=Norwegian lang.nor=Norwegisch
lang.oci=Occitan (post 1500) lang.oci=Okzitanisch (nach 1500)
lang.ori=Oriya lang.ori=Oriya
lang.osd=Orientation and script detection module lang.osd=Orientierungs- und Skripterkennungsmodul
lang.pan=Panjabi, Punjabi lang.pan=Panjabi, Punjabi
lang.pol=Polish lang.pol=Polnisch
lang.por=Portuguese lang.por=Portugiesisch
lang.pus=Pushto, Pashto lang.pus=Puschtu, Paschtu
lang.que=Quechua lang.que=Quechua
lang.ron=Romanian, Moldavian, Moldovan lang.ron=Rumänisch, Moldauisch, Moldauisch
lang.rus=Russian lang.rus=Russisch
lang.san=Sanskrit lang.san=Sanskrit
lang.sin=Sinhala, Sinhalese lang.sin=Singhalesisch
lang.slk=Slovak lang.slk=Slowakisch
lang.slk_frak=Slovak (Fraktur) lang.slk_frak=Slowakisch (Fraktur)
lang.slv=Slovenian lang.slv=Slowenisch
lang.snd=Sindhi lang.snd=Sindhi
lang.spa=Spanish lang.spa=Spanisch
lang.spa_old=Spanish (Old) lang.spa_old=Spanisch (Alt)
lang.sqi=Albanian lang.sqi=Albanisch
lang.srp=Serbian lang.srp=Serbisch
lang.srp_latn=Serbian (Latin) lang.srp_latn=Serbisch (Lateinisch)
lang.sun=Sundanese lang.sun=Sundanesisch
lang.swa=Swahili lang.swa=Suaheli
lang.swe=Swedish lang.swe=Schwedisch
lang.syr=Syriac lang.syr=Syrisch
lang.tam=Tamil lang.tam=Tamil
lang.tat=Tatar lang.tat=Tatarisch
lang.tel=Telugu lang.tel=Telugu
lang.tgk=Tajik lang.tgk=Tadschikisch
lang.tgl=Tagalog lang.tgl=Tagalog
lang.tha=Thai lang.tha=Thailändisch
lang.tir=Tigrinya lang.tir=Tigrinya
lang.ton=Tonga (Tonga Islands) lang.ton=Tonga (Tonga-Inseln)
lang.tur=Turkish lang.tur=Türkisch
lang.uig=Uighur, Uyghur lang.uig=Uigurisch
lang.ukr=Ukrainian lang.ukr=Ukrainisch
lang.urd=Urdu lang.urd=Urdu
lang.uzb=Uzbek lang.uzb=Usbekisch
lang.uzb_cyrl=Uzbek (Cyrillic) lang.uzb_cyrl=Usbekisch (Kyrillisch)
lang.vie=Vietnamese lang.vie=Vietnamesisch
lang.yid=Yiddish lang.yid=Jiddisch
lang.yor=Yoruba lang.yor=Yoruba
addPageNumbers.fontSize=Schriftgröße addPageNumbers.fontSize=Schriftgröße
@ -1570,37 +1570,37 @@ cookieBanner.preferencesModal.analytics.title=Analyse
cookieBanner.preferencesModal.analytics.description=Diese Cookies helfen uns zu verstehen, wie unsere Tools genutzt werden, damit wir uns darauf konzentrieren können, die Funktionen zu entwickeln, die unserer Community am meisten am Herzen liegen. Seien Sie beruhigt Stirling PDF kann und wird niemals den Inhalt der Dokumente verfolgen, mit denen Sie arbeiten. cookieBanner.preferencesModal.analytics.description=Diese Cookies helfen uns zu verstehen, wie unsere Tools genutzt werden, damit wir uns darauf konzentrieren können, die Funktionen zu entwickeln, die unserer Community am meisten am Herzen liegen. Seien Sie beruhigt Stirling PDF kann und wird niemals den Inhalt der Dokumente verfolgen, mit denen Sie arbeiten.
#fakeScan #fakeScan
fakeScan.title=Fake Scan fakeScan.title=Fake-Scan-PDF
fakeScan.header=Fake Scan fakeScan.header=Fake-Scan-PDF
fakeScan.description=Create a PDF that looks like it was scanned fakeScan.description=Erstellen Sie ein PDF, das so aussieht, als wäre es gescannt worden
fakeScan.selectPDF=Select PDF: fakeScan.selectPDF=Wählen Sie PDF:
fakeScan.quality=Scan Quality fakeScan.quality=Scan-Qualität
fakeScan.quality.low=Low fakeScan.quality.low=Niedrig
fakeScan.quality.medium=Medium fakeScan.quality.medium=Medium
fakeScan.quality.high=High fakeScan.quality.high=Hoch
fakeScan.rotation=Rotation Angle fakeScan.rotation=Rotationswinkel
fakeScan.rotation.none=None fakeScan.rotation.none=Keiner
fakeScan.rotation.slight=Slight fakeScan.rotation.slight=Leicht
fakeScan.rotation.moderate=Moderate fakeScan.rotation.moderate=Mäßig
fakeScan.rotation.severe=Severe fakeScan.rotation.severe=Schwer
fakeScan.submit=Create Fake Scan fakeScan.submit=Erstellen Sie einen Fake-Scan
#home.fakeScan #home.fakeScan
home.fakeScan.title=Fake Scan home.fakeScan.title=Fake-Scan-PDF
home.fakeScan.desc=Create a PDF that looks like it was scanned home.fakeScan.desc=Erstellen Sie ein PDF, das so aussieht, als wäre es gescannt worden
fakeScan.tags=scan,simulate,realistic,convert fakeScan.tags=scannen,simulieren,realistisch,konvertieren,fake,scan,pdf
# FakeScan advanced settings (frontend) # FakeScan advanced settings (frontend)
fakeScan.advancedSettings=Enable Advanced Scan Settings fakeScan.advancedSettings=Aktivieren Sie erweiterte Scaneinstellungen
fakeScan.colorspace=Colorspace fakeScan.colorspace=Farbraum
fakeScan.colorspace.grayscale=Grayscale fakeScan.colorspace.grayscale=Graustufen
fakeScan.colorspace.color=Color fakeScan.colorspace.color=Farbe
fakeScan.border=Border (px) fakeScan.border=Grenze (PX)
fakeScan.rotate=Base Rotation (degrees) fakeScan.rotate=Grundrotation (Grad)
fakeScan.rotateVariance=Rotation Variance (degrees) fakeScan.rotateVariance=Rotationsvarianz (Grad)
fakeScan.brightness=Brightness fakeScan.brightness=Helligkeit
fakeScan.contrast=Contrast fakeScan.contrast=Kontrast
fakeScan.blur=Blur fakeScan.blur=Verwischen
fakeScan.noise=Noise fakeScan.noise=Rauschen
fakeScan.yellowish=Yellowish (simulate old paper) fakeScan.yellowish=Gelblich (simulieren Sie altes Papier)
fakeScan.resolution=Resolution (DPI) fakeScan.resolution=Auflösung (DPI)

View File

@ -807,6 +807,28 @@ getPdfInfo.title=Get Info on PDF
getPdfInfo.header=Get Info on PDF getPdfInfo.header=Get Info on PDF
getPdfInfo.submit=Get Info getPdfInfo.submit=Get Info
getPdfInfo.downloadJson=Download JSON getPdfInfo.downloadJson=Download JSON
getPdfInfo.summary=PDF Summary
getPdfInfo.summary.encrypted=This PDF is encrypted so may face issues with some applications
getPdfInfo.summary.permissions=This PDF has {0} restricted permissions which may limit what you can do with it
getPdfInfo.summary.compliance=This PDF complies with the {0} standard
getPdfInfo.summary.basicInfo=Basic Information
getPdfInfo.summary.docInfo=Document Information
getPdfInfo.summary.encrypted.alert=Encrypted PDF - This document is password protected
getPdfInfo.summary.not.encrypted.alert=Unencrypted PDF - No password protection
getPdfInfo.summary.permissions.alert=Restricted Permissions - {0} actions are not allowed
getPdfInfo.summary.all.permissions.alert=All Permissions Allowed
getPdfInfo.summary.compliance.alert={0} Compliant
getPdfInfo.summary.no.compliance.alert=No Compliance Standards
getPdfInfo.summary.security.section=Security Status
getPdfInfo.section.BasicInfo=Basic Information about the PDF document including file size, page count, and language
getPdfInfo.section.Metadata=Document metadata including title, author, creation date and other document properties
getPdfInfo.section.DocumentInfo=Technical details about the PDF document structure and version
getPdfInfo.section.Compliancy=PDF standards compliance information (PDF/A, PDF/X, etc.)
getPdfInfo.section.Encryption=Security and encryption details of the document
getPdfInfo.section.Permissions=Document permission settings that control what actions can be performed
getPdfInfo.section.Other=Additional document components like bookmarks, layers, and embedded files
getPdfInfo.section.FormFields=Interactive form fields present in the document
getPdfInfo.section.PerPageInfo=Detailed information about each page in the document
#markdown-to-pdf #markdown-to-pdf

View File

@ -11,7 +11,7 @@
<br><br> <br><br>
<div class="container"> <div class="container">
<div class="row justify-content-center"> <div class="row justify-content-center">
<div class="col-md-6 bg-card"> <div class="col-md-7 bg-card">
<div class="tool-header"> <div class="tool-header">
<span class="material-symbols-rounded tool-header-icon other">info</span> <span class="material-symbols-rounded tool-header-icon other">info</span>
<span class="tool-header-text" th:text="#{getPdfInfo.header}"></span> <span class="tool-header-text" th:text="#{getPdfInfo.header}"></span>
@ -22,6 +22,82 @@
<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-0"> <div class="container mt-0">
<!-- PDF Summary section -->
<div id="pdf-summary" class="card mt-3 mb-3" style="display: none;">
<div class="card-header">
<h5 class="mb-0" id="summary-heading">PDF Summary</h5>
</div>
<div class="card-body">
<!-- Quick overview of key details -->
<div class="row">
<div class="col-md-6">
<h6 id="summary-basic-info-heading">Basic Information</h6>
<ul class="list-unstyled">
<li><strong>Pages:</strong> <span id="summary-pages">-</span></li>
<li><strong>File Size:</strong> <span id="summary-size">-</span></li>
<li><strong>PDF Version:</strong> <span id="summary-version">-</span></li>
<li><strong>Language:</strong> <span id="summary-language">-</span></li>
</ul>
</div>
<div class="col-md-6">
<h6 id="summary-doc-info-heading">Document Information</h6>
<ul class="list-unstyled">
<li><strong>Title:</strong> <span id="summary-title">-</span></li>
<li><strong>Author:</strong> <span id="summary-author">-</span></li>
<li><strong>Created:</strong> <span id="summary-created">-</span></li>
<li><strong>Modified:</strong> <span id="summary-modified">-</span></li>
</ul>
</div>
</div>
<!-- Security section -->
<div class="mt-4 mb-3">
<h6 id="summary-security-heading">Security Status</h6>
<div class="row">
<div class="col-md-4">
<div id="encryption-status" class="card mb-2 h-100">
<div class="card-body p-2 d-flex align-items-center">
<span id="encryption-icon" class="me-2"><i class="bi bi-lock"></i></span>
<span id="encryption-text" class="small">Encryption: Unknown</span>
</div>
</div>
</div>
<div class="col-md-4">
<div id="permissions-status" class="card mb-2 h-100">
<div class="card-body p-2 d-flex align-items-center">
<span id="permissions-icon" class="me-2"><i class="bi bi-shield"></i></span>
<span id="permissions-text" class="small">Permissions: Unknown</span>
</div>
</div>
</div>
<div class="col-md-4">
<div id="compliance-status" class="card mb-2 h-100">
<div class="card-body p-2 d-flex align-items-center">
<span id="compliance-icon" class="me-2"><i class="bi bi-check-circle"></i></span>
<span id="compliance-text" class="small">Compliance: Unknown</span>
</div>
</div>
</div>
</div>
</div>
<!-- Detailed alerts -->
<div id="summary-alerts" class="mt-3">
<!-- Will be populated with detailed alerts for encryption, permissions, etc. -->
</div>
<!-- Descriptive note about PDF characteristics -->
<div class="card mt-3">
<div class="card-header">
<h6 class="mb-0">PDF Overview</h6>
</div>
<div class="card-body">
<p id="summary-text" class="mb-0"></p>
</div>
</div>
</div>
</div>
<!-- Iterate over each main section in the JSON --> <!-- Iterate over each main section in the JSON -->
<div id="json-content"> <div id="json-content">
<!-- JavaScript will populate this section --> <!-- JavaScript will populate this section -->
@ -31,21 +107,379 @@
<a href="#" id="downloadJson" class="btn btn-primary mt-3" style="display: none;" th:text="#{getPdfInfo.downloadJson}">Download JSON</a> <a href="#" id="downloadJson" class="btn btn-primary mt-3" style="display: none;" th:text="#{getPdfInfo.downloadJson}">Download JSON</a>
</div> </div>
<script th:src="@{'/js/fetch-utils.js'}"></script> <script th:src="@{'/js/fetch-utils.js'}"></script>
<script> <script th:inline="javascript">
// Pre-load message translations
const getPdfInfoSummary = /*[[#{getPdfInfo.summary}]]*/ "PDF Summary";
const getPdfInfoSummaryEncrypted = /*[[#{getPdfInfo.summary.encrypted}]]*/ "This PDF is encrypted so may face issues with some applications";
const getPdfInfoSummaryPermissions = /*[[#{getPdfInfo.summary.permissions}]]*/ "This PDF has {0} restricted permissions which may limit what you can do with it";
const getPdfInfoSummaryCompliance = /*[[#{getPdfInfo.summary.compliance}]]*/ "This PDF complies with the {0} standard, meaning it is suitable for {1}";
const getPdfInfoSummaryBasicInfo = /*[[#{getPdfInfo.summary.basicInfo}]]*/ "Basic Information";
const getPdfInfoSummaryDocInfo = /*[[#{getPdfInfo.summary.docInfo}]]*/ "Document Information";
const getPdfInfoSummarySecuritySection = /*[[#{getPdfInfo.summary.security.section}]]*/ "Security Status";
const getPdfInfoSummaryEncryptedAlert = /*[[#{getPdfInfo.summary.encrypted.alert}]]*/ "Encrypted PDF - This document is password protected";
const getPdfInfoSummaryNotEncryptedAlert = /*[[#{getPdfInfo.summary.not.encrypted.alert}]]*/ "Unencrypted PDF - No password protection";
const getPdfInfoSummaryPermissionsAlert = /*[[#{getPdfInfo.summary.permissions.alert}]]*/ "Restricted Permissions - {0} actions are not allowed";
const getPdfInfoSummaryAllPermissionsAlert = /*[[#{getPdfInfo.summary.all.permissions.alert}]]*/ "All Permissions Allowed";
const getPdfInfoSummaryComplianceAlert = /*[[#{getPdfInfo.summary.compliance.alert}]]*/ "{0} Compliant";
const getPdfInfoSummaryNoComplianceAlert = /*[[#{getPdfInfo.summary.no.compliance.alert}]]*/ "No Compliance Standards";
// Update the summary headings
document.addEventListener('DOMContentLoaded', function() {
document.getElementById('summary-heading').textContent = getPdfInfoSummary;
document.getElementById('summary-basic-info-heading').textContent = getPdfInfoSummaryBasicInfo;
document.getElementById('summary-doc-info-heading').textContent = getPdfInfoSummaryDocInfo;
document.getElementById('summary-security-heading').textContent = getPdfInfoSummarySecuritySection;
});
// Pre-load section descriptions
const getPdfInfoSectionBasicInfo = /*[[#{getPdfInfo.section.BasicInfo}]]*/ "Basic Information about the PDF document including file size, page count, and language";
const getPdfInfoSectionMetadata = /*[[#{getPdfInfo.section.Metadata}]]*/ "Document metadata including title, author, creation date and other document properties";
const getPdfInfoSectionDocumentInfo = /*[[#{getPdfInfo.section.DocumentInfo}]]*/ "Technical details about the PDF document structure and version";
const getPdfInfoSectionCompliancy = /*[[#{getPdfInfo.section.Compliancy}]]*/ "PDF standards compliance information (PDF/A, PDF/X, etc.)";
const getPdfInfoSectionEncryption = /*[[#{getPdfInfo.section.Encryption}]]*/ "Security and encryption details of the document";
const getPdfInfoSectionPermissions = /*[[#{getPdfInfo.section.Permissions}]]*/ "Document permission settings that control what actions can be performed";
const getPdfInfoSectionOther = /*[[#{getPdfInfo.section.Other}]]*/ "Additional document components like bookmarks, layers, and embedded files";
const getPdfInfoSectionFormFields = /*[[#{getPdfInfo.section.FormFields}]]*/ "Interactive form fields present in the document";
const getPdfInfoSectionPerPageInfo = /*[[#{getPdfInfo.section.PerPageInfo}]]*/ "Detailed information about each page in the document";
document.getElementById("pdfInfoForm").addEventListener("submit", function(event) { document.getElementById("pdfInfoForm").addEventListener("submit", function(event) {
event.preventDefault(); event.preventDefault();
// Clear previous results when submitting a new form
document.getElementById('json-content').innerHTML = '';
document.getElementById('pdf-summary').style.display = 'none';
document.getElementById('downloadJson').style.display = 'none';
const formData = new FormData(event.target); const formData = new FormData(event.target);
fetchWithCsrf('api/v1/security/get-info-on-pdf', { fetchWithCsrf('api/v1/security/get-info-on-pdf', {
method: 'POST', method: 'POST',
body: formData body: formData
}).then(response => response.json()).then(data => { }).then(response => response.json()).then(data => {
// Populate and display the enhanced PDF summary
populateSummarySection(data);
displayJsonData(data); displayJsonData(data);
setDownloadLink(data); setDownloadLink(data);
document.getElementById("downloadJson").style.display = "block"; document.getElementById("downloadJson").style.display = "block";
}).catch(error => console.error('Error:', error)); }).catch(error => console.error('Error:', error));
// Function to reset all summary elements to default state
function resetSummaryElements() {
// Reset basic information fields
document.getElementById('summary-pages').textContent = '-';
document.getElementById('summary-size').textContent = '-';
document.getElementById('summary-version').textContent = '-';
document.getElementById('summary-language').textContent = '-';
// Reset document information fields
document.getElementById('summary-title').textContent = '-';
document.getElementById('summary-author').textContent = '-';
document.getElementById('summary-created').textContent = '-';
document.getElementById('summary-modified').textContent = '-';
// Reset security status cards
const cards = ['encryption-status', 'permissions-status', 'compliance-status'];
cards.forEach(id => {
const card = document.getElementById(id);
// Remove all classes except the base ones
card.className = 'card mb-2 h-100';
});
// Reset status text and icons
document.getElementById('encryption-icon').innerHTML = '<i class="bi bi-lock"></i>';
document.getElementById('encryption-text').textContent = 'Encryption: Unknown';
document.getElementById('permissions-icon').innerHTML = '<i class="bi bi-shield"></i>';
document.getElementById('permissions-text').textContent = 'Permissions: Unknown';
document.getElementById('compliance-icon').innerHTML = '<i class="bi bi-check-circle"></i>';
document.getElementById('compliance-text').textContent = 'Compliance: Unknown';
// Clear alerts container
document.getElementById('summary-alerts').innerHTML = '';
// Reset summary text
document.getElementById('summary-text').innerHTML = '';
}
// Function to populate the enhanced summary section
function populateSummarySection(data) {
// Reset all elements first
resetSummaryElements();
// Get basic information
if (data.BasicInfo) {
document.getElementById('summary-pages').textContent = data.BasicInfo["Number of pages"] || "-";
// Format file size nicely
let fileSize = data.BasicInfo["FileSizeInBytes"];
if (fileSize) {
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
const i = Math.floor(Math.log(fileSize) / Math.log(1024));
fileSize = (fileSize / Math.pow(1024, i)).toFixed(2) + ' ' + sizes[i];
document.getElementById('summary-size').textContent = fileSize;
}
document.getElementById('summary-language').textContent = data.BasicInfo["Language"] || "-";
}
// Get document information
if (data.DocumentInfo) {
document.getElementById('summary-version').textContent = data.DocumentInfo["PDF version"] || "-";
}
// Get metadata
if (data.Metadata) {
document.getElementById('summary-title').textContent = data.Metadata["Title"] || "-";
document.getElementById('summary-author').textContent = data.Metadata["Author"] || "-";
document.getElementById('summary-created').textContent = data.Metadata["CreationDate"] || "-";
document.getElementById('summary-modified').textContent = data.Metadata["ModificationDate"] || "-";
}
// Update security status cards
// Encryption status
const encryptionStatusCard = document.getElementById('encryption-status');
const encryptionIcon = document.getElementById('encryption-icon');
const encryptionText = document.getElementById('encryption-text');
if (data.Encryption && data.Encryption.IsEncrypted) {
encryptionIcon.innerHTML = '<i class="bi bi-lock-fill"></i>';
encryptionText.textContent = getPdfInfoSummaryEncryptedAlert;
} else {
encryptionIcon.innerHTML = '<i class="bi bi-unlock-fill"></i>';
encryptionText.textContent = getPdfInfoSummaryNotEncryptedAlert;
}
// Permissions status
const permissionsStatusCard = document.getElementById('permissions-status');
const permissionsIcon = document.getElementById('permissions-icon');
const permissionsText = document.getElementById('permissions-text');
let restrictedPermissions = [];
if (data.Permissions) {
for (const [permission, state] of Object.entries(data.Permissions)) {
if (state === "Not Allowed") {
restrictedPermissions.push(permission);
}
}
}
if (restrictedPermissions.length > 0) {
permissionsIcon.innerHTML = '<i class="bi bi-shield-lock-fill"></i>';
const formattedAlert = getPdfInfoSummaryPermissionsAlert.replace('{0}', restrictedPermissions.length);
permissionsText.textContent = formattedAlert;
} else {
permissionsIcon.innerHTML = '<i class="bi bi-shield-check"></i>';
permissionsText.textContent = getPdfInfoSummaryAllPermissionsAlert;
}
// Compliance status
const complianceStatusCard = document.getElementById('compliance-status');
const complianceIcon = document.getElementById('compliance-icon');
const complianceText = document.getElementById('compliance-text');
let hasCompliance = false;
let compliantStandards = [];
if (data.Compliancy) {
for (const [standard, compliant] of Object.entries(data.Compliancy)) {
if (compliant === true) {
hasCompliance = true;
const standardName = standard.replace("Is", "").replace("Compliant", "");
compliantStandards.push(standardName);
}
}
}
if (hasCompliance) {
complianceIcon.innerHTML = '<i class="bi bi-check-circle-fill"></i>';
const formattedAlert = getPdfInfoSummaryComplianceAlert.replace('{0}', compliantStandards.join(', '));
complianceText.textContent = formattedAlert;
} else {
complianceIcon.innerHTML = '<i class="bi bi-dash-circle"></i>';
complianceText.textContent = getPdfInfoSummaryNoComplianceAlert;
}
// Create detailed characteristic alerts
const alertsContainer = document.getElementById('summary-alerts');
// Clear previous alerts
alertsContainer.innerHTML = '';
// Create a single comprehensive security details section
let hasSummaryInfo = false;
// Create a consolidated security details card if there are security details worth highlighting
if ((data.Encryption && data.Encryption.IsEncrypted) ||
restrictedPermissions.length > 0 ||
hasCompliance) {
const securityDetailsCard = document.createElement('div');
securityDetailsCard.className = 'card mt-3 mb-3';
const cardHeader = document.createElement('div');
cardHeader.className = 'card-header';
cardHeader.innerHTML = '<h6 class="mb-0">Detailed Security Information</h6>';
securityDetailsCard.appendChild(cardHeader);
const cardBody = document.createElement('div');
cardBody.className = 'card-body';
// Add detailed encryption info
if (data.Encryption && data.Encryption.IsEncrypted) {
const encryptionDiv = document.createElement('div');
encryptionDiv.className = 'mb-3';
encryptionDiv.innerHTML = '<h6>Encryption Details:</h6>';
const encryptionList = document.createElement('ul');
encryptionList.className = 'list-unstyled';
if (data.Encryption.EncryptionAlgorithm) {
encryptionList.innerHTML += `<li><strong>Algorithm:</strong> ${data.Encryption.EncryptionAlgorithm}</li>`;
}
if (data.Encryption.KeyLength) {
encryptionList.innerHTML += `<li><strong>Key Length:</strong> ${data.Encryption.KeyLength} bits</li>`;
}
encryptionDiv.appendChild(encryptionList);
cardBody.appendChild(encryptionDiv);
hasSummaryInfo = true;
}
// Add detailed permissions info
if (restrictedPermissions.length > 0) {
const permissionsDiv = document.createElement('div');
permissionsDiv.className = 'mb-3';
permissionsDiv.innerHTML = '<h6>Restricted Permissions:</h6>';
const permissionsList = document.createElement('ul');
restrictedPermissions.forEach(perm => {
permissionsList.innerHTML += `<li>${perm}</li>`;
});
permissionsDiv.appendChild(permissionsList);
cardBody.appendChild(permissionsDiv);
hasSummaryInfo = true;
}
// Add detailed compliance info
if (hasCompliance) {
const complianceDiv = document.createElement('div');
complianceDiv.className = 'mb-3';
complianceDiv.innerHTML = '<h6>Standards Compliance:</h6>';
const complianceList = document.createElement('ul');
complianceList.className = 'list-unstyled';
compliantStandards.forEach(standard => {
let standardDescription = '';
// Add brief descriptions for standards
if (standard === "PDF/A") {
standardDescription = 'ISO standard for long-term document archiving';
} else if (standard === "PDF/X") {
standardDescription = 'ISO standard for printing and graphic arts exchange';
} else if (standard === "PDF/UA") {
standardDescription = 'ISO standard for universal accessibility';
} else if (standard === "PDF/E") {
standardDescription = 'ISO standard for engineering documents';
} else if (standard === "PDF/VT") {
standardDescription = 'ISO standard for variable and transactional printing';
}
complianceList.innerHTML += `<li><strong>${standard}:</strong> ${standardDescription}</li>`;
});
complianceDiv.appendChild(complianceList);
cardBody.appendChild(complianceDiv);
hasSummaryInfo = true;
}
securityDetailsCard.appendChild(cardBody);
if (hasSummaryInfo) {
alertsContainer.appendChild(securityDetailsCard);
}
}
// Generate a general document summary
const summaryTextElement = document.getElementById('summary-text');
// Create a general summary for the document
let generalSummary = `This is a ${data.BasicInfo["Number of pages"] || "multi"}-page PDF`;
if (data.Metadata && data.Metadata["Title"]) {
generalSummary += ` titled "${data.Metadata["Title"]}"`;
}
if (data.Metadata && data.Metadata["Author"]) {
generalSummary += ` created by ${data.Metadata["Author"]}`;
}
if (data.DocumentInfo && data.DocumentInfo["PDF version"]) {
generalSummary += ` (PDF version ${data.DocumentInfo["PDF version"]})`;
}
// Add security information to the general summary if relevant
if (data.Encryption && data.Encryption.IsEncrypted) {
generalSummary += '. The document is password protected';
if (data.Encryption.EncryptionAlgorithm) {
generalSummary += ` using ${data.Encryption.EncryptionAlgorithm}`;
if (data.Encryption.KeyLength) {
generalSummary += ` (${data.Encryption.KeyLength} bit)`;
}
}
}
if (restrictedPermissions.length > 0) {
generalSummary += `. It has ${restrictedPermissions.length} restricted permissions`;
}
// Add compliance standards if available
if (hasCompliance && compliantStandards.length > 0) {
generalSummary += `. This document complies with the ${compliantStandards.join(', ')} PDF standard${compliantStandards.length > 1 ? 's' : ''}`;
}
generalSummary += '.';
// Remove SummaryData from JSON to avoid duplication
if (data.SummaryData) {
delete data.SummaryData;
}
summaryTextElement.innerHTML = generalSummary;
// Display the summary section
document.getElementById('pdf-summary').style.display = 'block';
}
function generateSummaryFromData(summaryData) {
let summary = [];
// Handle encryption information
if (summaryData.encrypted) {
summary.push(getPdfInfoSummaryEncrypted);
}
// Handle permissions information
if (summaryData.restrictedPermissions && summaryData.restrictedPermissions.length > 0) {
const formattedPermissionsText = getPdfInfoSummaryPermissions.replace('{0}', summaryData.restrictedPermissionsCount);
summary.push(formattedPermissionsText);
}
// Handle standard compliance information
if (summaryData.standardCompliance) {
const formattedComplianceText = getPdfInfoSummaryCompliance
.replace('{0}', summaryData.standardCompliance);
summary.push(formattedComplianceText);
}
return summary.join(' ');
}
}); });
function displayJsonData(jsonData) { function displayJsonData(jsonData) {
@ -77,8 +511,9 @@
header.className = 'card-header'; header.className = 'card-header';
header.id = `${safeKey}-heading-${depth}`; header.id = `${safeKey}-heading-${depth}`;
const h5Elem = document.createElement('h5'); const h5Elem = document.createElement('h5');
h5Elem.className = 'mb-0'; h5Elem.className = 'mb-0 d-flex align-items-center';
// Create the main content (button or text)
if (key === 'XMPMetadata' && typeof value === "string") { if (key === 'XMPMetadata' && typeof value === "string") {
const buttonElem = createButtonElement(key, safeKey, depth); const buttonElem = createButtonElement(key, safeKey, depth);
h5Elem.appendChild(buttonElem); h5Elem.appendChild(buttonElem);
@ -95,6 +530,8 @@
h5Elem.textContent = `${key}: ${String(value)}`; h5Elem.textContent = `${key}: ${String(value)}`;
} }
// Info buttons removed as requested
header.appendChild(h5Elem); header.appendChild(h5Elem);
card.appendChild(header); card.appendChild(header);