diff --git a/src/main/java/stirling/software/SPDF/config/security/UserAuthenticationFilter.java b/src/main/java/stirling/software/SPDF/config/security/UserAuthenticationFilter.java index 61b209de..47423eb6 100644 --- a/src/main/java/stirling/software/SPDF/config/security/UserAuthenticationFilter.java +++ b/src/main/java/stirling/software/SPDF/config/security/UserAuthenticationFilter.java @@ -115,4 +115,4 @@ public class UserAuthenticationFilter extends OncePerRequestFilter { return false; } -} \ No newline at end of file +} diff --git a/src/main/java/stirling/software/SPDF/controller/api/misc/BlankPageController.java b/src/main/java/stirling/software/SPDF/controller/api/misc/BlankPageController.java index 8a17e3de..21e5987b 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/misc/BlankPageController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/misc/BlankPageController.java @@ -33,7 +33,6 @@ import io.swagger.v3.oas.annotations.tags.Tag; import stirling.software.SPDF.model.api.misc.RemoveBlankPagesRequest; import stirling.software.SPDF.utils.PdfUtils; import stirling.software.SPDF.utils.ProcessExecutor; -import stirling.software.SPDF.utils.ProcessExecutor.ProcessExecutorResult; import stirling.software.SPDF.utils.WebResponseUtils; @RestController @@ -85,7 +84,7 @@ public class BlankPageController { List command = new ArrayList<>( Arrays.asList( - "python3", + "python", System.getProperty("user.dir") + "/scripts/detect-blank-pages.py", tempFile.toString(), @@ -94,18 +93,25 @@ public class BlankPageController { "--white_percent", String.valueOf(whitePercent))); + Boolean blank = false; // Run CLI command - ProcessExecutorResult returnCode = - ProcessExecutor.getInstance(ProcessExecutor.Processes.PYTHON_OPENCV) - .runCommandWithOutputHandling(command); + try { + ProcessExecutor.getInstance(ProcessExecutor.Processes.PYTHON_OPENCV) + .runCommandWithOutputHandling(command); + } catch (IOException e) { + // From detect-blank-pages.py + // Return code 1: The image is considered blank. + // Return code 0: The image is not considered blank. + // Since the process returned with a failure code, it should be blank. + blank = true; + } - // does contain data - if (returnCode.getRc() == 0) { + if (blank) { + System.out.println("Skipping, Image was blank for page #" + pageIndex); + } else { System.out.println( "page " + pageIndex + " has image which is not blank"); pagesToKeepIndex.add(pageIndex); - } else { - System.out.println("Skipping, Image was blank for page #" + pageIndex); } } } diff --git a/src/main/resources/messages_de_DE.properties b/src/main/resources/messages_de_DE.properties index cac32d58..ce6e25ef 100644 --- a/src/main/resources/messages_de_DE.properties +++ b/src/main/resources/messages_de_DE.properties @@ -1,4 +1,4 @@ -########### +########### # Generic # ########### # the direction that the language is written (ltr=left to right, rtl = right to left) @@ -342,22 +342,22 @@ HTMLToPDF.tags=markup,web-content,transformation,convert home.MarkdownToPDF.title=Markdown zu PDF home.MarkdownToPDF.desc=Konvertiert jede Markdown-Datei zu PDF -MarkdownToPDF.tags=markup,web-content,transformation,convert +MarkdownToPDF.tags=markup,web-content,transformation,konvertieren home.getPdfInfo.title=Alle Informationen anzeigen home.getPdfInfo.desc=Erfasst alle möglichen Informationen in einer PDF -getPdfInfo.tags=infomation,data,stats,statistics +getPdfInfo.tags=infomation,daten,statistik home.extractPage.title=Seite(n) extrahieren home.extractPage.desc=Extrahiert ausgewählte Seiten aus einer PDF -extractPage.tags=extract +extractPage.tags=extrahieren home.PdfToSinglePage.title=PDF zu einer Seite zusammenfassen home.PdfToSinglePage.desc=Fügt alle PDF-Seiten zu einer einzigen großen Seite zusammen -PdfToSinglePage.tags=single page +PdfToSinglePage.tags=einzelseite home.showJS.title=Javascript anzeigen @@ -365,26 +365,26 @@ home.showJS.desc=Alle Javascript Funktionen in einer PDF anzeigen showJS.tags=JS home.autoRedact.title=Automatisch zensieren/schwärzen -home.autoRedact.desc=Automatisches zensierten (Schwärzen) von Text in einer PDF-Datei basierend auf dem eingegebenen Text -showJS.tags=JS +home.autoRedact.desc=Automatisches Zensieren (Schwärzen) von Text in einer PDF-Datei basierend auf dem eingegebenen Text +showJS.tags=zensieren,schwärzen home.tableExtraxt.title=Tabelle extrahieren home.tableExtraxt.desc=Tabelle aus PDF in CSV extrahieren tableExtraxt.tags=CSV -home.autoSizeSplitPDF.title=Auto Split by Size/Count -home.autoSizeSplitPDF.desc=Split a single PDF into multiple documents based on size, page count, or document count -autoSizeSplitPDF.tags=pdf,split,document,organization +home.autoSizeSplitPDF.title=Teilen nach Größe/Anzahl +home.autoSizeSplitPDF.desc=Teilen Sie ein einzelnes PDF basierend auf Größe, Seitenanzahl oder Dokumentanzahl in mehrere Dokumente auf +autoSizeSplitPDF.tags=pdf,teilen,dokument,organisation -home.overlay-pdfs.title=Overlay PDFs -home.overlay-pdfs.desc=Overlays PDFs on-top of another PDF -overlay-pdfs.tags=Overlay +home.overlay-pdfs.title=PDF mit Overlay versehen +home.overlay-pdfs.desc=Überlagert eine PDF über eine andere PDF +overlay-pdfs.tags=overlay,überlagern -home.split-by-sections.title=Split PDF by Sections -home.split-by-sections.desc=Divide each page of a PDF into smaller horizontal and vertical sections -split-by-sections.tags=Section Split, Divide, Customize +home.split-by-sections.title=PDF in Abschnitte teilen +home.split-by-sections.desc=Teilen Sie jede Seite einer PDF-Datei in kleinere horizontale und vertikale Abschnitte auf +split-by-sections.tags=abschnitte,teilen,bearbeiten home.AddStampRequest.title=Add Stamp to PDF home.AddStampRequest.desc=Add text or add image stamps at set locations @@ -415,7 +415,7 @@ autoRedact.useRegexLabel=Regex verwenden autoRedact.wholeWordSearchLabel=Ganzes Wort suchen autoRedact.customPaddingLabel=Benutzerdefinierte Extra-Padding autoRedact.convertPDFToImageLabel=PDF in PDF-Bild konvertieren (zum Entfernen von Text hinter dem Kasten) -autoRedact.submitButton=zensieren +autoRedact.submitButton=Zensieren #showJS @@ -609,9 +609,9 @@ removeBlanks.submit=Leere Seiten entfernen #removeAnnotations -removeAnnotations.title=Remove Annotations -removeAnnotations.header=Remove Annotations -removeAnnotations.submit=Remove +removeAnnotations.title=Kommentare entfernen +removeAnnotations.header=Kommentare entfernen +removeAnnotations.submit=Entfernen #compare @@ -628,7 +628,7 @@ sign.header=PDFs signieren sign.upload=Bild hochladen sign.draw=Signatur zeichnen sign.text=Texteingabe -sign.clear=Klar +sign.clear=Leeren sign.add=Signieren @@ -920,44 +920,44 @@ PDFToXML.submit=Konvertieren #PDFToCSV PDFToCSV.title=PDF zu CSV PDFToCSV.header=PDF zu CSV -PDFToCSV.prompt=Choose page to extract table -PDFToCSV.submit=Extrakt +PDFToCSV.prompt=Seite mit der zu extrahierenden Tabelle wählen +PDFToCSV.submit=Extrahieren #split-by-size-or-count -split-by-size-or-count.header=Split PDF by Size or Count -split-by-size-or-count.type.label=Select Split Type -split-by-size-or-count.type.size=By Size -split-by-size-or-count.type.pageCount=By Page Count -split-by-size-or-count.type.docCount=By Document Count -split-by-size-or-count.value.label=Enter Value -split-by-size-or-count.value.placeholder=Enter size (e.g., 2MB or 3KB) or count (e.g., 5) -split-by-size-or-count.submit=Submit +split-by-size-or-count.header=PDF nach Größe oder Anzahl teilen +split-by-size-or-count.type.label=Teil-Modus wählen +split-by-size-or-count.type.size=Nach Größe +split-by-size-or-count.type.pageCount=Nach Anzahl Seiten +split-by-size-or-count.type.docCount=Nach Anzahl Dokumenten +split-by-size-or-count.value.label=Wert eingeben +split-by-size-or-count.value.placeholder=Größe eingeben (z. B.: 2MB oder 3KB) oder Anzahl (z. B.: 5) +split-by-size-or-count.submit=Erstellen #overlay-pdfs -overlay-pdfs.header=Overlay PDF Files -overlay-pdfs.baseFile.label=Select Base PDF File -overlay-pdfs.overlayFiles.label=Select Overlay PDF Files -overlay-pdfs.mode.label=Select Overlay Mode -overlay-pdfs.mode.sequential=Sequential Overlay -overlay-pdfs.mode.interleaved=Interleaved Overlay -overlay-pdfs.mode.fixedRepeat=Fixed Repeat Overlay -overlay-pdfs.counts.label=Overlay Counts (for Fixed Repeat Mode) -overlay-pdfs.counts.placeholder=Enter comma-separated counts (e.g., 2,3,1) -overlay-pdfs.position.label=Select Overlay Position -overlay-pdfs.position.foreground=Foreground -overlay-pdfs.position.background=Background -overlay-pdfs.submit=Submit +overlay-pdfs.header=PDF mit Overlay versehen +overlay-pdfs.baseFile.label=Basis-PDF-Datei auswählen +overlay-pdfs.overlayFiles.label=Overlay-PDF-Datei auswählen +overlay-pdfs.mode.label=Overlay-Modus auswählen +overlay-pdfs.mode.sequential=Sequentielles Overlay +overlay-pdfs.mode.interleaved=Verschachteltes Overlay +overlay-pdfs.mode.fixedRepeat=Feste-Wiederholung Overlay +overlay-pdfs.counts.label=Overlay Anzahl (für Feste-Wiederholung) +overlay-pdfs.counts.placeholder=Komma-separierte Anzahl eingeben (z. B.: 2,3,1) +overlay-pdfs.position.label=Overlay Position auswählen +overlay-pdfs.position.foreground=Vordergrund +overlay-pdfs.position.background=Hintergrund +overlay-pdfs.submit=Erstellen #split-by-sections -split-by-sections.title=Split PDF by Sections -split-by-sections.header=Split PDF into Sections -split-by-sections.horizontal.label=Horizontal Divisions -split-by-sections.vertical.label=Vertical Divisions -split-by-sections.horizontal.placeholder=Enter number of horizontal divisions -split-by-sections.vertical.placeholder=Enter number of vertical divisions -split-by-sections.submit=Split PDF +split-by-sections.title=PDF in Abschnitte teilen +split-by-sections.header=PDF in Abschnitte teilen +split-by-sections.horizontal.label=Horizontale Teiler +split-by-sections.vertical.label=Vertikale Teiler +split-by-sections.horizontal.placeholder=Anzahl horizontaler Teiler eingeben +split-by-sections.vertical.placeholder=Anzahl vertikaler Teiler eingeben +split-by-sections.submit=PDF teilen #licenses @@ -968,4 +968,3 @@ licenses.module=Module licenses.version=Version licenses.license=License - diff --git a/src/main/resources/static/3rdPartyLicenses.json b/src/main/resources/static/3rdPartyLicenses.json index 88263b1d..699be591 100644 --- a/src/main/resources/static/3rdPartyLicenses.json +++ b/src/main/resources/static/3rdPartyLicenses.json @@ -357,22 +357,29 @@ { "moduleName": "org.apache.pdfbox:fontbox", "moduleUrl": "https://pdfbox.apache.org", - "moduleVersion": "2.0.30", - "moduleLicense": "Apache License, Version 2.0", + "moduleVersion": "3.0.1", + "moduleLicense": "Apache-2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt" }, { "moduleName": "org.apache.pdfbox:pdfbox", "moduleUrl": "https://pdfbox.apache.org", - "moduleVersion": "2.0.30", - "moduleLicense": "Apache License, Version 2.0", + "moduleVersion": "3.0.1", + "moduleLicense": "Apache-2.0", + "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt" + }, + { + "moduleName": "org.apache.pdfbox:pdfbox-io", + "moduleUrl": "https://pdfbox.apache.org", + "moduleVersion": "3.0.1", + "moduleLicense": "Apache-2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt" }, { "moduleName": "org.apache.pdfbox:xmpbox", "moduleUrl": "https://pdfbox.apache.org", - "moduleVersion": "2.0.30", - "moduleLicense": "Apache License, Version 2.0", + "moduleVersion": "3.0.1", + "moduleLicense": "Apache-2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt" }, { @@ -450,6 +457,12 @@ "moduleLicense": "BSD 2-Clause License", "moduleLicenseUrl": "https://opensource.org/licenses/BSD-2-Clause" }, + { + "moduleName": "org.commonmark:commonmark-ext-gfm-tables", + "moduleVersion": "0.21.0", + "moduleLicense": "BSD 2-Clause License", + "moduleLicenseUrl": "https://opensource.org/licenses/BSD-2-Clause" + }, { "moduleName": "org.eclipse.angus:angus-activation", "moduleUrl": "https://www.eclipse.org", @@ -506,6 +519,52 @@ "moduleLicense": "Public Domain", "moduleLicenseUrl": "http://repository.jboss.org/licenses/cc0-1.0.txt" }, + { + "moduleName": "org.junit.jupiter:junit-jupiter", + "moduleUrl": "https://junit.org/junit5/", + "moduleVersion": "5.10.1", + "moduleLicense": "Eclipse Public License v2.0", + "moduleLicenseUrl": "https://www.eclipse.org/legal/epl-v20.html" + }, + { + "moduleName": "org.junit.jupiter:junit-jupiter-api", + "moduleUrl": "https://junit.org/junit5/", + "moduleVersion": "5.10.1", + "moduleLicense": "Eclipse Public License v2.0", + "moduleLicenseUrl": "https://www.eclipse.org/legal/epl-v20.html" + }, + { + "moduleName": "org.junit.jupiter:junit-jupiter-engine", + "moduleUrl": "https://junit.org/junit5/", + "moduleVersion": "5.10.1", + "moduleLicense": "Eclipse Public License v2.0", + "moduleLicenseUrl": "https://www.eclipse.org/legal/epl-v20.html" + }, + { + "moduleName": "org.junit.jupiter:junit-jupiter-params", + "moduleUrl": "https://junit.org/junit5/", + "moduleVersion": "5.10.1", + "moduleLicense": "Eclipse Public License v2.0", + "moduleLicenseUrl": "https://www.eclipse.org/legal/epl-v20.html" + }, + { + "moduleName": "org.junit.platform:junit-platform-commons", + "moduleUrl": "https://junit.org/junit5/", + "moduleVersion": "1.10.1", + "moduleLicense": "Eclipse Public License v2.0", + "moduleLicenseUrl": "https://www.eclipse.org/legal/epl-v20.html" + }, + { + "moduleName": "org.junit.platform:junit-platform-engine", + "moduleUrl": "https://junit.org/junit5/", + "moduleVersion": "1.10.1", + "moduleLicense": "Eclipse Public License v2.0", + "moduleLicenseUrl": "https://www.eclipse.org/legal/epl-v20.html" + }, + { + "moduleName": "org.junit:junit-bom", + "moduleVersion": "5.10.1" + }, { "moduleName": "org.latencyutils:LatencyUtils", "moduleUrl": "http://latencyutils.github.io/LatencyUtils/", @@ -513,6 +572,13 @@ "moduleLicense": "Public Domain, per Creative Commons CC0", "moduleLicenseUrl": "http://creativecommons.org/publicdomain/zero/1.0/" }, + { + "moduleName": "org.opentest4j:opentest4j", + "moduleUrl": "https://github.com/ota4j-team/opentest4j", + "moduleVersion": "1.3.0", + "moduleLicense": "The Apache License, Version 2.0", + "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt" + }, { "moduleName": "org.slf4j:jul-to-slf4j", "moduleUrl": "http://www.slf4j.org", diff --git a/src/main/resources/static/js/multitool/PdfContainer.js b/src/main/resources/static/js/multitool/PdfContainer.js index 4a53c961..7ff2e945 100644 --- a/src/main/resources/static/js/multitool/PdfContainer.js +++ b/src/main/resources/static/js/multitool/PdfContainer.js @@ -26,6 +26,7 @@ class PdfContainer { movePageTo: this.movePageTo, addPdfs: this.addPdfs, rotateElement: this.rotateElement, + updateFilename: this.updateFilename }) }) @@ -38,7 +39,7 @@ class PdfContainer { filenameInput.onkeyup = this.updateFilename; filenameInput.onkeydown = this.preventIllegalChars; - filenameInput.disabled = true; + filenameInput.disabled = false; filenameInput.innerText = ""; downloadBtn.disabled = true; } @@ -59,7 +60,7 @@ class PdfContainer { const vector = (endIndex !== -1 && startIndex > endIndex) ? 0-width : width; - + this.pagesContainerWrapper.scroll({ left: this.pagesContainerWrapper.scrollLeft + vector, }) @@ -73,30 +74,9 @@ class PdfContainer { input.setAttribute("accept", "application/pdf"); input.onchange = async(e) => { const files = e.target.files; - if (files.length > 0) { - const filenameInput = document.getElementById('filename-input'); - const pagesContainer = document.getElementById('pages-container'); - const downloadBtn = document.getElementById('export-button'); - - filenameInput.disabled = false; - - if (pagesContainer.childElementCount === 0) { - filenameInput.value = ""; - this.filename = null; - downloadBtn.disabled = true; - } else { - this.filename = filenameInput.value; - } - - if (this.filename === null || this.filename === undefined) { - filenameInput.value = files[0].name; - } else { - filenameInput.value = this.filename; - } - - } this.addPdfsFromFiles(files, nextSiblingElement); + this.updateFilename(files ? files[0].name : ""); } input.click(); @@ -197,7 +177,7 @@ class PdfContainer { return pdfDoc; } - + rotateAll(deg) { for (var i=0; i { - let overlay; - let dragCounter = 0; +class FileDragManager { + overlay; + dragCounter; + updateFilename; - const dragenterListener = function() { - dragCounter++; - if (!overlay) { + constructor(cb = null) { + this.dragCounter = 0; + this.setCallback(cb); + + // Prevent default behavior for drag events + ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => { + document.body.addEventListener(eventName, preventDefaults, false); + }); + + function preventDefaults(e) { + e.preventDefault(); + e.stopPropagation(); + } + + this.dragenterListener = this.dragenterListener.bind(this); + this.dragleaveListener = this.dragleaveListener.bind(this); + this.dropListener = this.dropListener.bind(this); + + document.body.addEventListener('dragenter', this.dragenterListener); + document.body.addEventListener('dragleave', this.dragleaveListener); + // Add drop event listener + document.body.addEventListener('drop', this.dropListener); + } + + setActions({ updateFilename }) { + this.updateFilename = updateFilename; + } + + setCallback(cb) { + if (cb) { + this.callback = cb; + } else { + this.callback = (files) => console.warn("FileDragManager not set"); + } + } + + dragenterListener() { + this.dragCounter++; + if (!this.overlay) { // Create and show the overlay - overlay = document.createElement('div'); - overlay.style.position = 'fixed'; - overlay.style.top = 0; - overlay.style.left = 0; - overlay.style.width = '100%'; - overlay.style.height = '100%'; - overlay.style.background = 'rgba(0, 0, 0, 0.5)'; - overlay.style.color = '#fff'; - overlay.style.zIndex = '1000'; - overlay.style.display = 'flex'; - overlay.style.alignItems = 'center'; - overlay.style.justifyContent = 'center'; - overlay.style.pointerEvents = 'none'; - overlay.innerHTML = '

Drop files anywhere to upload

'; - document.getElementById('content-wrap').appendChild(overlay); + this.overlay = document.createElement('div'); + this.overlay.style.position = 'fixed'; + this.overlay.style.top = 0; + this.overlay.style.left = 0; + this.overlay.style.width = '100%'; + this.overlay.style.height = '100%'; + this.overlay.style.background = 'rgba(0, 0, 0, 0.5)'; + this.overlay.style.color = '#fff'; + this.overlay.style.zIndex = '1000'; + this.overlay.style.display = 'flex'; + this.overlay.style.alignItems = 'center'; + this.overlay.style.justifyContent = 'center'; + this.overlay.style.pointerEvents = 'none'; + this.overlay.innerHTML = '

Drop files anywhere to upload

'; + document.getElementById('content-wrap').appendChild(this.overlay); } }; - const dragleaveListener = function() { - dragCounter--; - if (dragCounter === 0) { + dragleaveListener() { + this.dragCounter--; + if (this.dragCounter === 0) { // Hide and remove the overlay - if (overlay) { - overlay.remove(); - overlay = null; + if (this.overlay) { + this.overlay.remove(); + this.overlay = null; } } }; - const dropListener = function(e) { + dropListener(e) { const dt = e.dataTransfer; const files = dt.files; - callback(files).catch((err) => { + this.callback(files).catch((err) => { console.error(err); //maybe }).finally(() => { - if (overlay) { - overlay.remove(); - overlay = null; + // Hide and remove the overlay + if (this.overlay) { + this.overlay.remove(); + this.overlay = null; } + + this.updateFilename(files ? files[0].name : ""); }); }; - - // Prevent default behavior for drag events - ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => { - document.body.addEventListener(eventName, preventDefaults, false); - }); - - function preventDefaults(e) { - e.preventDefault(); - e.stopPropagation(); - } - - document.body.addEventListener('dragenter', dragenterListener); - document.body.addEventListener('dragleave', dragleaveListener); - // Add drop event listener - document.body.addEventListener('drop', dropListener); } -export default addFileDragListener; \ No newline at end of file +export default FileDragManager; diff --git a/src/main/resources/templates/multi-tool.html b/src/main/resources/templates/multi-tool.html index d602cbe9..d49b7f83 100644 --- a/src/main/resources/templates/multi-tool.html +++ b/src/main/resources/templates/multi-tool.html @@ -49,7 +49,7 @@ - +