diff --git a/src/main/java/stirling/software/SPDF/controller/api/MergeController.java b/src/main/java/stirling/software/SPDF/controller/api/MergeController.java index a226bd02d..f8b8e194c 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/MergeController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/MergeController.java @@ -15,6 +15,8 @@ import org.apache.pdfbox.multipdf.PDFMergerUtility; import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocumentCatalog; import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline.PDDocumentOutline; +import org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline.PDOutlineItem; import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm; import org.apache.pdfbox.pdmodel.interactive.form.PDField; import org.apache.pdfbox.pdmodel.interactive.form.PDSignatureField; @@ -124,6 +126,7 @@ public class MergeController { PDDocument mergedDocument = null; boolean removeCertSign = form.isRemoveCertSign(); + boolean generateToc = form.isGenerateToc(); try { MultipartFile[] files = form.getFileInput(); @@ -169,6 +172,11 @@ public class MergeController { } } } + + // Generate table of contents if requested + if (generateToc) { + generateTableOfContents(mergedDocument, files); + } // Save the modified document to a new ByteArrayOutputStream ByteArrayOutputStream baos = new ByteArrayOutputStream(); @@ -197,4 +205,54 @@ public class MergeController { } } } + + /** + * Generates a table of contents for the merged PDF document using file names as chapter titles + * + * @param mergedDocument The merged PDF document + * @param files The original input files that were merged + */ + private void generateTableOfContents(PDDocument mergedDocument, MultipartFile[] files) { + PDDocumentCatalog catalog = mergedDocument.getDocumentCatalog(); + PDDocumentOutline outline = new PDDocumentOutline(); + catalog.setDocumentOutline(outline); + + int currentPageIndex = 0; + + for (MultipartFile file : files) { + try { + // Create a temporary file to load document and get page count + File tempFile = GeneralUtils.convertMultipartFileToFile(file); + PDDocument doc = pdfDocumentFactory.load(tempFile); + + try { + // Create an outline item for this file + String fileName = file.getOriginalFilename(); + if (fileName != null && fileName.toLowerCase().endsWith(".pdf")) { + fileName = fileName.substring(0, fileName.length() - 4); + } + + PDOutlineItem bookmark = new PDOutlineItem(); + bookmark.setTitle(fileName); + + // Set destination to the first page of this file in the merged document + PDPage page = mergedDocument.getPage(currentPageIndex); + bookmark.setDestination(page); + + // Add the bookmark to the outline + outline.addLast(bookmark); + + // Update current page index for next file + currentPageIndex += doc.getNumberOfPages(); + } finally { + doc.close(); + Files.deleteIfExists(tempFile.toPath()); + } + } catch (IOException e) { + log.error("Error creating bookmark for file: " + file.getOriginalFilename(), e); + // Continue with next file even if one fails + continue; + } + } + } } diff --git a/src/main/java/stirling/software/SPDF/model/api/general/MergePdfsRequest.java b/src/main/java/stirling/software/SPDF/model/api/general/MergePdfsRequest.java index 9b97d88dd..c6564153d 100644 --- a/src/main/java/stirling/software/SPDF/model/api/general/MergePdfsRequest.java +++ b/src/main/java/stirling/software/SPDF/model/api/general/MergePdfsRequest.java @@ -28,4 +28,10 @@ public class MergePdfsRequest extends MultiplePDFFiles { "Flag indicating whether to remove certification signatures from the merged PDF. If true, all certification signatures will be removed from the final merged document.", example = "true") private boolean isRemoveCertSign; + + @Schema( + description = + "Flag indicating whether to generate a table of contents for the merged PDF. If true, a table of contents will be created using the input filenames as chapter names.", + example = "true") + private boolean generateToc = false; } diff --git a/src/main/resources/messages_en_GB.properties b/src/main/resources/messages_en_GB.properties index 1a8860a12..02ced24c5 100644 --- a/src/main/resources/messages_en_GB.properties +++ b/src/main/resources/messages_en_GB.properties @@ -1013,6 +1013,7 @@ merge.header=Merge multiple PDFs (2+) merge.sortByName=Sort by name merge.sortByDate=Sort by date merge.removeCertSign=Remove digital signature in the merged file? +merge.generateToc=Generate table of contents in the merged file? merge.submit=Merge diff --git a/src/main/resources/templates/merge-pdfs.html b/src/main/resources/templates/merge-pdfs.html index d10b65801..f8d194a4e 100644 --- a/src/main/resources/templates/merge-pdfs.html +++ b/src/main/resources/templates/merge-pdfs.html @@ -32,6 +32,10 @@ +
+ + +