From 2c325a911bf44f29aa85f3d5b66076311428b028 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Sz=C3=BCcs?= <127139797+balazs-szucs@users.noreply.github.com> Date: Sat, 11 Oct 2025 19:13:12 +0200 Subject: [PATCH 1/7] docs: update and reorganize README (#4608) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description of Changes For demo/preview: https://github.com/balazs-szucs/Stirling-PDF/tree/update-and-reorganize-readme ### Pictures (samples): image image --- ## Checklist ### General - [x] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [x] I have read the [Stirling-PDF Developer Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/DeveloperGuide.md) (if applicable) - [ ] I have read the [How to add new languages to Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md) (if applicable) - [x] I have performed a self-review of my own code - [x] My changes generate no new warnings ### Documentation - [ ] I have updated relevant docs on [Stirling-PDF's doc repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/) (if functionality has heavily changed) - [ ] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### UI Changes (if applicable) - [x] Screenshots or videos demonstrating the UI changes are attached (e.g., as comments or direct attachments in the PR) ### Testing (if applicable) - [x] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/DeveloperGuide.md#6-testing) for more details. --------- Signed-off-by: Balázs Szücs --- README.md | 171 +++++++++++++++++++++++++++++------------------------- 1 file changed, 93 insertions(+), 78 deletions(-) diff --git a/README.md b/README.md index e21cae0d4b3..907af83f4bd 100644 --- a/README.md +++ b/README.md @@ -21,82 +21,97 @@ All documentation available at [https://docs.stirlingpdf.com/](https://docs.stir ## Features -- 50+ PDF Operations - Parallel file processing and downloads - Dark mode support - Custom download options -- Custom 'Pipelines' to run multiple features in a automated queue +- Custom 'Pipelines' to run multiple features in an automated queue - API for integration with external scripts - Optional Login and Authentication support (see [here](https://docs.stirlingpdf.com/Advanced%20Configuration/System%20and%20Security) for documentation) -- Database Backup and Import (see [here](https://docs.stirlingpdf.com/Advanced%20Configuration/DATABASE) for documentation) - Enterprise features like SSO (see [here](https://docs.stirlingpdf.com/Advanced%20Configuration/Single%20Sign-On%20Configuration) for documentation) +- Database Backup and Import (see [here](https://docs.stirlingpdf.com/Advanced%20Configuration/DATABASE) for documentation) -## PDF Features - -### Page Operations - -- View and modify PDFs - View multi-page PDFs with custom viewing, sorting, and searching. Plus, on-page edit features like annotating, drawing, and adding text and images. (Using PDF.js with Joxit and Liberation fonts) -- Full interactive GUI for merging/splitting/rotating/moving PDFs and their pages -- Merge multiple PDFs into a single resultant file -- Split PDFs into multiple files at specified page numbers or extract all pages as individual files -- Reorganize PDF pages into different orders -- Rotate PDFs in 90-degree increments -- Remove pages -- Multi-page layout (format PDFs into a multi-paged page) -- Scale page contents size by set percentage -- Adjust contrast -- Crop PDF -- Auto-split PDF (with physically scanned page dividers) -- Extract page(s) -- Convert PDF to a single page -- Overlay PDFs on top of each other -- PDF to a single page -- Split PDF by sections - -### Conversion Operations - -- Convert PDFs to and from images -- Convert any common file to PDF (using LibreOffice) -- Convert PDF to Word/PowerPoint/others (using LibreOffice) -- Convert HTML to PDF -- Convert PDF to XML -- Convert PDF to CSV -- URL to PDF -- Markdown to PDF - -### Security & Permissions - -- Add and remove passwords -- Change/set PDF permissions -- Add watermark(s) -- Certify/sign PDFs -- Sanitize PDFs -- Auto-redact text - -### Other Operations - -- Add/generate/write signatures -- Split by Size or PDF -- Repair PDFs -- Detect and remove blank pages -- Compare two PDFs and show differences in text -- Add images to PDFs -- Compress PDFs to decrease their filesize (using qpdf) -- Extract images from PDF -- Remove images from PDF -- Extract images from scans -- Remove annotations -- Add page numbers -- Auto-rename files by detecting PDF header text -- OCR on PDF (using Tesseract OCR) -- PDF/A conversion (using LibreOffice) -- Edit metadata -- Flatten PDFs -- Get all information on a PDF to view or export as JSON -- Show/detect embedded JavaScript - - - +### 50+ PDF Operations + +#### Organise +- **Merge**: Combine multiple PDFs into one +- **Split**: Divide PDFs into multiple files +- **Extract page(s)**: Extract specific pages from PDF +- **Remove**: Delete pages from PDF +- **Crop PDF**: Adjust PDF page boundaries +- **Rotate**: Rotate pages in 90-degree increments +- **Adjust page size/scale**: Resize page contents +- **Multi-Page Layout**: Add multiple pages to PDF +- **PDF to Single Large Page**: Convert to single continuous page +- **Organize**: Rearrange PDF pages + +#### Convert to PDF +- **Image to PDF**: Convert images to PDF format +- **Convert file to PDF**: Convert various common file types to PDF +- **HTML to PDF**: Transform HTML documents to PDF +- **Markdown to PDF**: Convert Markdown files to PDF +- **CBZ to PDF**: Convert comic book archives +- **CBR to PDF**: Convert comic book rar archives +- **Email to PDF**: Convert email files to PDF + +#### Convert from PDF +- **PDF to Word**: Convert to documet (docx, doc, odt) format +- **PDF to Image**: Extract PDF pages as images +- **PDF to RTF (Text)**: Convert to Rich Text Format +- **PDF to Presentation**: Convert to presentation (pptx, ppt, odp) format +- **PDF to CSV**: Extract tables to CSV +- **PDF to XML**: Convert to XML format +- **PDF to HTML**: Transform to HTML +- **PDF to PDF/A**: Convert to archival (PDF/A-1b, PDF/A-2b) format +- **PDF to Markdown**: Convert PDF to Markdown +- **PDF to CBZ**: Convert to comic book archive +- **PDF to CBR**: Convert to comic book rar archive + +#### Sign & Security +- **Sign**: Add digital signatures +- **Remove Password**: Remove PDF security +- **Add Watermark**: Apply watermarks +- **Sign with Certificate**: Certificate-based signing +- **Add Stamp to PDF**: Apply digital stamps +- **Auto Redact**: Automatically redact content +- **Change Permissions**: Modify access permissions +- **Add Password**: Apply PDF encryption +- **Manual Redaction**: Manual content redaction +- **Remove Certificate Sign**: Remove digital signatures +- **Sanitize**: Clean PDF of potential security issues +- **Validate PDF Signature**: Verify digital signatures + +#### View & Edit +- **OCR / Cleanup scans**: Optical Character Recognition +- **Add Image**: Insert images into PDF +- **Extract Images**: Extract embedded images +- **Change Metadata**: Edit PDF metadata +- **Get ALL Info on PDF**: Comprehensive PDF analysis +- **Advanced Colour options**: Colour manipulation (various options for colour inversion, CMYK conversion) +- **Compare**: Compare PDF documents +- **Add Page Numbers**: Insert page numbering +- **Flatten**: Flatten PDF layers, and interactive elements +- **Remove Annotations**: Delete comments and markups +- **Remove Blank pages**: Delete empty pages +- **Remove Image**: Delete embedded images +- **View/Edit PDF**: Interactive PDF editing +- **Unlock PDF Forms**: Enable form editing +- **Add Attachments**: Attach files to PDF + +#### Advanced +- **Compress**: Reduce file size +- **Pipeline**: Automated workflow processing (OCR images pipeline, prepare PDFs for emailing pipeline) +- **Adjust Colours/Contrast**: Colour and contrast adjustment +- **Auto Rename PDF File**: Automatic file renaming +- **Auto Split Pages**: Automatic page splitting +- **Detect/Split Scanned photos**: Photo detection and splitting +- **Overlay PDFs**: Layer PDFs over each other +- **Repair**: Fix corrupted PDFs +- **Show JavaScript**: Display embedded JavaScript +- **Auto Split by Size/Count**: Split by file size or page count +- **Split PDF by Chapters**: Chapter-based splitting +- **Split PDF by Sections**: Section-based splitting +- **Scanner Effect**: Apply scanner-like effects +- **Edit Table of Contents**: Modify PDF bookmarks and TOC # 📖 Get Started @@ -115,8 +130,8 @@ Visit our comprehensive documentation at [docs.stirlingpdf.com](https://docs.sti Stirling-PDF currently supports 40 languages! | Language | Progress | -| -------------------------------------------- | -------------------------------------- | -| Arabic (العربية) (ar_AR) | ![59%](https://geps.dev/progress/59) | +|----------------------------------------------|----------------------------------------| +| Arabic (العربية) (ar_AR) | ![59%](https://geps.dev/progress/59) | | Azerbaijani (Azərbaycan Dili) (az_AZ) | ![60%](https://geps.dev/progress/60) | | Basque (Euskara) (eu_ES) | ![35%](https://geps.dev/progress/35) | | Bulgarian (Български) (bg_BG) | ![66%](https://geps.dev/progress/66) | @@ -130,13 +145,13 @@ Stirling-PDF currently supports 40 languages! | French (Français) (fr_FR) | ![88%](https://geps.dev/progress/88) | | German (Deutsch) (de_DE) | ![95%](https://geps.dev/progress/95) | | Greek (Ελληνικά) (el_GR) | ![65%](https://geps.dev/progress/65) | -| Hindi (हिंदी) (hi_IN) | ![65%](https://geps.dev/progress/65) | +| Hindi (हिंदी) (hi_IN) | ![65%](https://geps.dev/progress/65) | | Hungarian (Magyar) (hu_HU) | ![97%](https://geps.dev/progress/97) | | Indonesian (Bahasa Indonesia) (id_ID) | ![60%](https://geps.dev/progress/60) | | Irish (Gaeilge) (ga_IE) | ![66%](https://geps.dev/progress/66) | | Italian (Italiano) (it_IT) | ![96%](https://geps.dev/progress/96) | -| Japanese (日本語) (ja_JP) | ![90%](https://geps.dev/progress/90) | -| Korean (한국어) (ko_KR) | ![66%](https://geps.dev/progress/66) | +| Japanese (日本語) (ja_JP) | ![90%](https://geps.dev/progress/90) | +| Korean (한국어) (ko_KR) | ![66%](https://geps.dev/progress/66) | | Norwegian (Norsk) (no_NB) | ![64%](https://geps.dev/progress/64) | | Persian (فارسی) (fa_IR) | ![62%](https://geps.dev/progress/62) | | Polish (Polski) (pl_PL) | ![70%](https://geps.dev/progress/70) | @@ -145,18 +160,18 @@ Stirling-PDF currently supports 40 languages! | Romanian (Română) (ro_RO) | ![56%](https://geps.dev/progress/56) | | Russian (Русский) (ru_RU) | ![90%](https://geps.dev/progress/90) | | Serbian Latin alphabet (Srpski) (sr_LATN_RS) | ![97%](https://geps.dev/progress/97) | -| Simplified Chinese (简体中文) (zh_CN) | ![91%](https://geps.dev/progress/91) | +| Simplified Chinese (简体中文) (zh_CN) | ![91%](https://geps.dev/progress/91) | | Slovakian (Slovensky) (sk_SK) | ![50%](https://geps.dev/progress/50) | | Slovenian (Slovenščina) (sl_SI) | ![69%](https://geps.dev/progress/69) | | Spanish (Español) (es_ES) | ![95%](https://geps.dev/progress/95) | | Swedish (Svenska) (sv_SE) | ![63%](https://geps.dev/progress/63) | | Thai (ไทย) (th_TH) | ![57%](https://geps.dev/progress/57) | -| Tibetan (བོད་ཡིག་) (bo_CN) | ![63%](https://geps.dev/progress/63) | -| Traditional Chinese (繁體中文) (zh_TW) | ![97%](https://geps.dev/progress/97) | +| Tibetan (བོད་ཡིག་) (bo_CN) | ![63%](https://geps.dev/progress/63) | +| Traditional Chinese (繁體中文) (zh_TW) | ![97%](https://geps.dev/progress/97) | | Turkish (Türkçe) (tr_TR) | ![96%](https://geps.dev/progress/96) | | Ukrainian (Українська) (uk_UA) | ![69%](https://geps.dev/progress/69) | | Vietnamese (Tiếng Việt) (vi_VN) | ![55%](https://geps.dev/progress/55) | -| Malayalam (മലയാളം) (ml_IN) | ![71%](https://geps.dev/progress/71) | +| Malayalam (മലയാളം) (ml_IN) | ![71%](https://geps.dev/progress/71) | ## Stirling PDF Enterprise From df870e6902113d05edbccf59a51716972063f9ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Sz=C3=BCcs?= <127139797+balazs-szucs@users.noreply.github.com> Date: Sat, 11 Oct 2025 19:14:32 +0200 Subject: [PATCH 2/7] fix(replace-and-invert-color): preserve original filename with '-inverted.pdf' suffix for output (#4594) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description of Changes As the PR name suggests, replace-and-invert-color returned files _only_ named inverted.pdf instead of original name + inverted.pdf. --- ## Checklist ### General - [x] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [x] I have read the [Stirling-PDF Developer Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/DeveloperGuide.md) (if applicable) - [ ] I have read the [How to add new languages to Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md) (if applicable) - [x] I have performed a self-review of my own code - [x] My changes generate no new warnings ### Documentation - [ ] I have updated relevant docs on [Stirling-PDF's doc repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/) (if functionality has heavily changed) - [ ] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### UI Changes (if applicable) - [ ] Screenshots or videos demonstrating the UI changes are attached (e.g., as comments or direct attachments in the PR) ### Testing (if applicable) - [x] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/DeveloperGuide.md#6-testing) for more details. --------- Signed-off-by: Balázs Szücs --- .../api/misc/ReplaceAndInvertColorController.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/app/core/src/main/java/stirling/software/SPDF/controller/api/misc/ReplaceAndInvertColorController.java b/app/core/src/main/java/stirling/software/SPDF/controller/api/misc/ReplaceAndInvertColorController.java index 1c7b589c023..aba627853a9 100644 --- a/app/core/src/main/java/stirling/software/SPDF/controller/api/misc/ReplaceAndInvertColorController.java +++ b/app/core/src/main/java/stirling/software/SPDF/controller/api/misc/ReplaceAndInvertColorController.java @@ -3,7 +3,6 @@ import java.io.IOException; import org.springframework.core.io.InputStreamResource; -import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ModelAttribute; @@ -18,6 +17,8 @@ import stirling.software.SPDF.model.api.misc.ReplaceAndInvertColorRequest; import stirling.software.SPDF.service.misc.ReplaceAndInvertColorService; +import stirling.software.common.util.GeneralUtils; +import stirling.software.common.util.WebResponseUtils; @RestController @RequestMapping("/api/v1/misc") @@ -33,7 +34,7 @@ public class ReplaceAndInvertColorController { description = "This endpoint accepts a PDF file and provides options to invert all colors, replace" + " text and background colors, or convert to CMYK color space for printing. Input:PDF Output:PDF Type:SISO") - public ResponseEntity replaceAndInvertColor( + public ResponseEntity replaceAndInvertColor( @ModelAttribute ReplaceAndInvertColorRequest request) throws IOException { InputStreamResource resource = @@ -45,9 +46,10 @@ public ResponseEntity replaceAndInvertColor( request.getTextColor()); // Return the modified PDF as a downloadable file - return ResponseEntity.ok() - .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=inverted.pdf") - .contentType(MediaType.APPLICATION_PDF) - .body(resource); + String filename = + GeneralUtils.generateFilename( + request.getFileInput().getOriginalFilename(), "_inverted.pdf"); + return WebResponseUtils.bytesToWebResponse( + resource.getContentAsByteArray(), filename, MediaType.APPLICATION_PDF); } } From 13a6fb9cfe6cfc44afe68fbd507018f4c8f6e43e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Sz=C3=BCcs?= <127139797+balazs-szucs@users.noreply.github.com> Date: Sat, 11 Oct 2025 19:14:54 +0200 Subject: [PATCH 3/7] Update Hungarian translations for improved consistency and clarity. (#4623) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description of Changes Re-read the translations, translated some missing ones, improved some translations, and fixed my previous missing of inconsistent (grammatically both correct) translation. --- ## Checklist ### General - [ ] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [ ] I have read the [Stirling-PDF Developer Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/DeveloperGuide.md) (if applicable) - [ ] I have read the [How to add new languages to Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md) (if applicable) - [ ] I have performed a self-review of my own code - [ ] My changes generate no new warnings ### Documentation - [ ] I have updated relevant docs on [Stirling-PDF's doc repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/) (if functionality has heavily changed) - [ ] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### UI Changes (if applicable) - [ ] Screenshots or videos demonstrating the UI changes are attached (e.g., as comments or direct attachments in the PR) ### Testing (if applicable) - [ ] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/DeveloperGuide.md#6-testing) for more details. Signed-off-by: Balázs Szücs --- .../main/resources/messages_hu_HU.properties | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/app/core/src/main/resources/messages_hu_HU.properties b/app/core/src/main/resources/messages_hu_HU.properties index e8891ea443b..ed86fa73658 100644 --- a/app/core/src/main/resources/messages_hu_HU.properties +++ b/app/core/src/main/resources/messages_hu_HU.properties @@ -484,7 +484,7 @@ adminUserSettings.teamName=Csapat neve adminUserSettings.teamExists=A csapat már létezik adminUserSettings.teamCreated=Csapat sikeresen létrehozva adminUserSettings.teamChanged=A felhasználó csapata frissítve lett -adminUserSettings.teamHidden=Hidden +adminUserSettings.teamHidden=Rejtett csapat adminUserSettings.totalMembers=Összes tag adminUserSettings.confirmDeleteTeam=Biztosan törli ezt a csapatot? @@ -504,7 +504,7 @@ team.confirm.moveUser=Biztosan át akarja helyezni ezt a felhasználót a(z) "{0 team.userAdded=Felhasználó sikeresen hozzáadva a csapathoz team.back=Vissza a csapatokhoz team.internal=Belső csapat -team.internalTeamNotAccessible=A belső csapat egy rendszer csapat, és nem érhető el +team.internalTeamNotAccessible=A belső csapat rendszerszintű, ezért nem érhető el. team.cannotMoveInternalUsers=A belső csapatban lévő felhasználók nem mozgathatók más csapatokba. team.hidden=Rejtett csapat team.name=Csapat neve @@ -606,19 +606,19 @@ home.imageToPdf.title=Kép PDF-be home.imageToPdf.desc=Kép (PNG, JPEG, GIF) konvertálása PDF-fé. imageToPdf.tags=konverzió,kép,jpg,fotó,fénykép -home.cbzToPdf.title=CBZ PDF-be +home.cbzToPdf.title=CBZ konvertálása PDF-be home.cbzToPdf.desc=CBZ képregény archívumok konvertálása PDF formátumba. cbzToPdf.tags=konverzió,képregény,könyv,archívum,cbz,zip -home.cbrToPdf.title=CBR PDF-be +home.cbrToPdf.title=CBR konvertálása PDF-be home.cbrToPdf.desc=CBR képregény archívumok konvertálása PDF formátumba. cbrToPdf.tags=konverzió,képregény,könyv,archívum,cbr,rar -home.pdfToCbz.title=PDF CBZ-be +home.pdfToCbz.title=PDF konvertálása CBZ-be home.pdfToCbz.desc=PDF fájlok konvertálása CBZ képregény archívumokba. pdfToCbz.tags=konverzió,képregény,könyv,archívum,cbz,pdf -home.pdfToCbr.title=PDF CBR-be +home.pdfToCbr.title=PDF konvertálása CBR-be home.pdfToCbr.desc=PDF fájlok konvertálása CBR képregény archívumokba. pdfToCbr.tags=konverzió,képregény,könyv,archívum,cbr,rar @@ -1241,7 +1241,7 @@ sign.previous=Előző oldal sign.maintainRatio=Képarány fenntartása váltása sign.undo=Visszavonás sign.redo=Újra -sign.colour=Signature Colour +sign.colour=Aláírás színe #repair repair.title=Javítás @@ -1384,7 +1384,7 @@ multiTool.split=Felosztás multiTool.moveLeft=Mozgatás balra multiTool.moveRight=Mozgatás jobbra multiTool.delete=Törlés -multiTool.dragDropMessage=Oldal(ak) kiválasztva +multiTool.dragDropMessage=Oldalak kiválasztva multiTool.undo=Visszavonás multiTool.redo=Újra @@ -1449,29 +1449,29 @@ imageToPDF.selectText.4=Egyesítés egy PDF-be imageToPDF.selectText.5=Konvertálás külön PDF-ekbe #cbzToPDF -cbzToPDF.title=CBZ to PDF -cbzToPDF.header=CBZ to PDF -cbzToPDF.submit=Convert to PDF -cbzToPDF.selectText=Select CBZ file -cbzToPDF.optimizeForEbook=Optimize PDF for ebook readers (uses Ghostscript) +cbzToPDF.title=CBZ konvertálása PDF-be +cbzToPDF.header=CBZ konvertálása PDF-be +cbzToPDF.submit=Konvertálás PDF-be +cbzToPDF.selectText=Válassza ki a CBZ fájlt +cbzToPDF.optimizeForEbook=PDF optimalizálása e-könyv olvasókhoz (Ghostscript használatával) #pdfToCBZ -pdfToCBZ.title=PDF CBZ-be -pdfToCBZ.header=PDF CBZ-be +pdfToCBZ.title=PDF konvertálása CBZ-be +pdfToCBZ.header=PDF konvertálása CBZ-be pdfToCBZ.submit=Konvertálás CBZ-be pdfToCBZ.selectText=PDF fájl kiválasztása pdfToCBZ.dpi=DPI (Dots Per Inch) #cbrToPDF -cbrToPDF.title=CBR PDF-be -cbrToPDF.header=CBR PDF-be +cbrToPDF.title=CBR konvertálása PDF-be +cbrToPDF.header=CBR konvertálása PDF-be cbrToPDF.submit=Konvertálás PDF-be cbrToPDF.selectText=CBR fájl kiválasztása cbrToPDF.optimizeForEbook=PDF optimalizálása e-könyv olvasókhoz (Ghostscript használatával) #pdfToCBR -pdfToCBR.title=PDF CBR-be -pdfToCBR.header=PDF CBR-be +pdfToCBR.title=PDF konvertálása CBR-be +pdfToCBR.header=PDF konvertálása CBR-be pdfToCBR.submit=Konvertálás CBR-be pdfToCBR.selectText=PDF fájl kiválasztása pdfToCBR.dpi=DPI (Dots Per Inch) @@ -1488,7 +1488,7 @@ pdfToImage.colorType=Színtípus pdfToImage.color=Színes pdfToImage.grey=Szürkeárnyalatos pdfToImage.blackwhite=Fekete-fehér (adatvesztéssel járhat!) -pdfToImage.dpi=DPI (The server limit is {0} dpi) +pdfToImage.dpi=DPI (a szerver felső határa: {0} dpi) pdfToImage.submit=Konvertálás pdfToImage.info=Python nincs telepítve. WebP konverzióhoz szükséges. pdfToImage.placeholder=(pl. 1,2,8 vagy 4,7,12-16 vagy 2n-1) From 60c7ba40a68eab9edaaa763ef1bb6779a2c40afe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Sz=C3=BCcs?= <127139797+balazs-szucs@users.noreply.github.com> Date: Sat, 11 Oct 2025 19:23:09 +0200 Subject: [PATCH 4/7] fix(scale): Throw exceptions for invalid page size instead of returning null in getTargetSize method (#4460) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description of Changes Fixed getTargetSize to never make an early null return, instead throw an appropriate customException when the submitted doc has 0 pages. ### Why this change was made: - If getTargetSize returned null, the call targetSize.getWidth() would throw a NullPointerException immediately. - PDDocument.getNumberOfPages() can be 0 e.g., new PDDocument() or a loader/factory that produced an empty document for a malformed or partial upload, or a custom pdfDocumentFactory that creates an empty document in some error paths. - Replacing the null return with throw ExceptionUtils.createInvalidPageSizeException("KEEP"): it fails fast with a clear error instead of producing an NPE. --- ## Checklist ### General - [x] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [x] I have read the [Stirling-PDF Developer Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/DeveloperGuide.md) (if applicable) - [ ] I have read the [How to add new languages to Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md) (if applicable) - [x] I have performed a self-review of my own code - [x] My changes generate no new warnings ### Documentation - [ ] I have updated relevant docs on [Stirling-PDF's doc repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/) (if functionality has heavily changed) - [ ] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### UI Changes (if applicable) - [ ] Screenshots or videos demonstrating the UI changes are attached (e.g., as comments or direct attachments in the PR) ### Testing (if applicable) - [x] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/DeveloperGuide.md#6-testing) for more details. --------- Signed-off-by: Balázs Szücs --- .../SPDF/controller/api/ScalePagesController.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/app/core/src/main/java/stirling/software/SPDF/controller/api/ScalePagesController.java b/app/core/src/main/java/stirling/software/SPDF/controller/api/ScalePagesController.java index 027457bbd2d..e90aab8eb77 100644 --- a/app/core/src/main/java/stirling/software/SPDF/controller/api/ScalePagesController.java +++ b/app/core/src/main/java/stirling/software/SPDF/controller/api/ScalePagesController.java @@ -105,13 +105,20 @@ public ResponseEntity scalePages(@ModelAttribute ScalePagesRequest reque private PDRectangle getTargetSize(String targetPDRectangle, PDDocument sourceDocument) { if ("KEEP".equals(targetPDRectangle)) { if (sourceDocument.getNumberOfPages() == 0) { - return null; + // Do not return null here; throw a clear exception so callers don't get a nullable + // PDRectangle. + throw ExceptionUtils.createInvalidPageSizeException("KEEP"); } // use the first page to determine the target page size PDPage sourcePage = sourceDocument.getPage(0); PDRectangle sourceSize = sourcePage.getMediaBox(); + if (sourceSize == null) { + // If media box is unexpectedly null, treat it as invalid + throw ExceptionUtils.createInvalidPageSizeException("KEEP"); + } + return sourceSize; } From fda1d6bc73ae4a93052e9642ae3a0570dbde3a55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Sz=C3=BCcs?= <127139797+balazs-szucs@users.noreply.github.com> Date: Sat, 11 Oct 2025 19:23:44 +0200 Subject: [PATCH 5/7] fix(repair): suppress corrupted PDF error/warning banner on repair page (#4434) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description of Changes Updated JS/HTML to ignore PDF corruption warning on repair-pdf html, since if user is already there, it may be safely assumed that the user is well-aware about said PDF corruption, and does not actually benefit from the information Closes: #4432 --- ## Checklist ### General - [x] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [x] I have read the [Stirling-PDF Developer Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/DeveloperGuide.md) (if applicable) - [ ] I have read the [How to add new languages to Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md) (if applicable) - [x] I have performed a self-review of my own code - [x] My changes generate no new warnings ### Documentation - [ ] I have updated relevant docs on [Stirling-PDF's doc repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/) (if functionality has heavily changed) - [ ] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### UI Changes (if applicable) - [ ] Screenshots or videos demonstrating the UI changes are attached (e.g., as comments or direct attachments in the PR) ### Testing (if applicable) - [x] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/DeveloperGuide.md#6-testing) for more details. --------- Signed-off-by: Balázs Szücs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../src/main/resources/static/js/DecryptFiles.js | 14 +++++++++----- .../src/main/resources/static/js/downloader.js | 13 +++++++++---- .../main/resources/templates/fragments/common.html | 1 + 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/app/core/src/main/resources/static/js/DecryptFiles.js b/app/core/src/main/resources/static/js/DecryptFiles.js index 0e5b58a9223..e569d8839a5 100644 --- a/app/core/src/main/resources/static/js/DecryptFiles.js +++ b/app/core/src/main/resources/static/js/DecryptFiles.js @@ -128,11 +128,15 @@ export class DecryptFile { (error.message && error.message.includes('Invalid PDF structure'))) { // Handle corrupted PDF files console.error('Corrupted PDF detected:', error); - this.showErrorBanner( - `${window.stirlingPDF.pdfCorruptedMessage.replace('{0}', file.name)}`, - error.stack || '', - `${window.stirlingPDF.tryRepairMessage}` - ); + if (window.stirlingPDF.currentPage !== 'repair') { + this.showErrorBanner( + `${window.stirlingPDF.pdfCorruptedMessage.replace('{0}', file.name)}`, + error.stack || '', + `${window.stirlingPDF.tryRepairMessage}` + ); + } else { + console.log('Suppressing corrupted PDF warning banner on repair page'); + } throw new Error('PDF file is corrupted.'); } diff --git a/app/core/src/main/resources/static/js/downloader.js b/app/core/src/main/resources/static/js/downloader.js index 47e1b06afc0..9e074be5e1c 100644 --- a/app/core/src/main/resources/static/js/downloader.js +++ b/app/core/src/main/resources/static/js/downloader.js @@ -249,10 +249,15 @@ (error.message && error.message.includes('Invalid PDF structure'))) { // Handle corrupted PDF files console.log(`Corrupted PDF detected: ${file.name}`, error); - showErrorBanner( - `${window.stirlingPDF.pdfCorruptedMessage.replace('{0}', file.name)}`, - `${window.stirlingPDF.tryRepairMessage}` - ); + if (window.stirlingPDF.currentPage !== 'repair') { + showErrorBanner( + `${window.stirlingPDF.pdfCorruptedMessage.replace('{0}', file.name)}`, + `${window.stirlingPDF.tryRepairMessage}` + ); + } else { + // On repair page, suppress banner; user already knows and is repairing + console.log('Suppressing corrupted PDF banner on repair page'); + } throw error; } else { console.log(`Error loading PDF: ${file.name}`, error); diff --git a/app/core/src/main/resources/templates/fragments/common.html b/app/core/src/main/resources/templates/fragments/common.html index d3b888a1d78..49c95891436 100644 --- a/app/core/src/main/resources/templates/fragments/common.html +++ b/app/core/src/main/resources/templates/fragments/common.html @@ -380,6 +380,7 @@ window.stirlingPDF.uploadLimitExceededPlural = /*[[#{uploadLimitExceededPlural}]]*/ 'are too large. Maximum allowed size is'; window.stirlingPDF.pdfCorruptedMessage = /*[[#{error.pdfInvalid}]]*/ 'The PDF file "{0}" appears to be corrupted or has an invalid structure. Please try using the \'Repair PDF\' feature to fix the file before proceeding.'; window.stirlingPDF.tryRepairMessage = /*[[#{error.tryRepair}]]*/ 'Try using the Repair PDF feature to fix corrupted files.'; + window.stirlingPDF.currentPage = /*[[${currentPage}]]*/ ''; })(); From 085b8795d58504656a77773b76c9a59f28a71a5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Sz=C3=BCcs?= <127139797+balazs-szucs@users.noreply.github.com> Date: Sat, 11 Oct 2025 19:35:24 +0200 Subject: [PATCH 6/7] feat(crop): Crop remove outside text (#4499) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description of Changes This PR adds option to remove text outside crop area via Ghostscript. ### Crop feature enhancements - Added a checkbox to the `crop.html` template and a corresponding label in the English properties file to allow users to select "Remove text outside crop (retains images)" when cropping PDFs. - Updated the `CropPdfForm` model to include a new boolean property `removeDataOutsideCrop` to capture the user's selection. image ### Backend logic changes - Modified the `CropController` so that if `removeDataOutsideCrop` is true, cropping is performed using a two-step process: first setting the crop box with PDFBox, then using Ghostscript to remove data outside the crop box. Otherwise, the crop is performed using only PDFBox. - Added necessary imports for handling files, paths, and process execution to support the new Ghostscript-based cropping workflow. ### Endpoint configuration - Registered the new "crop" endpoint under the "Ghostscript" group in the endpoint configuration, enabling routing for the enhanced cropping feature. ### UI image ### Sample files/Verification Before: image After: image See for yourself with: [true-pdf-sample-1_cropped.pdf](https://github.com/user-attachments/files/22546716/true-pdf-sample-1_cropped.pdf) other sample PDF: [output.pdf](https://github.com/user-attachments/files/22546785/output.pdf) Closes #2652 --- ## Checklist ### General - [x] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [x] I have read the [Stirling-PDF Developer Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/DeveloperGuide.md) (if applicable) - [ ] I have read the [How to add new languages to Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md) (if applicable) - [x] I have performed a self-review of my own code - [x] My changes generate no new warnings ### Documentation - [ ] I have updated relevant docs on [Stirling-PDF's doc repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/) (if functionality has heavily changed) - [ ] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### UI Changes (if applicable) - [x] Screenshots or videos demonstrating the UI changes are attached (e.g., as comments or direct attachments in the PR) ### Testing (if applicable) - [x] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/DeveloperGuide.md#6-testing) for more details. --------- Signed-off-by: Balázs Szücs --- .../SPDF/config/EndpointConfiguration.java | 1 + .../SPDF/controller/api/CropController.java | 70 +++++++++++++++++++ .../SPDF/model/api/general/CropPdfForm.java | 5 ++ .../src/main/resources/templates/crop.html | 1 + testing/webpage_urls.txt | 1 - 5 files changed, 77 insertions(+), 1 deletion(-) diff --git a/app/core/src/main/java/stirling/software/SPDF/config/EndpointConfiguration.java b/app/core/src/main/java/stirling/software/SPDF/config/EndpointConfiguration.java index 2b074640d22..02eb82163c1 100644 --- a/app/core/src/main/java/stirling/software/SPDF/config/EndpointConfiguration.java +++ b/app/core/src/main/java/stirling/software/SPDF/config/EndpointConfiguration.java @@ -401,6 +401,7 @@ public void init() { /* Ghostscript */ addEndpointToGroup("Ghostscript", "repair"); addEndpointToGroup("Ghostscript", "compress-pdf"); + addEndpointToGroup("Ghostscript", "crop"); addEndpointToGroup("Ghostscript", "replace-invert-pdf"); /* tesseract */ diff --git a/app/core/src/main/java/stirling/software/SPDF/controller/api/CropController.java b/app/core/src/main/java/stirling/software/SPDF/controller/api/CropController.java index 2fbbadf5ec3..8ca9604ce11 100644 --- a/app/core/src/main/java/stirling/software/SPDF/controller/api/CropController.java +++ b/app/core/src/main/java/stirling/software/SPDF/controller/api/CropController.java @@ -2,6 +2,9 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; import org.apache.pdfbox.multipdf.LayerUtility; import org.apache.pdfbox.pdmodel.PDDocument; @@ -21,16 +24,19 @@ import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import stirling.software.SPDF.model.api.general.CropPdfForm; import stirling.software.common.service.CustomPDFDocumentFactory; import stirling.software.common.util.GeneralUtils; +import stirling.software.common.util.ProcessExecutor; import stirling.software.common.util.WebResponseUtils; @RestController @RequestMapping("/api/v1/general") @Tag(name = "General", description = "General APIs") @RequiredArgsConstructor +@Slf4j public class CropController { private final CustomPDFDocumentFactory pdfDocumentFactory; @@ -42,6 +48,15 @@ public class CropController { "This operation takes an input PDF file and crops it according to the given" + " coordinates. Input:PDF Output:PDF Type:SISO") public ResponseEntity cropPdf(@ModelAttribute CropPdfForm request) throws IOException { + if (request.isRemoveDataOutsideCrop()) { + return cropWithGhostscript(request); + } else { + return cropWithPDFBox(request); + } + } + + private ResponseEntity cropWithPDFBox(@ModelAttribute CropPdfForm request) + throws IOException { PDDocument sourceDocument = pdfDocumentFactory.load(request); PDDocument newDocument = @@ -97,4 +112,59 @@ public ResponseEntity cropPdf(@ModelAttribute CropPdfForm request) throw GeneralUtils.generateFilename( request.getFileInput().getOriginalFilename(), "_cropped.pdf")); } + + private ResponseEntity cropWithGhostscript(@ModelAttribute CropPdfForm request) + throws IOException { + PDDocument sourceDocument = pdfDocumentFactory.load(request); + + for (int i = 0; i < sourceDocument.getNumberOfPages(); i++) { + PDPage page = sourceDocument.getPage(i); + PDRectangle cropBox = + new PDRectangle( + request.getX(), + request.getY(), + request.getWidth(), + request.getHeight()); + page.setCropBox(cropBox); + } + + Path tempInputFile = Files.createTempFile("crop_input", ".pdf"); + Path tempOutputFile = Files.createTempFile("crop_output", ".pdf"); + + try { + sourceDocument.save(tempInputFile.toFile()); + sourceDocument.close(); + + ProcessExecutor processExecutor = + ProcessExecutor.getInstance(ProcessExecutor.Processes.GHOSTSCRIPT); + List command = + List.of( + "gs", + "-sDEVICE=pdfwrite", + "-dUseCropBox", + "-o", + tempOutputFile.toString(), + tempInputFile.toString()); + + processExecutor.runCommandWithOutputHandling(command); + + byte[] pdfContent = Files.readAllBytes(tempOutputFile); + + return WebResponseUtils.bytesToWebResponse( + pdfContent, + request.getFileInput().getOriginalFilename().replaceFirst("[.][^.]+$", "") + + "_cropped.pdf"); + + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new IOException("Ghostscript processing was interrupted", e); + } finally { + try { + Files.deleteIfExists(tempInputFile); + Files.deleteIfExists(tempOutputFile); + } catch (IOException e) { + log.debug("Failed to delete temporary files", e); + } + } + } } diff --git a/app/core/src/main/java/stirling/software/SPDF/model/api/general/CropPdfForm.java b/app/core/src/main/java/stirling/software/SPDF/model/api/general/CropPdfForm.java index 913f94a1064..480169468e9 100644 --- a/app/core/src/main/java/stirling/software/SPDF/model/api/general/CropPdfForm.java +++ b/app/core/src/main/java/stirling/software/SPDF/model/api/general/CropPdfForm.java @@ -26,4 +26,9 @@ public class CropPdfForm extends PDFFile { @Schema(description = "The height of the crop area", type = "number") private float height; + + @Schema( + description = "Whether to remove text outside the crop area (keeps images)", + type = "boolean") + private boolean removeDataOutsideCrop = true; } diff --git a/app/core/src/main/resources/templates/crop.html b/app/core/src/main/resources/templates/crop.html index 0617bf9b69e..e91c481c33c 100644 --- a/app/core/src/main/resources/templates/crop.html +++ b/app/core/src/main/resources/templates/crop.html @@ -22,6 +22,7 @@ +
diff --git a/testing/webpage_urls.txt b/testing/webpage_urls.txt index c6c713dd092..6e7874eca03 100644 --- a/testing/webpage_urls.txt +++ b/testing/webpage_urls.txt @@ -8,7 +8,6 @@ /pdf-organizer /multi-page-layout /scale-pages -/crop /extract-page /pdf-to-single-page /img-to-pdf From 06efab5cb2e1fec496fb7fcaccb15dc0327e0d84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Sz=C3=BCcs?= <127139797+balazs-szucs@users.noreply.github.com> Date: Sat, 11 Oct 2025 19:37:58 +0200 Subject: [PATCH 7/7] fix(sanitize): fix JavaScript handling, embedded file sanitization (#4652) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description of Changes ### Fixes - Added document-level JavaScript removal: Now removes OpenAction and catalog additional actions (WC, WS, DS, WP, DP) that execute on document open, save, print, and close events - Added page-level JavaScript removal: Removes page open/close actions (O, C) that were previously missed - Added annotation additional actions removal: Removes all 10 annotation event handlers (Bl, D, E, Fo, PC, PI, PO, PV, U, X) for mouse/focus events - Fixed embedded file removal: Corrected implementation to use `catalog.getNames().setEmbeddedFiles(null)` instead of incorrectly targeting page resources ### Verification: Before (after embedded file "removal"): image After: image --- ## Checklist ### General - [x] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [x] I have read the [Stirling-PDF Developer Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/DeveloperGuide.md) (if applicable) - [ ] I have read the [How to add new languages to Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md) (if applicable) - [x] I have performed a self-review of my own code - [x] My changes generate no new warnings ### Documentation - [ ] I have updated relevant docs on [Stirling-PDF's doc repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/) (if functionality has heavily changed) - [ ] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### UI Changes (if applicable) - [ ] Screenshots or videos demonstrating the UI changes are attached (e.g., as comments or direct attachments in the PR) ### Testing (if applicable) - [x] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/DeveloperGuide.md#6-testing) for more details. Signed-off-by: Balázs Szücs --- .../api/security/SanitizeController.java | 110 ++++++++++++------ 1 file changed, 77 insertions(+), 33 deletions(-) diff --git a/app/core/src/main/java/stirling/software/SPDF/controller/api/security/SanitizeController.java b/app/core/src/main/java/stirling/software/SPDF/controller/api/security/SanitizeController.java index d7c4b675c7f..312584e3e55 100644 --- a/app/core/src/main/java/stirling/software/SPDF/controller/api/security/SanitizeController.java +++ b/app/core/src/main/java/stirling/software/SPDF/controller/api/security/SanitizeController.java @@ -2,22 +2,25 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.util.List; import org.apache.pdfbox.cos.COSDictionary; import org.apache.pdfbox.cos.COSName; import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocumentCatalog; import org.apache.pdfbox.pdmodel.PDDocumentInformation; +import org.apache.pdfbox.pdmodel.PDDocumentNameDictionary; import org.apache.pdfbox.pdmodel.PDPage; -import org.apache.pdfbox.pdmodel.PDPageTree; -import org.apache.pdfbox.pdmodel.PDResources; import org.apache.pdfbox.pdmodel.common.PDMetadata; import org.apache.pdfbox.pdmodel.interactive.action.PDAction; import org.apache.pdfbox.pdmodel.interactive.action.PDActionJavaScript; import org.apache.pdfbox.pdmodel.interactive.action.PDActionLaunch; import org.apache.pdfbox.pdmodel.interactive.action.PDActionURI; +import org.apache.pdfbox.pdmodel.interactive.action.PDDocumentCatalogAdditionalActions; import org.apache.pdfbox.pdmodel.interactive.action.PDFormFieldAdditionalActions; +import org.apache.pdfbox.pdmodel.interactive.action.PDPageAdditionalActions; import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotation; +import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationFileAttachment; import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationLink; import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationWidget; import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm; @@ -34,6 +37,7 @@ import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import stirling.software.SPDF.model.api.security.SanitizePdfRequest; import stirling.software.common.service.CustomPDFDocumentFactory; @@ -43,6 +47,7 @@ @RestController @RequestMapping("/api/v1/security") @Tag(name = "Security", description = "Security APIs") +@Slf4j @RequiredArgsConstructor public class SanitizeController { @@ -99,7 +104,7 @@ public ResponseEntity sanitizePDF(@ModelAttribute SanitizePdfRequest req GeneralUtils.generateFilename(inputFile.getOriginalFilename(), "_sanitized.pdf")); } - private void sanitizeJavaScript(PDDocument document) throws IOException { + private static void sanitizeJavaScript(PDDocument document) throws IOException { // Get the root dictionary (catalog) of the PDF PDDocumentCatalog catalog = document.getDocumentCatalog(); @@ -118,7 +123,61 @@ private void sanitizeJavaScript(PDDocument document) throws IOException { } } + if (catalog.getOpenAction() instanceof PDActionJavaScript) { + catalog.setOpenAction(null); + } + + PDDocumentCatalogAdditionalActions catalogActions = catalog.getActions(); + if (catalogActions != null) { + if (catalogActions.getWC() instanceof PDActionJavaScript) { + catalogActions.setWC(null); + } + if (catalogActions.getWS() instanceof PDActionJavaScript) { + catalogActions.setWS(null); + } + if (catalogActions.getDS() instanceof PDActionJavaScript) { + catalogActions.setDS(null); + } + if (catalogActions.getWP() instanceof PDActionJavaScript) { + catalogActions.setWP(null); + } + if (catalogActions.getDP() instanceof PDActionJavaScript) { + catalogActions.setDP(null); + } + } + + PDAcroForm acroForm = catalog.getAcroForm(); + if (acroForm != null) { + for (PDField field : acroForm.getFields()) { + PDFormFieldAdditionalActions actions = field.getActions(); + if (actions != null) { + if (actions.getC() instanceof PDActionJavaScript) { + actions.setC(null); + } + if (actions.getF() instanceof PDActionJavaScript) { + actions.setF(null); + } + if (actions.getK() instanceof PDActionJavaScript) { + actions.setK(null); + } + if (actions.getV() instanceof PDActionJavaScript) { + actions.setV(null); + } + } + } + } + for (PDPage page : document.getPages()) { + PDPageAdditionalActions pageActions = page.getActions(); + if (pageActions != null) { + if (pageActions.getO() instanceof PDActionJavaScript) { + pageActions.setO(null); + } + if (pageActions.getC() instanceof PDActionJavaScript) { + pageActions.setC(null); + } + } + for (PDAnnotation annotation : page.getAnnotations()) { if (annotation instanceof PDAnnotationWidget widget) { PDAction action = widget.getAction(); @@ -127,41 +186,26 @@ private void sanitizeJavaScript(PDDocument document) throws IOException { } } } - PDAcroForm acroForm = document.getDocumentCatalog().getAcroForm(); - if (acroForm != null) { - for (PDField field : acroForm.getFields()) { - PDFormFieldAdditionalActions actions = field.getActions(); - if (actions != null) { - if (actions.getC() instanceof PDActionJavaScript) { - actions.setC(null); - } - if (actions.getF() instanceof PDActionJavaScript) { - actions.setF(null); - } - if (actions.getK() instanceof PDActionJavaScript) { - actions.setK(null); - } - if (actions.getV() instanceof PDActionJavaScript) { - actions.setV(null); - } - } - } - } } } - private void sanitizeEmbeddedFiles(PDDocument document) { - PDPageTree allPages = document.getPages(); + private static void sanitizeEmbeddedFiles(PDDocument document) throws IOException { + PDDocumentCatalog catalog = document.getDocumentCatalog(); + PDDocumentNameDictionary names = catalog.getNames(); + if (names != null) { + names.setEmbeddedFiles(null); + } - for (PDPage page : allPages) { - PDResources res = page.getResources(); - if (res != null && res.getCOSObject() != null) { - res.getCOSObject().removeItem(COSName.getPDFName("EmbeddedFiles")); + for (PDPage page : document.getPages()) { + List annotations = page.getAnnotations(); + if (annotations != null && !annotations.isEmpty()) { + annotations.removeIf( + annotation -> annotation instanceof PDAnnotationFileAttachment); } } } - private void sanitizeXMPMetadata(PDDocument document) { + private static void sanitizeXMPMetadata(PDDocument document) { if (document.getDocumentCatalog() != null) { PDMetadata metadata = document.getDocumentCatalog().getMetadata(); if (metadata != null) { @@ -170,7 +214,7 @@ private void sanitizeXMPMetadata(PDDocument document) { } } - private void sanitizeDocumentInfoMetadata(PDDocument document) { + private static void sanitizeDocumentInfoMetadata(PDDocument document) { PDDocumentInformation docInfo = document.getDocumentInformation(); if (docInfo != null) { PDDocumentInformation newInfo = new PDDocumentInformation(); @@ -178,7 +222,7 @@ private void sanitizeDocumentInfoMetadata(PDDocument document) { } } - private void sanitizeLinks(PDDocument document) throws IOException { + private static void sanitizeLinks(PDDocument document) throws IOException { for (PDPage page : document.getPages()) { for (PDAnnotation annotation : page.getAnnotations()) { if (annotation instanceof PDAnnotationLink linkAnnotation) { @@ -191,7 +235,7 @@ private void sanitizeLinks(PDDocument document) throws IOException { } } - private void sanitizeFonts(PDDocument document) { + private static void sanitizeFonts(PDDocument document) { for (PDPage page : document.getPages()) { if (page != null && page.getResources() != null