From 530438123608567aad4a7ac5ea75bb7b0149e4fe Mon Sep 17 00:00:00 2001 From: Dario Ghunney Ware Date: Wed, 4 Jun 2025 15:59:08 +0100 Subject: [PATCH] created AttachmentsController --- .../api/misc/AttachmentsController.java | 126 ++++++++++++++++-- .../controller/web/OtherWebController.java | 7 + .../main/resources/messages_en_US.properties | 16 +++ 3 files changed, 138 insertions(+), 11 deletions(-) diff --git a/stirling-pdf/src/main/java/stirling/software/SPDF/controller/api/misc/AttachmentsController.java b/stirling-pdf/src/main/java/stirling/software/SPDF/controller/api/misc/AttachmentsController.java index dcafee4a2..90a176df4 100644 --- a/stirling-pdf/src/main/java/stirling/software/SPDF/controller/api/misc/AttachmentsController.java +++ b/stirling-pdf/src/main/java/stirling/software/SPDF/controller/api/misc/AttachmentsController.java @@ -1,28 +1,132 @@ package stirling.software.SPDF.controller.api.misc; -import io.swagger.v3.oas.annotations.tags.Tag; -import lombok.RequiredArgsConstructor; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.util.List; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDDocumentCatalog; +import org.apache.pdfbox.pdmodel.PDEmbeddedFilesNameTreeNode; +import org.apache.pdfbox.pdmodel.PageMode; +import org.apache.pdfbox.pdmodel.common.filespecification.PDComplexFileSpecification; +import org.apache.pdfbox.pdmodel.common.filespecification.PDEmbeddedFile; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import io.github.pixee.security.Filenames; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + import stirling.software.common.service.CustomPDFDocumentFactory; +import stirling.software.common.util.WebResponseUtils; @RestController @RequiredArgsConstructor @RequestMapping("/api/v1/misc") @Tag(name = "Misc", description = "Miscellaneous APIs") +@Slf4j public class AttachmentsController { private final CustomPDFDocumentFactory pdfDocumentFactory; @PostMapping(consumes = "multipart/form-data", value = "/add-attachments") + @Operation( + summary = "Add attachments to PDF", + description = "This endpoint adds embedded files (attachments) to a PDF and sets the PageMode to UseAttachments to make them visible. Input:PDF + Files Output:PDF Type:MISO") public ResponseEntity addAttachments( - String fileName, String attachmentName, byte[] attachmentData) { - // Implementation for adding attachments to a PDF file - // This is a placeholder method and should be implemented as per requirements. - return ResponseEntity.ok() - .header("Content-Disposition", "attachment; filename=\"" + fileName + "\"") - .body(attachmentData); + @RequestParam("fileInput") MultipartFile pdfFile, + @RequestParam("attachments") List attachments) + throws IOException { + + // Load the PDF document + PDDocument document = pdfDocumentFactory.load(pdfFile, true); + + // Get or create the document catalog + PDDocumentCatalog catalog = document.getDocumentCatalog(); + + // Create embedded files name tree if it doesn't exist + PDEmbeddedFilesNameTreeNode efTree = catalog.getNames().getEmbeddedFiles(); + if (efTree == null) { + efTree = new PDEmbeddedFilesNameTreeNode(); + catalog.getNames().setEmbeddedFiles(efTree); + } + + // Add each attachment + for (MultipartFile attachment : attachments) { + if (attachment != null && !attachment.isEmpty()) { + addEmbeddedFile(document, efTree, attachment); + } + } + + // Set PageMode to UseAttachments to show the attachments panel + catalog.setPageMode(PageMode.USE_ATTACHMENTS); + + // Return the modified PDF + return WebResponseUtils.pdfDocToWebResponse( + document, + Filenames.toSimpleFileName(pdfFile.getOriginalFilename()) + .replaceFirst("[.][^.]+$", "") + "_with_attachments.pdf"); + } + + @PostMapping(consumes = "multipart/form-data", value = "/remove-attachments") + @Operation( + summary = "Remove attachments from PDF", + description = "This endpoint removes all embedded files (attachments) from a PDF. Input:PDF Output:PDF Type:SISO") + public ResponseEntity removeAttachments( + @RequestParam("fileInput") MultipartFile pdfFile) + throws IOException { + + // Load the PDF document + PDDocument document = pdfDocumentFactory.load(pdfFile, true); + + // Get the document catalog + PDDocumentCatalog catalog = document.getDocumentCatalog(); + + // Remove embedded files + if (catalog.getNames() != null) { + catalog.getNames().setEmbeddedFiles(null); + } + + // Reset PageMode to UseNone (default) + catalog.setPageMode(PageMode.USE_NONE); + + // Return the modified PDF + return WebResponseUtils.pdfDocToWebResponse( + document, + Filenames.toSimpleFileName(pdfFile.getOriginalFilename()) + .replaceFirst("[.][^.]+$", "") + "_attachments_removed.pdf"); + } + + private void addEmbeddedFile(PDDocument document, PDEmbeddedFilesNameTreeNode efTree, MultipartFile file) + throws IOException { + + // Create file specification + PDComplexFileSpecification fs = new PDComplexFileSpecification(); + fs.setFile(file.getOriginalFilename()); + fs.setFileDescription("Embedded file: " + file.getOriginalFilename()); + + // Create embedded file + PDEmbeddedFile ef = new PDEmbeddedFile(document, new ByteArrayInputStream(file.getBytes())); + ef.setSize((int) file.getSize()); + ef.setCreationDate(new java.util.GregorianCalendar()); + ef.setModDate(new java.util.GregorianCalendar()); + + // Set MIME type if available + String contentType = file.getContentType(); + if (contentType != null && !contentType.isEmpty()) { + ef.setSubtype(contentType); + } + + // Associate embedded file with file specification + fs.setEmbeddedFile(ef); + + // Add to the name tree + efTree.setNames(java.util.Collections.singletonMap(file.getOriginalFilename(), fs)); + + log.info("Added embedded file: {} ({} bytes)", file.getOriginalFilename(), file.getSize()); } } diff --git a/stirling-pdf/src/main/java/stirling/software/SPDF/controller/web/OtherWebController.java b/stirling-pdf/src/main/java/stirling/software/SPDF/controller/web/OtherWebController.java index 25333d495..59c317581 100644 --- a/stirling-pdf/src/main/java/stirling/software/SPDF/controller/web/OtherWebController.java +++ b/stirling-pdf/src/main/java/stirling/software/SPDF/controller/web/OtherWebController.java @@ -191,4 +191,11 @@ public class OtherWebController { model.addAttribute("currentPage", "auto-rename"); return "misc/auto-rename"; } + + @GetMapping("/attachments") + @Hidden + public String attachmentsForm(Model model) { + model.addAttribute("currentPage", "attachments"); + return "misc/attachments"; + } } diff --git a/stirling-pdf/src/main/resources/messages_en_US.properties b/stirling-pdf/src/main/resources/messages_en_US.properties index a37e745d0..fb814f8e1 100644 --- a/stirling-pdf/src/main/resources/messages_en_US.properties +++ b/stirling-pdf/src/main/resources/messages_en_US.properties @@ -472,6 +472,10 @@ home.addImage.title=Add image home.addImage.desc=Adds a image onto a set location on the PDF addImage.tags=img,jpg,picture,photo +home.attachments.title=Attachments +home.attachments.desc=Add or remove embedded files (attachments) to/from a PDF +attachments.tags=embed,attach,file,attachment + home.watermark.title=Add Watermark home.watermark.desc=Add a custom watermark to your PDF document. watermark.tags=Text,repeating,label,own,copyright,trademark,img,jpg,picture,photo @@ -1153,6 +1157,18 @@ addImage.upload=Add image addImage.submit=Add image +#attachments +attachments.title=Attachments +attachments.header=Add attachments to PDF +attachments.removeHeader=Remove attachments from PDF +attachments.selectFiles=Select files to attach +attachments.description=Allows you to add attachments to the PDF +attachments.descriptionPlaceholder=Enter a description for the attachments... +attachments.addButton=Add Attachments +attachments.removeDescription=This will remove all embedded files from the PDF. +attachments.removeButton=Remove All Attachments + + #merge merge.title=Merge merge.header=Merge multiple PDFs (2+)