diff --git a/src/main/java/stirling/software/SPDF/config/PdfMetadataService.java b/src/main/java/stirling/software/SPDF/config/PdfMetadataService.java index 9eba472e..7b40a878 100644 --- a/src/main/java/stirling/software/SPDF/config/PdfMetadataService.java +++ b/src/main/java/stirling/software/SPDF/config/PdfMetadataService.java @@ -7,7 +7,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Service; -import jakarta.annotation.PostConstruct; import stirling.software.SPDF.controller.api.pipeline.UserServiceInterface; import stirling.software.SPDF.model.ApplicationProperties; import stirling.software.SPDF.model.PdfMetadata; @@ -15,8 +14,6 @@ import stirling.software.SPDF.model.PdfMetadata; @Service public class PdfMetadataService { - private static PdfMetadataService instance; - private final ApplicationProperties applicationProperties; private final String appVersion; private final UserServiceInterface userService; @@ -31,33 +28,7 @@ public class PdfMetadataService { this.userService = userService; } - @PostConstruct - public void init() { - instance = this; - } - - // Static methods for easy access - - public static PdfMetadata extractMetadataFromPdf(PDDocument pdf) { - return instance.extractMetadataFromPdfInstance(pdf); - } - - public static void setDefaultMetadata(PDDocument pdf) { - instance.setDefaultMetadataInstance(pdf); - } - - public static void setMetadataToPdf(PDDocument pdf, PdfMetadata pdfMetadata) { - instance.setMetadataToPdfInstance(pdf, pdfMetadata); - } - - public static void setMetadataToPdf( - PDDocument pdf, PdfMetadata pdfMetadata, boolean newlyCreated) { - instance.setMetadataToPdfInstance(pdf, pdfMetadata, newlyCreated); - } - - // Instance methods - - private PdfMetadata extractMetadataFromPdfInstance(PDDocument pdf) { + public PdfMetadata extractMetadataFromPdf(PDDocument pdf) { return PdfMetadata.builder() .author(pdf.getDocumentInformation().getAuthor()) .producer(pdf.getDocumentInformation().getProducer()) @@ -70,17 +41,16 @@ public class PdfMetadataService { .build(); } - private void setDefaultMetadataInstance(PDDocument pdf) { - PdfMetadata metadata = extractMetadataFromPdfInstance(pdf); - setMetadataToPdfInstance(pdf, metadata); + public void setDefaultMetadata(PDDocument pdf) { + PdfMetadata metadata = extractMetadataFromPdf(pdf); + setMetadataToPdf(pdf, metadata); } - private void setMetadataToPdfInstance(PDDocument pdf, PdfMetadata pdfMetadata) { - setMetadataToPdfInstance(pdf, pdfMetadata, true); + public void setMetadataToPdf(PDDocument pdf, PdfMetadata pdfMetadata) { + setMetadataToPdf(pdf, pdfMetadata, false); } - private void setMetadataToPdfInstance( - PDDocument pdf, PdfMetadata pdfMetadata, boolean newlyCreated) { + public void setMetadataToPdf(PDDocument pdf, PdfMetadata pdfMetadata, boolean newlyCreated) { if (newlyCreated || pdfMetadata.getCreationDate() == null) { setNewDocumentMetadata(pdf, pdfMetadata); } @@ -89,35 +59,35 @@ public class PdfMetadataService { private void setNewDocumentMetadata(PDDocument pdf, PdfMetadata pdfMetadata) { - String title = pdfMetadata.getTitle(); String creator = "Stirling-PDF"; -// if (applicationProperties -// .getEnterpriseEdition() -// .getCustomMetadata() -// .isAutoUpdateMetadata()) { + // if (applicationProperties + // .getEnterpriseEdition() + // .getCustomMetadata() + // .isAutoUpdateMetadata()) { - // producer = - // - // applicationProperties.getEnterpriseEdition().getCustomMetadata().getProducer(); - // creator = - // applicationProperties.getEnterpriseEdition().getCustomMetadata().getCreator(); - // title = applicationProperties.getEnterpriseEdition().getCustomMetadata().getTitle(); + // producer = + // + // applicationProperties.getEnterpriseEdition().getCustomMetadata().getProducer(); + // creator = + // applicationProperties.getEnterpriseEdition().getCustomMetadata().getCreator(); + // title = applicationProperties.getEnterpriseEdition().getCustomMetadata().getTitle(); -// if ("{filename}".equals(title)) { -// title = "Filename"; // Replace with actual filename logic -// } else if ("{unchanged}".equals(title)) { -// title = pdfMetadata.getTitle(); // Keep the original title -// } -// } + // if ("{filename}".equals(title)) { + // title = "Filename"; // Replace with actual filename logic + // } else if ("{unchanged}".equals(title)) { + // title = pdfMetadata.getTitle(); // Keep the original title + // } + // } - pdf.getDocumentInformation().setTitle(title); pdf.getDocumentInformation().setCreator(creator + " " + appVersion); pdf.getDocumentInformation().setCreationDate(Calendar.getInstance()); } private void setCommonMetadata(PDDocument pdf, PdfMetadata pdfMetadata) { String producer = "Stirling-PDF"; + String title = pdfMetadata.getTitle(); + pdf.getDocumentInformation().setTitle(title); pdf.getDocumentInformation().setProducer(producer + " " + appVersion); pdf.getDocumentInformation().setSubject(pdfMetadata.getSubject()); pdf.getDocumentInformation().setKeywords(pdfMetadata.getKeywords()); diff --git a/src/main/java/stirling/software/SPDF/controller/api/CropController.java b/src/main/java/stirling/software/SPDF/controller/api/CropController.java index 42addb26..551cd72d 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/CropController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/CropController.java @@ -13,6 +13,7 @@ import org.apache.pdfbox.pdmodel.common.PDRectangle; import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PostMapping; @@ -23,6 +24,7 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import stirling.software.SPDF.model.api.general.CropPdfForm; +import stirling.software.SPDF.service.CustomPDDocumentFactory; import stirling.software.SPDF.utils.WebResponseUtils; @RestController @@ -32,6 +34,13 @@ public class CropController { private static final Logger logger = LoggerFactory.getLogger(CropController.class); + private final CustomPDDocumentFactory pdfDocumentFactory; + + @Autowired + public CropController(CustomPDDocumentFactory pdfDocumentFactory) { + this.pdfDocumentFactory = pdfDocumentFactory; + } + @PostMapping(value = "/crop", consumes = "multipart/form-data") @Operation( summary = "Crops a PDF document", @@ -40,7 +49,8 @@ public class CropController { public ResponseEntity cropPdf(@ModelAttribute CropPdfForm form) throws IOException { PDDocument sourceDocument = Loader.loadPDF(form.getFileInput().getBytes()); - PDDocument newDocument = new PDDocument(); + PDDocument newDocument = + pdfDocumentFactory.createNewDocumentBasedOnOldDocument(sourceDocument); int totalPages = sourceDocument.getNumberOfPages(); 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 6bb25650..9a60fcf6 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/MergeController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/MergeController.java @@ -22,6 +22,7 @@ import org.apache.pdfbox.pdmodel.interactive.form.PDField; import org.apache.pdfbox.pdmodel.interactive.form.PDSignatureField; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PostMapping; @@ -33,6 +34,7 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import stirling.software.SPDF.model.api.general.MergePdfsRequest; +import stirling.software.SPDF.service.CustomPDDocumentFactory; import stirling.software.SPDF.utils.GeneralUtils; import stirling.software.SPDF.utils.WebResponseUtils; @@ -43,9 +45,16 @@ public class MergeController { private static final Logger logger = LoggerFactory.getLogger(MergeController.class); + private final CustomPDDocumentFactory pdfDocumentFactory; + + @Autowired + public MergeController(CustomPDDocumentFactory pdfDocumentFactory) { + this.pdfDocumentFactory = pdfDocumentFactory; + } + // Merges a list of PDDocument objects into a single PDDocument public PDDocument mergeDocuments(List documents) throws IOException { - PDDocument mergedDoc = new PDDocument(); + PDDocument mergedDoc = pdfDocumentFactory.createNewDocument(); for (PDDocument doc : documents) { for (PDPage page : doc.getPages()) { mergedDoc.addPage(page); diff --git a/src/main/java/stirling/software/SPDF/controller/api/MultiPageLayoutController.java b/src/main/java/stirling/software/SPDF/controller/api/MultiPageLayoutController.java index adc5424d..fbfec731 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/MultiPageLayoutController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/MultiPageLayoutController.java @@ -14,6 +14,7 @@ import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject; import org.apache.pdfbox.util.Matrix; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PostMapping; @@ -26,6 +27,7 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import stirling.software.SPDF.model.api.general.MergeMultiplePagesRequest; +import stirling.software.SPDF.service.CustomPDDocumentFactory; import stirling.software.SPDF.utils.WebResponseUtils; @RestController @@ -35,6 +37,13 @@ public class MultiPageLayoutController { private static final Logger logger = LoggerFactory.getLogger(MultiPageLayoutController.class); + private final CustomPDDocumentFactory pdfDocumentFactory; + + @Autowired + public MultiPageLayoutController(CustomPDDocumentFactory pdfDocumentFactory) { + this.pdfDocumentFactory = pdfDocumentFactory; + } + @PostMapping(value = "/multi-page-layout", consumes = "multipart/form-data") @Operation( summary = "Merge multiple pages of a PDF document into a single page", @@ -60,7 +69,8 @@ public class MultiPageLayoutController { int rows = pagesPerSheet == 2 || pagesPerSheet == 3 ? 1 : (int) Math.sqrt(pagesPerSheet); PDDocument sourceDocument = Loader.loadPDF(file.getBytes()); - PDDocument newDocument = new PDDocument(); + PDDocument newDocument = + pdfDocumentFactory.createNewDocumentBasedOnOldDocument(sourceDocument); PDPage newPage = new PDPage(PDRectangle.A4); newDocument.addPage(newPage); diff --git a/src/main/java/stirling/software/SPDF/controller/api/PdfImageRemovalController.java b/src/main/java/stirling/software/SPDF/controller/api/PdfImageRemovalController.java index d56b7d2d..302b00f4 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/PdfImageRemovalController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/PdfImageRemovalController.java @@ -3,16 +3,15 @@ package stirling.software.SPDF.controller.api; import java.io.ByteArrayOutputStream; import java.io.IOException; -import org.apache.pdfbox.Loader; import org.apache.pdfbox.pdmodel.PDDocument; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -import org.springframework.web.multipart.MultipartFile; import io.swagger.v3.oas.annotations.Operation; import stirling.software.SPDF.model.api.PDFFile; +import stirling.software.SPDF.service.CustomPDDocumentFactory; import stirling.software.SPDF.service.PdfImageRemovalService; import stirling.software.SPDF.utils.WebResponseUtils; @@ -25,15 +24,21 @@ import stirling.software.SPDF.utils.WebResponseUtils; public class PdfImageRemovalController { // Service for removing images from PDFs - @Autowired private PdfImageRemovalService pdfImageRemovalService; + private final PdfImageRemovalService pdfImageRemovalService; + + private final CustomPDDocumentFactory pdfDocumentFactory; /** * Constructor for dependency injection of PdfImageRemovalService. * * @param pdfImageRemovalService The service used for removing images from PDFs. */ - public PdfImageRemovalController(PdfImageRemovalService pdfImageRemovalService) { + @Autowired + public PdfImageRemovalController( + PdfImageRemovalService pdfImageRemovalService, + CustomPDDocumentFactory pdfDocumentFactory) { this.pdfImageRemovalService = pdfImageRemovalService; + this.pdfDocumentFactory = pdfDocumentFactory; } /** @@ -53,14 +58,8 @@ public class PdfImageRemovalController { description = "This endpoint remove images from file to reduce the file size.Input:PDF Output:PDF Type:MISO") public ResponseEntity removeImages(@ModelAttribute PDFFile file) throws IOException { - - MultipartFile pdf = file.getFileInput(); - - // Convert the MultipartFile to a byte array - byte[] pdfBytes = pdf.getBytes(); - - // Load the PDF document from the byte array - PDDocument document = Loader.loadPDF(pdfBytes); + // Load the PDF document + PDDocument document = pdfDocumentFactory.load(file); // Remove images from the PDF document using the service PDDocument modifiedDocument = pdfImageRemovalService.removeImagesFromPdf(document); @@ -74,7 +73,8 @@ public class PdfImageRemovalController { // Generate a new filename for the modified PDF String mergedFileName = - pdf.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_removed_images.pdf"; + file.getFileInput().getOriginalFilename().replaceFirst("[.][^.]+$", "") + + "_removed_images.pdf"; // Convert the byte array to a web response and return it return WebResponseUtils.bytesToWebResponse(outputStream.toByteArray(), mergedFileName); 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 48c987cf..7ff17ccb 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/PdfOverlayController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/PdfOverlayController.java @@ -12,6 +12,7 @@ import java.util.Map; import org.apache.pdfbox.Loader; import org.apache.pdfbox.multipdf.Overlay; import org.apache.pdfbox.pdmodel.PDDocument; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ModelAttribute; @@ -25,6 +26,7 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import stirling.software.SPDF.model.api.general.OverlayPdfsRequest; +import stirling.software.SPDF.service.CustomPDDocumentFactory; import stirling.software.SPDF.utils.GeneralUtils; import stirling.software.SPDF.utils.WebResponseUtils; @@ -33,6 +35,13 @@ import stirling.software.SPDF.utils.WebResponseUtils; @Tag(name = "General", description = "General APIs") public class PdfOverlayController { + private final CustomPDDocumentFactory pdfDocumentFactory; + + @Autowired + public PdfOverlayController(CustomPDDocumentFactory pdfDocumentFactory) { + this.pdfDocumentFactory = pdfDocumentFactory; + } + @PostMapping(value = "/overlay-pdfs", consumes = "multipart/form-data") @Operation( summary = "Overlay PDF files in various modes", @@ -56,7 +65,7 @@ public class PdfOverlayController { // "FixedRepeatOverlay" int[] counts = request.getCounts(); // Used for FixedRepeatOverlay mode - try (PDDocument basePdf = Loader.loadPDF(baseFile.getBytes()); + try (PDDocument basePdf = pdfDocumentFactory.load(baseFile); Overlay overlay = new Overlay()) { Map overlayGuide = prepareOverlayGuide( diff --git a/src/main/java/stirling/software/SPDF/controller/api/RearrangePagesPDFController.java b/src/main/java/stirling/software/SPDF/controller/api/RearrangePagesPDFController.java index 3a9ed47b..8eb08dbf 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/RearrangePagesPDFController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/RearrangePagesPDFController.java @@ -10,6 +10,7 @@ import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDPage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PostMapping; @@ -24,6 +25,7 @@ import io.swagger.v3.oas.annotations.tags.Tag; import stirling.software.SPDF.model.SortTypes; import stirling.software.SPDF.model.api.PDFWithPageNums; import stirling.software.SPDF.model.api.general.RearrangePagesRequest; +import stirling.software.SPDF.service.CustomPDDocumentFactory; import stirling.software.SPDF.utils.GeneralUtils; import stirling.software.SPDF.utils.WebResponseUtils; @@ -34,6 +36,13 @@ public class RearrangePagesPDFController { private static final Logger logger = LoggerFactory.getLogger(RearrangePagesPDFController.class); + private final CustomPDDocumentFactory pdfDocumentFactory; + + @Autowired + public RearrangePagesPDFController(CustomPDDocumentFactory pdfDocumentFactory) { + this.pdfDocumentFactory = pdfDocumentFactory; + } + @PostMapping(consumes = "multipart/form-data", value = "/remove-pages") @Operation( summary = "Remove pages from a PDF file", @@ -45,7 +54,7 @@ public class RearrangePagesPDFController { MultipartFile pdfFile = request.getFileInput(); String pagesToDelete = request.getPageNumbers(); - PDDocument document = Loader.loadPDF(pdfFile.getBytes()); + PDDocument document = pdfDocumentFactory.load(pdfFile); // Split the page order string into an array of page numbers or range of numbers String[] pageOrderArr = pagesToDelete.split(","); diff --git a/src/main/java/stirling/software/SPDF/controller/api/RotationController.java b/src/main/java/stirling/software/SPDF/controller/api/RotationController.java index 76f508d3..1c416a07 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/RotationController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/RotationController.java @@ -2,12 +2,12 @@ package stirling.software.SPDF.controller.api; import java.io.IOException; -import org.apache.pdfbox.Loader; import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDPage; import org.apache.pdfbox.pdmodel.PDPageTree; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PostMapping; @@ -20,6 +20,7 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import stirling.software.SPDF.model.api.general.RotatePDFRequest; +import stirling.software.SPDF.service.CustomPDDocumentFactory; import stirling.software.SPDF.utils.WebResponseUtils; @RestController @@ -29,6 +30,13 @@ public class RotationController { private static final Logger logger = LoggerFactory.getLogger(RotationController.class); + private final CustomPDDocumentFactory pdfDocumentFactory; + + @Autowired + public RotationController(CustomPDDocumentFactory pdfDocumentFactory) { + this.pdfDocumentFactory = pdfDocumentFactory; + } + @PostMapping(consumes = "multipart/form-data", value = "/rotate-pdf") @Operation( summary = "Rotate a PDF file", @@ -39,7 +47,7 @@ public class RotationController { MultipartFile pdfFile = request.getFileInput(); Integer angle = request.getAngle(); // Load the PDF document - PDDocument document = Loader.loadPDF(pdfFile.getBytes()); + PDDocument document = pdfDocumentFactory.load(request); // Get the list of pages in the document PDPageTree pages = document.getPages(); diff --git a/src/main/java/stirling/software/SPDF/controller/api/ScalePagesController.java b/src/main/java/stirling/software/SPDF/controller/api/ScalePagesController.java index 5eb1205d..9b7714ad 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/ScalePagesController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/ScalePagesController.java @@ -15,6 +15,7 @@ import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject; import org.apache.pdfbox.util.Matrix; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PostMapping; @@ -27,6 +28,7 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import stirling.software.SPDF.model.api.general.ScalePagesRequest; +import stirling.software.SPDF.service.CustomPDDocumentFactory; import stirling.software.SPDF.utils.WebResponseUtils; @RestController @@ -36,6 +38,13 @@ public class ScalePagesController { private static final Logger logger = LoggerFactory.getLogger(ScalePagesController.class); + private final CustomPDDocumentFactory pdfDocumentFactory; + + @Autowired + public ScalePagesController(CustomPDDocumentFactory pdfDocumentFactory) { + this.pdfDocumentFactory = pdfDocumentFactory; + } + @PostMapping(value = "/scale-pages", consumes = "multipart/form-data") @Operation( summary = "Change the size of a PDF page/document", @@ -48,7 +57,8 @@ public class ScalePagesController { float scaleFactor = request.getScaleFactor(); PDDocument sourceDocument = Loader.loadPDF(file.getBytes()); - PDDocument outputDocument = new PDDocument(); + PDDocument outputDocument = + pdfDocumentFactory.createNewDocumentBasedOnOldDocument(sourceDocument); PDRectangle targetSize = getTargetSize(targetPDRectangle, sourceDocument); diff --git a/src/main/java/stirling/software/SPDF/controller/api/SplitPDFController.java b/src/main/java/stirling/software/SPDF/controller/api/SplitPDFController.java index af662433..2da76fa3 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/SplitPDFController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/SplitPDFController.java @@ -15,6 +15,7 @@ import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDPage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ModelAttribute; @@ -27,9 +28,8 @@ import io.github.pixee.security.Filenames; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; -import stirling.software.SPDF.config.PdfMetadataService; -import stirling.software.SPDF.model.PdfMetadata; import stirling.software.SPDF.model.api.PDFWithPageNums; +import stirling.software.SPDF.service.CustomPDDocumentFactory; import stirling.software.SPDF.utils.WebResponseUtils; @RestController @@ -38,6 +38,12 @@ import stirling.software.SPDF.utils.WebResponseUtils; public class SplitPDFController { private static final Logger logger = LoggerFactory.getLogger(SplitPDFController.class); + private final CustomPDDocumentFactory pdfDocumentFactory; + + @Autowired + public SplitPDFController(CustomPDDocumentFactory pdfDocumentFactory) { + this.pdfDocumentFactory = pdfDocumentFactory; + } @PostMapping(consumes = "multipart/form-data", value = "/split-pages") @Operation( @@ -51,7 +57,7 @@ public class SplitPDFController { // open the pdf document PDDocument document = Loader.loadPDF(file.getBytes()); - PdfMetadata metadata = PdfMetadataService.extractMetadataFromPdf(document); + // PdfMetadata metadata = PdfMetadataService.extractMetadataFromPdf(document); int totalPages = document.getNumberOfPages(); List pageNumbers = request.getPageNumbersList(document, false); System.out.println( @@ -70,7 +76,8 @@ public class SplitPDFController { List splitDocumentsBoas = new ArrayList<>(); int previousPageNumber = 0; for (int splitPoint : pageNumbers) { - try (PDDocument splitDocument = new PDDocument()) { + try (PDDocument splitDocument = + pdfDocumentFactory.createNewDocumentBasedOnOldDocument(document)) { for (int i = previousPageNumber; i <= splitPoint; i++) { PDPage page = document.getPage(i); splitDocument.addPage(page); @@ -79,7 +86,7 @@ public class SplitPDFController { previousPageNumber = splitPoint + 1; // Transfer metadata to split pdf - PdfMetadataService.setMetadataToPdf(splitDocument, metadata); + // PdfMetadataService.setMetadataToPdf(splitDocument, metadata); ByteArrayOutputStream baos = new ByteArrayOutputStream(); splitDocument.save(baos); diff --git a/src/main/java/stirling/software/SPDF/controller/api/SplitPdfByChaptersController.java b/src/main/java/stirling/software/SPDF/controller/api/SplitPdfByChaptersController.java index 8d020b16..9d03f8b6 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/SplitPdfByChaptersController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/SplitPdfByChaptersController.java @@ -15,6 +15,7 @@ import org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline.PDDocume import org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline.PDOutlineItem; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ModelAttribute; @@ -44,6 +45,13 @@ public class SplitPdfByChaptersController { private static final Logger logger = LoggerFactory.getLogger(SplitPdfByChaptersController.class); + private final PdfMetadataService pdfMetadataService; + + @Autowired + public SplitPdfByChaptersController(PdfMetadataService pdfMetadataService) { + this.pdfMetadataService = pdfMetadataService; + } + @PostMapping(value = "/split-pdf-by-chapters", consumes = "multipart/form-data") @Operation( summary = "Split PDFs by Chapters", @@ -258,7 +266,7 @@ public class SplitPdfByChaptersController { List splitDocumentsBoas = new ArrayList<>(); PdfMetadata metadata = null; if (includeMetadata) { - metadata = PdfMetadataService.extractMetadataFromPdf(sourceDocument); + metadata = pdfMetadataService.extractMetadataFromPdf(sourceDocument); } for (Bookmark bookmark : bookmarks) { try (PDDocument splitDocument = new PDDocument()) { @@ -273,7 +281,7 @@ public class SplitPdfByChaptersController { } ByteArrayOutputStream baos = new ByteArrayOutputStream(); if (includeMetadata) { - PdfMetadataService.setMetadataToPdf(splitDocument, metadata); + pdfMetadataService.setMetadataToPdf(splitDocument, metadata); } splitDocument.save(baos); diff --git a/src/main/java/stirling/software/SPDF/controller/api/SplitPdfBySectionsController.java b/src/main/java/stirling/software/SPDF/controller/api/SplitPdfBySectionsController.java index ce4a61d4..2b4f1313 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/SplitPdfBySectionsController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/SplitPdfBySectionsController.java @@ -20,6 +20,7 @@ import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject; import org.apache.pdfbox.util.Matrix; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ModelAttribute; @@ -33,6 +34,7 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import stirling.software.SPDF.model.api.SplitPdfBySectionsRequest; +import stirling.software.SPDF.service.CustomPDDocumentFactory; import stirling.software.SPDF.utils.WebResponseUtils; @RestController @@ -43,6 +45,13 @@ public class SplitPdfBySectionsController { private static final Logger logger = LoggerFactory.getLogger(SplitPdfBySectionsController.class); + private final CustomPDDocumentFactory pdfDocumentFactory; + + @Autowired + public SplitPdfBySectionsController(CustomPDDocumentFactory pdfDocumentFactory) { + this.pdfDocumentFactory = pdfDocumentFactory; + } + @PostMapping(value = "/split-pdf-by-sections", consumes = "multipart/form-data") @Operation( summary = "Split PDF pages into smaller sections", @@ -65,7 +74,7 @@ public class SplitPdfBySectionsController { Filenames.toSimpleFileName(file.getOriginalFilename()) .replaceFirst("[.][^.]+$", ""); if (merge) { - MergeController mergeController = new MergeController(); + MergeController mergeController = new MergeController(pdfDocumentFactory); ByteArrayOutputStream baos = new ByteArrayOutputStream(); mergeController.mergeDocuments(splitDocuments).save(baos); return WebResponseUtils.bytesToWebResponse(baos.toByteArray(), filename + "_split.pdf"); diff --git a/src/main/java/stirling/software/SPDF/controller/api/SplitPdfBySizeController.java b/src/main/java/stirling/software/SPDF/controller/api/SplitPdfBySizeController.java index b3772b3b..6016e532 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/SplitPdfBySizeController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/SplitPdfBySizeController.java @@ -12,6 +12,7 @@ import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDPage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ModelAttribute; @@ -25,6 +26,7 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import stirling.software.SPDF.model.api.general.SplitPdfBySizeOrCountRequest; +import stirling.software.SPDF.service.CustomPDDocumentFactory; import stirling.software.SPDF.utils.GeneralUtils; import stirling.software.SPDF.utils.WebResponseUtils; @@ -34,6 +36,12 @@ import stirling.software.SPDF.utils.WebResponseUtils; public class SplitPdfBySizeController { private static final Logger logger = LoggerFactory.getLogger(SplitPdfBySizeController.class); + private final CustomPDDocumentFactory pdfDocumentFactory; + + @Autowired + public SplitPdfBySizeController(CustomPDDocumentFactory pdfDocumentFactory) { + this.pdfDocumentFactory = pdfDocumentFactory; + } @PostMapping(value = "/split-by-size-or-count", consumes = "multipart/form-data") @Operation( @@ -84,7 +92,8 @@ public class SplitPdfBySizeController { PDDocument sourceDocument, long maxBytes, ZipOutputStream zipOut, String baseFilename) throws IOException { long currentSize = 0; - PDDocument currentDoc = new PDDocument(); + PDDocument currentDoc = + pdfDocumentFactory.createNewDocumentBasedOnOldDocument(sourceDocument); int fileIndex = 1; for (int pageIndex = 0; pageIndex < sourceDocument.getNumberOfPages(); pageIndex++) { @@ -121,7 +130,8 @@ public class SplitPdfBySizeController { PDDocument sourceDocument, int pageCount, ZipOutputStream zipOut, String baseFilename) throws IOException { int currentPageCount = 0; - PDDocument currentDoc = new PDDocument(); + PDDocument currentDoc = + pdfDocumentFactory.createNewDocumentBasedOnOldDocument(sourceDocument); int fileIndex = 1; for (PDPage page : sourceDocument.getPages()) { currentDoc.addPage(page); @@ -152,7 +162,8 @@ public class SplitPdfBySizeController { int currentPageIndex = 0; int fileIndex = 1; for (int i = 0; i < documentCount; i++) { - PDDocument currentDoc = new PDDocument(); + PDDocument currentDoc = + pdfDocumentFactory.createNewDocumentBasedOnOldDocument(sourceDocument); int pagesToAdd = pagesPerDocument + (i < extraPages ? 1 : 0); for (int j = 0; j < pagesToAdd; j++) { diff --git a/src/main/java/stirling/software/SPDF/controller/api/ToSinglePageController.java b/src/main/java/stirling/software/SPDF/controller/api/ToSinglePageController.java index 30406ec2..9401f3a6 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/ToSinglePageController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/ToSinglePageController.java @@ -13,6 +13,7 @@ import org.apache.pdfbox.pdmodel.common.PDRectangle; import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PostMapping; @@ -23,6 +24,7 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import stirling.software.SPDF.model.api.PDFFile; +import stirling.software.SPDF.service.CustomPDDocumentFactory; import stirling.software.SPDF.utils.WebResponseUtils; @RestController @@ -32,6 +34,13 @@ public class ToSinglePageController { private static final Logger logger = LoggerFactory.getLogger(ToSinglePageController.class); + private final CustomPDDocumentFactory pdfDocumentFactory; + + @Autowired + public ToSinglePageController(CustomPDDocumentFactory pdfDocumentFactory) { + this.pdfDocumentFactory = pdfDocumentFactory; + } + @PostMapping(consumes = "multipart/form-data", value = "/pdf-to-single-page") @Operation( summary = "Convert a multi-page PDF into a single long page PDF", @@ -53,7 +62,8 @@ public class ToSinglePageController { } // Create new document and page with calculated dimensions - PDDocument newDocument = new PDDocument(); + PDDocument newDocument = + pdfDocumentFactory.createNewDocumentBasedOnOldDocument(sourceDocument); PDPage newPage = new PDPage(new PDRectangle(maxWidth, totalHeight)); newDocument.addPage(newPage); diff --git a/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertBookToPDFController.java b/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertBookToPDFController.java index 41e6520d..694d30ab 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertBookToPDFController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertBookToPDFController.java @@ -1,30 +1,36 @@ package stirling.software.SPDF.controller.api.converters; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestMapping; -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 stirling.software.SPDF.model.api.GeneralFile; +import stirling.software.SPDF.service.CustomPDDocumentFactory; import stirling.software.SPDF.utils.FileToPdf; import stirling.software.SPDF.utils.WebResponseUtils; -@RestController -@Tag(name = "Convert", description = "Convert APIs") -@RequestMapping("/api/v1/convert") +// Disabled for now +// @RestController +// @Tag(name = "Convert", description = "Convert APIs") +// @RequestMapping("/api/v1/convert") public class ConvertBookToPDFController { - @Autowired - @Qualifier("bookAndHtmlFormatsInstalled") - private boolean bookAndHtmlFormatsInstalled; + private final boolean bookAndHtmlFormatsInstalled; + + private final CustomPDDocumentFactory pdfDocumentFactory; + + // @Autowired + public ConvertBookToPDFController( + CustomPDDocumentFactory pdfDocumentFactory, + @Qualifier("bookAndHtmlFormatsInstalled") boolean bookAndHtmlFormatsInstalled) { + this.pdfDocumentFactory = pdfDocumentFactory; + this.bookAndHtmlFormatsInstalled = bookAndHtmlFormatsInstalled; + } @PostMapping(consumes = "multipart/form-data", value = "/book/pdf") @Operation( diff --git a/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertHtmlToPDF.java b/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertHtmlToPDF.java index c7cfc196..19ba1ac4 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertHtmlToPDF.java +++ b/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertHtmlToPDF.java @@ -1,28 +1,25 @@ package stirling.software.SPDF.controller.api.converters; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestMapping; -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 stirling.software.SPDF.model.api.converters.HTMLToPdfRequest; import stirling.software.SPDF.utils.FileToPdf; import stirling.software.SPDF.utils.WebResponseUtils; -@RestController -@Tag(name = "Convert", description = "Convert APIs") -@RequestMapping("/api/v1/convert") +// Disabled for now +// @RestController +// @Tag(name = "Convert", description = "Convert APIs") +// @RequestMapping("/api/v1/convert") public class ConvertHtmlToPDF { - @Autowired + // @Autowired @Qualifier("bookAndHtmlFormatsInstalled") private boolean bookAndHtmlFormatsInstalled; diff --git a/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertImgPDFController.java b/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertImgPDFController.java index e54f1fe4..8cf4246e 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertImgPDFController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertImgPDFController.java @@ -16,6 +16,7 @@ import org.apache.commons.io.FileUtils; import org.apache.pdfbox.rendering.ImageType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ModelAttribute; @@ -30,6 +31,7 @@ import io.swagger.v3.oas.annotations.tags.Tag; import stirling.software.SPDF.model.api.converters.ConvertToImageRequest; import stirling.software.SPDF.model.api.converters.ConvertToPdfRequest; +import stirling.software.SPDF.service.CustomPDDocumentFactory; import stirling.software.SPDF.utils.CheckProgramInstall; import stirling.software.SPDF.utils.PdfUtils; import stirling.software.SPDF.utils.ProcessExecutor; @@ -43,6 +45,13 @@ public class ConvertImgPDFController { private static final Logger logger = LoggerFactory.getLogger(ConvertImgPDFController.class); + private final CustomPDDocumentFactory pdfDocumentFactory; + + @Autowired + public ConvertImgPDFController(CustomPDDocumentFactory pdfDocumentFactory) { + this.pdfDocumentFactory = pdfDocumentFactory; + } + @PostMapping(consumes = "multipart/form-data", value = "/pdf/img") @Operation( summary = "Convert PDF to image(s)", @@ -178,7 +187,8 @@ public class ConvertImgPDFController { boolean autoRotate = request.isAutoRotate(); // Convert the file to PDF and get the resulting bytes - byte[] bytes = PdfUtils.imageToPdf(file, fitOption, autoRotate, colorType); + byte[] bytes = + PdfUtils.imageToPdf(file, fitOption, autoRotate, colorType, pdfDocumentFactory); return WebResponseUtils.bytesToWebResponse( bytes, file[0].getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_converted.pdf"); diff --git a/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertMarkdownToPdf.java b/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertMarkdownToPdf.java index 9cefe1ff..5b2f3fdf 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertMarkdownToPdf.java +++ b/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertMarkdownToPdf.java @@ -10,29 +10,26 @@ import org.commonmark.node.Node; import org.commonmark.parser.Parser; import org.commonmark.renderer.html.AttributeProvider; import org.commonmark.renderer.html.HtmlRenderer; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestMapping; -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 stirling.software.SPDF.model.api.GeneralFile; import stirling.software.SPDF.utils.FileToPdf; import stirling.software.SPDF.utils.WebResponseUtils; -@RestController -@Tag(name = "Convert", description = "Convert APIs") -@RequestMapping("/api/v1/convert") +// Disabled for now +// @RestController +// @Tag(name = "Convert", description = "Convert APIs") +// @RequestMapping("/api/v1/convert") public class ConvertMarkdownToPdf { - @Autowired + // @Autowired @Qualifier("bookAndHtmlFormatsInstalled") private boolean bookAndHtmlFormatsInstalled; diff --git a/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertOfficeController.java b/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertOfficeController.java index 5e6b3dfd..36e29f27 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertOfficeController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertOfficeController.java @@ -1,5 +1,6 @@ package stirling.software.SPDF.controller.api.converters; +import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; @@ -8,6 +9,8 @@ import java.util.Arrays; import java.util.List; import org.apache.commons.io.FilenameUtils; +import org.apache.pdfbox.pdmodel.PDDocument; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PostMapping; @@ -20,6 +23,7 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import stirling.software.SPDF.model.api.GeneralFile; +import stirling.software.SPDF.service.CustomPDDocumentFactory; import stirling.software.SPDF.utils.ProcessExecutor; import stirling.software.SPDF.utils.ProcessExecutor.ProcessExecutorResult; import stirling.software.SPDF.utils.WebResponseUtils; @@ -29,7 +33,7 @@ import stirling.software.SPDF.utils.WebResponseUtils; @RequestMapping("/api/v1/convert") public class ConvertOfficeController { - public byte[] convertToPdf(MultipartFile inputFile) throws IOException, InterruptedException { + public File convertToPdf(MultipartFile inputFile) throws IOException, InterruptedException { // Check for valid file extension String originalFilename = Filenames.toSimpleFileName(inputFile.getOriginalFilename()); if (originalFilename == null @@ -62,12 +66,10 @@ public class ConvertOfficeController { .runCommandWithOutputHandling(command); // Read the converted PDF file - byte[] pdfBytes = Files.readAllBytes(tempOutputFile); - return pdfBytes; + return tempOutputFile.toFile(); } finally { // Clean up the temporary files if (tempInputFile != null) Files.deleteIfExists(tempInputFile); - Files.deleteIfExists(tempOutputFile); } } @@ -76,6 +78,13 @@ public class ConvertOfficeController { return fileExtension.matches(extensionPattern); } + private final CustomPDDocumentFactory pdfDocumentFactory; + + @Autowired + public ConvertOfficeController(CustomPDDocumentFactory pdfDocumentFactory) { + this.pdfDocumentFactory = pdfDocumentFactory; + } + @PostMapping(consumes = "multipart/form-data", value = "/file/pdf") @Operation( summary = "Convert a file to a PDF using LibreOffice", @@ -86,12 +95,18 @@ public class ConvertOfficeController { MultipartFile inputFile = request.getFileInput(); // unused but can start server instance if startup time is to long // LibreOfficeListener.getInstance().start(); + File file = null; + try { + file = convertToPdf(inputFile); - byte[] pdfByteArray = convertToPdf(inputFile); - return WebResponseUtils.bytesToWebResponse( - pdfByteArray, - Filenames.toSimpleFileName(inputFile.getOriginalFilename()) - .replaceFirst("[.][^.]+$", "") - + "_convertedToPDF.pdf"); + PDDocument doc = pdfDocumentFactory.load(file); + return WebResponseUtils.pdfDocToWebResponse( + doc, + Filenames.toSimpleFileName(inputFile.getOriginalFilename()) + .replaceFirst("[.][^.]+$", "") + + "_convertedToPDF.pdf"); + } finally { + if (file != null) file.delete(); + } } } diff --git a/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertPDFToBookController.java b/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertPDFToBookController.java index c8b9dd4d..181b5713 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertPDFToBookController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertPDFToBookController.java @@ -6,30 +6,27 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestMapping; -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 stirling.software.SPDF.model.api.converters.PdfToBookRequest; import stirling.software.SPDF.utils.ProcessExecutor; import stirling.software.SPDF.utils.ProcessExecutor.ProcessExecutorResult; import stirling.software.SPDF.utils.WebResponseUtils; -@RestController -@Tag(name = "Convert", description = "Convert APIs") -@RequestMapping("/api/v1/convert") +// Disabled for now +// @RestController +// @Tag(name = "Convert", description = "Convert APIs") +// @RequestMapping("/api/v1/convert") public class ConvertPDFToBookController { - @Autowired + // @Autowired @Qualifier("bookAndHtmlFormatsInstalled") private boolean bookAndHtmlFormatsInstalled; diff --git a/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertPDFToPDFA.java b/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertPDFToPDFA.java index cd748d53..eaaf04bc 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertPDFToPDFA.java +++ b/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertPDFToPDFA.java @@ -9,7 +9,6 @@ import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; -import org.apache.pdfbox.Loader; import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocumentCatalog; import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm; @@ -17,6 +16,7 @@ import org.apache.pdfbox.pdmodel.interactive.form.PDField; import org.apache.pdfbox.pdmodel.interactive.form.PDSignatureField; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PostMapping; @@ -29,6 +29,7 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import stirling.software.SPDF.model.api.converters.PdfToPdfARequest; +import stirling.software.SPDF.service.CustomPDDocumentFactory; import stirling.software.SPDF.utils.ProcessExecutor; import stirling.software.SPDF.utils.ProcessExecutor.ProcessExecutorResult; import stirling.software.SPDF.utils.WebResponseUtils; @@ -40,6 +41,13 @@ public class ConvertPDFToPDFA { private static final Logger logger = LoggerFactory.getLogger(ConvertPDFToPDFA.class); + private final CustomPDDocumentFactory pdfDocumentFactory; + + @Autowired + public ConvertPDFToPDFA(CustomPDDocumentFactory pdfDocumentFactory) { + this.pdfDocumentFactory = pdfDocumentFactory; + } + @PostMapping(consumes = "multipart/form-data", value = "/pdf/pdfa") @Operation( summary = "Convert a PDF to a PDF/A", @@ -54,7 +62,7 @@ public class ConvertPDFToPDFA { byte[] pdfBytes = inputFile.getBytes(); // Load the PDF document - PDDocument document = Loader.loadPDF(pdfBytes); + PDDocument document = pdfDocumentFactory.load(pdfBytes); // Get the document catalog PDDocumentCatalog catalog = document.getDocumentCatalog(); @@ -101,18 +109,18 @@ public class ConvertPDFToPDFA { ProcessExecutor.getInstance(ProcessExecutor.Processes.OCR_MY_PDF) .runCommandWithOutputHandling(command); - // Read the optimized PDF file - byte[] optimizedPdfBytes = Files.readAllBytes(tempOutputFile); - - // Clean up the temporary files - Files.deleteIfExists(tempInputFile); - Files.deleteIfExists(tempOutputFile); - - // Return the optimized PDF as a response - String outputFilename = - Filenames.toSimpleFileName(inputFile.getOriginalFilename()) - .replaceFirst("[.][^.]+$", "") - + "_PDFA.pdf"; - return WebResponseUtils.bytesToWebResponse(optimizedPdfBytes, outputFilename); + try { + PDDocument doc = pdfDocumentFactory.load(tempOutputFile.toFile()); + // Return the optimized PDF as a response + String outputFilename = + Filenames.toSimpleFileName(inputFile.getOriginalFilename()) + .replaceFirst("[.][^.]+$", "") + + "_PDFA.pdf"; + return WebResponseUtils.pdfDocToWebResponse(doc, outputFilename); + } finally { + // Clean up the temporary files + Files.deleteIfExists(tempInputFile); + Files.deleteIfExists(tempOutputFile); + } } } diff --git a/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertWebsiteToPDF.java b/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertWebsiteToPDF.java index 026690fd..6119bfcf 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertWebsiteToPDF.java +++ b/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertWebsiteToPDF.java @@ -6,6 +6,10 @@ import java.nio.file.Path; import java.util.ArrayList; import java.util.List; +import org.apache.pdfbox.pdmodel.PDDocument; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PostMapping; @@ -16,6 +20,7 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import stirling.software.SPDF.model.api.converters.UrlToPdfRequest; +import stirling.software.SPDF.service.CustomPDDocumentFactory; import stirling.software.SPDF.utils.GeneralUtils; import stirling.software.SPDF.utils.ProcessExecutor; import stirling.software.SPDF.utils.ProcessExecutor.ProcessExecutorResult; @@ -26,6 +31,15 @@ import stirling.software.SPDF.utils.WebResponseUtils; @RequestMapping("/api/v1/convert") public class ConvertWebsiteToPDF { + private static final Logger logger = LoggerFactory.getLogger(ConvertWebsiteToPDF.class); + + private final CustomPDDocumentFactory pdfDocumentFactory; + + @Autowired + public ConvertWebsiteToPDF(CustomPDDocumentFactory pdfDocumentFactory) { + this.pdfDocumentFactory = pdfDocumentFactory; + } + @PostMapping(consumes = "multipart/form-data", value = "/url/pdf") @Operation( summary = "Convert a URL to a PDF", @@ -46,12 +60,12 @@ public class ConvertWebsiteToPDF { } Path tempOutputFile = null; - byte[] pdfBytes; + PDDocument doc = null; try { // Prepare the output file path tempOutputFile = Files.createTempFile("output_", ".pdf"); - // Prepare the OCRmyPDF command + // Prepare the WeasyPrint command List command = new ArrayList<>(); command.add("weasyprint"); command.add(URL); @@ -61,16 +75,23 @@ public class ConvertWebsiteToPDF { ProcessExecutor.getInstance(ProcessExecutor.Processes.WEASYPRINT) .runCommandWithOutputHandling(command); - // Read the optimized PDF file - pdfBytes = Files.readAllBytes(tempOutputFile); - } finally { - // Clean up the temporary files - Files.deleteIfExists(tempOutputFile); - } - // Convert URL to a safe filename - String outputFilename = convertURLToFileName(URL); + // Load the PDF using pdfDocumentFactory + doc = pdfDocumentFactory.load(tempOutputFile.toFile()); - return WebResponseUtils.bytesToWebResponse(pdfBytes, outputFilename); + // Convert URL to a safe filename + String outputFilename = convertURLToFileName(URL); + + return WebResponseUtils.pdfDocToWebResponse(doc, outputFilename); + } finally { + + if (tempOutputFile != null) { + try { + Files.deleteIfExists(tempOutputFile); + } catch (IOException e) { + logger.error("Error deleting temporary output file", e); + } + } + } } private String convertURLToFileName(String url) { diff --git a/src/main/java/stirling/software/SPDF/controller/api/converters/ExtractController.java b/src/main/java/stirling/software/SPDF/controller/api/converters/ExtractCSVController.java similarity index 98% rename from src/main/java/stirling/software/SPDF/controller/api/converters/ExtractController.java rename to src/main/java/stirling/software/SPDF/controller/api/converters/ExtractCSVController.java index 6bd9d8b9..8cf8aa4f 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/converters/ExtractController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/converters/ExtractCSVController.java @@ -30,13 +30,13 @@ import stirling.software.SPDF.model.api.extract.PDFFilePage; @RestController @RequestMapping("/api/v1/convert") @Tag(name = "Convert", description = "Convert APIs") -public class ExtractController { +public class ExtractCSVController { private static final Logger logger = LoggerFactory.getLogger(CropController.class); @PostMapping(value = "/pdf/csv", consumes = "multipart/form-data") @Operation( - summary = "Extracts a PDF document to csv", + summary = "Extracts a CSV document from a PDF", description = "This operation takes an input PDF file and returns CSV file of whole page. Input:PDF Output:CSV Type:SISO") public ResponseEntity PdfToCsv(@ModelAttribute PDFFilePage form) throws Exception { diff --git a/src/main/java/stirling/software/SPDF/controller/api/misc/AutoSplitPdfController.java b/src/main/java/stirling/software/SPDF/controller/api/misc/AutoSplitPdfController.java index d57a8bda..88d113ec 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/misc/AutoSplitPdfController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/misc/AutoSplitPdfController.java @@ -12,11 +12,11 @@ import java.util.List; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; -import org.apache.pdfbox.Loader; import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.rendering.PDFRenderer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ModelAttribute; @@ -38,6 +38,7 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import stirling.software.SPDF.model.api.misc.AutoSplitPdfRequest; +import stirling.software.SPDF.service.CustomPDDocumentFactory; import stirling.software.SPDF.utils.WebResponseUtils; @RestController @@ -49,6 +50,13 @@ public class AutoSplitPdfController { private static final String QR_CONTENT = "https://github.com/Stirling-Tools/Stirling-PDF"; private static final String QR_CONTENT_OLD = "https://github.com/Frooodle/Stirling-PDF"; + private final CustomPDDocumentFactory pdfDocumentFactory; + + @Autowired + public AutoSplitPdfController(CustomPDDocumentFactory pdfDocumentFactory) { + this.pdfDocumentFactory = pdfDocumentFactory; + } + @PostMapping(value = "/auto-split-pdf", consumes = "multipart/form-data") @Operation( summary = "Auto split PDF pages into separate documents", @@ -59,73 +67,93 @@ public class AutoSplitPdfController { MultipartFile file = request.getFileInput(); boolean duplexMode = request.isDuplexMode(); - PDDocument document = Loader.loadPDF(file.getBytes()); - PDFRenderer pdfRenderer = new PDFRenderer(document); - pdfRenderer.setSubsamplingAllowed(true); + PDDocument document = null; List splitDocuments = new ArrayList<>(); - List splitDocumentsBoas = new ArrayList<>(); + Path zipFile = null; + byte[] data = null; - for (int page = 0; page < document.getNumberOfPages(); ++page) { - BufferedImage bim = pdfRenderer.renderImageWithDPI(page, 150); - String result = decodeQRCode(bim); - if ((QR_CONTENT.equals(result) || QR_CONTENT_OLD.equals(result)) && page != 0) { - splitDocuments.add(new PDDocument()); + try { + document = pdfDocumentFactory.load(file.getInputStream()); + PDFRenderer pdfRenderer = new PDFRenderer(document); + pdfRenderer.setSubsamplingAllowed(true); + + for (int page = 0; page < document.getNumberOfPages(); ++page) { + BufferedImage bim = pdfRenderer.renderImageWithDPI(page, 150); + String result = decodeQRCode(bim); + if ((QR_CONTENT.equals(result) || QR_CONTENT_OLD.equals(result)) && page != 0) { + splitDocuments.add(new PDDocument()); + } + + if (!splitDocuments.isEmpty() + && !QR_CONTENT.equals(result) + && !QR_CONTENT_OLD.equals(result)) { + splitDocuments.get(splitDocuments.size() - 1).addPage(document.getPage(page)); + } else if (page == 0) { + PDDocument firstDocument = new PDDocument(); + firstDocument.addPage(document.getPage(page)); + splitDocuments.add(firstDocument); + } + + // If duplexMode is true and current page is a divider, then skip next page + if (duplexMode && (QR_CONTENT.equals(result) || QR_CONTENT_OLD.equals(result))) { + page++; + } } - if (!splitDocuments.isEmpty() - && !QR_CONTENT.equals(result) - && !QR_CONTENT_OLD.equals(result)) { - splitDocuments.get(splitDocuments.size() - 1).addPage(document.getPage(page)); - } else if (page == 0) { - PDDocument firstDocument = new PDDocument(); - firstDocument.addPage(document.getPage(page)); - splitDocuments.add(firstDocument); + // Remove split documents that have no pages + splitDocuments.removeIf(pdDocument -> pdDocument.getNumberOfPages() == 0); + + zipFile = Files.createTempFile("split_documents", ".zip"); + String filename = + Filenames.toSimpleFileName(file.getOriginalFilename()) + .replaceFirst("[.][^.]+$", ""); + + try (ZipOutputStream zipOut = new ZipOutputStream(Files.newOutputStream(zipFile))) { + for (int i = 0; i < splitDocuments.size(); i++) { + String fileName = filename + "_" + (i + 1) + ".pdf"; + PDDocument splitDocument = splitDocuments.get(i); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + splitDocument.save(baos); + byte[] pdf = baos.toByteArray(); + + ZipEntry pdfEntry = new ZipEntry(fileName); + zipOut.putNextEntry(pdfEntry); + zipOut.write(pdf); + zipOut.closeEntry(); + } } - // If duplexMode is true and current page is a divider, then skip next page - if (duplexMode && (QR_CONTENT.equals(result) || QR_CONTENT_OLD.equals(result))) { - page++; - } - } - - // Remove split documents that have no pages - splitDocuments.removeIf(pdDocument -> pdDocument.getNumberOfPages() == 0); - - for (PDDocument splitDocument : splitDocuments) { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - splitDocument.save(baos); - splitDocumentsBoas.add(baos); - splitDocument.close(); - } - - document.close(); - - Path zipFile = Files.createTempFile("split_documents", ".zip"); - String filename = - Filenames.toSimpleFileName(file.getOriginalFilename()) - .replaceFirst("[.][^.]+$", ""); - byte[] data; - - try (ZipOutputStream zipOut = new ZipOutputStream(Files.newOutputStream(zipFile))) { - for (int i = 0; i < splitDocumentsBoas.size(); i++) { - String fileName = filename + "_" + (i + 1) + ".pdf"; - ByteArrayOutputStream baos = splitDocumentsBoas.get(i); - byte[] pdf = baos.toByteArray(); - - ZipEntry pdfEntry = new ZipEntry(fileName); - zipOut.putNextEntry(pdfEntry); - zipOut.write(pdf); - zipOut.closeEntry(); - } - } catch (Exception e) { - logger.error("exception", e); - } finally { data = Files.readAllBytes(zipFile); - Files.deleteIfExists(zipFile); - } - return WebResponseUtils.bytesToWebResponse( - data, filename + ".zip", MediaType.APPLICATION_OCTET_STREAM); + return WebResponseUtils.bytesToWebResponse( + data, filename + ".zip", MediaType.APPLICATION_OCTET_STREAM); + } finally { + // Clean up resources + if (document != null) { + try { + document.close(); + } catch (IOException e) { + logger.error("Error closing main PDDocument", e); + } + } + + for (PDDocument splitDoc : splitDocuments) { + try { + splitDoc.close(); + } catch (IOException e) { + logger.error("Error closing split PDDocument", e); + } + } + + if (zipFile != null) { + try { + Files.deleteIfExists(zipFile); + } catch (IOException e) { + logger.error("Error deleting temporary zip file", e); + } + } + } } private static String decodeQRCode(BufferedImage bufferedImage) { diff --git a/src/main/java/stirling/software/SPDF/controller/api/misc/BlankPageController.java b/src/main/java/stirling/software/SPDF/controller/api/misc/BlankPageController.java index 4c050355..bf046d65 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/misc/BlankPageController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/misc/BlankPageController.java @@ -16,6 +16,7 @@ import org.apache.pdfbox.rendering.PDFRenderer; import org.apache.pdfbox.text.PDFTextStripper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; @@ -30,6 +31,7 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import stirling.software.SPDF.model.api.misc.RemoveBlankPagesRequest; +import stirling.software.SPDF.service.CustomPDDocumentFactory; import stirling.software.SPDF.utils.PdfUtils; import stirling.software.SPDF.utils.WebResponseUtils; @@ -40,6 +42,13 @@ public class BlankPageController { private static final Logger logger = LoggerFactory.getLogger(BlankPageController.class); + private final CustomPDDocumentFactory pdfDocumentFactory; + + @Autowired + public BlankPageController(CustomPDDocumentFactory pdfDocumentFactory) { + this.pdfDocumentFactory = pdfDocumentFactory; + } + @PostMapping(consumes = "multipart/form-data", value = "/remove-blanks") @Operation( summary = "Remove blank pages from a PDF file", @@ -124,7 +133,7 @@ public class BlankPageController { public void createZipEntry(ZipOutputStream zos, List pages, String entryName) throws IOException { - try (PDDocument document = new PDDocument()) { + try (PDDocument document = pdfDocumentFactory.createNewDocument()) { for (PDPage page : pages) { document.addPage(page); diff --git a/src/main/java/stirling/software/SPDF/controller/api/misc/CompressController.java b/src/main/java/stirling/software/SPDF/controller/api/misc/CompressController.java index 2240dbed..3f55a4f5 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/misc/CompressController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/misc/CompressController.java @@ -20,6 +20,7 @@ import org.apache.pdfbox.pdmodel.graphics.PDXObject; import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PostMapping; @@ -32,6 +33,7 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import stirling.software.SPDF.model.api.misc.OptimizePdfRequest; +import stirling.software.SPDF.service.CustomPDDocumentFactory; import stirling.software.SPDF.utils.GeneralUtils; import stirling.software.SPDF.utils.ProcessExecutor; import stirling.software.SPDF.utils.ProcessExecutor.ProcessExecutorResult; @@ -44,6 +46,13 @@ public class CompressController { private static final Logger logger = LoggerFactory.getLogger(CompressController.class); + private final CustomPDDocumentFactory pdfDocumentFactory; + + @Autowired + public CompressController(CustomPDDocumentFactory pdfDocumentFactory) { + this.pdfDocumentFactory = pdfDocumentFactory; + } + @PostMapping(consumes = "multipart/form-data", value = "/compress-pdf") @Operation( summary = "Optimize PDF file", @@ -258,7 +267,7 @@ public class CompressController { } // Read the optimized PDF file pdfBytes = Files.readAllBytes(tempOutputFile); - + Path finalFile = tempOutputFile; // Check if optimized file is larger than the original if (pdfBytes.length > inputFileSize) { // Log the occurrence @@ -266,14 +275,15 @@ public class CompressController { "Optimized file is larger than the original. Returning the original file instead."); // Read the original file again - pdfBytes = Files.readAllBytes(tempInputFile); + finalFile = tempInputFile; } // Return the optimized PDF as a response String outputFilename = Filenames.toSimpleFileName(inputFile.getOriginalFilename()) .replaceFirst("[.][^.]+$", "") + "_Optimized.pdf"; - return WebResponseUtils.bytesToWebResponse(pdfBytes, outputFilename); + return WebResponseUtils.pdfDocToWebResponse( + pdfDocumentFactory.load(finalFile.toFile()), outputFilename); } finally { // Clean up the temporary files diff --git a/src/main/java/stirling/software/SPDF/controller/api/misc/FlattenController.java b/src/main/java/stirling/software/SPDF/controller/api/misc/FlattenController.java index c180fafa..c99b5f73 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/misc/FlattenController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/misc/FlattenController.java @@ -14,6 +14,7 @@ import org.apache.pdfbox.rendering.ImageType; import org.apache.pdfbox.rendering.PDFRenderer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PostMapping; @@ -25,9 +26,8 @@ import io.github.pixee.security.Filenames; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; -import stirling.software.SPDF.config.PdfMetadataService; -import stirling.software.SPDF.model.PdfMetadata; import stirling.software.SPDF.model.api.misc.FlattenRequest; +import stirling.software.SPDF.service.CustomPDDocumentFactory; import stirling.software.SPDF.utils.WebResponseUtils; @RestController @@ -37,6 +37,13 @@ public class FlattenController { private static final Logger logger = LoggerFactory.getLogger(FlattenController.class); + private final CustomPDDocumentFactory pdfDocumentFactory; + + @Autowired + public FlattenController(CustomPDDocumentFactory pdfDocumentFactory) { + this.pdfDocumentFactory = pdfDocumentFactory; + } + @PostMapping(consumes = "multipart/form-data", value = "/flatten") @Operation( summary = "Flatten PDF form fields or full page", @@ -46,7 +53,6 @@ public class FlattenController { MultipartFile file = request.getFileInput(); PDDocument document = Loader.loadPDF(file.getBytes()); - PdfMetadata metadata = PdfMetadataService.extractMetadataFromPdf(document); Boolean flattenOnlyForms = request.getFlattenOnlyForms(); if (Boolean.TRUE.equals(flattenOnlyForms)) { @@ -60,7 +66,8 @@ public class FlattenController { // flatten whole page aka convert each page to image and readd it (making text // unselectable) PDFRenderer pdfRenderer = new PDFRenderer(document); - PDDocument newDocument = new PDDocument(); + PDDocument newDocument = + pdfDocumentFactory.createNewDocumentBasedOnOldDocument(document); int numPages = document.getNumberOfPages(); for (int i = 0; i < numPages; i++) { try { @@ -80,7 +87,6 @@ public class FlattenController { logger.error("exception", e); } } - PdfMetadataService.setMetadataToPdf(newDocument, metadata); return WebResponseUtils.pdfDocToWebResponse( newDocument, Filenames.toSimpleFileName(file.getOriginalFilename())); } diff --git a/src/main/java/stirling/software/SPDF/controller/api/misc/OCRController.java b/src/main/java/stirling/software/SPDF/controller/api/misc/OCRController.java index f3ccabf4..96cabb60 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/misc/OCRController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/misc/OCRController.java @@ -1,5 +1,6 @@ package stirling.software.SPDF.controller.api.misc; +import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; @@ -28,6 +29,7 @@ import io.swagger.v3.oas.annotations.tags.Tag; import stirling.software.SPDF.model.ApplicationProperties; import stirling.software.SPDF.model.api.misc.ProcessPdfWithOcrRequest; +import stirling.software.SPDF.service.CustomPDDocumentFactory; import stirling.software.SPDF.utils.ProcessExecutor; import stirling.software.SPDF.utils.ProcessExecutor.ProcessExecutorResult; import stirling.software.SPDF.utils.WebResponseUtils; @@ -52,6 +54,13 @@ public class OCRController { .collect(Collectors.toList()); } + private final CustomPDDocumentFactory pdfDocumentFactory; + + @Autowired + public OCRController(CustomPDDocumentFactory pdfDocumentFactory) { + this.pdfDocumentFactory = pdfDocumentFactory; + } + @PostMapping(consumes = "multipart/form-data", value = "/ocr-pdf") @Operation( summary = "Process a PDF file with OCR", @@ -175,7 +184,7 @@ public class OCRController { tempOutputFile = tempPdfWithoutImages; } // Read the OCR processed PDF file - byte[] pdfBytes = Files.readAllBytes(tempOutputFile); + byte[] pdfBytes = pdfDocumentFactory.loadToBytes(tempOutputFile.toFile()); // Return the OCR processed PDF as a response String outputFilename = @@ -196,7 +205,13 @@ public class OCRController { // Add PDF file to the zip ZipEntry pdfEntry = new ZipEntry(outputFilename); zipOut.putNextEntry(pdfEntry); - Files.copy(tempOutputFile, zipOut); + try (ByteArrayInputStream pdfInputStream = new ByteArrayInputStream(pdfBytes)) { + byte[] buffer = new byte[1024]; + int length; + while ((length = pdfInputStream.read(buffer)) != -1) { + zipOut.write(buffer, 0, length); + } + } zipOut.closeEntry(); // Add text file to the zip diff --git a/src/main/java/stirling/software/SPDF/controller/api/misc/OverlayImageController.java b/src/main/java/stirling/software/SPDF/controller/api/misc/OverlayImageController.java index c3fad457..893fc1e5 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/misc/OverlayImageController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/misc/OverlayImageController.java @@ -4,6 +4,7 @@ import java.io.IOException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ModelAttribute; @@ -17,6 +18,7 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import stirling.software.SPDF.model.api.misc.OverlayImageRequest; +import stirling.software.SPDF.service.CustomPDDocumentFactory; import stirling.software.SPDF.utils.PdfUtils; import stirling.software.SPDF.utils.WebResponseUtils; @@ -27,6 +29,13 @@ public class OverlayImageController { private static final Logger logger = LoggerFactory.getLogger(OverlayImageController.class); + private final CustomPDDocumentFactory pdfDocumentFactory; + + @Autowired + public OverlayImageController(CustomPDDocumentFactory pdfDocumentFactory) { + this.pdfDocumentFactory = pdfDocumentFactory; + } + @PostMapping(consumes = "multipart/form-data", value = "/add-image") @Operation( summary = "Overlay image onto a PDF file", @@ -41,7 +50,9 @@ public class OverlayImageController { try { byte[] pdfBytes = pdfFile.getBytes(); byte[] imageBytes = imageFile.getBytes(); - byte[] result = PdfUtils.overlayImage(pdfBytes, imageBytes, x, y, everyPage); + byte[] result = + PdfUtils.overlayImage( + pdfDocumentFactory, pdfBytes, imageBytes, x, y, everyPage); return WebResponseUtils.bytesToWebResponse( result, diff --git a/src/main/java/stirling/software/SPDF/controller/api/misc/PageNumbersController.java b/src/main/java/stirling/software/SPDF/controller/api/misc/PageNumbersController.java index ba31dc83..e42998d1 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/misc/PageNumbersController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/misc/PageNumbersController.java @@ -4,7 +4,6 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.List; -import org.apache.pdfbox.Loader; import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDPage; import org.apache.pdfbox.pdmodel.PDPageContentStream; @@ -13,6 +12,7 @@ import org.apache.pdfbox.pdmodel.font.PDType1Font; import org.apache.pdfbox.pdmodel.font.Standard14Fonts; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ModelAttribute; @@ -26,6 +26,7 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import stirling.software.SPDF.model.api.misc.AddPageNumbersRequest; +import stirling.software.SPDF.service.CustomPDDocumentFactory; import stirling.software.SPDF.utils.GeneralUtils; import stirling.software.SPDF.utils.WebResponseUtils; @@ -36,6 +37,13 @@ public class PageNumbersController { private static final Logger logger = LoggerFactory.getLogger(PageNumbersController.class); + private final CustomPDDocumentFactory pdfDocumentFactory; + + @Autowired + public PageNumbersController(CustomPDDocumentFactory pdfDocumentFactory) { + this.pdfDocumentFactory = pdfDocumentFactory; + } + @PostMapping(value = "/add-page-numbers", consumes = "multipart/form-data") @Operation( summary = "Add page numbers to a PDF document", @@ -52,7 +60,7 @@ public class PageNumbersController { String customText = request.getCustomText(); int pageNumber = startingNumber; byte[] fileBytes = file.getBytes(); - PDDocument document = Loader.loadPDF(fileBytes); + PDDocument document = pdfDocumentFactory.load(fileBytes); float font_size = request.getFontSize(); String font_type = request.getFontType(); float marginFactor; diff --git a/src/main/java/stirling/software/SPDF/controller/api/misc/RepairController.java b/src/main/java/stirling/software/SPDF/controller/api/misc/RepairController.java index 06c652a2..be0827cc 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/misc/RepairController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/misc/RepairController.java @@ -8,6 +8,7 @@ import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PostMapping; @@ -20,6 +21,7 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import stirling.software.SPDF.model.api.PDFFile; +import stirling.software.SPDF.service.CustomPDDocumentFactory; import stirling.software.SPDF.utils.ProcessExecutor; import stirling.software.SPDF.utils.ProcessExecutor.ProcessExecutorResult; import stirling.software.SPDF.utils.WebResponseUtils; @@ -31,6 +33,13 @@ public class RepairController { private static final Logger logger = LoggerFactory.getLogger(RepairController.class); + private final CustomPDDocumentFactory pdfDocumentFactory; + + @Autowired + public RepairController(CustomPDDocumentFactory pdfDocumentFactory) { + this.pdfDocumentFactory = pdfDocumentFactory; + } + @PostMapping(consumes = "multipart/form-data", value = "/repair") @Operation( summary = "Repair a PDF file", @@ -58,7 +67,7 @@ public class RepairController { .runCommandWithOutputHandling(command); // Read the optimized PDF file - pdfBytes = Files.readAllBytes(tempOutputFile); + pdfBytes = pdfDocumentFactory.loadToBytes(tempOutputFile.toFile()); // Return the optimized PDF as a response String outputFilename = diff --git a/src/main/java/stirling/software/SPDF/controller/api/misc/StampController.java b/src/main/java/stirling/software/SPDF/controller/api/misc/StampController.java index ecf41557..b15b76e7 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/misc/StampController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/misc/StampController.java @@ -12,7 +12,6 @@ import java.util.List; import javax.imageio.ImageIO; import org.apache.commons.io.IOUtils; -import org.apache.pdfbox.Loader; import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDPage; import org.apache.pdfbox.pdmodel.PDPageContentStream; @@ -25,6 +24,7 @@ import org.apache.pdfbox.pdmodel.graphics.image.LosslessFactory; import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject; import org.apache.pdfbox.pdmodel.graphics.state.PDExtendedGraphicsState; import org.apache.pdfbox.util.Matrix; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.io.ClassPathResource; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ModelAttribute; @@ -38,6 +38,7 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import stirling.software.SPDF.model.api.misc.AddStampRequest; +import stirling.software.SPDF.service.CustomPDDocumentFactory; import stirling.software.SPDF.utils.WebResponseUtils; @RestController @@ -45,6 +46,13 @@ import stirling.software.SPDF.utils.WebResponseUtils; @Tag(name = "Misc", description = "Miscellaneous APIs") public class StampController { + private final CustomPDDocumentFactory pdfDocumentFactory; + + @Autowired + public StampController(CustomPDDocumentFactory pdfDocumentFactory) { + this.pdfDocumentFactory = pdfDocumentFactory; + } + @PostMapping(consumes = "multipart/form-data", value = "/add-stamp") @Operation( summary = "Add stamp to a PDF file", @@ -86,7 +94,7 @@ public class StampController { } // Load the input PDF - PDDocument document = Loader.loadPDF(pdfFile.getBytes()); + PDDocument document = pdfDocumentFactory.load(pdfFile); List pageNumbers = request.getPageNumbersList(document, true); diff --git a/src/main/java/stirling/software/SPDF/controller/api/security/CertSignController.java b/src/main/java/stirling/software/SPDF/controller/api/security/CertSignController.java index 6ff75ebd..c37fcc9c 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/security/CertSignController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/security/CertSignController.java @@ -16,7 +16,6 @@ import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.util.Calendar; -import org.apache.pdfbox.Loader; import org.apache.pdfbox.examples.signature.CreateSignatureBase; import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature; @@ -35,6 +34,7 @@ import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo; import org.bouncycastle.pkcs.PKCSException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PostMapping; @@ -47,6 +47,7 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import stirling.software.SPDF.model.api.security.SignPDFWithCertRequest; +import stirling.software.SPDF.service.CustomPDDocumentFactory; import stirling.software.SPDF.utils.WebResponseUtils; @RestController @@ -71,6 +72,13 @@ public class CertSignController { } } + private final CustomPDDocumentFactory pdfDocumentFactory; + + @Autowired + public CertSignController(CustomPDDocumentFactory pdfDocumentFactory) { + this.pdfDocumentFactory = pdfDocumentFactory; + } + @PostMapping(consumes = "multipart/form-data", value = "/cert-sign") @Operation( summary = "Sign PDF with a Digital Certificate", @@ -122,7 +130,7 @@ public class CertSignController { CreateSignature createSignature = new CreateSignature(ks, password.toCharArray()); ByteArrayOutputStream baos = new ByteArrayOutputStream(); - sign(pdf.getBytes(), baos, createSignature, name, location, reason); + sign(pdfDocumentFactory, pdf.getBytes(), baos, createSignature, name, location, reason); return WebResponseUtils.boasToWebResponse( baos, Filenames.toSimpleFileName(pdf.getOriginalFilename()).replaceFirst("[.][^.]+$", "") @@ -130,13 +138,14 @@ public class CertSignController { } private static void sign( + CustomPDDocumentFactory pdfDocumentFactory, byte[] input, OutputStream output, CreateSignature instance, String name, String location, String reason) { - try (PDDocument doc = Loader.loadPDF(input)) { + try (PDDocument doc = pdfDocumentFactory.load(input)) { PDSignature signature = new PDSignature(); signature.setFilter(PDSignature.FILTER_ADOBE_PPKLITE); signature.setSubFilter(PDSignature.SUBFILTER_ADBE_PKCS7_DETACHED); diff --git a/src/main/java/stirling/software/SPDF/controller/api/security/PasswordController.java b/src/main/java/stirling/software/SPDF/controller/api/security/PasswordController.java index 84c44933..d738ae79 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/security/PasswordController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/security/PasswordController.java @@ -2,12 +2,12 @@ package stirling.software.SPDF.controller.api.security; import java.io.IOException; -import org.apache.pdfbox.Loader; import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.encryption.AccessPermission; import org.apache.pdfbox.pdmodel.encryption.StandardProtectionPolicy; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PostMapping; @@ -21,6 +21,7 @@ import io.swagger.v3.oas.annotations.tags.Tag; import stirling.software.SPDF.model.api.security.AddPasswordRequest; import stirling.software.SPDF.model.api.security.PDFPasswordRequest; +import stirling.software.SPDF.service.CustomPDDocumentFactory; import stirling.software.SPDF.utils.WebResponseUtils; @RestController @@ -30,6 +31,13 @@ public class PasswordController { private static final Logger logger = LoggerFactory.getLogger(PasswordController.class); + private final CustomPDDocumentFactory pdfDocumentFactory; + + @Autowired + public PasswordController(CustomPDDocumentFactory pdfDocumentFactory) { + this.pdfDocumentFactory = pdfDocumentFactory; + } + @PostMapping(consumes = "multipart/form-data", value = "/remove-password") @Operation( summary = "Remove password from a PDF file", @@ -39,8 +47,7 @@ public class PasswordController { throws IOException { MultipartFile fileInput = request.getFileInput(); String password = request.getPassword(); - - PDDocument document = Loader.loadPDF(fileInput.getBytes(), password); + PDDocument document = pdfDocumentFactory.load(fileInput, password); document.setAllSecurityToBeRemoved(true); return WebResponseUtils.pdfDocToWebResponse( document, @@ -69,7 +76,7 @@ public class PasswordController { boolean canPrint = request.isCanPrint(); boolean canPrintFaithful = request.isCanPrintFaithful(); - PDDocument document = Loader.loadPDF(fileInput.getBytes()); + PDDocument document = pdfDocumentFactory.load(fileInput); AccessPermission ap = new AccessPermission(); ap.setCanAssembleDocument(!canAssembleDocument); ap.setCanExtractContent(!canExtractContent); diff --git a/src/main/java/stirling/software/SPDF/controller/api/security/RedactController.java b/src/main/java/stirling/software/SPDF/controller/api/security/RedactController.java index b3eec2b1..40dc2c75 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/security/RedactController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/security/RedactController.java @@ -5,12 +5,12 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.List; -import org.apache.pdfbox.Loader; import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDPageContentStream; import org.apache.pdfbox.pdmodel.common.PDRectangle; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PostMapping; @@ -25,6 +25,7 @@ import io.swagger.v3.oas.annotations.tags.Tag; import stirling.software.SPDF.model.PDFText; import stirling.software.SPDF.model.api.security.RedactPdfRequest; import stirling.software.SPDF.pdf.TextFinder; +import stirling.software.SPDF.service.CustomPDDocumentFactory; import stirling.software.SPDF.utils.PdfUtils; import stirling.software.SPDF.utils.WebResponseUtils; @@ -35,6 +36,13 @@ public class RedactController { private static final Logger logger = LoggerFactory.getLogger(RedactController.class); + private final CustomPDDocumentFactory pdfDocumentFactory; + + @Autowired + public RedactController(CustomPDDocumentFactory pdfDocumentFactory) { + this.pdfDocumentFactory = pdfDocumentFactory; + } + @PostMapping(value = "/auto-redact", consumes = "multipart/form-data") @Operation( summary = "Redacts listOfText in a PDF document", @@ -52,8 +60,7 @@ public class RedactController { System.out.println(listOfTextString); String[] listOfText = listOfTextString.split("\n"); - byte[] bytes = file.getBytes(); - PDDocument document = Loader.loadPDF(bytes); + PDDocument document = pdfDocumentFactory.load(file); Color redactColor; try { diff --git a/src/main/java/stirling/software/SPDF/controller/api/security/RemoveCertSignController.java b/src/main/java/stirling/software/SPDF/controller/api/security/RemoveCertSignController.java index 6a131a98..bd4c8c3d 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/security/RemoveCertSignController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/security/RemoveCertSignController.java @@ -1,10 +1,8 @@ package stirling.software.SPDF.controller.api.security; -import java.io.ByteArrayOutputStream; import java.util.List; import java.util.stream.Collectors; -import org.apache.pdfbox.Loader; import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocumentCatalog; import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm; @@ -12,6 +10,7 @@ import org.apache.pdfbox.pdmodel.interactive.form.PDField; import org.apache.pdfbox.pdmodel.interactive.form.PDSignatureField; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PostMapping; @@ -24,6 +23,7 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import stirling.software.SPDF.model.api.PDFFile; +import stirling.software.SPDF.service.CustomPDDocumentFactory; import stirling.software.SPDF.utils.WebResponseUtils; @RestController @@ -33,6 +33,13 @@ public class RemoveCertSignController { private static final Logger logger = LoggerFactory.getLogger(RemoveCertSignController.class); + private final CustomPDDocumentFactory pdfDocumentFactory; + + @Autowired + public RemoveCertSignController(CustomPDDocumentFactory pdfDocumentFactory) { + this.pdfDocumentFactory = pdfDocumentFactory; + } + @PostMapping(consumes = "multipart/form-data", value = "/remove-cert-sign") @Operation( summary = "Remove digital signature from PDF", @@ -42,14 +49,8 @@ public class RemoveCertSignController { throws Exception { MultipartFile pdf = request.getFileInput(); - // Convert MultipartFile to byte[] - byte[] pdfBytes = pdf.getBytes(); - - // Create a ByteArrayOutputStream to hold the resulting PDF - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - // Load the PDF document - PDDocument document = Loader.loadPDF(pdfBytes); + PDDocument document = pdfDocumentFactory.load(pdf); // Get the document catalog PDDocumentCatalog catalog = document.getDocumentCatalog(); @@ -67,14 +68,9 @@ public class RemoveCertSignController { acroForm.flatten(fieldsToRemove, false); } } - - // Save the modified document to the ByteArrayOutputStream - document.save(baos); - document.close(); - // Return the modified PDF as a response - return WebResponseUtils.boasToWebResponse( - baos, + return WebResponseUtils.pdfDocToWebResponse( + document, Filenames.toSimpleFileName(pdf.getOriginalFilename()).replaceFirst("[.][^.]+$", "") + "_unsigned.pdf"); } diff --git a/src/main/java/stirling/software/SPDF/controller/api/security/SanitizeController.java b/src/main/java/stirling/software/SPDF/controller/api/security/SanitizeController.java index dd2c79da..b034d45d 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/security/SanitizeController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/security/SanitizeController.java @@ -2,7 +2,6 @@ package stirling.software.SPDF.controller.api.security; import java.io.IOException; -import org.apache.pdfbox.Loader; import org.apache.pdfbox.cos.COSDictionary; import org.apache.pdfbox.cos.COSName; import org.apache.pdfbox.pdmodel.PDDocument; @@ -21,6 +20,7 @@ import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationLink; import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationWidget; import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm; import org.apache.pdfbox.pdmodel.interactive.form.PDField; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PostMapping; @@ -33,6 +33,7 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import stirling.software.SPDF.model.api.security.SanitizePdfRequest; +import stirling.software.SPDF.service.CustomPDDocumentFactory; import stirling.software.SPDF.utils.WebResponseUtils; @RestController @@ -40,6 +41,13 @@ import stirling.software.SPDF.utils.WebResponseUtils; @Tag(name = "Security", description = "Security APIs") public class SanitizeController { + private final CustomPDDocumentFactory pdfDocumentFactory; + + @Autowired + public SanitizeController(CustomPDDocumentFactory pdfDocumentFactory) { + this.pdfDocumentFactory = pdfDocumentFactory; + } + @PostMapping(consumes = "multipart/form-data", value = "/sanitize-pdf") @Operation( summary = "Sanitize a PDF file", @@ -54,7 +62,7 @@ public class SanitizeController { boolean removeLinks = request.isRemoveLinks(); boolean removeFonts = request.isRemoveFonts(); - PDDocument document = Loader.loadPDF(inputFile.getBytes()); + PDDocument document = pdfDocumentFactory.load(inputFile); if (removeJavaScript) { sanitizeJavaScript(document); } diff --git a/src/main/java/stirling/software/SPDF/controller/api/security/WatermarkController.java b/src/main/java/stirling/software/SPDF/controller/api/security/WatermarkController.java index 0b36cd5e..de7efd71 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/security/WatermarkController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/security/WatermarkController.java @@ -11,7 +11,6 @@ import java.nio.file.Files; import javax.imageio.ImageIO; import org.apache.commons.io.IOUtils; -import org.apache.pdfbox.Loader; import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDPage; import org.apache.pdfbox.pdmodel.PDPageContentStream; @@ -23,6 +22,7 @@ import org.apache.pdfbox.pdmodel.graphics.image.LosslessFactory; import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject; import org.apache.pdfbox.pdmodel.graphics.state.PDExtendedGraphicsState; import org.apache.pdfbox.util.Matrix; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.io.ClassPathResource; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ModelAttribute; @@ -36,6 +36,7 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import stirling.software.SPDF.model.api.security.AddWatermarkRequest; +import stirling.software.SPDF.service.CustomPDDocumentFactory; import stirling.software.SPDF.utils.PdfUtils; import stirling.software.SPDF.utils.WebResponseUtils; @@ -44,6 +45,13 @@ import stirling.software.SPDF.utils.WebResponseUtils; @Tag(name = "Security", description = "Security APIs") public class WatermarkController { + private final CustomPDDocumentFactory pdfDocumentFactory; + + @Autowired + public WatermarkController(CustomPDDocumentFactory pdfDocumentFactory) { + this.pdfDocumentFactory = pdfDocumentFactory; + } + @PostMapping(consumes = "multipart/form-data", value = "/add-watermark") @Operation( summary = "Add watermark to a PDF file", @@ -64,7 +72,7 @@ public class WatermarkController { boolean convertPdfToImage = request.isConvertPDFToImage(); // Load the input PDF - PDDocument document = Loader.loadPDF(pdfFile.getBytes()); + PDDocument document = pdfDocumentFactory.load(pdfFile); // Create a page in the document for (PDPage page : document.getPages()) { diff --git a/src/main/java/stirling/software/SPDF/service/CustomPDDocumentFactory.java b/src/main/java/stirling/software/SPDF/service/CustomPDDocumentFactory.java new file mode 100644 index 00000000..b3f9eec7 --- /dev/null +++ b/src/main/java/stirling/software/SPDF/service/CustomPDDocumentFactory.java @@ -0,0 +1,100 @@ +package stirling.software.SPDF.service; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; + +import org.apache.pdfbox.Loader; +import org.apache.pdfbox.pdmodel.PDDocument; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.web.multipart.MultipartFile; + +import stirling.software.SPDF.config.PdfMetadataService; +import stirling.software.SPDF.model.PdfMetadata; +import stirling.software.SPDF.model.api.PDFFile; + +@Component +public class CustomPDDocumentFactory { + + private final PdfMetadataService pdfMetadataService; + + @Autowired + public CustomPDDocumentFactory(PdfMetadataService pdfMetadataService) { + this.pdfMetadataService = pdfMetadataService; + } + + public PDDocument createNewDocument() throws IOException { + PDDocument document = new PDDocument(); + pdfMetadataService.setMetadataToPdf(document, PdfMetadata.builder().build(), true); + return document; + } + + public PDDocument createNewDocumentBasedOnOldDocument(PDDocument oldDocument) + throws IOException { + PDDocument document = new PDDocument(); + pdfMetadataService.setMetadataToPdf( + document, pdfMetadataService.extractMetadataFromPdf(oldDocument), true); + return document; + } + + public byte[] loadToBytes(File file) throws IOException { + PDDocument document = load(file); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + document.save(baos); + // Close the document + document.close(); + return baos.toByteArray(); + } + + public byte[] loadToBytes(byte[] bytes) throws IOException { + PDDocument document = load(bytes); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + document.save(baos); + // Close the document + document.close(); + return baos.toByteArray(); + } + + // if loading from a file, assume the file has been made with Stirling-PDF + public PDDocument load(File file) throws IOException { + PDDocument document = Loader.loadPDF(file); + pdfMetadataService.setMetadataToPdf(document, PdfMetadata.builder().build(), true); + return document; + } + + public PDDocument load(InputStream input) throws IOException { + return load(input.readAllBytes()); + } + + public PDDocument load(byte[] input) throws IOException { + PDDocument document = Loader.loadPDF(input); + pdfMetadataService.setDefaultMetadata(document); + return document; + } + + public PDDocument load(PDFFile pdfFile) throws IOException { + return load(pdfFile.getFileInput()); + } + + public PDDocument load(MultipartFile pdfFile) throws IOException { + return load(pdfFile.getBytes()); + } + + public PDDocument load(String path) throws IOException { + return load(new File(path)); + } + + public PDDocument load(MultipartFile fileInput, String password) throws IOException { + return load(fileInput.getBytes(), password); + } + + private PDDocument load(byte[] bytes, String password) throws IOException { + PDDocument document = Loader.loadPDF(bytes, password); + pdfMetadataService.setDefaultMetadata(document); + return document; + } + + // Add other load methods as needed, following the same pattern +} diff --git a/src/main/java/stirling/software/SPDF/utils/PdfUtils.java b/src/main/java/stirling/software/SPDF/utils/PdfUtils.java index 160e01da..3416dee6 100644 --- a/src/main/java/stirling/software/SPDF/utils/PdfUtils.java +++ b/src/main/java/stirling/software/SPDF/utils/PdfUtils.java @@ -36,6 +36,8 @@ import org.springframework.web.multipart.MultipartFile; import io.github.pixee.security.Filenames; +import stirling.software.SPDF.service.CustomPDDocumentFactory; + public class PdfUtils { private static final Logger logger = LoggerFactory.getLogger(PdfUtils.class); @@ -380,9 +382,13 @@ public class PdfUtils { } public static byte[] imageToPdf( - MultipartFile[] files, String fitOption, boolean autoRotate, String colorType) + MultipartFile[] files, + String fitOption, + boolean autoRotate, + String colorType, + CustomPDDocumentFactory pdfDocumentFactory) throws IOException { - try (PDDocument doc = new PDDocument()) { + try (PDDocument doc = pdfDocumentFactory.createNewDocument()) { for (MultipartFile file : files) { String contentType = file.getContentType(); String originalFilename = Filenames.toSimpleFileName(file.getOriginalFilename()); @@ -470,10 +476,15 @@ public class PdfUtils { } public static byte[] overlayImage( - byte[] pdfBytes, byte[] imageBytes, float x, float y, boolean everyPage) + CustomPDDocumentFactory pdfDocumentFactory, + byte[] pdfBytes, + byte[] imageBytes, + float x, + float y, + boolean everyPage) throws IOException { - PDDocument document = Loader.loadPDF(pdfBytes); + PDDocument document = pdfDocumentFactory.load(pdfBytes); // Get the first page of the PDF int pages = document.getNumberOfPages(); diff --git a/src/main/resources/messages_it_IT.properties b/src/main/resources/messages_it_IT.properties index 9798fb34..58505fcd 100644 --- a/src/main/resources/messages_it_IT.properties +++ b/src/main/resources/messages_it_IT.properties @@ -77,10 +77,10 @@ color=Colore sponsor=Sponsor info=Info -legal.privacy=Informativa sulla privacy -legal.terms=Termini e Condizioni -legal.accessibility=Accessibilità -legal.cookie=Informativa sui cookie +legal.privacy=Privacy Policy +legal.terms=Terms and Conditions +legal.accessibility=Accessibility +legal.cookie=Cookie Policy legal.impressum=Impressum ############### diff --git a/src/test/java/stirling/software/SPDF/controller/api/RearrangePagesPDFControllerTest.java b/src/test/java/stirling/software/SPDF/controller/api/RearrangePagesPDFControllerTest.java index 209c7f54..4c91ffae 100644 --- a/src/test/java/stirling/software/SPDF/controller/api/RearrangePagesPDFControllerTest.java +++ b/src/test/java/stirling/software/SPDF/controller/api/RearrangePagesPDFControllerTest.java @@ -8,15 +8,33 @@ import java.util.Arrays; import java.util.List; import static org.junit.jupiter.api.Assertions.*; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import stirling.software.SPDF.service.CustomPDDocumentFactory; +import java.util.Arrays; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull;import org.junit.jupiter.api.BeforeEach; class RearrangePagesPDFControllerTest { + @Mock + private CustomPDDocumentFactory mockPdfDocumentFactory; + + private RearrangePagesPDFController sut; + + @BeforeEach + void setUp() { + MockitoAnnotations.openMocks(this); + sut = new RearrangePagesPDFController(mockPdfDocumentFactory); + } + /** * Tests the behavior of the oddEvenMerge method when there are no pages in the document. */ @Test void oddEvenMerge_noPages() { - RearrangePagesPDFController sut = new RearrangePagesPDFController(); int totalNumberOfPages = 0; List newPageOrder = sut.oddEvenMerge(totalNumberOfPages); @@ -30,7 +48,6 @@ class RearrangePagesPDFControllerTest { */ @Test void oddEvenMerge_oddTotalPageNumber() { - RearrangePagesPDFController sut = new RearrangePagesPDFController(); int totalNumberOfPages = 5; List newPageOrder = sut.oddEvenMerge(totalNumberOfPages); @@ -44,7 +61,6 @@ class RearrangePagesPDFControllerTest { */ @Test void oddEvenMerge_evenTotalPageNumber() { - RearrangePagesPDFController sut = new RearrangePagesPDFController(); int totalNumberOfPages = 6; List newPageOrder = sut.oddEvenMerge(totalNumberOfPages); @@ -72,8 +88,6 @@ class RearrangePagesPDFControllerTest { "22,47,23,48,24,49'" }) void oddEvenMerge_multi_test(int totalNumberOfPages, String expectedPageOrder) { - RearrangePagesPDFController sut = new RearrangePagesPDFController(); - List newPageOrder = sut.oddEvenMerge(totalNumberOfPages); assertNotNull(newPageOrder, "Returning null instead of page order list"); diff --git a/src/test/java/stirling/software/SPDF/controller/api/converters/ConvertWebsiteToPdfTest.java b/src/test/java/stirling/software/SPDF/controller/api/converters/ConvertWebsiteToPdfTest.java index 0e63b99f..ecf814b0 100644 --- a/src/test/java/stirling/software/SPDF/controller/api/converters/ConvertWebsiteToPdfTest.java +++ b/src/test/java/stirling/software/SPDF/controller/api/converters/ConvertWebsiteToPdfTest.java @@ -1,18 +1,36 @@ package stirling.software.SPDF.controller.api.converters; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; import org.springframework.http.ResponseEntity; +import stirling.software.SPDF.controller.api.RearrangePagesPDFController; import stirling.software.SPDF.model.api.converters.UrlToPdfRequest; +import stirling.software.SPDF.service.CustomPDDocumentFactory; + import static org.junit.jupiter.api.Assertions.*; public class ConvertWebsiteToPdfTest { + + + @Mock + private CustomPDDocumentFactory mockPdfDocumentFactory; + + private ConvertWebsiteToPDF convertWebsiteToPDF; + + @BeforeEach + void setUp() { + MockitoAnnotations.openMocks(this); + convertWebsiteToPDF = new ConvertWebsiteToPDF(mockPdfDocumentFactory); + } + @Test public void test_exemption_is_thrown_when_invalid_url_format_provided() { String invalid_format_Url = "invalid-url"; - // Arrange - ConvertWebsiteToPDF convertWebsiteToPDF = new ConvertWebsiteToPDF(); + UrlToPdfRequest request = new UrlToPdfRequest(); request.setUrlInput(invalid_format_Url); // Act @@ -28,7 +46,6 @@ public class ConvertWebsiteToPdfTest { String unreachable_Url = "https://www.googleeeexyz.com"; // Arrange - ConvertWebsiteToPDF convertWebsiteToPDF = new ConvertWebsiteToPDF(); UrlToPdfRequest request = new UrlToPdfRequest(); request.setUrlInput(unreachable_Url); // Act