mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2025-06-23 07:55:07 +00:00
Compare commits
4 Commits
da767eccbf
...
978e6f3d0c
Author | SHA1 | Date | |
---|---|---|---|
![]() |
978e6f3d0c | ||
![]() |
beb1f35abd | ||
![]() |
f731c00457 | ||
![]() |
c281dbb4bb |
@ -19,14 +19,15 @@ import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.apache.pdfbox.cos.COSDictionary;
|
||||
import org.apache.pdfbox.cos.COSName;
|
||||
import lombok.Data;
|
||||
import lombok.Getter;
|
||||
import lombok.experimental.UtilityClass;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||
import org.apache.pdfbox.pdmodel.PDDocumentCatalog;
|
||||
import org.apache.pdfbox.pdmodel.PDDocumentNameDictionary;
|
||||
import org.apache.pdfbox.pdmodel.PDEmbeddedFilesNameTreeNode;
|
||||
import org.apache.pdfbox.pdmodel.PDPage;
|
||||
import org.apache.pdfbox.pdmodel.PageMode;
|
||||
import org.apache.pdfbox.pdmodel.common.PDRectangle;
|
||||
import org.apache.pdfbox.pdmodel.common.filespecification.PDComplexFileSpecification;
|
||||
import org.apache.pdfbox.pdmodel.common.filespecification.PDEmbeddedFile;
|
||||
@ -35,12 +36,8 @@ import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceDictionary;
|
||||
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceStream;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.Getter;
|
||||
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
|
||||
@ -1137,7 +1134,7 @@ public class EmlToPdf {
|
||||
efTree.setNames(efMap);
|
||||
|
||||
// Set catalog viewer preferences to automatically show attachments pane
|
||||
setCatalogViewerPreferences(document);
|
||||
setCatalogViewerPreferences(document, PageMode.USE_ATTACHMENTS);
|
||||
}
|
||||
|
||||
// Add attachment annotations to the first page for each embedded file
|
||||
@ -1285,38 +1282,6 @@ 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) {
|
||||
|
@ -0,0 +1,45 @@
|
||||
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, PageMode pageMode) {
|
||||
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
|
||||
catalog.setPageMode(pageMode);
|
||||
catalogDict.setName(COSName.PAGE_MODE, pageMode.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.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);
|
||||
}
|
||||
}
|
||||
}
|
@ -16,12 +16,12 @@ import io.github.pixee.security.Filenames;
|
||||
|
||||
public class WebResponseUtils {
|
||||
|
||||
public static ResponseEntity<byte[]> boasToWebResponse(
|
||||
public static ResponseEntity<byte[]> baosToWebResponse(
|
||||
ByteArrayOutputStream baos, String docName) throws IOException {
|
||||
return WebResponseUtils.bytesToWebResponse(baos.toByteArray(), docName);
|
||||
}
|
||||
|
||||
public static ResponseEntity<byte[]> boasToWebResponse(
|
||||
public static ResponseEntity<byte[]> 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);
|
||||
@ -61,9 +61,8 @@ public class WebResponseUtils {
|
||||
// Open Byte Array and save document to it
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
document.save(baos);
|
||||
// Close the document
|
||||
document.close();
|
||||
|
||||
return boasToWebResponse(baos, docName);
|
||||
return baosToWebResponse(baos, docName);
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ public class WebResponseUtilsTest {
|
||||
String docName = "sample.pdf";
|
||||
|
||||
ResponseEntity<byte[]> responseEntity =
|
||||
WebResponseUtils.boasToWebResponse(baos, docName);
|
||||
WebResponseUtils.baosToWebResponse(baos, docName);
|
||||
|
||||
assertNotNull(responseEntity);
|
||||
assertEquals(HttpStatus.OK, responseEntity.getStatusCode());
|
||||
|
@ -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");
|
||||
|
@ -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) {
|
||||
|
@ -1,15 +1,9 @@
|
||||
package stirling.software.SPDF.controller.api.misc;
|
||||
|
||||
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.apache.pdfbox.pdmodel.*;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
@ -21,69 +15,70 @@ 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")
|
||||
@Slf4j
|
||||
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")
|
||||
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<byte[]> addAttachments(
|
||||
@RequestParam("fileInput") MultipartFile pdfFile,
|
||||
@RequestParam("attachments") List<MultipartFile> attachments)
|
||||
throws IOException {
|
||||
|
||||
// Load the PDF document
|
||||
PDDocument document = pdfDocumentFactory.load(pdfFile, true);
|
||||
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
|
||||
PDEmbeddedFilesNameTreeNode efTree = catalog.getNames().getEmbeddedFiles();
|
||||
if (efTree == null) {
|
||||
efTree = new PDEmbeddedFilesNameTreeNode();
|
||||
catalog.getNames().setEmbeddedFiles(efTree);
|
||||
PDDocumentNameDictionary documentNames = catalog.getNames();
|
||||
PDEmbeddedFilesNameTreeNode embeddedFilesTree = new PDEmbeddedFilesNameTreeNode();
|
||||
|
||||
if (documentNames != null) {
|
||||
embeddedFilesTree = documentNames.getEmbeddedFiles();
|
||||
} else {
|
||||
documentNames = new PDDocumentNameDictionary(catalog);
|
||||
documentNames.setEmbeddedFiles(embeddedFilesTree);
|
||||
}
|
||||
|
||||
// Add each attachment
|
||||
for (MultipartFile attachment : attachments) {
|
||||
if (attachment != null && !attachment.isEmpty()) {
|
||||
addEmbeddedFile(document, efTree, attachment);
|
||||
}
|
||||
}
|
||||
// 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 the modified PDF
|
||||
return WebResponseUtils.pdfDocToWebResponse(
|
||||
document,
|
||||
Filenames.toSimpleFileName(pdfFile.getOriginalFilename())
|
||||
.replaceFirst("[.][^.]+$", "") + "_with_attachments.pdf");
|
||||
.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")
|
||||
description =
|
||||
"This endpoint removes all embedded files (attachments) from a PDF. Input:PDF Output:PDF Type:SISO")
|
||||
public ResponseEntity<byte[]> removeAttachments(
|
||||
@RequestParam("fileInput") MultipartFile pdfFile)
|
||||
throws IOException {
|
||||
@RequestParam("fileInput") MultipartFile pdfFile) throws IOException {
|
||||
|
||||
// Load the PDF document
|
||||
PDDocument document = pdfDocumentFactory.load(pdfFile, true);
|
||||
|
||||
// Get the document catalog
|
||||
// Load the PDF document and document catalog
|
||||
PDDocument document = pdfDocumentFactory.load(pdfFile);
|
||||
PDDocumentCatalog catalog = document.getDocumentCatalog();
|
||||
|
||||
// Remove embedded files
|
||||
@ -98,35 +93,7 @@ public class AttachmentsController {
|
||||
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());
|
||||
.replaceFirst("[.][^.]+$", "")
|
||||
+ "_attachments_removed.pdf");
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -205,7 +205,7 @@ public class CertSignController {
|
||||
location,
|
||||
reason,
|
||||
showLogo);
|
||||
return WebResponseUtils.boasToWebResponse(
|
||||
return WebResponseUtils.baosToWebResponse(
|
||||
baos,
|
||||
Filenames.toSimpleFileName(pdf.getOriginalFilename()).replaceFirst("[.][^.]+$", "")
|
||||
+ "_signed.pdf");
|
||||
|
@ -192,10 +192,10 @@ public class OtherWebController {
|
||||
return "misc/auto-rename";
|
||||
}
|
||||
|
||||
@GetMapping("/attachments")
|
||||
@GetMapping("/add-attachments")
|
||||
@Hidden
|
||||
public String attachmentsForm(Model model) {
|
||||
model.addAttribute("currentPage", "attachments");
|
||||
return "misc/attachments";
|
||||
model.addAttribute("currentPage", "add-attachments");
|
||||
return "misc/add-attachments";
|
||||
}
|
||||
}
|
||||
|
@ -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.PageMode;
|
||||
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<MultipartFile> attachments)
|
||||
throws IOException {
|
||||
Map<String, PDComplexFileSpecification> 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<String, PDComplexFileSpecification> 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);
|
||||
|
||||
grantAccessPermissions(document);
|
||||
PDFAttachmentUtils.setCatalogViewerPreferences(document, PageMode.USE_ATTACHMENTS);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
@ -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<MultipartFile> attachments)
|
||||
throws IOException;
|
||||
}
|
@ -471,6 +471,10 @@ home.addImage.title=????? ???? ??? ??? PDF
|
||||
home.addImage.desc=????? ???? ??? ???? ???? ?? PDF (????? ??? ??????)
|
||||
addImage.tags=????,jpg,????,???? ??????????
|
||||
|
||||
home.attachments.title=Add Attachments
|
||||
home.attachments.desc=Add or remove embedded files (attachments) to/from a PDF
|
||||
attachments.tags=embed,attach,file,attachment,attachments
|
||||
|
||||
home.watermark.title=????? ????? ?????
|
||||
home.watermark.desc=??? ????? ????? ????? ??? ????? PDF ????? ??.
|
||||
watermark.tags=??,?????,?????,???,???? ?????,????? ??????,????,jpg,????,???? ??????????
|
||||
@ -1642,3 +1646,17 @@ fakeScan.blur=Blur
|
||||
fakeScan.noise=Noise
|
||||
fakeScan.yellowish=Yellowish (simulate old paper)
|
||||
fakeScan.resolution=Resolution (DPI)
|
||||
|
||||
#attachments
|
||||
home.attachments.title=Add Attachments
|
||||
home.attachments.desc=Add or remove embedded files (attachments) to/from a PDF
|
||||
attachments.tags=embed,attach,file,attachment,attachments
|
||||
attachments.title=Attachments
|
||||
attachments.header=Add attachments
|
||||
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
|
||||
|
@ -1642,3 +1642,17 @@ fakeScan.blur=Blur
|
||||
fakeScan.noise=Noise
|
||||
fakeScan.yellowish=Yellowish (simulate old paper)
|
||||
fakeScan.resolution=Resolution (DPI)
|
||||
|
||||
#attachments
|
||||
home.attachments.title=Add Attachments
|
||||
home.attachments.desc=Add or remove embedded files (attachments) to/from a PDF
|
||||
attachments.tags=embed,attach,file,attachment,attachments
|
||||
attachments.title=Attachments
|
||||
attachments.header=Add attachments
|
||||
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
|
||||
|
@ -1642,3 +1642,17 @@ fakeScan.blur=Blur
|
||||
fakeScan.noise=Noise
|
||||
fakeScan.yellowish=Yellowish (simulate old paper)
|
||||
fakeScan.resolution=Resolution (DPI)
|
||||
|
||||
#attachments
|
||||
home.attachments.title=Add Attachments
|
||||
home.attachments.desc=Add or remove embedded files (attachments) to/from a PDF
|
||||
attachments.tags=embed,attach,file,attachment,attachments
|
||||
attachments.title=Attachments
|
||||
attachments.header=Add attachments
|
||||
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
|
||||
|
@ -1642,3 +1642,17 @@ fakeScan.blur=Blur
|
||||
fakeScan.noise=Noise
|
||||
fakeScan.yellowish=Yellowish (simulate old paper)
|
||||
fakeScan.resolution=Resolution (DPI)
|
||||
|
||||
#attachments
|
||||
home.attachments.title=Add Attachments
|
||||
home.attachments.desc=Add or remove embedded files (attachments) to/from a PDF
|
||||
attachments.tags=embed,attach,file,attachment,attachments
|
||||
attachments.title=Attachments
|
||||
attachments.header=Add attachments
|
||||
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
|
||||
|
@ -1642,3 +1642,17 @@ fakeScan.blur=Blur
|
||||
fakeScan.noise=Noise
|
||||
fakeScan.yellowish=Yellowish (simulate old paper)
|
||||
fakeScan.resolution=Resolution (DPI)
|
||||
|
||||
#attachments
|
||||
home.attachments.title=Add Attachments
|
||||
home.attachments.desc=Add or remove embedded files (attachments) to/from a PDF
|
||||
attachments.tags=embed,attach,file,attachment,attachments
|
||||
attachments.title=Attachments
|
||||
attachments.header=Add attachments
|
||||
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
|
||||
|
@ -1642,3 +1642,16 @@ fakeScan.blur=Blur
|
||||
fakeScan.noise=Noise
|
||||
fakeScan.yellowish=Yellowish (simulate old paper)
|
||||
fakeScan.resolution=Resolution (DPI)
|
||||
#attachments
|
||||
home.attachments.title=Add Attachments
|
||||
home.attachments.desc=Add or remove embedded files (attachments) to/from a PDF
|
||||
attachments.tags=embed,attach,file,attachment,attachments
|
||||
attachments.title=Attachments
|
||||
attachments.header=Add attachments
|
||||
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
|
||||
|
@ -1642,3 +1642,17 @@ fakeScan.blur=Blur
|
||||
fakeScan.noise=Noise
|
||||
fakeScan.yellowish=Yellowish (simulate old paper)
|
||||
fakeScan.resolution=Resolution (DPI)
|
||||
|
||||
#attachments
|
||||
home.attachments.title=Add Attachments
|
||||
home.attachments.desc=Add or remove embedded files (attachments) to/from a PDF
|
||||
attachments.tags=embed,attach,file,attachment,attachments
|
||||
attachments.title=Attachments
|
||||
attachments.header=Add attachments
|
||||
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
|
||||
|
@ -1642,3 +1642,17 @@ fakeScan.blur=Verwischen
|
||||
fakeScan.noise=Rauschen
|
||||
fakeScan.yellowish=Gelblich (simulieren Sie altes Papier)
|
||||
fakeScan.resolution=Auflösung (DPI)
|
||||
|
||||
#attachments
|
||||
home.attachments.title=Add Attachments
|
||||
home.attachments.desc=Add or remove embedded files (attachments) to/from a PDF
|
||||
attachments.tags=embed,attach,file,attachment,attachments
|
||||
attachments.title=Attachments
|
||||
attachments.header=Add attachments
|
||||
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
|
||||
|
@ -1642,3 +1642,17 @@ fakeScan.blur=Blur
|
||||
fakeScan.noise=Noise
|
||||
fakeScan.yellowish=Yellowish (simulate old paper)
|
||||
fakeScan.resolution=Resolution (DPI)
|
||||
|
||||
#attachments
|
||||
home.attachments.title=Add Attachments
|
||||
home.attachments.desc=Add or remove embedded files (attachments) to/from a PDF
|
||||
attachments.tags=embed,attach,file,attachment,attachments
|
||||
attachments.title=Attachments
|
||||
attachments.header=Add attachments
|
||||
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
|
||||
|
@ -513,6 +513,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=Add 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
|
||||
@ -1193,6 +1197,16 @@ addImage.everyPage=Every Page?
|
||||
addImage.upload=Add image
|
||||
addImage.submit=Add image
|
||||
|
||||
#attachments
|
||||
attachments.title=Add Attachments
|
||||
attachments.header=Add attachments
|
||||
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
|
||||
|
@ -472,9 +472,9 @@ 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.title=Add Attachments
|
||||
home.attachments.desc=Add or remove embedded files (attachments) to/from a PDF
|
||||
attachments.tags=embed,attach,file,attachment
|
||||
attachments.tags=embed,attach,file,attachment,attachments
|
||||
|
||||
home.watermark.title=Add Watermark
|
||||
home.watermark.desc=Add a custom watermark to your PDF document.
|
||||
@ -484,7 +484,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
|
||||
@ -1159,7 +1158,7 @@ addImage.submit=Add image
|
||||
|
||||
#attachments
|
||||
attachments.title=Attachments
|
||||
attachments.header=Add attachments to PDF
|
||||
attachments.header=Add attachments
|
||||
attachments.removeHeader=Remove attachments from PDF
|
||||
attachments.selectFiles=Select files to attach
|
||||
attachments.description=Allows you to add attachments to the PDF
|
||||
|
@ -1642,3 +1642,17 @@ fakeScan.blur=Blur
|
||||
fakeScan.noise=Noise
|
||||
fakeScan.yellowish=Yellowish (simulate old paper)
|
||||
fakeScan.resolution=Resolution (DPI)
|
||||
|
||||
#attachments
|
||||
home.attachments.title=Add Attachments
|
||||
home.attachments.desc=Add or remove embedded files (attachments) to/from a PDF
|
||||
attachments.tags=embed,attach,file,attachment,attachments
|
||||
attachments.title=Attachments
|
||||
attachments.header=Add attachments
|
||||
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
|
||||
|
@ -1642,3 +1642,16 @@ fakeScan.blur=Blur
|
||||
fakeScan.noise=Noise
|
||||
fakeScan.yellowish=Yellowish (simulate old paper)
|
||||
fakeScan.resolution=Resolution (DPI)
|
||||
#attachments
|
||||
home.attachments.title=Add Attachments
|
||||
home.attachments.desc=Add or remove embedded files (attachments) to/from a PDF
|
||||
attachments.tags=embed,attach,file,attachment,attachments
|
||||
attachments.title=Attachments
|
||||
attachments.header=Add attachments
|
||||
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
|
||||
|
@ -1642,3 +1642,17 @@ fakeScan.blur=Blur
|
||||
fakeScan.noise=Noise
|
||||
fakeScan.yellowish=Yellowish (simulate old paper)
|
||||
fakeScan.resolution=Resolution (DPI)
|
||||
|
||||
#attachments
|
||||
home.attachments.title=Add Attachments
|
||||
home.attachments.desc=Add or remove embedded files (attachments) to/from a PDF
|
||||
attachments.tags=embed,attach,file,attachment,attachments
|
||||
attachments.title=Attachments
|
||||
attachments.header=Add attachments
|
||||
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
|
||||
|
@ -1642,3 +1642,17 @@ fakeScan.blur=Blur
|
||||
fakeScan.noise=Noise
|
||||
fakeScan.yellowish=Yellowish (simulate old paper)
|
||||
fakeScan.resolution=Resolution (DPI)
|
||||
|
||||
#attachments
|
||||
home.attachments.title=Add Attachments
|
||||
home.attachments.desc=Add or remove embedded files (attachments) to/from a PDF
|
||||
attachments.tags=embed,attach,file,attachment,attachments
|
||||
attachments.title=Attachments
|
||||
attachments.header=Add attachments
|
||||
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
|
||||
|
@ -1642,3 +1642,17 @@ fakeScan.blur=Blur
|
||||
fakeScan.noise=Noise
|
||||
fakeScan.yellowish=Yellowish (simulate old paper)
|
||||
fakeScan.resolution=Resolution (DPI)
|
||||
|
||||
#attachments
|
||||
home.attachments.title=Add Attachments
|
||||
home.attachments.desc=Add or remove embedded files (attachments) to/from a PDF
|
||||
attachments.tags=embed,attach,file,attachment,attachments
|
||||
attachments.title=Attachments
|
||||
attachments.header=Add attachments
|
||||
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
|
||||
|
@ -1642,3 +1642,17 @@ fakeScan.blur=Blur
|
||||
fakeScan.noise=Noise
|
||||
fakeScan.yellowish=Yellowish (simulate old paper)
|
||||
fakeScan.resolution=Resolution (DPI)
|
||||
|
||||
#attachments
|
||||
home.attachments.title=Add Attachments
|
||||
home.attachments.desc=Add or remove embedded files (attachments) to/from a PDF
|
||||
attachments.tags=embed,attach,file,attachment,attachments
|
||||
attachments.title=Attachments
|
||||
attachments.header=Add attachments
|
||||
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
|
||||
|
@ -1642,3 +1642,17 @@ fakeScan.blur=Blur
|
||||
fakeScan.noise=Noise
|
||||
fakeScan.yellowish=Yellowish (simulate old paper)
|
||||
fakeScan.resolution=Resolution (DPI)
|
||||
|
||||
#attachments
|
||||
home.attachments.title=Add Attachments
|
||||
home.attachments.desc=Add or remove embedded files (attachments) to/from a PDF
|
||||
attachments.tags=embed,attach,file,attachment,attachments
|
||||
attachments.title=Attachments
|
||||
attachments.header=Add attachments
|
||||
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
|
||||
|
@ -1642,3 +1642,18 @@ fakeScan.blur=Elmos
|
||||
fakeScan.noise=Zaj
|
||||
fakeScan.yellowish=Sárgás (régi papír szimulálása)
|
||||
fakeScan.resolution=Felbontás (DPI)
|
||||
|
||||
|
||||
#attachments
|
||||
home.attachments.title=Add Attachments
|
||||
home.attachments.desc=Add or remove embedded files (attachments) to/from a PDF
|
||||
attachments.tags=embed,attach,file,attachment,attachments
|
||||
attachments.title=Attachments
|
||||
attachments.header=Add attachments
|
||||
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
|
||||
|
@ -1642,3 +1642,18 @@ fakeScan.blur=Blur
|
||||
fakeScan.noise=Noise
|
||||
fakeScan.yellowish=Yellowish (simulate old paper)
|
||||
fakeScan.resolution=Resolution (DPI)
|
||||
|
||||
#attachments
|
||||
home.attachments.title=Add Attachments
|
||||
home.attachments.desc=Add or remove embedded files (attachments) to/from a PDF
|
||||
attachments.tags=embed,attach,file,attachment,attachments
|
||||
|
||||
attachments.title=Attachments
|
||||
attachments.header=Add attachments
|
||||
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
|
||||
|
@ -1642,3 +1642,18 @@ fakeScan.blur=Sfocatura
|
||||
fakeScan.noise=Rumore
|
||||
fakeScan.yellowish=Giallastro (simula carta vecchia)
|
||||
fakeScan.resolution=Risoluzione (DPI)
|
||||
|
||||
#attachments
|
||||
home.attachments.title=Add Attachments
|
||||
home.attachments.desc=Add or remove embedded files (attachments) to/from a PDF
|
||||
attachments.tags=embed,attach,file,attachment,attachments
|
||||
|
||||
attachments.title=Attachments
|
||||
attachments.header=Add attachments
|
||||
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
|
||||
|
@ -1642,3 +1642,18 @@ fakeScan.blur=Blur
|
||||
fakeScan.noise=Noise
|
||||
fakeScan.yellowish=Yellowish (simulate old paper)
|
||||
fakeScan.resolution=Resolution (DPI)
|
||||
|
||||
#attachments
|
||||
home.attachments.title=Add Attachments
|
||||
home.attachments.desc=Add or remove embedded files (attachments) to/from a PDF
|
||||
attachments.tags=embed,attach,file,attachment,attachments
|
||||
|
||||
attachments.title=Attachments
|
||||
attachments.header=Add attachments
|
||||
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
|
||||
|
@ -1642,3 +1642,18 @@ fakeScan.blur=Blur
|
||||
fakeScan.noise=Noise
|
||||
fakeScan.yellowish=Yellowish (simulate old paper)
|
||||
fakeScan.resolution=Resolution (DPI)
|
||||
|
||||
#attachments
|
||||
home.attachments.title=Add Attachments
|
||||
home.attachments.desc=Add or remove embedded files (attachments) to/from a PDF
|
||||
attachments.tags=embed,attach,file,attachment,attachments
|
||||
|
||||
attachments.title=Attachments
|
||||
attachments.header=Add attachments
|
||||
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
|
||||
|
@ -1642,3 +1642,18 @@ fakeScan.blur=Blur
|
||||
fakeScan.noise=Noise
|
||||
fakeScan.yellowish=Yellowish (simulate old paper)
|
||||
fakeScan.resolution=Resolution (DPI)
|
||||
|
||||
#attachments
|
||||
home.attachments.title=Add Attachments
|
||||
home.attachments.desc=Add or remove embedded files (attachments) to/from a PDF
|
||||
attachments.tags=embed,attach,file,attachment,attachments
|
||||
|
||||
attachments.title=Attachments
|
||||
attachments.header=Add attachments
|
||||
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
|
||||
|
@ -1475,3 +1475,17 @@ cookieBanner.preferencesModal.necessary.description=വെബ്സൈറ്റ
|
||||
cookieBanner.preferencesModal.analytics.title=അനലിറ്റിക്സ്
|
||||
cookieBanner.preferencesModal.analytics.description=ഞങ്ങളുടെ ടൂളുകൾ എങ്ങനെ ഉപയോഗിക്കുന്നുവെന്ന് മനസ്സിലാക്കാൻ ഈ കുക്കികൾ ഞങ്ങളെ സഹായിക്കുന്നു, അതിനാൽ ഞങ്ങളുടെ കമ്മ്യൂണിറ്റി ഏറ്റവും കൂടുതൽ വിലമതിക്കുന്ന ഫീച്ചറുകൾ നിർമ്മിക്കുന്നതിൽ ഞങ്ങൾക്ക് ശ്രദ്ധ കേന്ദ്രീകരിക്കാൻ കഴിയും. ഉറപ്പാക്കുക—സ്റ്റെർലിംഗ് PDF-ന് നിങ്ങൾ പ്രവർത്തിക്കുന്ന പ്രമാണങ്ങളുടെ ഉള്ളടക്കം ട്രാക്ക് ചെയ്യാൻ കഴിയില്ല, ഒരിക്കലും കഴിയില്ല.
|
||||
|
||||
#attachments
|
||||
home.attachments.title=Add Attachments
|
||||
home.attachments.desc=Add or remove embedded files (attachments) to/from a PDF
|
||||
attachments.tags=embed,attach,file,attachment,attachments
|
||||
|
||||
attachments.title=Attachments
|
||||
attachments.header=Add attachments
|
||||
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
|
||||
|
@ -1642,3 +1642,18 @@ fakeScan.blur=Blur
|
||||
fakeScan.noise=Noise
|
||||
fakeScan.yellowish=Yellowish (simulate old paper)
|
||||
fakeScan.resolution=Resolution (DPI)
|
||||
|
||||
#attachments
|
||||
home.attachments.title=Add Attachments
|
||||
home.attachments.desc=Add or remove embedded files (attachments) to/from a PDF
|
||||
attachments.tags=embed,attach,file,attachment,attachments
|
||||
|
||||
attachments.title=Attachments
|
||||
attachments.header=Add attachments
|
||||
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
|
||||
|
@ -1642,3 +1642,18 @@ fakeScan.blur=Blur
|
||||
fakeScan.noise=Noise
|
||||
fakeScan.yellowish=Yellowish (simulate old paper)
|
||||
fakeScan.resolution=Resolution (DPI)
|
||||
|
||||
#attachments
|
||||
home.attachments.title=Add Attachments
|
||||
home.attachments.desc=Add or remove embedded files (attachments) to/from a PDF
|
||||
attachments.tags=embed,attach,file,attachment,attachments
|
||||
|
||||
attachments.title=Attachments
|
||||
attachments.header=Add attachments
|
||||
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
|
||||
|
@ -1642,3 +1642,18 @@ fakeScan.blur=Blur
|
||||
fakeScan.noise=Noise
|
||||
fakeScan.yellowish=Yellowish (simulate old paper)
|
||||
fakeScan.resolution=Resolution (DPI)
|
||||
|
||||
#attachments
|
||||
home.attachments.title=Add Attachments
|
||||
home.attachments.desc=Add or remove embedded files (attachments) to/from a PDF
|
||||
attachments.tags=embed,attach,file,attachment,attachments
|
||||
|
||||
attachments.title=Attachments
|
||||
attachments.header=Add attachments
|
||||
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
|
||||
|
@ -1642,3 +1642,18 @@ fakeScan.blur=Blur
|
||||
fakeScan.noise=Noise
|
||||
fakeScan.yellowish=Yellowish (simulate old paper)
|
||||
fakeScan.resolution=Resolution (DPI)
|
||||
|
||||
#attachments
|
||||
home.attachments.title=Add Attachments
|
||||
home.attachments.desc=Add or remove embedded files (attachments) to/from a PDF
|
||||
attachments.tags=embed,attach,file,attachment,attachments
|
||||
|
||||
attachments.title=Attachments
|
||||
attachments.header=Add attachments
|
||||
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
|
||||
|
@ -1642,3 +1642,19 @@ fakeScan.blur=Blur
|
||||
fakeScan.noise=Noise
|
||||
fakeScan.yellowish=Yellowish (simulate old paper)
|
||||
fakeScan.resolution=Resolution (DPI)
|
||||
|
||||
|
||||
#attachments
|
||||
home.attachments.title=Add Attachments
|
||||
home.attachments.desc=Add or remove embedded files (attachments) to/from a PDF
|
||||
attachments.tags=embed,attach,file,attachment,attachments
|
||||
|
||||
attachments.title=Attachments
|
||||
attachments.header=Add attachments
|
||||
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
|
||||
|
@ -1642,3 +1642,18 @@ fakeScan.blur=Blur
|
||||
fakeScan.noise=Noise
|
||||
fakeScan.yellowish=Yellowish (simulate old paper)
|
||||
fakeScan.resolution=Resolution (DPI)
|
||||
|
||||
#attachments
|
||||
home.attachments.title=Add Attachments
|
||||
home.attachments.desc=Add or remove embedded files (attachments) to/from a PDF
|
||||
attachments.tags=embed,attach,file,attachment,attachments
|
||||
|
||||
attachments.title=Attachments
|
||||
attachments.header=Add attachments
|
||||
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
|
||||
|
@ -1642,3 +1642,18 @@ fakeScan.blur=Blur
|
||||
fakeScan.noise=Noise
|
||||
fakeScan.yellowish=Yellowish (simulate old paper)
|
||||
fakeScan.resolution=Resolution (DPI)
|
||||
|
||||
#attachments
|
||||
home.attachments.title=Add Attachments
|
||||
home.attachments.desc=Add or remove embedded files (attachments) to/from a PDF
|
||||
attachments.tags=embed,attach,file,attachment,attachments
|
||||
|
||||
attachments.title=Attachments
|
||||
attachments.header=Add attachments
|
||||
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
|
||||
|
@ -1642,3 +1642,18 @@ fakeScan.blur=Blur
|
||||
fakeScan.noise=Noise
|
||||
fakeScan.yellowish=Yellowish (simulate old paper)
|
||||
fakeScan.resolution=Resolution (DPI)
|
||||
|
||||
#attachments
|
||||
home.attachments.title=Add Attachments
|
||||
home.attachments.desc=Add or remove embedded files (attachments) to/from a PDF
|
||||
attachments.tags=embed,attach,file,attachment,attachments
|
||||
|
||||
attachments.title=Attachments
|
||||
attachments.header=Add attachments
|
||||
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
|
||||
|
@ -1642,3 +1642,18 @@ fakeScan.blur=Blur
|
||||
fakeScan.noise=Noise
|
||||
fakeScan.yellowish=Yellowish (simulate old paper)
|
||||
fakeScan.resolution=Resolution (DPI)
|
||||
|
||||
#attachments
|
||||
home.attachments.title=Add Attachments
|
||||
home.attachments.desc=Add or remove embedded files (attachments) to/from a PDF
|
||||
attachments.tags=embed,attach,file,attachment,attachments
|
||||
|
||||
attachments.title=Attachments
|
||||
attachments.header=Add attachments
|
||||
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
|
||||
|
@ -1642,3 +1642,18 @@ fakeScan.blur=Blur
|
||||
fakeScan.noise=Noise
|
||||
fakeScan.yellowish=Yellowish (simulate old paper)
|
||||
fakeScan.resolution=Resolution (DPI)
|
||||
|
||||
#attachments
|
||||
home.attachments.title=Add Attachments
|
||||
home.attachments.desc=Add or remove embedded files (attachments) to/from a PDF
|
||||
attachments.tags=embed,attach,file,attachment,attachments
|
||||
|
||||
attachments.title=Attachments
|
||||
attachments.header=Add attachments
|
||||
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
|
||||
|
@ -1642,3 +1642,18 @@ fakeScan.blur=Blur
|
||||
fakeScan.noise=Noise
|
||||
fakeScan.yellowish=Yellowish (simulate old paper)
|
||||
fakeScan.resolution=Resolution (DPI)
|
||||
|
||||
#attachments
|
||||
home.attachments.title=Add Attachments
|
||||
home.attachments.desc=Add or remove embedded files (attachments) to/from a PDF
|
||||
attachments.tags=embed,attach,file,attachment,attachments
|
||||
|
||||
attachments.title=Attachments
|
||||
attachments.header=Add attachments
|
||||
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
|
||||
|
@ -1642,3 +1642,18 @@ fakeScan.blur=Blur
|
||||
fakeScan.noise=Noise
|
||||
fakeScan.yellowish=Yellowish (simulate old paper)
|
||||
fakeScan.resolution=Resolution (DPI)
|
||||
|
||||
#attachments
|
||||
home.attachments.title=Add Attachments
|
||||
home.attachments.desc=Add or remove embedded files (attachments) to/from a PDF
|
||||
attachments.tags=embed,attach,file,attachment,attachments
|
||||
|
||||
attachments.title=Attachments
|
||||
attachments.header=Add attachments
|
||||
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
|
||||
|
@ -1642,3 +1642,18 @@ fakeScan.blur=Blur
|
||||
fakeScan.noise=Noise
|
||||
fakeScan.yellowish=Yellowish (simulate old paper)
|
||||
fakeScan.resolution=Resolution (DPI)
|
||||
|
||||
#attachments
|
||||
home.attachments.title=Add Attachments
|
||||
home.attachments.desc=Add or remove embedded files (attachments) to/from a PDF
|
||||
attachments.tags=embed,attach,file,attachment,attachments
|
||||
|
||||
attachments.title=Attachments
|
||||
attachments.header=Add attachments
|
||||
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
|
||||
|
@ -1642,3 +1642,18 @@ fakeScan.blur=Blur
|
||||
fakeScan.noise=Noise
|
||||
fakeScan.yellowish=Yellowish (simulate old paper)
|
||||
fakeScan.resolution=Resolution (DPI)
|
||||
|
||||
#attachments
|
||||
home.attachments.title=Add Attachments
|
||||
home.attachments.desc=Add or remove embedded files (attachments) to/from a PDF
|
||||
attachments.tags=embed,attach,file,attachment,attachments
|
||||
|
||||
attachments.title=Attachments
|
||||
attachments.header=Add attachments
|
||||
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
|
||||
|
@ -1642,3 +1642,18 @@ fakeScan.blur=Blur
|
||||
fakeScan.noise=Noise
|
||||
fakeScan.yellowish=Yellowish (simulate old paper)
|
||||
fakeScan.resolution=Resolution (DPI)
|
||||
|
||||
#attachments
|
||||
home.attachments.title=Add Attachments
|
||||
home.attachments.desc=Add or remove embedded files (attachments) to/from a PDF
|
||||
attachments.tags=embed,attach,file,attachment,attachments
|
||||
|
||||
attachments.title=Attachments
|
||||
attachments.header=Add attachments
|
||||
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
|
||||
|
@ -1642,3 +1642,18 @@ fakeScan.blur=Blur
|
||||
fakeScan.noise=Noise
|
||||
fakeScan.yellowish=Yellowish (simulate old paper)
|
||||
fakeScan.resolution=Resolution (DPI)
|
||||
|
||||
#attachments
|
||||
home.attachments.title=Add Attachments
|
||||
home.attachments.desc=Add or remove embedded files (attachments) to/from a PDF
|
||||
attachments.tags=embed,attach,file,attachment,attachments
|
||||
|
||||
attachments.title=Attachments
|
||||
attachments.header=Add attachments
|
||||
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
|
||||
|
@ -1642,3 +1642,18 @@ fakeScan.blur=Blur
|
||||
fakeScan.noise=Noise
|
||||
fakeScan.yellowish=Yellowish (simulate old paper)
|
||||
fakeScan.resolution=Resolution (DPI)
|
||||
|
||||
#attachments
|
||||
home.attachments.title=Add Attachments
|
||||
home.attachments.desc=Add or remove embedded files (attachments) to/from a PDF
|
||||
attachments.tags=embed,attach,file,attachment,attachments
|
||||
|
||||
attachments.title=Attachments
|
||||
attachments.header=Add attachments
|
||||
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
|
||||
|
@ -237,6 +237,9 @@
|
||||
<div
|
||||
th:replace="~{fragments/navbarEntry :: navbarEntry('unlock-pdf-forms', 'preview_off', 'home.unlockPDFForms.title', 'home.unlockPDFForms.desc', 'unlockPDFForms.tags', 'other')}">
|
||||
</div>
|
||||
<div
|
||||
th:replace="~{fragments/navbarEntry :: navbarEntry('add-attachments', 'attachment', 'home.attachments.title', 'home.attachments.desc', 'attachments.tags', 'other')}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="groupAdvanced" class="feature-group">
|
||||
|
@ -290,6 +290,9 @@
|
||||
<div
|
||||
th:replace="~{fragments/card :: card(id='unlock-pdf-forms', cardTitle=#{home.unlockPDFForms.title}, cardText=#{home.unlockPDFForms.desc}, cardLink='unlock-pdf-forms', toolIcon='preview_off', tags=#{unlockPDFForms.tags}, toolGroup='other')}">
|
||||
</div>
|
||||
<div
|
||||
th:replace="~{fragments/navbarEntry :: navbarEntry('add-attachments', 'attachment', 'home.attachments.title', 'home.attachments.desc', 'attachments.tags', 'other')}">
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
@ -0,0 +1,42 @@
|
||||
<!DOCTYPE html>
|
||||
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}"
|
||||
xmlns:th="https://www.thymeleaf.org">
|
||||
|
||||
<head>
|
||||
<th:block th:insert="~{fragments/common :: head(title=#{attachments.title}, header=#{attachments.header})}"></th:block>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="page-container">
|
||||
<div id="content-wrap">
|
||||
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
|
||||
<br><br>
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6 bg-card">
|
||||
<div class="tool-header">
|
||||
<span class="material-symbols-rounded tool-header-icon other">attachment</span>
|
||||
<span class="tool-header-text" th:text="#{attachments.header}"></span>
|
||||
</div>
|
||||
|
||||
<form action="#" th:action="@{/api/v1/misc/add-attachments}" method="post" enctype="multipart/form-data">
|
||||
<!-- PDF file selector -->
|
||||
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', disableMultipleFiles=true, multipleInputsForSingleRequest=false, accept='application/pdf')}">
|
||||
</div>
|
||||
|
||||
<!-- Attachment files selector -->
|
||||
<div th:replace="~{fragments/common :: fileSelector(name='attachments', disableMultipleFiles=false, multipleInputsForSingleRequest=true, accept='*/*', inputText=#{attachments.selectFiles})}">
|
||||
</div>
|
||||
|
||||
<!-- Submit button -->
|
||||
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{attachments.addButton}">Add Attachments</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -0,0 +1,209 @@
|
||||
package stirling.software.SPDF.controller.api.misc;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
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.PDDocumentNameDictionary;
|
||||
import org.apache.pdfbox.pdmodel.PDEmbeddedFilesNameTreeNode;
|
||||
import org.apache.pdfbox.pdmodel.PageMode;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.mock.web.MockMultipartFile;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import stirling.software.SPDF.service.PDFAttachmentServiceInterface;
|
||||
import stirling.software.common.service.CustomPDFDocumentFactory;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class AttachmentsControllerTest {
|
||||
|
||||
@Mock
|
||||
private CustomPDFDocumentFactory pdfDocumentFactory;
|
||||
|
||||
@Mock
|
||||
private PDFAttachmentServiceInterface pdfAttachmentService;
|
||||
|
||||
@InjectMocks
|
||||
private AttachmentsController attachmentsController;
|
||||
|
||||
private MockMultipartFile pdfFile;
|
||||
private MockMultipartFile attachment1;
|
||||
private MockMultipartFile attachment2;
|
||||
private PDDocument mockDocument;
|
||||
private PDDocumentCatalog mockCatalog;
|
||||
private PDDocumentNameDictionary mockNameDict;
|
||||
private PDEmbeddedFilesNameTreeNode mockEmbeddedFilesTree;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
pdfFile = new MockMultipartFile("fileInput", "test.pdf", "application/pdf", "PDF content".getBytes());
|
||||
attachment1 = new MockMultipartFile("attachment1", "file1.txt", "text/plain", "File 1 content".getBytes());
|
||||
attachment2 = new MockMultipartFile("attachment2", "file2.jpg", "image/jpeg", "Image content".getBytes());
|
||||
|
||||
mockDocument = mock(PDDocument.class);
|
||||
mockCatalog = mock(PDDocumentCatalog.class);
|
||||
mockNameDict = mock(PDDocumentNameDictionary.class);
|
||||
mockEmbeddedFilesTree = mock(PDEmbeddedFilesNameTreeNode.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void addAttachments_WithExistingNames() throws IOException {
|
||||
List<MultipartFile> attachments = List.of(attachment1, attachment2);
|
||||
|
||||
when(pdfDocumentFactory.load(pdfFile, false)).thenReturn(mockDocument);
|
||||
when(mockDocument.getDocumentCatalog()).thenReturn(mockCatalog);
|
||||
when(mockCatalog.getNames()).thenReturn(mockNameDict);
|
||||
when(mockNameDict.getEmbeddedFiles()).thenReturn(mockEmbeddedFilesTree);
|
||||
|
||||
ResponseEntity<byte[]> response = attachmentsController.addAttachments(pdfFile, attachments);
|
||||
|
||||
assertNotNull(response);
|
||||
assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||
verify(pdfDocumentFactory).load(pdfFile, false);
|
||||
verify(mockCatalog).setNames(mockNameDict);
|
||||
verify(pdfAttachmentService).addAttachment(mockDocument, mockEmbeddedFilesTree, attachments);
|
||||
}
|
||||
|
||||
@Test
|
||||
void addAttachments_WithoutExistingNames() throws IOException {
|
||||
List<MultipartFile> attachments = List.of(attachment1);
|
||||
|
||||
try (PDDocument realDocument = new PDDocument()) {
|
||||
when(pdfDocumentFactory.load(pdfFile, false)).thenReturn(realDocument);
|
||||
|
||||
ResponseEntity<byte[]> response = attachmentsController.addAttachments(pdfFile, attachments);
|
||||
|
||||
assertNotNull(response);
|
||||
assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||
verify(pdfDocumentFactory).load(pdfFile, false);
|
||||
verify(pdfAttachmentService).addAttachment(eq(realDocument), any(PDEmbeddedFilesNameTreeNode.class), eq(attachments));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void addAttachments_IOExceptionFromPDFLoad() throws IOException {
|
||||
List<MultipartFile> attachments = List.of(attachment1);
|
||||
IOException ioException = new IOException("Failed to load PDF");
|
||||
|
||||
when(pdfDocumentFactory.load(pdfFile, false)).thenThrow(ioException);
|
||||
|
||||
assertThrows(IOException.class, () -> attachmentsController.addAttachments(pdfFile, attachments));
|
||||
verify(pdfDocumentFactory).load(pdfFile, false);
|
||||
verifyNoInteractions(pdfAttachmentService);
|
||||
}
|
||||
|
||||
@Test
|
||||
void addAttachments_IOExceptionFromAttachmentService() throws IOException {
|
||||
List<MultipartFile> attachments = List.of(attachment1);
|
||||
IOException ioException = new IOException("Failed to add attachment");
|
||||
|
||||
when(pdfDocumentFactory.load(pdfFile, false)).thenReturn(mockDocument);
|
||||
when(mockDocument.getDocumentCatalog()).thenReturn(mockCatalog);
|
||||
when(mockCatalog.getNames()).thenReturn(mockNameDict);
|
||||
when(mockNameDict.getEmbeddedFiles()).thenReturn(mockEmbeddedFilesTree);
|
||||
doThrow(ioException).when(pdfAttachmentService).addAttachment(mockDocument, mockEmbeddedFilesTree, attachments);
|
||||
|
||||
assertThrows(IOException.class, () -> attachmentsController.addAttachments(pdfFile, attachments));
|
||||
verify(pdfAttachmentService).addAttachment(mockDocument, mockEmbeddedFilesTree, attachments);
|
||||
}
|
||||
|
||||
@Test
|
||||
void removeAttachments_WithExistingNames() throws IOException {
|
||||
when(pdfDocumentFactory.load(pdfFile)).thenReturn(mockDocument);
|
||||
when(mockDocument.getDocumentCatalog()).thenReturn(mockCatalog);
|
||||
when(mockCatalog.getNames()).thenReturn(mockNameDict);
|
||||
|
||||
ResponseEntity<byte[]> response = attachmentsController.removeAttachments(pdfFile);
|
||||
|
||||
assertNotNull(response);
|
||||
assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||
verify(pdfDocumentFactory).load(pdfFile);
|
||||
verify(mockNameDict).setEmbeddedFiles(null);
|
||||
verify(mockCatalog).setPageMode(PageMode.USE_NONE);
|
||||
}
|
||||
|
||||
@Test
|
||||
void removeAttachments_WithoutExistingNames() throws IOException {
|
||||
when(pdfDocumentFactory.load(pdfFile)).thenReturn(mockDocument);
|
||||
when(mockDocument.getDocumentCatalog()).thenReturn(mockCatalog);
|
||||
when(mockCatalog.getNames()).thenReturn(null);
|
||||
|
||||
ResponseEntity<byte[]> response = attachmentsController.removeAttachments(pdfFile);
|
||||
|
||||
assertNotNull(response);
|
||||
assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||
verify(pdfDocumentFactory).load(pdfFile);
|
||||
verify(mockCatalog).setPageMode(PageMode.USE_NONE);
|
||||
verifyNoInteractions(mockNameDict);
|
||||
}
|
||||
|
||||
@Test
|
||||
void removeAttachments_IOExceptionFromPDFLoad() throws IOException {
|
||||
IOException ioException = new IOException("Failed to load PDF");
|
||||
|
||||
when(pdfDocumentFactory.load(pdfFile)).thenThrow(ioException);
|
||||
|
||||
assertThrows(IOException.class, () -> attachmentsController.removeAttachments(pdfFile));
|
||||
verify(pdfDocumentFactory).load(pdfFile);
|
||||
}
|
||||
|
||||
@Test
|
||||
void addAttachments_EmptyAttachmentsList() throws IOException {
|
||||
List<MultipartFile> emptyAttachments = List.of();
|
||||
|
||||
when(pdfDocumentFactory.load(pdfFile, false)).thenReturn(mockDocument);
|
||||
when(mockDocument.getDocumentCatalog()).thenReturn(mockCatalog);
|
||||
when(mockCatalog.getNames()).thenReturn(mockNameDict);
|
||||
when(mockNameDict.getEmbeddedFiles()).thenReturn(mockEmbeddedFilesTree);
|
||||
|
||||
ResponseEntity<byte[]> response = attachmentsController.addAttachments(pdfFile, emptyAttachments);
|
||||
|
||||
assertNotNull(response);
|
||||
assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||
verify(pdfAttachmentService).addAttachment(mockDocument, mockEmbeddedFilesTree, emptyAttachments);
|
||||
}
|
||||
|
||||
@Test
|
||||
void addAttachments_NullFilename() throws IOException {
|
||||
MockMultipartFile attachmentWithNullName = new MockMultipartFile("attachment", null, "text/plain", "content".getBytes());
|
||||
List<MultipartFile> attachments = List.of(attachmentWithNullName);
|
||||
|
||||
when(pdfDocumentFactory.load(pdfFile, false)).thenReturn(mockDocument);
|
||||
when(mockDocument.getDocumentCatalog()).thenReturn(mockCatalog);
|
||||
when(mockCatalog.getNames()).thenReturn(mockNameDict);
|
||||
when(mockNameDict.getEmbeddedFiles()).thenReturn(mockEmbeddedFilesTree);
|
||||
|
||||
ResponseEntity<byte[]> response = attachmentsController.addAttachments(pdfFile, attachments);
|
||||
|
||||
assertNotNull(response);
|
||||
assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||
verify(pdfAttachmentService).addAttachment(mockDocument, mockEmbeddedFilesTree, attachments);
|
||||
}
|
||||
|
||||
@Test
|
||||
void removeAttachments_NullPDFFilename() throws IOException {
|
||||
MockMultipartFile pdfWithNullName = new MockMultipartFile("fileInput", null, "application/pdf", "PDF content".getBytes());
|
||||
|
||||
when(pdfDocumentFactory.load(pdfWithNullName)).thenReturn(mockDocument);
|
||||
when(mockDocument.getDocumentCatalog()).thenReturn(mockCatalog);
|
||||
when(mockCatalog.getNames()).thenReturn(null);
|
||||
|
||||
ResponseEntity<byte[]> response = attachmentsController.removeAttachments(pdfWithNullName);
|
||||
|
||||
assertNotNull(response);
|
||||
assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||
verify(mockCatalog).setPageMode(PageMode.USE_NONE);
|
||||
}
|
||||
}
|
@ -0,0 +1,119 @@
|
||||
package stirling.software.SPDF.service;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||
import org.apache.pdfbox.pdmodel.PDEmbeddedFilesNameTreeNode;
|
||||
import org.apache.pdfbox.pdmodel.common.filespecification.PDComplexFileSpecification;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.mockito.ArgumentMatchers.anyMap;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
class PDFAttachmentServiceTest {
|
||||
|
||||
private PDFAttachmentService pdfAttachmentService;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
pdfAttachmentService = new PDFAttachmentService();
|
||||
}
|
||||
|
||||
@Test
|
||||
void addAttachmentToPDF() throws IOException {
|
||||
try (var document = new PDDocument()) {
|
||||
var embeddedFilesTree = mock(PDEmbeddedFilesNameTreeNode.class);
|
||||
var attachments = List.of(mock(MultipartFile.class));
|
||||
var existingNames = new HashMap<String, PDComplexFileSpecification>();
|
||||
|
||||
when(embeddedFilesTree.getNames()).thenReturn(existingNames);
|
||||
when(attachments.get(0).getOriginalFilename()).thenReturn("test.txt");
|
||||
when(attachments.get(0).getInputStream()).thenReturn(
|
||||
new ByteArrayInputStream("Test content".getBytes()));
|
||||
when(attachments.get(0).getSize()).thenReturn(12L);
|
||||
when(attachments.get(0).getContentType()).thenReturn("text/plain");
|
||||
|
||||
pdfAttachmentService.addAttachment(document, embeddedFilesTree, attachments);
|
||||
|
||||
verify(embeddedFilesTree).setNames(anyMap());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void addAttachmentToPDF_WithNullExistingNames() throws IOException {
|
||||
try (var document = new PDDocument()) {
|
||||
var embeddedFilesTree = mock(PDEmbeddedFilesNameTreeNode.class);
|
||||
var attachments = List.of(mock(MultipartFile.class));
|
||||
|
||||
when(embeddedFilesTree.getNames()).thenReturn(null);
|
||||
when(attachments.get(0).getOriginalFilename()).thenReturn("document.pdf");
|
||||
when(attachments.get(0).getInputStream()).thenReturn(
|
||||
new ByteArrayInputStream("PDF content".getBytes()));
|
||||
when(attachments.get(0).getSize()).thenReturn(15L);
|
||||
when(attachments.get(0).getContentType()).thenReturn("application/pdf");
|
||||
|
||||
pdfAttachmentService.addAttachment(document, embeddedFilesTree, attachments);
|
||||
|
||||
verify(embeddedFilesTree).setNames(anyMap());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void addAttachmentToPDF_WithBlankContentType() throws IOException {
|
||||
try (var document = new PDDocument()) {
|
||||
var embeddedFilesTree = mock(PDEmbeddedFilesNameTreeNode.class);
|
||||
var attachments = List.of(mock(MultipartFile.class));
|
||||
var existingNames = new HashMap<String, PDComplexFileSpecification>();
|
||||
|
||||
when(embeddedFilesTree.getNames()).thenReturn(existingNames);
|
||||
when(attachments.get(0).getOriginalFilename()).thenReturn("image.jpg");
|
||||
when(attachments.get(0).getInputStream()).thenReturn(
|
||||
new ByteArrayInputStream("Image content".getBytes()));
|
||||
when(attachments.get(0).getSize()).thenReturn(25L);
|
||||
when(attachments.get(0).getContentType()).thenReturn("");
|
||||
|
||||
pdfAttachmentService.addAttachment(document, embeddedFilesTree, attachments);
|
||||
|
||||
verify(embeddedFilesTree).setNames(anyMap());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void addAttachmentToPDF_GetNamesThrowsIOException() throws IOException {
|
||||
var document = mock(PDDocument.class);
|
||||
var embeddedFilesTree = mock(PDEmbeddedFilesNameTreeNode.class);
|
||||
var attachments = List.of(mock(MultipartFile.class));
|
||||
var ioException = new IOException("Failed to retrieve embedded files");
|
||||
|
||||
when(embeddedFilesTree.getNames()).thenThrow(ioException);
|
||||
|
||||
assertThrows(IOException.class, () -> pdfAttachmentService.addAttachment(document, embeddedFilesTree, attachments));
|
||||
|
||||
verify(embeddedFilesTree).getNames();
|
||||
}
|
||||
|
||||
@Test
|
||||
void addAttachmentToPDF_AttachmentInputStreamThrowsIOException() throws IOException {
|
||||
try (var document = new PDDocument()) {
|
||||
var embeddedFilesTree = mock(PDEmbeddedFilesNameTreeNode.class);
|
||||
var attachments = List.of(mock(MultipartFile.class));
|
||||
var existingNames = new HashMap<String, PDComplexFileSpecification>();
|
||||
var ioException = new IOException("Failed to read attachment stream");
|
||||
|
||||
when(embeddedFilesTree.getNames()).thenReturn(existingNames);
|
||||
when(attachments.get(0).getOriginalFilename()).thenReturn("corrupted.file");
|
||||
when(attachments.get(0).getInputStream()).thenThrow(ioException);
|
||||
when(attachments.get(0).getSize()).thenReturn(10L);
|
||||
|
||||
pdfAttachmentService.addAttachment(document, embeddedFilesTree, attachments);
|
||||
|
||||
verify(embeddedFilesTree).setNames(anyMap());
|
||||
}
|
||||
}
|
||||
}
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -51,3 +51,4 @@
|
||||
/swagger-ui/index.html
|
||||
/licenses
|
||||
/releases
|
||||
/add-attachments
|
||||
|
Loading…
x
Reference in New Issue
Block a user