diff --git a/src/main/java/stirling/software/SPDF/controller/api/PdfOverlayController.java b/src/main/java/stirling/software/SPDF/controller/api/PdfOverlayController.java index 1dc81e9f..9551754a 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/PdfOverlayController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/PdfOverlayController.java @@ -3,8 +3,9 @@ import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.util.HashMap; +import java.util.List; import java.util.Map; - +import java.util.ArrayList; import org.apache.pdfbox.multipdf.Overlay; import org.apache.pdfbox.pdmodel.PDDocument; import org.springframework.http.MediaType; @@ -25,7 +26,7 @@ import stirling.software.SPDF.utils.WebResponseUtils; @Tag(name = "General", description = "General APIs") public class PdfOverlayController { - @PostMapping(value = "/overlay-pdfs", consumes = "multipart/form-data") + @PostMapping(value = "/overlay-pdfs", consumes = "multipart/form-data") @Operation(summary = "Overlay PDF files in various modes", description = "Overlay PDF files onto a base PDF with different modes: Sequential, Interleaved, or Fixed Repeat. Input:PDF Output:PDF Type:MIMO") public ResponseEntity overlayPdfs(@ModelAttribute OverlayPdfsRequest request) throws IOException { MultipartFile baseFile = request.getFileInput(); @@ -33,44 +34,53 @@ public class PdfOverlayController { MultipartFile[] overlayFiles = request.getOverlayFiles(); File[] overlayPdfFiles = new File[overlayFiles.length]; - try{ - for (int i = 0; i < overlayFiles.length; i++) { - overlayPdfFiles[i] = GeneralUtils.multipartToFile(overlayFiles[i]); - } - - String mode = request.getOverlayMode(); // "SequentialOverlay", "InterleavedOverlay", "FixedRepeatOverlay" - int[] counts = request.getCounts(); // Used for FixedRepeatOverlay mode - - try (PDDocument basePdf = PDDocument.load(baseFile.getInputStream()); - Overlay overlay = new Overlay()) { - Map overlayGuide = prepareOverlayGuide(basePdf.getNumberOfPages(), overlayPdfFiles, mode, counts); - - overlay.setInputPDF(basePdf); - if(overlayPos == 0) { - overlay.setOverlayPosition(Overlay.Position.FOREGROUND); - } else { - overlay.setOverlayPosition(Overlay.Position.BACKGROUND); - } - - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - overlay.overlay(overlayGuide).save(outputStream); - byte[] data = outputStream.toByteArray(); - String outputFilename = baseFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_overlayed.pdf"; // Remove file extension and append .pdf - - return WebResponseUtils.bytesToWebResponse(data, outputFilename, MediaType.APPLICATION_PDF); - } + List tempFiles = new ArrayList<>(); // List to keep track of temporary files + + try { + for (int i = 0; i < overlayFiles.length; i++) { + overlayPdfFiles[i] = GeneralUtils.multipartToFile(overlayFiles[i]); + } + + String mode = request.getOverlayMode(); // "SequentialOverlay", "InterleavedOverlay", "FixedRepeatOverlay" + int[] counts = request.getCounts(); // Used for FixedRepeatOverlay mode + + try (PDDocument basePdf = PDDocument.load(baseFile.getInputStream()); + Overlay overlay = new Overlay()) { + Map overlayGuide = prepareOverlayGuide(basePdf.getNumberOfPages(), overlayPdfFiles, mode, counts, tempFiles); + + overlay.setInputPDF(basePdf); + if (overlayPos == 0) { + overlay.setOverlayPosition(Overlay.Position.FOREGROUND); + } else { + overlay.setOverlayPosition(Overlay.Position.BACKGROUND); + } + + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + overlay.overlay(overlayGuide).save(outputStream); + byte[] data = outputStream.toByteArray(); + String outputFilename = baseFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_overlayed.pdf"; // Remove file extension and append .pdf + + return WebResponseUtils.bytesToWebResponse(data, outputFilename, MediaType.APPLICATION_PDF); + } } finally { for (File overlayPdfFile : overlayPdfFiles) { - if (overlayPdfFile != null) overlayPdfFile.delete(); + if (overlayPdfFile != null) { + overlayPdfFile.delete(); + } + } + for (File tempFile : tempFiles) { // Delete temporary files + if (tempFile != null) { + tempFile.delete(); + } } } } - private Map prepareOverlayGuide(int basePageCount, File[] overlayFiles, String mode, int[] counts) throws IOException { + private Map prepareOverlayGuide(int basePageCount, File[] overlayFiles, String mode, int[] counts, List tempFiles) throws IOException { Map overlayGuide = new HashMap<>(); switch (mode) { case "SequentialOverlay": - sequentialOverlay(overlayGuide, overlayFiles, basePageCount); + sequentialOverlay(overlayGuide, overlayFiles, basePageCount, tempFiles); break; case "InterleavedOverlay": interleavedOverlay(overlayGuide, overlayFiles, basePageCount); @@ -84,42 +94,80 @@ public class PdfOverlayController { return overlayGuide; } - private void sequentialOverlay(Map overlayGuide, File[] overlayFiles, int basePageCount) throws IOException { - if (overlayFiles.length != 1 || basePageCount != PDDocument.load(overlayFiles[0]).getNumberOfPages()) { - throw new IllegalArgumentException("Overlay file count and base page count must match for sequential overlay."); - } + private void sequentialOverlay(Map overlayGuide, File[] overlayFiles, int basePageCount, List tempFiles) throws IOException { + int overlayFileIndex = 0; + int pageCountInCurrentOverlay = 0; - File overlayFile = overlayFiles[0]; - try (PDDocument overlayPdf = PDDocument.load(overlayFile)) { - for (int i = 1; i <= overlayPdf.getNumberOfPages(); i++) { - if (i > basePageCount) break; - overlayGuide.put(i, overlayFile.getAbsolutePath()); + for (int basePageIndex = 1; basePageIndex <= basePageCount; basePageIndex++) { + if (pageCountInCurrentOverlay == 0 || pageCountInCurrentOverlay >= getNumberOfPages(overlayFiles[overlayFileIndex])) { + pageCountInCurrentOverlay = 0; + overlayFileIndex = (overlayFileIndex + 1) % overlayFiles.length; + } + + try (PDDocument overlayPdf = PDDocument.load(overlayFiles[overlayFileIndex])) { + PDDocument singlePageDocument = new PDDocument(); + singlePageDocument.addPage(overlayPdf.getPage(pageCountInCurrentOverlay)); + File tempFile = File.createTempFile("overlay-page-", ".pdf"); + singlePageDocument.save(tempFile); + singlePageDocument.close(); + + overlayGuide.put(basePageIndex, tempFile.getAbsolutePath()); + tempFiles.add(tempFile); // Keep track of the temporary file for cleanup + } + + pageCountInCurrentOverlay++; + } + } + + private int getNumberOfPages(File file) throws IOException { + try (PDDocument doc = PDDocument.load(file)) { + return doc.getNumberOfPages(); + } + } + + + + + + + + private void interleavedOverlay(Map overlayGuide, File[] overlayFiles, int basePageCount) throws IOException { + for (int basePageIndex = 1; basePageIndex <= basePageCount; basePageIndex++) { + File overlayFile = overlayFiles[(basePageIndex - 1) % overlayFiles.length]; + + // Load the overlay document to check its page count + try (PDDocument overlayPdf = PDDocument.load(overlayFile)) { + int overlayPageCount = overlayPdf.getNumberOfPages(); + if ((basePageIndex - 1) % overlayPageCount < overlayPageCount) { + overlayGuide.put(basePageIndex, overlayFile.getAbsolutePath()); + } } } } - private void interleavedOverlay(Map overlayGuide, File[] overlayFiles, int basePageCount) throws IOException { - for (int i = 0; i < basePageCount; i++) { - File overlayFile = overlayFiles[i % overlayFiles.length]; - overlayGuide.put(i + 1, overlayFile.getAbsolutePath()); - } - } - private void fixedRepeatOverlay(Map overlayGuide, File[] overlayFiles, int[] counts, int basePageCount) throws IOException { if (overlayFiles.length != counts.length) { throw new IllegalArgumentException("Counts array length must match the number of overlay files"); } int currentPage = 1; for (int i = 0; i < overlayFiles.length; i++) { - File overlayFile = overlayFiles[i]; + File overlayFile = overlayFiles[i]; int repeatCount = counts[i]; - for (int j = 0; j < repeatCount; j++) { - if (currentPage > basePageCount) break; - overlayGuide.put(currentPage++, overlayFile.getAbsolutePath()); + + // Load the overlay document to check its page count + try (PDDocument overlayPdf = PDDocument.load(overlayFile)) { + int overlayPageCount = overlayPdf.getNumberOfPages(); + for (int j = 0; j < repeatCount; j++) { + for (int page = 0; page < overlayPageCount; page++) { + if (currentPage > basePageCount) break; + overlayGuide.put(currentPage++, overlayFile.getAbsolutePath()); + } + } } } } + } // Additional classes like OverlayPdfsRequest, WebResponseUtils, etc. are assumed to be defined elsewhere.