2025-06-04 12:34:14 +01:00
|
|
|
package stirling.software.SPDF.service;
|
|
|
|
|
2025-06-20 13:22:06 +01:00
|
|
|
import static stirling.software.common.util.PDFAttachmentUtils.setCatalogViewerPreferences;
|
|
|
|
|
2025-06-04 12:34:14 +01:00
|
|
|
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;
|
2025-06-20 13:22:06 +01:00
|
|
|
import org.apache.pdfbox.pdmodel.PDDocumentCatalog;
|
|
|
|
import org.apache.pdfbox.pdmodel.PDDocumentNameDictionary;
|
2025-06-04 12:34:14 +01:00
|
|
|
import org.apache.pdfbox.pdmodel.PDEmbeddedFilesNameTreeNode;
|
2025-06-18 11:35:15 +01:00
|
|
|
import org.apache.pdfbox.pdmodel.PageMode;
|
2025-06-04 12:34:14 +01:00
|
|
|
import org.apache.pdfbox.pdmodel.common.filespecification.PDComplexFileSpecification;
|
|
|
|
import org.apache.pdfbox.pdmodel.common.filespecification.PDEmbeddedFile;
|
|
|
|
import org.springframework.stereotype.Service;
|
|
|
|
import org.springframework.web.multipart.MultipartFile;
|
|
|
|
|
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
|
|
|
|
|
@Slf4j
|
|
|
|
@Service
|
2025-06-20 13:22:06 +01:00
|
|
|
public class AttachmentService implements AttachmentServiceInterface {
|
2025-06-04 12:34:14 +01:00
|
|
|
|
|
|
|
@Override
|
2025-06-20 13:22:06 +01:00
|
|
|
public PDDocument addAttachment(PDDocument document, List<MultipartFile> attachments)
|
2025-06-04 12:34:14 +01:00
|
|
|
throws IOException {
|
2025-06-20 13:22:06 +01:00
|
|
|
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);
|
2025-06-04 12:34:14 +01:00
|
|
|
Map<String, PDComplexFileSpecification> existingNames;
|
|
|
|
|
|
|
|
try {
|
2025-06-20 13:22:06 +01:00
|
|
|
Map<String, PDComplexFileSpecification> originalNames = embeddedFilesTree.getNames();
|
2025-06-18 15:13:27 +01:00
|
|
|
|
2025-06-20 13:22:06 +01:00
|
|
|
if (originalNames == null) {
|
2025-06-04 12:34:14 +01:00
|
|
|
log.debug("No existing embedded files found, creating new names map.");
|
|
|
|
existingNames = new HashMap<>();
|
2025-06-20 13:22:06 +01:00
|
|
|
} else {
|
|
|
|
existingNames = new HashMap<>(originalNames);
|
|
|
|
log.debug("Embedded files: {}", existingNames.keySet());
|
2025-06-04 12:34:14 +01:00
|
|
|
}
|
|
|
|
} catch (IOException e) {
|
|
|
|
log.error("Could not retrieve existing embedded files", e);
|
|
|
|
throw e;
|
|
|
|
}
|
|
|
|
|
|
|
|
attachments.forEach(
|
|
|
|
attachment -> {
|
2025-06-18 15:13:27 +01:00
|
|
|
String filename = attachment.getOriginalFilename();
|
2025-06-04 12:34:14 +01:00
|
|
|
|
|
|
|
try {
|
|
|
|
PDEmbeddedFile embeddedFile =
|
|
|
|
new PDEmbeddedFile(document, attachment.getInputStream());
|
|
|
|
embeddedFile.setSize((int) attachment.getSize());
|
|
|
|
embeddedFile.setCreationDate(new GregorianCalendar());
|
|
|
|
embeddedFile.setModDate(new GregorianCalendar());
|
|
|
|
String contentType = attachment.getContentType();
|
|
|
|
if (StringUtils.isNotBlank(contentType)) {
|
|
|
|
embeddedFile.setSubtype(contentType);
|
|
|
|
}
|
|
|
|
|
2025-06-18 15:13:27 +01:00
|
|
|
// Create attachments specification and associate embedded attachment with
|
|
|
|
// file
|
|
|
|
PDComplexFileSpecification fileSpecification =
|
|
|
|
new PDComplexFileSpecification();
|
|
|
|
fileSpecification.setFile(filename);
|
|
|
|
fileSpecification.setFileUnicode(filename);
|
|
|
|
fileSpecification.setFileDescription("Embedded attachment: " + filename);
|
2025-06-04 12:34:14 +01:00
|
|
|
fileSpecification.setEmbeddedFile(embeddedFile);
|
|
|
|
fileSpecification.setEmbeddedFileUnicode(embeddedFile);
|
|
|
|
|
2025-06-20 13:22:06 +01:00
|
|
|
existingNames.put(filename, fileSpecification);
|
2025-06-04 12:34:14 +01:00
|
|
|
|
2025-06-18 15:13:27 +01:00
|
|
|
log.info("Added attachment: {} ({} bytes)", filename, attachment.getSize());
|
2025-06-04 12:34:14 +01:00
|
|
|
} catch (IOException e) {
|
2025-06-18 15:13:27 +01:00
|
|
|
log.warn("Failed to create embedded file for attachment: {}", filename, e);
|
2025-06-04 12:34:14 +01:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
embeddedFilesTree.setNames(existingNames);
|
2025-06-20 13:22:06 +01:00
|
|
|
setCatalogViewerPreferences(document, PageMode.USE_ATTACHMENTS);
|
2025-06-04 12:34:14 +01:00
|
|
|
|
2025-06-20 13:22:06 +01:00
|
|
|
return document;
|
2025-06-04 12:34:14 +01:00
|
|
|
}
|
|
|
|
}
|