From caef177ec3cbe8ba7b2d352e5eb9bc470417e3ec Mon Sep 17 00:00:00 2001 From: Dario Ghunney Ware Date: Wed, 4 Jun 2025 12:34:14 +0100 Subject: [PATCH] Setting up AttachmentsController --- .../software/common/util/EmlToPdf.java | 38 +----- .../common/util/PDFAttachmentUtils.java | 44 +++++++ .../common/util/WebResponseUtils.java | 8 +- .../common/util/WebResponseUtilsTest.java | 2 +- .../SPDF/config/EndpointConfiguration.java | 2 + .../SPDF/controller/api/MergeController.java | 2 +- .../api/misc/AttachmentsController.java | 102 ++++++++++++++++ .../api/misc/BlankPageController.java | 2 +- .../api/misc/ExtractImagesController.java | 2 +- .../api/pipeline/PipelineController.java | 2 +- .../api/security/CertSignController.java | 2 +- .../controller/web/OtherWebController.java | 7 ++ .../SPDF/service/PDFAttachmentService.java | 113 ++++++++++++++++++ .../PDFAttachmentServiceInterface.java | 17 +++ .../main/resources/messages_en_GB.properties | 14 +++ .../main/resources/messages_en_US.properties | 17 ++- .../templates/fragments/navElements.html | 5 +- .../main/resources/templates/home-legacy.html | 3 + .../templates/misc/add-attachments.html | 42 +++++++ testing/allEndpointsRemovedSettings.yml | 2 +- testing/endpoints.txt | 1 + testing/webpage_urls.txt | 1 + 22 files changed, 380 insertions(+), 48 deletions(-) create mode 100644 common/src/main/java/stirling/software/common/util/PDFAttachmentUtils.java create mode 100644 stirling-pdf/src/main/java/stirling/software/SPDF/controller/api/misc/AttachmentsController.java create mode 100644 stirling-pdf/src/main/java/stirling/software/SPDF/service/PDFAttachmentService.java create mode 100644 stirling-pdf/src/main/java/stirling/software/SPDF/service/PDFAttachmentServiceInterface.java create mode 100644 stirling-pdf/src/main/resources/templates/misc/add-attachments.html diff --git a/common/src/main/java/stirling/software/common/util/EmlToPdf.java b/common/src/main/java/stirling/software/common/util/EmlToPdf.java index b08bc16a5..fe7638fb9 100644 --- a/common/src/main/java/stirling/software/common/util/EmlToPdf.java +++ b/common/src/main/java/stirling/software/common/util/EmlToPdf.java @@ -42,10 +42,12 @@ import lombok.experimental.UtilityClass; import lombok.extern.slf4j.Slf4j; import stirling.software.common.model.api.converters.EmlToPdfRequest; +import static stirling.software.common.util.PDFAttachmentUtils.setCatalogViewerPreferences; @Slf4j @UtilityClass public class EmlToPdf { + private static final class StyleConstants { // Font and layout constants static final int DEFAULT_FONT_SIZE = 12; @@ -1423,41 +1425,7 @@ public class EmlToPdf { } } - private static void setCatalogViewerPreferences(PDDocument document) { - try { - PDDocumentCatalog catalog = document.getDocumentCatalog(); - if (catalog != null) { - // Get the catalog's COS dictionary to work with low-level PDF objects - COSDictionary catalogDict = catalog.getCOSObject(); - - // Set PageMode to UseAttachments - this is the standard PDF specification approach - // PageMode values: UseNone, UseOutlines, UseThumbs, FullScreen, UseOC, - // UseAttachments - catalogDict.setName(COSName.PAGE_MODE, "UseAttachments"); - - // Also set viewer preferences for better attachment viewing experience - COSDictionary viewerPrefs = - (COSDictionary) catalogDict.getDictionaryObject(COSName.VIEWER_PREFERENCES); - if (viewerPrefs == null) { - viewerPrefs = new COSDictionary(); - catalogDict.setItem(COSName.VIEWER_PREFERENCES, viewerPrefs); - } - - // Set NonFullScreenPageMode to UseAttachments as fallback for viewers that support - // it - viewerPrefs.setName(COSName.getPDFName("NonFullScreenPageMode"), "UseAttachments"); - - // Additional viewer preferences that may help with attachment display - viewerPrefs.setBoolean(COSName.getPDFName("DisplayDocTitle"), true); - - log.info( - "Set PDF PageMode to UseAttachments to automatically show attachments pane"); - } - } catch (Exception e) { - // Log warning but don't fail the entire operation for viewer preferences - log.warn("Failed to set catalog viewer preferences for attachments", e); - } - } + // MIME header decoding functionality for RFC 2047 encoded headers - moved to constants private static String decodeMimeHeader(String encodedText) { if (encodedText == null || encodedText.trim().isEmpty()) { diff --git a/common/src/main/java/stirling/software/common/util/PDFAttachmentUtils.java b/common/src/main/java/stirling/software/common/util/PDFAttachmentUtils.java new file mode 100644 index 000000000..a20c1fb00 --- /dev/null +++ b/common/src/main/java/stirling/software/common/util/PDFAttachmentUtils.java @@ -0,0 +1,44 @@ +package stirling.software.common.util; + +import lombok.extern.slf4j.Slf4j; +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.PageMode; + +@Slf4j +public class PDFAttachmentUtils { + + public static void setCatalogViewerPreferences(PDDocument document) { + try { + PDDocumentCatalog catalog = document.getDocumentCatalog(); + if (catalog != null) { + // Get the catalog's COS dictionary to work with low-level PDF objects + COSDictionary catalogDict = catalog.getCOSObject(); + + // Set PageMode to UseAttachments - this is the standard PDF specification approach + // PageMode values: UseNone, UseOutlines, UseThumbs, FullScreen, UseOC, UseAttachments + catalogDict.setName(COSName.PAGE_MODE, PageMode.USE_ATTACHMENTS.stringValue()); + + // Also set viewer preferences for better attachment viewing experience + COSDictionary viewerPrefs = (COSDictionary) catalogDict.getDictionaryObject(COSName.VIEWER_PREFERENCES); + if (viewerPrefs == null) { + viewerPrefs = new COSDictionary(); + catalogDict.setItem(COSName.VIEWER_PREFERENCES, viewerPrefs); + } + + // Set NonFullScreenPageMode to UseAttachments as fallback for viewers that support it + viewerPrefs.setName(COSName.getPDFName("NonFullScreenPageMode"), PageMode.USE_ATTACHMENTS.stringValue()); + + // Additional viewer preferences that may help with attachment display + viewerPrefs.setBoolean(COSName.getPDFName("DisplayDocTitle"), true); + + log.info("Set PDF PageMode to UseAttachments to automatically show attachments pane"); + } + } catch (Exception e) { + // Log error but don't fail the entire operation for viewer preferences + log.error("Failed to set catalog viewer preferences for attachments", e); + } + } +} diff --git a/common/src/main/java/stirling/software/common/util/WebResponseUtils.java b/common/src/main/java/stirling/software/common/util/WebResponseUtils.java index 62a0e3246..c96ff16b1 100644 --- a/common/src/main/java/stirling/software/common/util/WebResponseUtils.java +++ b/common/src/main/java/stirling/software/common/util/WebResponseUtils.java @@ -16,12 +16,12 @@ import io.github.pixee.security.Filenames; public class WebResponseUtils { - public static ResponseEntity boasToWebResponse( + public static ResponseEntity baosToWebResponse( ByteArrayOutputStream baos, String docName) throws IOException { return WebResponseUtils.bytesToWebResponse(baos.toByteArray(), docName); } - public static ResponseEntity boasToWebResponse( + public static ResponseEntity baosToWebResponse( ByteArrayOutputStream baos, String docName, MediaType mediaType) throws IOException { return WebResponseUtils.bytesToWebResponse(baos.toByteArray(), docName, mediaType); } @@ -44,7 +44,7 @@ public class WebResponseUtils { headers.setContentType(mediaType); headers.setContentLength(bytes.length); String encodedDocName = - URLEncoder.encode(docName, StandardCharsets.UTF_8.toString()) + URLEncoder.encode(docName, StandardCharsets.UTF_8) .replaceAll("\\+", "%20"); headers.setContentDispositionFormData("attachment", encodedDocName); return new ResponseEntity<>(bytes, headers, HttpStatus.OK); @@ -64,6 +64,6 @@ public class WebResponseUtils { // Close the document document.close(); - return boasToWebResponse(baos, docName); + return baosToWebResponse(baos, docName); } } diff --git a/common/src/test/java/stirling/software/common/util/WebResponseUtilsTest.java b/common/src/test/java/stirling/software/common/util/WebResponseUtilsTest.java index f5ce5a6b1..70286fbf7 100644 --- a/common/src/test/java/stirling/software/common/util/WebResponseUtilsTest.java +++ b/common/src/test/java/stirling/software/common/util/WebResponseUtilsTest.java @@ -25,7 +25,7 @@ public class WebResponseUtilsTest { String docName = "sample.pdf"; ResponseEntity responseEntity = - WebResponseUtils.boasToWebResponse(baos, docName); + WebResponseUtils.baosToWebResponse(baos, docName); assertNotNull(responseEntity); assertEquals(HttpStatus.OK, responseEntity.getStatusCode()); diff --git a/stirling-pdf/src/main/java/stirling/software/SPDF/config/EndpointConfiguration.java b/stirling-pdf/src/main/java/stirling/software/SPDF/config/EndpointConfiguration.java index 2e7a197de..adab85c62 100644 --- a/stirling-pdf/src/main/java/stirling/software/SPDF/config/EndpointConfiguration.java +++ b/stirling-pdf/src/main/java/stirling/software/SPDF/config/EndpointConfiguration.java @@ -173,6 +173,7 @@ public class EndpointConfiguration { addEndpointToGroup("Other", "get-info-on-pdf"); addEndpointToGroup("Other", "show-javascript"); addEndpointToGroup("Other", "remove-image-pdf"); + addEndpointToGroup("Other", "add-attachments"); // CLI addEndpointToGroup("CLI", "compress-pdf"); @@ -251,6 +252,7 @@ public class EndpointConfiguration { addEndpointToGroup("Java", "pdf-to-text"); addEndpointToGroup("Java", "remove-image-pdf"); addEndpointToGroup("Java", "pdf-to-markdown"); + addEndpointToGroup("Java", "add-attachments"); // Javascript addEndpointToGroup("Javascript", "pdf-organizer"); diff --git a/stirling-pdf/src/main/java/stirling/software/SPDF/controller/api/MergeController.java b/stirling-pdf/src/main/java/stirling/software/SPDF/controller/api/MergeController.java index 5e37314a6..ddd988ef9 100644 --- a/stirling-pdf/src/main/java/stirling/software/SPDF/controller/api/MergeController.java +++ b/stirling-pdf/src/main/java/stirling/software/SPDF/controller/api/MergeController.java @@ -225,7 +225,7 @@ public class MergeController { String mergedFileName = files[0].getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_merged_unsigned.pdf"; - return WebResponseUtils.boasToWebResponse( + return WebResponseUtils.baosToWebResponse( baos, mergedFileName); // Return the modified PDF } catch (Exception ex) { 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 new file mode 100644 index 000000000..d709b0000 --- /dev/null +++ b/stirling-pdf/src/main/java/stirling/software/SPDF/controller/api/misc/AttachmentsController.java @@ -0,0 +1,102 @@ +package stirling.software.SPDF.controller.api.misc; + +import java.io.IOException; +import java.util.List; + +import org.apache.pdfbox.pdmodel.*; +import org.springframework.http.ResponseEntity; +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.SPDF.service.PDFAttachmentServiceInterface; +import stirling.software.common.service.CustomPDFDocumentFactory; +import stirling.software.common.util.WebResponseUtils; + +@Slf4j +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/v1/misc") +@Tag(name = "Misc", description = "Miscellaneous APIs") +public class AttachmentsController { + + private final CustomPDFDocumentFactory pdfDocumentFactory; + + private final PDFAttachmentServiceInterface pdfAttachmentService; + + @SuppressWarnings("DataFlowIssue") + @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( + @RequestParam("fileInput") MultipartFile pdfFile, + @RequestParam("attachments") List attachments) + throws IOException { + + // Load the PDF document + PDDocument document = pdfDocumentFactory.load(pdfFile, false); + + // Get or create the document catalog + PDDocumentCatalog catalog = document.getDocumentCatalog(); + + // Create embedded files name tree if it doesn't exist + PDDocumentNameDictionary documentNames = catalog.getNames(); + PDEmbeddedFilesNameTreeNode embeddedFilesTree = new PDEmbeddedFilesNameTreeNode(); + + if (documentNames != null) { + embeddedFilesTree = documentNames.getEmbeddedFiles(); + } else { + documentNames = new PDDocumentNameDictionary(catalog); + documentNames.setEmbeddedFiles(embeddedFilesTree); + } + + // Add attachments + catalog.setNames(documentNames); + pdfAttachmentService.addAttachment(document, embeddedFilesTree, attachments); + + // Set PageMode to UseAttachments to show the attachments panel + catalog.setPageMode(PageMode.USE_ATTACHMENTS); + + 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 and document catalog + PDDocument document = pdfDocumentFactory.load(pdfFile); + 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"); + } +} diff --git a/stirling-pdf/src/main/java/stirling/software/SPDF/controller/api/misc/BlankPageController.java b/stirling-pdf/src/main/java/stirling/software/SPDF/controller/api/misc/BlankPageController.java index a7314fc7e..7d5086b4c 100644 --- a/stirling-pdf/src/main/java/stirling/software/SPDF/controller/api/misc/BlankPageController.java +++ b/stirling-pdf/src/main/java/stirling/software/SPDF/controller/api/misc/BlankPageController.java @@ -144,7 +144,7 @@ public class BlankPageController { zos.close(); log.info("Returning ZIP file: {}", filename + "_processed.zip"); - return WebResponseUtils.boasToWebResponse( + return WebResponseUtils.baosToWebResponse( baos, filename + "_processed.zip", MediaType.APPLICATION_OCTET_STREAM); } catch (IOException e) { diff --git a/stirling-pdf/src/main/java/stirling/software/SPDF/controller/api/misc/ExtractImagesController.java b/stirling-pdf/src/main/java/stirling/software/SPDF/controller/api/misc/ExtractImagesController.java index 4ec844485..cb06b9f4d 100644 --- a/stirling-pdf/src/main/java/stirling/software/SPDF/controller/api/misc/ExtractImagesController.java +++ b/stirling-pdf/src/main/java/stirling/software/SPDF/controller/api/misc/ExtractImagesController.java @@ -148,7 +148,7 @@ public class ExtractImagesController { // Create ByteArrayResource from byte array byte[] zipContents = baos.toByteArray(); - return WebResponseUtils.boasToWebResponse( + return WebResponseUtils.baosToWebResponse( baos, filename + "_extracted-images.zip", MediaType.APPLICATION_OCTET_STREAM); } diff --git a/stirling-pdf/src/main/java/stirling/software/SPDF/controller/api/pipeline/PipelineController.java b/stirling-pdf/src/main/java/stirling/software/SPDF/controller/api/pipeline/PipelineController.java index d573301d0..d6b4fa0da 100644 --- a/stirling-pdf/src/main/java/stirling/software/SPDF/controller/api/pipeline/PipelineController.java +++ b/stirling-pdf/src/main/java/stirling/software/SPDF/controller/api/pipeline/PipelineController.java @@ -118,7 +118,7 @@ public class PipelineController { } zipOut.close(); log.info("Returning zipped file response..."); - return WebResponseUtils.boasToWebResponse( + return WebResponseUtils.baosToWebResponse( baos, "output.zip", MediaType.APPLICATION_OCTET_STREAM); } catch (Exception e) { log.error("Error handling data: ", e); diff --git a/stirling-pdf/src/main/java/stirling/software/SPDF/controller/api/security/CertSignController.java b/stirling-pdf/src/main/java/stirling/software/SPDF/controller/api/security/CertSignController.java index 3260eb31f..612c666c4 100644 --- a/stirling-pdf/src/main/java/stirling/software/SPDF/controller/api/security/CertSignController.java +++ b/stirling-pdf/src/main/java/stirling/software/SPDF/controller/api/security/CertSignController.java @@ -205,7 +205,7 @@ public class CertSignController { location, reason, showLogo); - return WebResponseUtils.boasToWebResponse( + return WebResponseUtils.baosToWebResponse( baos, Filenames.toSimpleFileName(pdf.getOriginalFilename()).replaceFirst("[.][^.]+$", "") + "_signed.pdf"); 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..84b656e40 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("/add-attachments") + @Hidden + public String attachmentsForm(Model model) { + model.addAttribute("currentPage", "attachments"); + return "misc/add-attachments"; + } } diff --git a/stirling-pdf/src/main/java/stirling/software/SPDF/service/PDFAttachmentService.java b/stirling-pdf/src/main/java/stirling/software/SPDF/service/PDFAttachmentService.java new file mode 100644 index 000000000..6c5bcfad1 --- /dev/null +++ b/stirling-pdf/src/main/java/stirling/software/SPDF/service/PDFAttachmentService.java @@ -0,0 +1,113 @@ +package stirling.software.SPDF.service; + +import java.io.IOException; +import java.util.GregorianCalendar; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.commons.lang3.StringUtils; +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDEmbeddedFilesNameTreeNode; +import org.apache.pdfbox.pdmodel.common.filespecification.PDComplexFileSpecification; +import org.apache.pdfbox.pdmodel.common.filespecification.PDEmbeddedFile; +import org.apache.pdfbox.pdmodel.encryption.AccessPermission; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import lombok.extern.slf4j.Slf4j; + +import stirling.software.common.util.PDFAttachmentUtils; + +@Slf4j +@Service +public class PDFAttachmentService implements PDFAttachmentServiceInterface { + + @Override + public void addAttachment( + PDDocument document, + PDEmbeddedFilesNameTreeNode embeddedFilesTree, + List attachments) + throws IOException { + Map existingNames; + + try { + existingNames = embeddedFilesTree.getNames(); + if (existingNames == null) { + log.debug("No existing embedded files found, creating new names map."); + existingNames = new HashMap<>(); + } + + log.debug("Embedded files: {}", existingNames.keySet()); + } catch (IOException e) { + log.error("Could not retrieve existing embedded files", e); + throw e; + } + + final Map existingEmbeddedFiles = existingNames; + attachments.forEach( + attachment -> { + // Create attachments specification + PDComplexFileSpecification fileSpecification = new PDComplexFileSpecification(); + fileSpecification.setFile(attachment.getOriginalFilename()); + fileSpecification.setFileUnicode(attachment.getOriginalFilename()); + fileSpecification.setFileDescription( + "Embedded attachment: " + attachment.getOriginalFilename()); + + try { + // Create embedded attachment + PDEmbeddedFile embeddedFile = + new PDEmbeddedFile(document, attachment.getInputStream()); + embeddedFile.setSize((int) attachment.getSize()); + embeddedFile.setCreationDate(new GregorianCalendar()); + embeddedFile.setModDate(new GregorianCalendar()); + + // Set MIME type if available + String contentType = attachment.getContentType(); + if (StringUtils.isNotBlank(contentType)) { + embeddedFile.setSubtype(contentType); + } + + // Associate embedded attachment with file specification + embeddedFile.setFile(fileSpecification); + fileSpecification.setEmbeddedFile(embeddedFile); + fileSpecification.setEmbeddedFileUnicode(embeddedFile); + + // Add to the existing files map + existingEmbeddedFiles.put( + attachment.getOriginalFilename(), fileSpecification); + + log.info( + "Added attachment: {} ({} bytes)", + attachment.getOriginalFilename(), + attachment.getSize()); + } catch (IOException e) { + log.warn( + "Failed to create embedded file for attachment: {}", + attachment.getOriginalFilename(), + e); + } + }); + + embeddedFilesTree.setNames(existingNames); + + // Ensure document has proper access permissions for embedded files + grantAccessPermissions(document); + PDFAttachmentUtils.setCatalogViewerPreferences(document); + } + + private void grantAccessPermissions(PDDocument document) { + AccessPermission currentPermissions = document.getCurrentAccessPermission(); + + currentPermissions.setCanAssembleDocument(true); + currentPermissions.setCanFillInForm(currentPermissions.canFillInForm()); + currentPermissions.setCanModify(true); + currentPermissions.setCanPrint(true); + currentPermissions.setCanPrintFaithful(true); + + // Ensure these permissions are enabled for embedded file access + currentPermissions.setCanExtractContent(true); + currentPermissions.setCanExtractForAccessibility(true); + currentPermissions.setCanModifyAnnotations(true); + } +} diff --git a/stirling-pdf/src/main/java/stirling/software/SPDF/service/PDFAttachmentServiceInterface.java b/stirling-pdf/src/main/java/stirling/software/SPDF/service/PDFAttachmentServiceInterface.java new file mode 100644 index 000000000..90ae33013 --- /dev/null +++ b/stirling-pdf/src/main/java/stirling/software/SPDF/service/PDFAttachmentServiceInterface.java @@ -0,0 +1,17 @@ +package stirling.software.SPDF.service; + +import java.io.IOException; +import java.util.List; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDEmbeddedFilesNameTreeNode; +import org.springframework.web.multipart.MultipartFile; + +public interface PDFAttachmentServiceInterface { + + void addAttachment( + PDDocument document, + PDEmbeddedFilesNameTreeNode efTree, + List attachments) + throws IOException; +} diff --git a/stirling-pdf/src/main/resources/messages_en_GB.properties b/stirling-pdf/src/main/resources/messages_en_GB.properties index 22cbfaf17..be1ea8600 100644 --- a/stirling-pdf/src/main/resources/messages_en_GB.properties +++ b/stirling-pdf/src/main/resources/messages_en_GB.properties @@ -525,6 +525,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,attachments + 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 @@ -1205,6 +1209,16 @@ addImage.everyPage=Every Page? 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 diff --git a/stirling-pdf/src/main/resources/messages_en_US.properties b/stirling-pdf/src/main/resources/messages_en_US.properties index 4b76acbb8..d7ccef038 100644 --- a/stirling-pdf/src/main/resources/messages_en_US.properties +++ b/stirling-pdf/src/main/resources/messages_en_US.properties @@ -525,6 +525,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,attachments + 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 @@ -533,7 +537,6 @@ home.permissions.title=Change Permissions home.permissions.desc=Change the permissions of your PDF document permissions.tags=read,write,edit,print - home.removePages.title=Remove home.removePages.desc=Delete unwanted pages from your PDF document. removePages.tags=Remove pages,delete pages @@ -1206,6 +1209,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+) diff --git a/stirling-pdf/src/main/resources/templates/fragments/navElements.html b/stirling-pdf/src/main/resources/templates/fragments/navElements.html index cd7fae74b..38dfa0ac6 100644 --- a/stirling-pdf/src/main/resources/templates/fragments/navElements.html +++ b/stirling-pdf/src/main/resources/templates/fragments/navElements.html @@ -236,7 +236,10 @@
-
+ +
+
diff --git a/stirling-pdf/src/main/resources/templates/home-legacy.html b/stirling-pdf/src/main/resources/templates/home-legacy.html index d60ac220e..9531a359b 100644 --- a/stirling-pdf/src/main/resources/templates/home-legacy.html +++ b/stirling-pdf/src/main/resources/templates/home-legacy.html @@ -290,6 +290,9 @@
+
+
diff --git a/stirling-pdf/src/main/resources/templates/misc/add-attachments.html b/stirling-pdf/src/main/resources/templates/misc/add-attachments.html new file mode 100644 index 000000000..60bb16c96 --- /dev/null +++ b/stirling-pdf/src/main/resources/templates/misc/add-attachments.html @@ -0,0 +1,42 @@ + + + + + + + + +
+
+ +

+
+
+
+
+ attachment + +
+ +
+ +
+
+ + +
+
+ + + +
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/testing/allEndpointsRemovedSettings.yml b/testing/allEndpointsRemovedSettings.yml index 3290d6fef..8230b4418 100644 --- a/testing/allEndpointsRemovedSettings.yml +++ b/testing/allEndpointsRemovedSettings.yml @@ -128,7 +128,7 @@ ui: languages: [] # If empty, all languages are enabled. To display only German and Polish ["de_DE", "pl_PL"]. British English is always enabled. endpoints: # All the possible endpoints are disabled - toRemove: [crop, merge-pdfs, multi-page-layout, overlay-pdfs, pdf-to-single-page, rearrange-pages, remove-image-pdf, remove-pages, rotate-pdf, scale-pages, split-by-size-or-count, split-pages, split-pdf-by-chapters, split-pdf-by-sections, add-password, add-watermark, auto-redact, cert-sign, get-info-on-pdf, redact, remove-cert-sign, remove-password, sanitize-pdf, validate-signature, file-to-pdf, html-to-pdf, img-to-pdf, markdown-to-pdf, pdf-to-csv, pdf-to-html, pdf-to-img, pdf-to-markdown, pdf-to-pdfa, pdf-to-presentation, pdf-to-text, pdf-to-word, pdf-to-xml, url-to-pdf, add-image, add-page-numbers, add-stamp, auto-rename, auto-split-pdf, compress-pdf, decompress-pdf, extract-image-scans, extract-images, flatten, ocr-pdf, remove-blanks, repair, replace-invert-pdf, show-javascript, update-metadata, filter-contains-image, filter-contains-text, filter-file-size, filter-page-count, filter-page-rotation, filter-page-size] # list endpoints to disable (e.g. ['img-to-pdf', 'remove-pages']) + toRemove: [crop, merge-pdfs, multi-page-layout, overlay-pdfs, pdf-to-single-page, rearrange-pages, remove-image-pdf, remove-pages, rotate-pdf, scale-pages, split-by-size-or-count, split-pages, split-pdf-by-chapters, split-pdf-by-sections, add-password, add-watermark, auto-redact, cert-sign, get-info-on-pdf, redact, remove-cert-sign, remove-password, sanitize-pdf, validate-signature, file-to-pdf, html-to-pdf, img-to-pdf, markdown-to-pdf, pdf-to-csv, pdf-to-html, pdf-to-img, pdf-to-markdown, pdf-to-pdfa, pdf-to-presentation, pdf-to-text, pdf-to-word, pdf-to-xml, url-to-pdf, add-image, add-page-numbers, add-stamp, auto-rename, auto-split-pdf, compress-pdf, decompress-pdf, extract-image-scans, extract-images, flatten, ocr-pdf, remove-blanks, repair, replace-invert-pdf, show-javascript, update-metadata, filter-contains-image, filter-contains-text, filter-file-size, filter-page-count, filter-page-rotation, filter-page-size, add-attachments] # list endpoints to disable (e.g. ['img-to-pdf', 'remove-pages']) groupsToRemove: [] # list groups to disable (e.g. ['LibreOffice']) metrics: diff --git a/testing/endpoints.txt b/testing/endpoints.txt index 5468ad6c1..149e3af3a 100644 --- a/testing/endpoints.txt +++ b/testing/endpoints.txt @@ -30,6 +30,7 @@ /api/v1/misc/add-stamp /api/v1/misc/add-page-numbers /api/v1/misc/add-image +/api/v1/misc/add-attachments /api/v1/convert/url/pdf /api/v1/convert/pdf/xml /api/v1/convert/pdf/word diff --git a/testing/webpage_urls.txt b/testing/webpage_urls.txt index 8ccaaf0b1..c6c713dd0 100644 --- a/testing/webpage_urls.txt +++ b/testing/webpage_urls.txt @@ -51,3 +51,4 @@ /swagger-ui/index.html /licenses /releases +/add-attachments