mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2025-06-21 23:15:03 +00:00
Fixed inaccessible attachments, clean up
This commit is contained in:
parent
244cbe36ff
commit
f16ddeb583
@ -27,5 +27,5 @@ dependencies {
|
||||
api 'jakarta.servlet:jakarta.servlet-api:6.1.0'
|
||||
api 'org.snakeyaml:snakeyaml-engine:2.9'
|
||||
api "org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.9"
|
||||
api 'jakarta.mail:jakarta.mail-api:2.1.3'
|
||||
api 'com.sun.mail:jakarta.mail:2.0.1'
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
package stirling.software.common.util;
|
||||
|
||||
import static stirling.software.common.util.PDFAttachmentUtils.setCatalogViewerPreferences;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
@ -20,10 +22,7 @@ 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 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;
|
||||
@ -43,7 +42,8 @@ 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;
|
||||
import stirling.software.common.model.api.converters.HTMLToPdfRequest;
|
||||
import stirling.software.common.service.CustomPDFDocumentFactory;
|
||||
|
||||
@Slf4j
|
||||
@UtilityClass
|
||||
@ -197,8 +197,7 @@ public class EmlToPdf {
|
||||
boolean disableSanitize)
|
||||
throws IOException, InterruptedException {
|
||||
|
||||
stirling.software.common.model.api.converters.HTMLToPdfRequest htmlRequest =
|
||||
createHtmlRequest(request);
|
||||
HTMLToPdfRequest htmlRequest = createHtmlRequest(request);
|
||||
|
||||
try {
|
||||
return FileToPdf.convertHtmlToPdf(
|
||||
@ -882,33 +881,33 @@ public class EmlToPdf {
|
||||
Class<?> messageClass = message.getClass();
|
||||
|
||||
// Extract headers via reflection
|
||||
java.lang.reflect.Method getSubject = messageClass.getMethod("getSubject");
|
||||
Method getSubject = messageClass.getMethod("getSubject");
|
||||
String subject = (String) getSubject.invoke(message);
|
||||
content.setSubject(subject != null ? safeMimeDecode(subject) : "No Subject");
|
||||
|
||||
java.lang.reflect.Method getFrom = messageClass.getMethod("getFrom");
|
||||
Method getFrom = messageClass.getMethod("getFrom");
|
||||
Object[] fromAddresses = (Object[]) getFrom.invoke(message);
|
||||
content.setFrom(
|
||||
fromAddresses != null && fromAddresses.length > 0
|
||||
? safeMimeDecode(fromAddresses[0].toString())
|
||||
: "");
|
||||
|
||||
java.lang.reflect.Method getAllRecipients = messageClass.getMethod("getAllRecipients");
|
||||
Method getAllRecipients = messageClass.getMethod("getAllRecipients");
|
||||
Object[] recipients = (Object[]) getAllRecipients.invoke(message);
|
||||
content.setTo(
|
||||
recipients != null && recipients.length > 0
|
||||
? safeMimeDecode(recipients[0].toString())
|
||||
: "");
|
||||
|
||||
java.lang.reflect.Method getSentDate = messageClass.getMethod("getSentDate");
|
||||
Method getSentDate = messageClass.getMethod("getSentDate");
|
||||
content.setDate((Date) getSentDate.invoke(message));
|
||||
|
||||
// Extract content
|
||||
java.lang.reflect.Method getContent = messageClass.getMethod("getContent");
|
||||
Method getContent = messageClass.getMethod("getContent");
|
||||
Object messageContent = getContent.invoke(message);
|
||||
|
||||
if (messageContent instanceof String stringContent) {
|
||||
java.lang.reflect.Method getContentType = messageClass.getMethod("getContentType");
|
||||
Method getContentType = messageClass.getMethod("getContentType");
|
||||
String contentType = (String) getContentType.invoke(message);
|
||||
if (contentType != null && contentType.toLowerCase().contains("text/html")) {
|
||||
content.setHtmlBody(stringContent);
|
||||
@ -947,11 +946,10 @@ public class EmlToPdf {
|
||||
}
|
||||
|
||||
Class<?> multipartClass = multipart.getClass();
|
||||
java.lang.reflect.Method getCount = multipartClass.getMethod("getCount");
|
||||
Method getCount = multipartClass.getMethod("getCount");
|
||||
int count = (Integer) getCount.invoke(multipart);
|
||||
|
||||
java.lang.reflect.Method getBodyPart =
|
||||
multipartClass.getMethod("getBodyPart", int.class);
|
||||
Method getBodyPart = multipartClass.getMethod("getBodyPart", int.class);
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
Object part = getBodyPart.invoke(multipart, i);
|
||||
@ -972,12 +970,12 @@ public class EmlToPdf {
|
||||
}
|
||||
|
||||
Class<?> partClass = part.getClass();
|
||||
java.lang.reflect.Method isMimeType = partClass.getMethod("isMimeType", String.class);
|
||||
java.lang.reflect.Method getContent = partClass.getMethod("getContent");
|
||||
java.lang.reflect.Method getDisposition = partClass.getMethod("getDisposition");
|
||||
java.lang.reflect.Method getFileName = partClass.getMethod("getFileName");
|
||||
java.lang.reflect.Method getContentType = partClass.getMethod("getContentType");
|
||||
java.lang.reflect.Method getHeader = partClass.getMethod("getHeader", String.class);
|
||||
Method isMimeType = partClass.getMethod("isMimeType", String.class);
|
||||
Method getContent = partClass.getMethod("getContent");
|
||||
Method getDisposition = partClass.getMethod("getDisposition");
|
||||
Method getFileName = partClass.getMethod("getFileName");
|
||||
Method getContentType = partClass.getMethod("getContentType");
|
||||
Method getHeader = partClass.getMethod("getHeader", String.class);
|
||||
|
||||
Object disposition = getDisposition.invoke(part);
|
||||
String filename = (String) getFileName.invoke(part);
|
||||
@ -1184,7 +1182,7 @@ public class EmlToPdf {
|
||||
private static byte[] attachFilesToPdf(
|
||||
byte[] pdfBytes,
|
||||
List<EmailAttachment> attachments,
|
||||
stirling.software.common.service.CustomPDFDocumentFactory pdfDocumentFactory)
|
||||
CustomPDFDocumentFactory pdfDocumentFactory)
|
||||
throws IOException {
|
||||
try (PDDocument document = pdfDocumentFactory.load(pdfBytes);
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
|
||||
|
@ -1,12 +1,13 @@
|
||||
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;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
public class PDFAttachmentUtils {
|
||||
|
||||
@ -18,24 +19,29 @@ public class PDFAttachmentUtils {
|
||||
COSDictionary catalogDict = catalog.getCOSObject();
|
||||
|
||||
// Set PageMode to UseAttachments - this is the standard PDF specification approach
|
||||
// PageMode values: UseNone, UseOutlines, UseThumbs, FullScreen, UseOC, UseAttachments
|
||||
// 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);
|
||||
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());
|
||||
// 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");
|
||||
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
|
||||
|
@ -44,8 +44,7 @@ public class WebResponseUtils {
|
||||
headers.setContentType(mediaType);
|
||||
headers.setContentLength(bytes.length);
|
||||
String encodedDocName =
|
||||
URLEncoder.encode(docName, StandardCharsets.UTF_8)
|
||||
.replaceAll("\\+", "%20");
|
||||
URLEncoder.encode(docName, StandardCharsets.UTF_8).replaceAll("\\+", "%20");
|
||||
headers.setContentDispositionFormData("attachment", encodedDocName);
|
||||
return new ResponseEntity<>(bytes, headers, HttpStatus.OK);
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import stirling.software.common.model.ApplicationProperties;
|
||||
|
||||
@Service
|
||||
@ -83,9 +84,9 @@ public class EndpointConfiguration {
|
||||
}
|
||||
|
||||
public void disableGroup(String group) {
|
||||
Set<String> endpoints = endpointGroups.get(group);
|
||||
if (endpoints != null) {
|
||||
for (String endpoint : endpoints) {
|
||||
Set<String> disabledEndpoints = endpointGroups.get(group);
|
||||
if (disabledEndpoints != null) {
|
||||
for (String endpoint : disabledEndpoints) {
|
||||
disableEndpoint(endpoint);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,57 @@
|
||||
package stirling.software.SPDF.controller.api.misc;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
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.AttachmentServiceInterface;
|
||||
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 AttachmentController {
|
||||
|
||||
private final CustomPDFDocumentFactory pdfDocumentFactory;
|
||||
|
||||
private final AttachmentServiceInterface 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<byte[]> addAttachments(
|
||||
@RequestParam("fileInput") MultipartFile pdfFile,
|
||||
@RequestParam("attachments") List<MultipartFile> attachments)
|
||||
throws IOException {
|
||||
|
||||
PDDocument document =
|
||||
pdfAttachmentService.addAttachment(
|
||||
pdfDocumentFactory.load(pdfFile, false), attachments);
|
||||
|
||||
return WebResponseUtils.pdfDocToWebResponse(
|
||||
document,
|
||||
Filenames.toSimpleFileName(pdfFile.getOriginalFilename())
|
||||
.replaceFirst("[.][^.]+$", "")
|
||||
+ "_with_attachments.pdf");
|
||||
}
|
||||
}
|
@ -1,100 +0,0 @@
|
||||
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<byte[]> addAttachments(
|
||||
@RequestParam("fileInput") MultipartFile pdfFile,
|
||||
@RequestParam("attachments") List<MultipartFile> 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);
|
||||
byte[] output =
|
||||
pdfAttachmentService.addAttachment(document, embeddedFilesTree, attachments);
|
||||
|
||||
return WebResponseUtils.bytesToWebResponse(
|
||||
output,
|
||||
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<byte[]> 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");
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
package stirling.software.SPDF.service;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import static stirling.software.common.util.PDFAttachmentUtils.setCatalogViewerPreferences;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.HashMap;
|
||||
@ -9,48 +10,53 @@ import java.util.Map;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
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.apache.pdfbox.pdmodel.common.filespecification.PDComplexFileSpecification;
|
||||
import org.apache.pdfbox.pdmodel.common.filespecification.PDEmbeddedFile;
|
||||
import org.apache.pdfbox.pdmodel.encryption.AccessPermission;
|
||||
import org.apache.pdfbox.pdmodel.encryption.StandardProtectionPolicy;
|
||||
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 {
|
||||
public class AttachmentService implements AttachmentServiceInterface {
|
||||
|
||||
@Override
|
||||
public byte[] addAttachment(
|
||||
PDDocument document,
|
||||
PDEmbeddedFilesNameTreeNode embeddedFilesTree,
|
||||
List<MultipartFile> attachments)
|
||||
public PDDocument addAttachment(PDDocument document, List<MultipartFile> attachments)
|
||||
throws IOException {
|
||||
PDDocumentCatalog catalog = document.getDocumentCatalog();
|
||||
PDDocumentNameDictionary documentNames = catalog.getNames();
|
||||
PDEmbeddedFilesNameTreeNode embeddedFilesTree = new PDEmbeddedFilesNameTreeNode();
|
||||
|
||||
if (documentNames != null) {
|
||||
embeddedFilesTree = documentNames.getEmbeddedFiles();
|
||||
} else {
|
||||
documentNames = new PDDocumentNameDictionary(catalog);
|
||||
documentNames.setEmbeddedFiles(embeddedFilesTree);
|
||||
}
|
||||
|
||||
catalog.setNames(documentNames);
|
||||
Map<String, PDComplexFileSpecification> existingNames;
|
||||
|
||||
try {
|
||||
existingNames = embeddedFilesTree.getNames();
|
||||
Map<String, PDComplexFileSpecification> originalNames = embeddedFilesTree.getNames();
|
||||
|
||||
if (existingNames == null) {
|
||||
if (originalNames == null) {
|
||||
log.debug("No existing embedded files found, creating new names map.");
|
||||
existingNames = new HashMap<>();
|
||||
} else {
|
||||
existingNames = new HashMap<>(originalNames);
|
||||
log.debug("Embedded files: {}", existingNames.keySet());
|
||||
}
|
||||
|
||||
log.debug("Embedded files: {}", existingNames.keySet());
|
||||
} catch (IOException e) {
|
||||
log.error("Could not retrieve existing embedded files", e);
|
||||
throw e;
|
||||
}
|
||||
|
||||
grantAccessPermissions(document);
|
||||
final Map<String, PDComplexFileSpecification> existingEmbeddedFiles = existingNames;
|
||||
|
||||
attachments.forEach(
|
||||
attachment -> {
|
||||
String filename = attachment.getOriginalFilename();
|
||||
@ -73,12 +79,10 @@ public class PDFAttachmentService implements PDFAttachmentServiceInterface {
|
||||
fileSpecification.setFile(filename);
|
||||
fileSpecification.setFileUnicode(filename);
|
||||
fileSpecification.setFileDescription("Embedded attachment: " + filename);
|
||||
embeddedFile.setFile(fileSpecification);
|
||||
fileSpecification.setEmbeddedFile(embeddedFile);
|
||||
fileSpecification.setEmbeddedFileUnicode(embeddedFile);
|
||||
|
||||
// Add to the existing files map
|
||||
existingEmbeddedFiles.put(filename, fileSpecification);
|
||||
existingNames.put(filename, fileSpecification);
|
||||
|
||||
log.info("Added attachment: {} ({} bytes)", filename, attachment.getSize());
|
||||
} catch (IOException e) {
|
||||
@ -87,39 +91,8 @@ public class PDFAttachmentService implements PDFAttachmentServiceInterface {
|
||||
});
|
||||
|
||||
embeddedFilesTree.setNames(existingNames);
|
||||
PDFAttachmentUtils.setCatalogViewerPreferences(document, PageMode.USE_ATTACHMENTS);
|
||||
ByteArrayOutputStream output = new ByteArrayOutputStream();
|
||||
document.save(output);
|
||||
setCatalogViewerPreferences(document, PageMode.USE_ATTACHMENTS);
|
||||
|
||||
return output.toByteArray();
|
||||
}
|
||||
|
||||
private void grantAccessPermissions(PDDocument document) {
|
||||
try {
|
||||
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);
|
||||
|
||||
var protectionPolicy = new StandardProtectionPolicy(null, null, currentPermissions);
|
||||
|
||||
if (!document.isAllSecurityToBeRemoved()) {
|
||||
document.setAllSecurityToBeRemoved(true);
|
||||
}
|
||||
|
||||
document.protect(protectionPolicy);
|
||||
ByteArrayOutputStream output = new ByteArrayOutputStream();
|
||||
document.save(output);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return document;
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package stirling.software.SPDF.service;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
public interface AttachmentServiceInterface {
|
||||
|
||||
PDDocument addAttachment(PDDocument document, List<MultipartFile> attachments)
|
||||
throws IOException;
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
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 {
|
||||
|
||||
byte[] addAttachment(
|
||||
PDDocument document,
|
||||
PDEmbeddedFilesNameTreeNode efTree,
|
||||
List<MultipartFile> attachments)
|
||||
throws IOException;
|
||||
}
|
@ -1205,6 +1205,13 @@ addImage.everyPage=كل صفحة؟
|
||||
addImage.upload=إضافة صورة
|
||||
addImage.submit=إضافة صورة
|
||||
|
||||
#attachments
|
||||
attachments.title=Add Attachments
|
||||
attachments.header=Add attachments
|
||||
attachments.description=Allows you to add attachments to the PDF
|
||||
attachments.descriptionPlaceholder=Enter a description for the attachments...
|
||||
attachments.addButton=Add Attachments
|
||||
|
||||
|
||||
#merge
|
||||
merge.title=دمج
|
||||
@ -1594,6 +1601,7 @@ fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||
fileChooser.hoveredDragAndDrop=قم بسحب المفات وإفلاتها هنا
|
||||
fileChooser.extractPDF=جاري الاستخراج...
|
||||
fileChooser.addAttachments=drag & drop attachments here
|
||||
|
||||
#release notes
|
||||
releases.footer=Releases
|
||||
|
@ -1594,6 +1594,7 @@ fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
||||
fileChooser.extractPDF=Extracting...
|
||||
fileChooser.addAttachments=drag & drop attachments here
|
||||
|
||||
#release notes
|
||||
releases.footer=Buraxılışlar
|
||||
|
@ -1594,6 +1594,7 @@ fileChooser.dragAndDropPDF=Влачете и пуснете PDF файл
|
||||
fileChooser.dragAndDropImage=Влачете и пуснете изображение
|
||||
fileChooser.hoveredDragAndDrop=Влачете и пуснете файл(ове) тук
|
||||
fileChooser.extractPDF=Извличане...
|
||||
fileChooser.addAttachments=drag & drop attachments here
|
||||
|
||||
#release notes
|
||||
releases.footer=Версии
|
||||
|
@ -1594,6 +1594,7 @@ fileChooser.dragAndDropPDF=PDF ཡིག་ཆ་འཐེན་ནས་འཇ
|
||||
fileChooser.dragAndDropImage=པར་རིས་ཡིག་ཆ་འཐེན་ནས་འཇོག་པ།
|
||||
fileChooser.hoveredDragAndDrop=ཡིག་ཆ་འདིར་འཐེན་ནས་འཇོག་པ།
|
||||
fileChooser.extractPDF=འདོན་རིས་འགྱུར་བའི་སྒྲིག་བཏང་བ།
|
||||
fileChooser.addAttachments=drag & drop attachments here
|
||||
|
||||
#release notes
|
||||
releases.footer=པར་གཞི།
|
||||
|
@ -1594,7 +1594,7 @@ fileChooser.dragAndDropPDF=Arrossega i deixa anar un fitxer PDF
|
||||
fileChooser.dragAndDropImage=Arrossega i deixa anar un fitxer d'imatge
|
||||
fileChooser.hoveredDragAndDrop=Arrossega i deixa anar fitxer(s) aquí
|
||||
fileChooser.extractPDF=Extracting...
|
||||
|
||||
fileChooser.addAttachments=drag & drop attachments here
|
||||
#release notes
|
||||
releases.footer=Llançaments
|
||||
releases.title=Notes de Llançament
|
||||
|
@ -1594,7 +1594,7 @@ fileChooser.dragAndDropPDF=Přetáhnout PDF soubor
|
||||
fileChooser.dragAndDropImage=Přetáhnout obrázek
|
||||
fileChooser.hoveredDragAndDrop=Přetáhněte soubor(y) sem
|
||||
fileChooser.extractPDF=Extrahování...
|
||||
|
||||
fileChooser.addAttachments=drag & drop attachments here
|
||||
#release notes
|
||||
releases.footer=Vydání
|
||||
releases.title=Poznámky k vydání
|
||||
|
@ -1594,7 +1594,7 @@ fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
||||
fileChooser.extractPDF=Extracting...
|
||||
|
||||
fileChooser.addAttachments=drag & drop attachments here
|
||||
#release notes
|
||||
releases.footer=Releases
|
||||
releases.title=Release Notes
|
||||
|
@ -1594,6 +1594,7 @@ fileChooser.dragAndDropPDF=Drag & Drop PDF-Datei
|
||||
fileChooser.dragAndDropImage=Drag & Drop Bilddatei
|
||||
fileChooser.hoveredDragAndDrop=Datei(en) hierhin Ziehen & Fallenlassen
|
||||
fileChooser.extractPDF=Extrahiere...
|
||||
fileChooser.addAttachments=drag & drop attachments here
|
||||
|
||||
#release notes
|
||||
releases.footer=Veröffentlichungen
|
||||
|
@ -1594,7 +1594,7 @@ fileChooser.dragAndDropPDF=Σύρετε & αφήστε αρχείο PDF
|
||||
fileChooser.dragAndDropImage=Σύρετε & αφήστε αρχείο εικόνας
|
||||
fileChooser.hoveredDragAndDrop=Σύρετε & αφήστε αρχείο(α) εδώ
|
||||
fileChooser.extractPDF=Εξαγωγή...
|
||||
|
||||
fileChooser.addAttachments=drag & drop attachments here
|
||||
#release notes
|
||||
releases.footer=Εκδόσεις
|
||||
releases.title=Σημειώσεις έκδοσης
|
||||
|
@ -1212,13 +1212,9 @@ 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
|
||||
@ -1608,6 +1604,7 @@ fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
||||
fileChooser.extractPDF=Extracting...
|
||||
fileChooser.addAttachments=drag & drop attachments here
|
||||
|
||||
#release notes
|
||||
releases.footer=Releases
|
||||
|
@ -1210,15 +1210,11 @@ addImage.submit=Add image
|
||||
|
||||
|
||||
#attachments
|
||||
attachments.title=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
|
||||
@ -1609,6 +1605,7 @@ fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
||||
fileChooser.extractPDF=Extracting...
|
||||
fileChooser.addAttachments=drag & drop attachments here
|
||||
|
||||
#release notes
|
||||
releases.footer=Releases
|
||||
|
@ -1205,6 +1205,12 @@ addImage.everyPage=¿Todas las páginas?
|
||||
addImage.upload=Añadir imagen
|
||||
addImage.submit=Enviar imagen
|
||||
|
||||
#attachments
|
||||
attachments.title=Add Attachments
|
||||
attachments.header=Add attachments
|
||||
attachments.description=Allows you to add attachments to the PDF
|
||||
attachments.descriptionPlaceholder=Enter a description for the attachments...
|
||||
attachments.addButton=Add Attachments
|
||||
|
||||
#merge
|
||||
merge.title=Unir
|
||||
@ -1594,6 +1600,7 @@ fileChooser.dragAndDropPDF=Arrastrar & Soltar archivo PDF
|
||||
fileChooser.dragAndDropImage=Arrastrar & Soltar archivo de Imagen
|
||||
fileChooser.hoveredDragAndDrop=Arrastrar & Soltar archivos(s) aquí
|
||||
fileChooser.extractPDF=Extrayendo...
|
||||
fileChooser.addAttachments=drag & drop attachments here
|
||||
|
||||
#release notes
|
||||
releases.footer=Versiones
|
||||
|
@ -1594,6 +1594,7 @@ fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
||||
fileChooser.extractPDF=Extracting...
|
||||
fileChooser.addAttachments=drag & drop attachments here
|
||||
|
||||
#release notes
|
||||
releases.footer=Releases
|
||||
|
@ -1205,6 +1205,13 @@ addImage.everyPage=هر صفحه؟
|
||||
addImage.upload=افزودن تصویر
|
||||
addImage.submit=افزودن تصویر
|
||||
|
||||
#attachments
|
||||
attachments.title=Add Attachments
|
||||
attachments.header=Add attachments
|
||||
attachments.description=Allows you to add attachments to the PDF
|
||||
attachments.descriptionPlaceholder=Enter a description for the attachments...
|
||||
attachments.addButton=Add Attachments
|
||||
|
||||
|
||||
#merge
|
||||
merge.title=ادغام
|
||||
@ -1594,6 +1601,7 @@ fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||
fileChooser.hoveredDragAndDrop=فایل(های) خود را اینجا بکشید و رها کنید
|
||||
fileChooser.extractPDF=در حال استخراج...
|
||||
fileChooser.addAttachments=drag & drop attachments here
|
||||
|
||||
#release notes
|
||||
releases.footer=نسخهها
|
||||
|
@ -1205,6 +1205,13 @@ addImage.everyPage=Toutes les pages ?
|
||||
addImage.upload=Télécharger une image
|
||||
addImage.submit=Ajouter une image
|
||||
|
||||
#attachments
|
||||
attachments.title=Add Attachments
|
||||
attachments.header=Add attachments
|
||||
attachments.description=Allows you to add attachments to the PDF
|
||||
attachments.descriptionPlaceholder=Enter a description for the attachments...
|
||||
attachments.addButton=Add Attachments
|
||||
|
||||
|
||||
#merge
|
||||
merge.title=Fusionner
|
||||
@ -1594,6 +1601,7 @@ fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||
fileChooser.hoveredDragAndDrop=Glisser & Déposer le(s) fichier(s) ici
|
||||
fileChooser.extractPDF=Extraction en cours...
|
||||
fileChooser.addAttachments=drag & drop attachments here
|
||||
|
||||
#release notes
|
||||
releases.footer=Versions
|
||||
|
@ -1205,6 +1205,13 @@ addImage.everyPage=Gach Leathanach?
|
||||
addImage.upload=Cuir íomhá leis
|
||||
addImage.submit=Cuir íomhá leis
|
||||
|
||||
#attachments
|
||||
attachments.title=Add Attachments
|
||||
attachments.header=Add attachments
|
||||
attachments.description=Allows you to add attachments to the PDF
|
||||
attachments.descriptionPlaceholder=Enter a description for the attachments...
|
||||
attachments.addButton=Add Attachments
|
||||
|
||||
|
||||
#merge
|
||||
merge.title=Cumaisc
|
||||
@ -1594,6 +1601,7 @@ fileChooser.dragAndDropPDF=Tarraing & Scaoil comhad PDF
|
||||
fileChooser.dragAndDropImage=Tarraing & Scaoil comhad Íomhá
|
||||
fileChooser.hoveredDragAndDrop=Tarraing agus scaoil comhad(í) anseo
|
||||
fileChooser.extractPDF=Ag Aistriú...
|
||||
fileChooser.addAttachments=drag & drop attachments here
|
||||
|
||||
#release notes
|
||||
releases.footer=Eisiúintí
|
||||
|
@ -1205,6 +1205,13 @@ addImage.everyPage=हर पृष्ठ?
|
||||
addImage.upload=छवि जोड़ें
|
||||
addImage.submit=छवि जोड़ें
|
||||
|
||||
#attachments
|
||||
attachments.title=Add Attachments
|
||||
attachments.header=Add attachments
|
||||
attachments.description=Allows you to add attachments to the PDF
|
||||
attachments.descriptionPlaceholder=Enter a description for the attachments...
|
||||
attachments.addButton=Add Attachments
|
||||
|
||||
|
||||
#merge
|
||||
merge.title=मर्ज करें
|
||||
@ -1594,6 +1601,7 @@ fileChooser.dragAndDropPDF=PDF फ़ाइल खींचें और छो
|
||||
fileChooser.dragAndDropImage=छवि फ़ाइल खींचें और छोड़ें
|
||||
fileChooser.hoveredDragAndDrop=फ़ाइल(ें) यहाँ खींचें और छोड़ें
|
||||
fileChooser.extractPDF=निकालना...
|
||||
fileChooser.addAttachments=drag & drop attachments here
|
||||
|
||||
#release notes
|
||||
releases.footer=रिलीज़
|
||||
|
@ -1206,6 +1206,14 @@ addImage.upload=Dodaj sliku
|
||||
addImage.submit=Dodaj sliku
|
||||
|
||||
|
||||
#attachments
|
||||
attachments.title=Add Attachments
|
||||
attachments.header=Add attachments
|
||||
attachments.description=Allows you to add attachments to the PDF
|
||||
attachments.descriptionPlaceholder=Enter a description for the attachments...
|
||||
attachments.addButton=Add Attachments
|
||||
|
||||
|
||||
#merge
|
||||
merge.title=Spajanje
|
||||
merge.header=Spajanje više PDF-ova (2+)
|
||||
@ -1594,6 +1602,7 @@ fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
||||
fileChooser.extractPDF=Extracting...
|
||||
fileChooser.addAttachments=drag & drop attachments here
|
||||
|
||||
#release notes
|
||||
releases.footer=Releases
|
||||
|
@ -1205,6 +1205,13 @@ addImage.everyPage=Minden oldalra?
|
||||
addImage.upload=Kép hozzáadása
|
||||
addImage.submit=Kép hozzáadása
|
||||
|
||||
#attachments
|
||||
attachments.title=Add Attachments
|
||||
attachments.header=Add attachments
|
||||
attachments.description=Allows you to add attachments to the PDF
|
||||
attachments.descriptionPlaceholder=Enter a description for the attachments...
|
||||
attachments.addButton=Add Attachments
|
||||
|
||||
|
||||
#merge
|
||||
merge.title=Egyesítés
|
||||
@ -1594,6 +1601,7 @@ fileChooser.dragAndDropPDF=Húzza ide a PDF fájlt
|
||||
fileChooser.dragAndDropImage=Húzza ide a képfájlt
|
||||
fileChooser.hoveredDragAndDrop=Húzza ide a fájl(oka)t
|
||||
fileChooser.extractPDF=Kinyerés...
|
||||
fileChooser.addAttachments=drag & drop attachments here
|
||||
|
||||
#release notes
|
||||
releases.footer=Kiadási jegyzék
|
||||
|
@ -1206,6 +1206,14 @@ addImage.upload=Tambahkan Gambar
|
||||
addImage.submit=Tambahkan Gambar
|
||||
|
||||
|
||||
#attachments
|
||||
attachments.title=Add Attachments
|
||||
attachments.header=Add attachments
|
||||
attachments.description=Allows you to add attachments to the PDF
|
||||
attachments.descriptionPlaceholder=Enter a description for the attachments...
|
||||
attachments.addButton=Add Attachments
|
||||
|
||||
|
||||
#merge
|
||||
merge.title=Gabungkan
|
||||
merge.header=Gabungkan beberapa PDFs (2+)
|
||||
@ -1594,6 +1602,7 @@ fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
||||
fileChooser.extractPDF=Extracting...
|
||||
fileChooser.addAttachments=drag & drop attachments here
|
||||
|
||||
#release notes
|
||||
releases.footer=Releases
|
||||
|
@ -1205,6 +1205,13 @@ addImage.everyPage=Ogni pagina?
|
||||
addImage.upload=Aggiungi immagine
|
||||
addImage.submit=Aggiungi immagine
|
||||
|
||||
#attachments
|
||||
attachments.title=Add Attachments
|
||||
attachments.header=Add attachments
|
||||
attachments.description=Allows you to add attachments to the PDF
|
||||
attachments.descriptionPlaceholder=Enter a description for the attachments...
|
||||
attachments.addButton=Add Attachments
|
||||
|
||||
|
||||
#merge
|
||||
merge.title=Unisci
|
||||
@ -1594,6 +1601,7 @@ fileChooser.dragAndDropPDF=Trascina & rilascia il file PDF
|
||||
fileChooser.dragAndDropImage=Trascina & rilascia il file immagine
|
||||
fileChooser.hoveredDragAndDrop=Trascina & rilascia i file qui
|
||||
fileChooser.extractPDF=Estraendo...
|
||||
fileChooser.addAttachments=drag & drop attachments here
|
||||
|
||||
#release notes
|
||||
releases.footer=Rilasci
|
||||
|
@ -1205,6 +1205,12 @@ addImage.everyPage=全ページ?
|
||||
addImage.upload=画像の追加
|
||||
addImage.submit=画像の追加
|
||||
|
||||
#attachments
|
||||
attachments.title=Add Attachments
|
||||
attachments.header=Add attachments
|
||||
attachments.description=Allows you to add attachments to the PDF
|
||||
attachments.descriptionPlaceholder=Enter a description for the attachments...
|
||||
attachments.addButton=Add Attachments
|
||||
|
||||
#merge
|
||||
merge.title=結合
|
||||
@ -1594,6 +1600,7 @@ fileChooser.dragAndDropPDF=PDFファイルをドラッグ&ドロップ
|
||||
fileChooser.dragAndDropImage=画像ファイルをドラッグ&ドロップ
|
||||
fileChooser.hoveredDragAndDrop=ファイルをここにドラッグ&ドロップ
|
||||
fileChooser.extractPDF=抽出中...
|
||||
fileChooser.addAttachments=drag & drop attachments here
|
||||
|
||||
#release notes
|
||||
releases.footer=リリース
|
||||
|
@ -1206,6 +1206,14 @@ addImage.upload=이미지 추가
|
||||
addImage.submit=이미지 추가
|
||||
|
||||
|
||||
#attachments
|
||||
attachments.title=Add Attachments
|
||||
attachments.header=Add attachments
|
||||
attachments.description=Allows you to add attachments to the PDF
|
||||
attachments.descriptionPlaceholder=Enter a description for the attachments...
|
||||
attachments.addButton=Add Attachments
|
||||
|
||||
|
||||
#merge
|
||||
merge.title=병합
|
||||
merge.header=여러 PDF 병합 (2개 이상)
|
||||
@ -1594,6 +1602,7 @@ fileChooser.dragAndDropPDF=PDF 파일을 드래그 앤 드롭
|
||||
fileChooser.dragAndDropImage=이미지 파일을 드래그 앤 드롭
|
||||
fileChooser.hoveredDragAndDrop=여기에 파일을 드래그 앤 드롭하세요
|
||||
fileChooser.extractPDF=추출 중...
|
||||
fileChooser.addAttachments=drag & drop attachments here
|
||||
|
||||
#release notes
|
||||
releases.footer=릴리스
|
||||
|
@ -1205,6 +1205,13 @@ addImage.everyPage=എല്ലാ പേജിലും?
|
||||
addImage.upload=ചിത്രം ചേർക്കുക
|
||||
addImage.submit=ചിത്രം ചേർക്കുക
|
||||
|
||||
#attachments
|
||||
attachments.title=Add Attachments
|
||||
attachments.header=Add attachments
|
||||
attachments.description=Allows you to add attachments to the PDF
|
||||
attachments.descriptionPlaceholder=Enter a description for the attachments...
|
||||
attachments.addButton=Add Attachments
|
||||
|
||||
|
||||
#merge
|
||||
merge.title=ലയിപ്പിക്കുക
|
||||
@ -1594,6 +1601,7 @@ fileChooser.dragAndDropPDF=PDF ഫയൽ വലിച്ചിടുക
|
||||
fileChooser.dragAndDropImage=ചിത്ര ഫയൽ വലിച്ചിടുക
|
||||
fileChooser.hoveredDragAndDrop=ഫയൽ(കൾ) ഇവിടെ വലിച്ചിടുക
|
||||
fileChooser.extractPDF=വേർതിരിച്ചെടുക്കുന്നു...
|
||||
fileChooser.addAttachments=drag & drop attachments here
|
||||
|
||||
#release notes
|
||||
releases.footer=റിലീസുകൾ
|
||||
|
@ -1206,6 +1206,14 @@ addImage.upload=Afbeelding toevoegen
|
||||
addImage.submit=Afbeelding toevoegen
|
||||
|
||||
|
||||
#attachments
|
||||
attachments.title=Add Attachments
|
||||
attachments.header=Add attachments
|
||||
attachments.description=Allows you to add attachments to the PDF
|
||||
attachments.descriptionPlaceholder=Enter a description for the attachments...
|
||||
attachments.addButton=Add Attachments
|
||||
|
||||
|
||||
#merge
|
||||
merge.title=Samenvoegen
|
||||
merge.header=Meerdere PDF's samenvoegen (2+)
|
||||
@ -1594,6 +1602,7 @@ fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
||||
fileChooser.extractPDF=Extracting...
|
||||
fileChooser.addAttachments=drag & drop attachments here
|
||||
|
||||
#release notes
|
||||
releases.footer=Releases
|
||||
|
@ -1205,6 +1205,13 @@ addImage.everyPage=På hver side?
|
||||
addImage.upload=Legg til bilde
|
||||
addImage.submit=Legg til bilde
|
||||
|
||||
#attachments
|
||||
attachments.title=Add Attachments
|
||||
attachments.header=Add attachments
|
||||
attachments.description=Allows you to add attachments to the PDF
|
||||
attachments.descriptionPlaceholder=Enter a description for the attachments...
|
||||
attachments.addButton=Add Attachments
|
||||
|
||||
|
||||
#merge
|
||||
merge.title=Slå sammen
|
||||
@ -1594,6 +1601,7 @@ fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
||||
fileChooser.extractPDF=Extracting...
|
||||
fileChooser.addAttachments=drag & drop attachments here
|
||||
|
||||
#release notes
|
||||
releases.footer=Versjoner
|
||||
|
@ -1206,6 +1206,14 @@ addImage.upload=Dodaj obraz
|
||||
addImage.submit=Dodaj obraz
|
||||
|
||||
|
||||
#attachments
|
||||
attachments.title=Add Attachments
|
||||
attachments.header=Add attachments
|
||||
attachments.description=Allows you to add attachments to the PDF
|
||||
attachments.descriptionPlaceholder=Enter a description for the attachments...
|
||||
attachments.addButton=Add Attachments
|
||||
|
||||
|
||||
#merge
|
||||
merge.title=Połącz
|
||||
merge.header=Połącz wiele dokumentów PDF (2+)
|
||||
@ -1594,6 +1602,7 @@ fileChooser.dragAndDropPDF=Przeciągnij i upuść plik PDF
|
||||
fileChooser.dragAndDropImage=Przeciągnij i upuść plik obrazu
|
||||
fileChooser.hoveredDragAndDrop=Przeciągnij i upuść plik(i) tutaj
|
||||
fileChooser.extractPDF=Trwa wyodrębnianie...
|
||||
fileChooser.addAttachments=drag & drop attachments here
|
||||
|
||||
#release notes
|
||||
releases.footer=Wydania
|
||||
|
@ -1206,6 +1206,14 @@ addImage.upload=Carregar imagem
|
||||
addImage.submit=Adicionar imagem
|
||||
|
||||
|
||||
#attachments
|
||||
attachments.title=Add Attachments
|
||||
attachments.header=Add attachments
|
||||
attachments.description=Allows you to add attachments to the PDF
|
||||
attachments.descriptionPlaceholder=Enter a description for the attachments...
|
||||
attachments.addButton=Add Attachments
|
||||
|
||||
|
||||
#merge
|
||||
merge.title=Mesclar
|
||||
merge.header=Mesclar
|
||||
@ -1594,6 +1602,7 @@ fileChooser.dragAndDropPDF=Arraste & Solte PDF(s)
|
||||
fileChooser.dragAndDropImage=Arraste & Solte Imagem(ns)
|
||||
fileChooser.hoveredDragAndDrop=Arraste & Solte arquivo(s) aqui
|
||||
fileChooser.extractPDF=Extraindo...
|
||||
fileChooser.addAttachments=drag & drop attachments here
|
||||
|
||||
#release notes
|
||||
releases.footer=Versões
|
||||
|
@ -1206,6 +1206,14 @@ addImage.upload=Adicionar imagem
|
||||
addImage.submit=Adicionar imagem
|
||||
|
||||
|
||||
#attachments
|
||||
attachments.title=Add Attachments
|
||||
attachments.header=Add attachments
|
||||
attachments.description=Allows you to add attachments to the PDF
|
||||
attachments.descriptionPlaceholder=Enter a description for the attachments...
|
||||
attachments.addButton=Add Attachments
|
||||
|
||||
|
||||
#merge
|
||||
merge.title=Juntar
|
||||
merge.header=Juntar múltiplos PDFs (2+)
|
||||
@ -1594,6 +1602,7 @@ fileChooser.dragAndDropPDF=Arrastar e Largar ficheiro PDF
|
||||
fileChooser.dragAndDropImage=Arrastar e Largar ficheiro de Imagem
|
||||
fileChooser.hoveredDragAndDrop=Arrastar e Largar ficheiro(s) aqui
|
||||
fileChooser.extractPDF=Extraindo...
|
||||
fileChooser.addAttachments=drag & drop attachments here
|
||||
|
||||
#release notes
|
||||
releases.footer=Lançamentos
|
||||
|
@ -1206,6 +1206,14 @@ addImage.upload=Adăugare imagine
|
||||
addImage.submit=Adăugare imagine
|
||||
|
||||
|
||||
#attachments
|
||||
attachments.title=Add Attachments
|
||||
attachments.header=Add attachments
|
||||
attachments.description=Allows you to add attachments to the PDF
|
||||
attachments.descriptionPlaceholder=Enter a description for the attachments...
|
||||
attachments.addButton=Add Attachments
|
||||
|
||||
|
||||
#merge
|
||||
merge.title=Unire
|
||||
merge.header=Unirea mai multor PDF-uri (2+)
|
||||
@ -1594,6 +1602,7 @@ fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
||||
fileChooser.extractPDF=Extracting...
|
||||
fileChooser.addAttachments=drag & drop attachments here
|
||||
|
||||
#release notes
|
||||
releases.footer=Releases
|
||||
|
@ -1205,6 +1205,13 @@ addImage.everyPage=Каждая страница?
|
||||
addImage.upload=Добавить изображение
|
||||
addImage.submit=Добавить изображение
|
||||
|
||||
#attachments
|
||||
attachments.title=Add Attachments
|
||||
attachments.header=Add attachments
|
||||
attachments.description=Allows you to add attachments to the PDF
|
||||
attachments.descriptionPlaceholder=Enter a description for the attachments...
|
||||
attachments.addButton=Add Attachments
|
||||
|
||||
|
||||
#merge
|
||||
merge.title=Объединить
|
||||
@ -1594,6 +1601,7 @@ fileChooser.dragAndDropPDF=Перетащите PDF-файл
|
||||
fileChooser.dragAndDropImage=Перетащите файл изображения
|
||||
fileChooser.hoveredDragAndDrop=Перетащите файл(ы) сюда
|
||||
fileChooser.extractPDF=Извлечение...
|
||||
fileChooser.addAttachments=drag & drop attachments here
|
||||
|
||||
#release notes
|
||||
releases.footer=Релизы
|
||||
|
@ -1205,6 +1205,13 @@ addImage.everyPage=Každá stránka?
|
||||
addImage.upload=Pridať obrázok
|
||||
addImage.submit=Pridať obrázok
|
||||
|
||||
#attachments
|
||||
attachments.title=Add Attachments
|
||||
attachments.header=Add attachments
|
||||
attachments.description=Allows you to add attachments to the PDF
|
||||
attachments.descriptionPlaceholder=Enter a description for the attachments...
|
||||
attachments.addButton=Add Attachments
|
||||
|
||||
|
||||
#merge
|
||||
merge.title=Zlúčiť
|
||||
@ -1594,6 +1601,7 @@ fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
||||
fileChooser.extractPDF=Extracting...
|
||||
fileChooser.addAttachments=drag & drop attachments here
|
||||
|
||||
#release notes
|
||||
releases.footer=Releases
|
||||
|
@ -1206,6 +1206,14 @@ addImage.upload=Dodaj sliko
|
||||
addImage.submit=Dodaj sliko
|
||||
|
||||
|
||||
#attachments
|
||||
attachments.title=Add Attachments
|
||||
attachments.header=Add attachments
|
||||
attachments.description=Allows you to add attachments to the PDF
|
||||
attachments.descriptionPlaceholder=Enter a description for the attachments...
|
||||
attachments.addButton=Add Attachments
|
||||
|
||||
|
||||
#merge
|
||||
merge.title=Združi
|
||||
merge.header=Združi več PDF-jev (2+)
|
||||
@ -1594,6 +1602,7 @@ fileChooser.dragAndDropPDF=Povleci in spusti datoteko PDF
|
||||
fileChooser.dragAndDropImage=Povleci in spusti slikovno datoteko
|
||||
fileChooser.hoveredDragAndDrop=Povleci in spusti datoteko(e) sem
|
||||
fileChooser.extractPDF=Izvlečenje...
|
||||
fileChooser.addAttachments=drag & drop attachments here
|
||||
|
||||
#release notes
|
||||
releases.footer=Izdaje
|
||||
|
@ -1206,6 +1206,14 @@ addImage.upload=Dodaj sliku
|
||||
addImage.submit=Dodaj sliku
|
||||
|
||||
|
||||
#attachments
|
||||
attachments.title=Add Attachments
|
||||
attachments.header=Add attachments
|
||||
attachments.description=Allows you to add attachments to the PDF
|
||||
attachments.descriptionPlaceholder=Enter a description for the attachments...
|
||||
attachments.addButton=Add Attachments
|
||||
|
||||
|
||||
#merge
|
||||
merge.title=Spajanje
|
||||
merge.header=Spajanje više PDF fajlova (2+)
|
||||
@ -1594,6 +1602,7 @@ fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
||||
fileChooser.extractPDF=Extracting...
|
||||
fileChooser.addAttachments=drag & drop attachments here
|
||||
|
||||
#release notes
|
||||
releases.footer=Releases
|
||||
|
@ -1205,6 +1205,13 @@ addImage.everyPage=Varje sida?
|
||||
addImage.upload=Lägg till bild
|
||||
addImage.submit=Lägg till bild
|
||||
|
||||
#attachments
|
||||
attachments.title=Add Attachments
|
||||
attachments.header=Add attachments
|
||||
attachments.description=Allows you to add attachments to the PDF
|
||||
attachments.descriptionPlaceholder=Enter a description for the attachments...
|
||||
attachments.addButton=Add Attachments
|
||||
|
||||
|
||||
#merge
|
||||
merge.title=Sammanfoga
|
||||
@ -1594,6 +1601,7 @@ fileChooser.dragAndDropPDF=Dra & Släpp PDF fil
|
||||
fileChooser.dragAndDropImage=Dra & Släpp bildfil
|
||||
fileChooser.hoveredDragAndDrop=Dra & Släpp fil(er) här
|
||||
fileChooser.extractPDF=Extraherar...
|
||||
fileChooser.addAttachments=drag & drop attachments here
|
||||
|
||||
#release notes
|
||||
releases.footer=Utgåvor
|
||||
|
@ -1205,6 +1205,13 @@ addImage.everyPage=ทุกหน้า?
|
||||
addImage.upload=เพิ่มรูปภาพ
|
||||
addImage.submit=เพิ่มรูปภาพ
|
||||
|
||||
#attachments
|
||||
attachments.title=Add Attachments
|
||||
attachments.header=Add attachments
|
||||
attachments.description=Allows you to add attachments to the PDF
|
||||
attachments.descriptionPlaceholder=Enter a description for the attachments...
|
||||
attachments.addButton=Add Attachments
|
||||
|
||||
|
||||
#merge
|
||||
merge.title=รวม
|
||||
@ -1594,6 +1601,7 @@ fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
||||
fileChooser.extractPDF=Extracting...
|
||||
fileChooser.addAttachments=drag & drop attachments here
|
||||
|
||||
#release notes
|
||||
releases.footer=Releases
|
||||
|
@ -1205,6 +1205,13 @@ addImage.everyPage=Her Sayfa mı?
|
||||
addImage.upload=Resim ekle
|
||||
addImage.submit=Resim ekle
|
||||
|
||||
#attachments
|
||||
attachments.title=Add Attachments
|
||||
attachments.header=Add attachments
|
||||
attachments.description=Allows you to add attachments to the PDF
|
||||
attachments.descriptionPlaceholder=Enter a description for the attachments...
|
||||
attachments.addButton=Add Attachments
|
||||
|
||||
|
||||
#merge
|
||||
merge.title=Birleştir
|
||||
@ -1594,6 +1601,7 @@ fileChooser.dragAndDropPDF=PDF dosyasını Sürükle & Bırak
|
||||
fileChooser.dragAndDropImage=Görsel dosyasını Sürükle & Bırak
|
||||
fileChooser.hoveredDragAndDrop=Dosya(lar)ı buraya sürükleyip bırakın
|
||||
fileChooser.extractPDF=PDF Çıkarılıyor...
|
||||
fileChooser.addAttachments=drag & drop attachments here
|
||||
|
||||
#release notes
|
||||
releases.footer=Sürümler
|
||||
|
@ -1206,6 +1206,14 @@ addImage.upload=Додати зображення
|
||||
addImage.submit=Додати зображення
|
||||
|
||||
|
||||
#attachments
|
||||
attachments.title=Add Attachments
|
||||
attachments.header=Add attachments
|
||||
attachments.description=Allows you to add attachments to the PDF
|
||||
attachments.descriptionPlaceholder=Enter a description for the attachments...
|
||||
attachments.addButton=Add Attachments
|
||||
|
||||
|
||||
#merge
|
||||
merge.title=Об'єднати
|
||||
merge.header=Об'єднання кількох PDF-файлів (2+)
|
||||
@ -1594,6 +1602,7 @@ fileChooser.dragAndDropPDF=Перетащите PDF-файл
|
||||
fileChooser.dragAndDropImage=Перетащите файл зображення
|
||||
fileChooser.hoveredDragAndDrop=Перетащите файл(и) сюда
|
||||
fileChooser.extractPDF=Видобування...
|
||||
fileChooser.addAttachments=drag & drop attachments here
|
||||
|
||||
#release notes
|
||||
releases.footer=Релізи
|
||||
|
@ -1206,6 +1206,14 @@ addImage.upload=Thêm hình ảnh
|
||||
addImage.submit=Thêm hình ảnh
|
||||
|
||||
|
||||
#attachments
|
||||
attachments.title=Add Attachments
|
||||
attachments.header=Add attachments
|
||||
attachments.description=Allows you to add attachments to the PDF
|
||||
attachments.descriptionPlaceholder=Enter a description for the attachments...
|
||||
attachments.addButton=Add Attachments
|
||||
|
||||
|
||||
#merge
|
||||
merge.title=Trộn
|
||||
merge.header=Trộn nhiều PDF (2+)
|
||||
@ -1594,6 +1602,7 @@ fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
||||
fileChooser.extractPDF=Extracting...
|
||||
fileChooser.addAttachments=drag & drop attachments here
|
||||
|
||||
#release notes
|
||||
releases.footer=Releases
|
||||
|
@ -1206,6 +1206,14 @@ addImage.upload=添加图片
|
||||
addImage.submit=添加图片
|
||||
|
||||
|
||||
#attachments
|
||||
attachments.title=Add Attachments
|
||||
attachments.header=Add attachments
|
||||
attachments.description=Allows you to add attachments to the PDF
|
||||
attachments.descriptionPlaceholder=Enter a description for the attachments...
|
||||
attachments.addButton=Add Attachments
|
||||
|
||||
|
||||
#merge
|
||||
merge.title=合并
|
||||
merge.header=合并多个 PDF(2个以上)。
|
||||
@ -1594,6 +1602,7 @@ fileChooser.dragAndDropPDF=拖放PDF文件
|
||||
fileChooser.dragAndDropImage=拖放图片文件
|
||||
fileChooser.hoveredDragAndDrop=拖放文件到此处
|
||||
fileChooser.extractPDF=处理中...
|
||||
fileChooser.addAttachments=drag & drop attachments here
|
||||
|
||||
#release notes
|
||||
releases.footer=版本
|
||||
|
@ -1206,6 +1206,14 @@ addImage.upload=新增圖片
|
||||
addImage.submit=新增圖片
|
||||
|
||||
|
||||
#attachments
|
||||
attachments.title=Add Attachments
|
||||
attachments.header=Add attachments
|
||||
attachments.description=Allows you to add attachments to the PDF
|
||||
attachments.descriptionPlaceholder=Enter a description for the attachments...
|
||||
attachments.addButton=Add Attachments
|
||||
|
||||
|
||||
#merge
|
||||
merge.title=合併
|
||||
merge.header=合併多個 PDF
|
||||
@ -1594,6 +1602,7 @@ fileChooser.dragAndDropPDF=拖放 PDF 檔案
|
||||
fileChooser.dragAndDropImage=拖放圖片檔案
|
||||
fileChooser.hoveredDragAndDrop=將檔案拖放至此
|
||||
fileChooser.extractPDF=處理中...
|
||||
fileChooser.addAttachments=drag & drop attachments here
|
||||
|
||||
#release notes
|
||||
releases.footer=版本資訊
|
||||
|
@ -6,6 +6,9 @@ class FileIconFactory {
|
||||
return this.createPDFIcon();
|
||||
case "csv":
|
||||
return this.createCSVIcon();
|
||||
case "xls":
|
||||
case "xlsx":
|
||||
return this.createXLSXIcon();
|
||||
case "jpe":
|
||||
case "jpg":
|
||||
case "jpeg":
|
||||
@ -44,8 +47,29 @@ class FileIconFactory {
|
||||
return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960" fill="currentColor"><path d="M216-144q-30 0-51-21.5T144-216v-528q0-29 21-50.5t51-21.5h528q30 0 51 21.5t21 50.5v528q0 29-21 50.5T744-144H216Zm48-144h432L552-480 444-336l-72-96-108 144Z"/></svg>`;
|
||||
}
|
||||
|
||||
static createCSVIcon() {
|
||||
return `
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" class="bi bi-filetype-csv" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd" d="M14 4.5V14a2 2 0 0 1-2 2h-1v-1h1a1 1 0 0 0 1-1V4.5h-2A1.5 1.5 0 0 1 9.5 3V1H4a1 1 0 0 0-1 1v9H2V2a2 2 0 0 1 2-2h5.5zM3.517 14.841a1.13 1.13 0 0 0 .401.823q.195.162.478.252.284.091.665.091.507 0 .859-.158.354-.158.539-.54.185-.382.185-.816 0-.335-.123-.628a1.4 1.4 0 0 0-.366-.486 1.8 1.8 0 0 0-.614-.314 2.8 2.8 0 0 0-.865-.118 2.1 2.1 0 0 0-.614.094 1.4 1.4 0 0 0-.471.264 1.1 1.1 0 0 0-.298.429.9.9 0 0 0-.103.539h.606a.4.4 0 0 1 .096-.258.5.5 0 0 1 .213-.164.6.6 0 0 1 .33-.082.7.7 0 0 1 .458.132.4.4 0 0 1 .153.372.4.4 0 0 1-.085.235.7.7 0 0 1-.25.192 1.4 1.4 0 0 1-.407.115c-.127.023-.266.05-.416.081a1.8 1.8 0 0 0-.534.187 1.2 1.2 0 0 0-.382.346 1 1 0 0 0-.138.537q0 .295.101.517M8.717 14.841a1.13 1.13 0 0 0 .401.823q.195.162.478.252.284.091.665.091.507 0 .859-.158.354-.158.539-.54.185-.382.185-.816 0-.335-.123-.628a1.4 1.4 0 0 0-.366-.486 1.8 1.8 0 0 0-.614-.314 2.8 2.8 0 0 0-.865-.118 2.1 2.1 0 0 0-.614.094 1.4 1.4 0 0 0-.471.264 1.1 1.1 0 0 0-.298.429.9.9 0 0 0-.103.539h.606a.4.4 0 0 1 .096-.258.5.5 0 0 1 .213-.164.6.6 0 0 1 .33-.082.7.7 0 0 1 .458.132.4.4 0 0 1 .153.372.4.4 0 0 1-.085.235.7.7 0 0 1-.25.192 1.4 1.4 0 0 1-.407.115c-.127.023-.266.05-.416.081a1.8 1.8 0 0 0-.534.187 1.2 1.2 0 0 0-.382.346 1 1 0 0 0-.138.537q0 .295.101.517M14.229 13.12v.506H11.85v-.506h1.063v-1.277H11.85v-.506h2.379v.506h-1.063z"/>
|
||||
</svg>
|
||||
`;
|
||||
}
|
||||
|
||||
static createXLSXIcon() {
|
||||
return `
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" class="bi bi-file-earmark-excel" viewBox="0 0 16 16">
|
||||
<path d="M5.884 6.68a.5.5 0 1 0-.768.64L7.349 10l-2.233 2.68a.5.5 0 0 0 .768.64L8 10.781l2.116 2.54a.5.5 0 0 0 .768-.641L8.651 10l2.233-2.68a.5.5 0 0 0-.768-.64L8 9.219l-2.116-2.54z"/>
|
||||
<path d="M14 14V4.5L9.5 0H4a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2M9.5 3A1.5 1.5 0 0 0 11 4.5h2V14a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1h5.5z"/>
|
||||
</svg>
|
||||
`;
|
||||
}
|
||||
|
||||
static createUnknownFileIcon() {
|
||||
return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960" fill="currentColor"><path d="M263.72-96Q234-96 213-117.15T192-168v-624q0-29.7 21.15-50.85Q234.3-864 264-864h312l192 192v504q0 29.7-21.16 50.85Q725.68-96 695.96-96H263.72ZM528-624h168L528-792v168Z"/></svg>`;
|
||||
return `
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" class="bi bi-file-earmark" viewBox="0 0 16 16">
|
||||
<path d="M14 4.5V14a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2h5.5zm-3 0A1.5 1.5 0 0 1 9.5 3V1H4a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V4.5z"/>
|
||||
</svg>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -45,6 +45,8 @@ function setupFileInput(chooser) {
|
||||
inputContainer.querySelector('#dragAndDrop').innerHTML = window.fileInput.dragAndDropPDF;
|
||||
} else if (inputContainer.id === 'image-upload-input-container') {
|
||||
inputContainer.querySelector('#dragAndDrop').innerHTML = window.fileInput.dragAndDropImage;
|
||||
} else if (inputContainer.id === 'attachments-input-container') {
|
||||
inputContainer.querySelector('#dragAndDrop').innerHTML = window.fileInput.addAttachments;
|
||||
}
|
||||
let allFiles = [];
|
||||
let overlay;
|
||||
|
@ -268,6 +268,7 @@
|
||||
window.fileInput = {
|
||||
dragAndDropPDF: '[[#{fileChooser.dragAndDropPDF}]]',
|
||||
dragAndDropImage: '[[#{fileChooser.dragAndDropImage}]]',
|
||||
addAttachments: '[[#{fileChooser.addAttachments}]]',
|
||||
extractPDF: '[[#{fileChooser.extractPDF}]]',
|
||||
loading: '[[#{loading}]]'
|
||||
};</script>
|
||||
|
@ -105,6 +105,9 @@
|
||||
<div
|
||||
th:replace="~{fragments/navbarEntry :: navbarEntry('img-to-pdf', 'picture_as_pdf', 'home.imageToPdf.title', 'home.imageToPdf.desc', 'imageToPdf.tags', 'convertto')}">
|
||||
</div>
|
||||
<div
|
||||
th:replace="~{fragments/navbarEntry :: navbarEntry('eml-to-pdf', 'email', 'home.EMLToPDF.title', 'home.EMLToPDF.desc', 'EMLToPDF.tags', 'convertto')}">
|
||||
</div>
|
||||
<div
|
||||
th:replace="~{fragments/navbarEntry :: navbarEntry('file-to-pdf', 'draft', 'home.fileToPDF.title', 'home.fileToPDF.desc', 'fileToPDF.tags', 'convertto')}">
|
||||
</div>
|
||||
|
@ -0,0 +1,123 @@
|
||||
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 org.mockito.MockedStatic;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||
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.AttachmentServiceInterface;
|
||||
import stirling.software.common.service.CustomPDFDocumentFactory;
|
||||
import stirling.software.common.util.WebResponseUtils;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class AttachmentControllerTest {
|
||||
|
||||
@Mock
|
||||
private CustomPDFDocumentFactory pdfDocumentFactory;
|
||||
|
||||
@Mock
|
||||
private AttachmentServiceInterface pdfAttachmentService;
|
||||
|
||||
@InjectMocks
|
||||
private AttachmentController attachmentController;
|
||||
|
||||
private MockMultipartFile pdfFile;
|
||||
private MockMultipartFile attachment1;
|
||||
private MockMultipartFile attachment2;
|
||||
private PDDocument mockDocument;
|
||||
private PDDocument modifiedMockDocument;
|
||||
|
||||
@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);
|
||||
modifiedMockDocument = mock(PDDocument.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void addAttachments_Success() throws IOException {
|
||||
List<MultipartFile> attachments = List.of(attachment1, attachment2);
|
||||
ResponseEntity<byte[]> expectedResponse = ResponseEntity.ok("modified PDF content".getBytes());
|
||||
|
||||
when(pdfDocumentFactory.load(pdfFile, false)).thenReturn(mockDocument);
|
||||
when(pdfAttachmentService.addAttachment(mockDocument, attachments)).thenReturn(modifiedMockDocument);
|
||||
|
||||
try (MockedStatic<WebResponseUtils> mockedWebResponseUtils = mockStatic(WebResponseUtils.class)) {
|
||||
mockedWebResponseUtils.when(() -> WebResponseUtils.pdfDocToWebResponse(eq(modifiedMockDocument), eq("test_with_attachments.pdf")))
|
||||
.thenReturn(expectedResponse);
|
||||
|
||||
ResponseEntity<byte[]> response = attachmentController.addAttachments(pdfFile, attachments);
|
||||
|
||||
assertNotNull(response);
|
||||
assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||
assertNotNull(response.getBody());
|
||||
verify(pdfDocumentFactory).load(pdfFile, false);
|
||||
verify(pdfAttachmentService).addAttachment(mockDocument, attachments);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void addAttachments_SingleAttachment() throws IOException {
|
||||
List<MultipartFile> attachments = List.of(attachment1);
|
||||
ResponseEntity<byte[]> expectedResponse = ResponseEntity.ok("modified PDF content".getBytes());
|
||||
|
||||
when(pdfDocumentFactory.load(pdfFile, false)).thenReturn(mockDocument);
|
||||
when(pdfAttachmentService.addAttachment(mockDocument, attachments)).thenReturn(modifiedMockDocument);
|
||||
|
||||
try (MockedStatic<WebResponseUtils> mockedWebResponseUtils = mockStatic(WebResponseUtils.class)) {
|
||||
mockedWebResponseUtils.when(() -> WebResponseUtils.pdfDocToWebResponse(eq(modifiedMockDocument), eq("test_with_attachments.pdf")))
|
||||
.thenReturn(expectedResponse);
|
||||
|
||||
ResponseEntity<byte[]> response = attachmentController.addAttachments(pdfFile, attachments);
|
||||
|
||||
assertNotNull(response);
|
||||
assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||
assertNotNull(response.getBody());
|
||||
verify(pdfDocumentFactory).load(pdfFile, false);
|
||||
verify(pdfAttachmentService).addAttachment(mockDocument, 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, () -> attachmentController.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(pdfAttachmentService.addAttachment(mockDocument, attachments)).thenThrow(ioException);
|
||||
|
||||
assertThrows(IOException.class, () -> attachmentController.addAttachments(pdfFile, attachments));
|
||||
verify(pdfAttachmentService).addAttachment(mockDocument, attachments);
|
||||
}
|
||||
}
|
@ -1,221 +0,0 @@
|
||||
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);
|
||||
byte[] expectedOutput = "modified PDF content".getBytes();
|
||||
|
||||
when(pdfDocumentFactory.load(pdfFile, false)).thenReturn(mockDocument);
|
||||
when(mockDocument.getDocumentCatalog()).thenReturn(mockCatalog);
|
||||
when(mockCatalog.getNames()).thenReturn(mockNameDict);
|
||||
when(mockNameDict.getEmbeddedFiles()).thenReturn(mockEmbeddedFilesTree);
|
||||
when(pdfAttachmentService.addAttachment(mockDocument, mockEmbeddedFilesTree, attachments)).thenReturn(expectedOutput);
|
||||
|
||||
ResponseEntity<byte[]> response = attachmentsController.addAttachments(pdfFile, attachments);
|
||||
|
||||
assertNotNull(response);
|
||||
assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||
assertNotNull(response.getBody());
|
||||
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);
|
||||
byte[] expectedOutput = "modified PDF content".getBytes();
|
||||
|
||||
try (PDDocument realDocument = new PDDocument()) {
|
||||
when(pdfDocumentFactory.load(pdfFile, false)).thenReturn(realDocument);
|
||||
when(pdfAttachmentService.addAttachment(eq(realDocument), any(PDEmbeddedFilesNameTreeNode.class), eq(attachments))).thenReturn(expectedOutput);
|
||||
|
||||
ResponseEntity<byte[]> response = attachmentsController.addAttachments(pdfFile, attachments);
|
||||
|
||||
assertNotNull(response);
|
||||
assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||
assertNotNull(response.getBody());
|
||||
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);
|
||||
when(pdfAttachmentService.addAttachment(mockDocument, mockEmbeddedFilesTree, attachments)).thenThrow(ioException);
|
||||
|
||||
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();
|
||||
byte[] expectedOutput = "PDF content without new attachments".getBytes();
|
||||
|
||||
when(pdfDocumentFactory.load(pdfFile, false)).thenReturn(mockDocument);
|
||||
when(mockDocument.getDocumentCatalog()).thenReturn(mockCatalog);
|
||||
when(mockCatalog.getNames()).thenReturn(mockNameDict);
|
||||
when(mockNameDict.getEmbeddedFiles()).thenReturn(mockEmbeddedFilesTree);
|
||||
when(pdfAttachmentService.addAttachment(mockDocument, mockEmbeddedFilesTree, emptyAttachments)).thenReturn(expectedOutput);
|
||||
|
||||
ResponseEntity<byte[]> response = attachmentsController.addAttachments(pdfFile, emptyAttachments);
|
||||
|
||||
assertNotNull(response);
|
||||
assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||
assertNotNull(response.getBody());
|
||||
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);
|
||||
byte[] expectedOutput = "PDF with null filename attachment".getBytes();
|
||||
|
||||
when(pdfDocumentFactory.load(pdfFile, false)).thenReturn(mockDocument);
|
||||
when(mockDocument.getDocumentCatalog()).thenReturn(mockCatalog);
|
||||
when(mockCatalog.getNames()).thenReturn(mockNameDict);
|
||||
when(mockNameDict.getEmbeddedFiles()).thenReturn(mockEmbeddedFilesTree);
|
||||
when(pdfAttachmentService.addAttachment(mockDocument, mockEmbeddedFilesTree, attachments)).thenReturn(expectedOutput);
|
||||
|
||||
ResponseEntity<byte[]> response = attachmentsController.addAttachments(pdfFile, attachments);
|
||||
|
||||
assertNotNull(response);
|
||||
assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||
assertNotNull(response.getBody());
|
||||
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,105 @@
|
||||
package stirling.software.SPDF.service;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||
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.*;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
class AttachmentServiceTest {
|
||||
|
||||
private AttachmentService attachmentService;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
attachmentService = new AttachmentService();
|
||||
}
|
||||
|
||||
@Test
|
||||
void addAttachmentToPDF() throws IOException {
|
||||
try (var document = new PDDocument()) {
|
||||
document.setDocumentId(100L);
|
||||
var attachments = List.of(mock(MultipartFile.class));
|
||||
|
||||
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");
|
||||
|
||||
PDDocument result = attachmentService.addAttachment(document, attachments);
|
||||
|
||||
assertNotNull(result);
|
||||
assertEquals(document.getDocumentId(), result.getDocumentId());
|
||||
assertNotNull(result.getDocumentCatalog().getNames());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void addAttachmentToPDF_MultipleAttachments() throws IOException {
|
||||
try (var document = new PDDocument()) {
|
||||
document.setDocumentId(100L);
|
||||
var attachment1 = mock(MultipartFile.class);
|
||||
var attachment2 = mock(MultipartFile.class);
|
||||
var attachments = List.of(attachment1, attachment2);
|
||||
|
||||
when(attachment1.getOriginalFilename()).thenReturn("document.pdf");
|
||||
when(attachment1.getInputStream()).thenReturn(
|
||||
new ByteArrayInputStream("PDF content".getBytes()));
|
||||
when(attachment1.getSize()).thenReturn(15L);
|
||||
when(attachment1.getContentType()).thenReturn("application/pdf");
|
||||
|
||||
when(attachment2.getOriginalFilename()).thenReturn("image.jpg");
|
||||
when(attachment2.getInputStream()).thenReturn(
|
||||
new ByteArrayInputStream("Image content".getBytes()));
|
||||
when(attachment2.getSize()).thenReturn(20L);
|
||||
when(attachment2.getContentType()).thenReturn("image/jpeg");
|
||||
|
||||
PDDocument result = attachmentService.addAttachment(document, attachments);
|
||||
|
||||
assertNotNull(result);
|
||||
assertNotNull(result.getDocumentCatalog().getNames());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void addAttachmentToPDF_WithBlankContentType() throws IOException {
|
||||
try (var document = new PDDocument()) {
|
||||
document.setDocumentId(100L);
|
||||
var attachments = List.of(mock(MultipartFile.class));
|
||||
|
||||
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("");
|
||||
|
||||
PDDocument result = attachmentService.addAttachment(document, attachments);
|
||||
|
||||
assertNotNull(result);
|
||||
assertNotNull(result.getDocumentCatalog().getNames());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void addAttachmentToPDF_AttachmentInputStreamThrowsIOException() throws IOException {
|
||||
try (var document = new PDDocument()) {
|
||||
var attachments = List.of(mock(MultipartFile.class));
|
||||
var ioException = new IOException("Failed to read attachment stream");
|
||||
|
||||
when(attachments.get(0).getOriginalFilename()).thenReturn("test.txt");
|
||||
when(attachments.get(0).getInputStream()).thenThrow(ioException);
|
||||
when(attachments.get(0).getSize()).thenReturn(10L);
|
||||
|
||||
PDDocument result = attachmentService.addAttachment(document, attachments);
|
||||
|
||||
assertNotNull(result);
|
||||
assertNotNull(result.getDocumentCatalog().getNames());
|
||||
}
|
||||
}
|
||||
}
|
@ -1,216 +0,0 @@
|
||||
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.apache.pdfbox.pdmodel.encryption.AccessPermission;
|
||||
import org.apache.pdfbox.pdmodel.encryption.StandardProtectionPolicy;
|
||||
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.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
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");
|
||||
|
||||
byte[] result = pdfAttachmentService.addAttachment(document, embeddedFilesTree, attachments);
|
||||
|
||||
assertNotNull(result);
|
||||
assertTrue(result.length > 0);
|
||||
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");
|
||||
|
||||
byte[] result = pdfAttachmentService.addAttachment(document, embeddedFilesTree, attachments);
|
||||
|
||||
assertNotNull(result);
|
||||
assertTrue(result.length > 0);
|
||||
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("");
|
||||
|
||||
byte[] result = pdfAttachmentService.addAttachment(document, embeddedFilesTree, attachments);
|
||||
|
||||
assertNotNull(result);
|
||||
assertTrue(result.length > 0);
|
||||
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);
|
||||
|
||||
byte[] result = pdfAttachmentService.addAttachment(document, embeddedFilesTree, attachments);
|
||||
|
||||
assertNotNull(result);
|
||||
assertTrue(result.length > 0);
|
||||
verify(embeddedFilesTree).setNames(anyMap());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void addAttachmentToPDF_WithProtectedDocument() throws IOException {
|
||||
try (var document = new PDDocument()) {
|
||||
// Create a document with restricted permissions (this simulates an encrypted/protected document)
|
||||
AccessPermission ap = new AccessPermission();
|
||||
ap.setCanExtractContent(false); // Restrict content extraction initially
|
||||
var spp = new StandardProtectionPolicy("owner", "user", ap);
|
||||
document.protect(spp);
|
||||
|
||||
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");
|
||||
|
||||
byte[] result = pdfAttachmentService.addAttachment(document, embeddedFilesTree, attachments);
|
||||
|
||||
assertNotNull(result);
|
||||
assertTrue(result.length > 0);
|
||||
verify(embeddedFilesTree).setNames(anyMap());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void addAttachmentToPDF_WithRestrictedPermissions() throws IOException {
|
||||
try (var document = new PDDocument()) {
|
||||
// Create a document with very restricted permissions that should block permission changes
|
||||
AccessPermission ap = new AccessPermission();
|
||||
ap.setCanModify(false);
|
||||
ap.setCanAssembleDocument(false);
|
||||
ap.setCanExtractContent(false);
|
||||
var spp = new StandardProtectionPolicy("owner", "user", ap);
|
||||
document.protect(spp);
|
||||
|
||||
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");
|
||||
|
||||
byte[] result = pdfAttachmentService.addAttachment(document, embeddedFilesTree, attachments);
|
||||
|
||||
assertNotNull(result);
|
||||
assertTrue(result.length > 0);
|
||||
verify(embeddedFilesTree).setNames(anyMap());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void addAttachmentToPDF_WithNonEncryptedDocument() 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");
|
||||
|
||||
byte[] result = pdfAttachmentService.addAttachment(document, embeddedFilesTree, attachments);
|
||||
|
||||
assertNotNull(result);
|
||||
assertTrue(result.length > 0);
|
||||
// Verify permissions are set correctly for non-encrypted documents
|
||||
AccessPermission permissions = document.getCurrentAccessPermission();
|
||||
assertTrue(permissions.canExtractContent());
|
||||
assertTrue(permissions.canExtractForAccessibility());
|
||||
assertTrue(permissions.canModifyAnnotations());
|
||||
verify(embeddedFilesTree).setNames(anyMap());
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user