From 6581bb4ab4c12bc11e7ddf46e78a3a2bc4bf1166 Mon Sep 17 00:00:00 2001 From: Anthony Stirling <77850077+Frooodle@users.noreply.github.com> Date: Sat, 13 May 2023 10:32:47 +0100 Subject: [PATCH] Improved API documentation --- .../SPDF/controller/api/MergeController.java | 13 ++- .../controller/api/RotationController.java | 16 ++- .../converters/ConvertOfficeController.java | 16 ++- .../api/converters/ConvertPDFToOffice.java | 97 +++++++++---------- .../api/converters/ConvertPDFToPDFA.java | 13 ++- .../api/other/BlankPageController.java | 24 ++++- .../api/other/CompressController.java | 26 ++++- .../api/other/OverlayImageController.java | 24 ++++- .../api/other/RepairController.java | 12 ++- .../api/security/PasswordController.java | 65 ++++++++++--- .../api/security/WatermarkController.java | 65 ------------- .../controller/web/SecurityWebController.java | 9 -- .../software/SPDF/utils/WatermarkRemover.java | 69 ------------- 13 files changed, 223 insertions(+), 226 deletions(-) delete mode 100644 src/main/java/stirling/software/SPDF/utils/WatermarkRemover.java 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 4a9cb2fb..ed68aeb5 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/MergeController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/MergeController.java @@ -15,6 +15,8 @@ import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; import stirling.software.SPDF.utils.PdfUtils; @RestController @@ -43,8 +45,15 @@ public class MergeController { } @PostMapping(consumes = "multipart/form-data", value = "/merge-pdfs") - public ResponseEntity mergePdfs(@RequestPart(required = true, value = "fileInput") MultipartFile[] files) throws IOException { - // Read the input PDF files into PDDocument objects + @Operation( + summary = "Merge multiple PDF files into one", + description = "This endpoint merges multiple PDF files into a single PDF file. The merged file will contain all pages from the input files in the order they were provided." + ) + public ResponseEntity mergePdfs( + @RequestPart(required = true, value = "fileInput") + @Parameter(description = "The input PDF files to be merged into a single file", required = true) + MultipartFile[] files) throws IOException { + // Read the input PDF files into PDDocument objects List documents = new ArrayList<>(); // Loop through the files array and read each file into a PDDocument 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 e9d73a43..5db10a3e 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/RotationController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/RotationController.java @@ -14,6 +14,8 @@ import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; import stirling.software.SPDF.utils.PdfUtils; @RestController @@ -22,8 +24,18 @@ public class RotationController { private static final Logger logger = LoggerFactory.getLogger(RotationController.class); @PostMapping(consumes = "multipart/form-data", value = "/rotate-pdf") - public ResponseEntity rotatePDF(@RequestPart(required = true, value = "fileInput") MultipartFile pdfFile, @RequestParam("angle") Integer angle) throws IOException { - + @Operation( + summary = "Rotate a PDF file", + description = "This endpoint rotates a given PDF file by a specified angle. The angle must be a multiple of 90." + ) + public ResponseEntity rotatePDF( + @RequestPart(required = true, value = "fileInput") + @Parameter(description = "The PDF file to be rotated", required = true) + MultipartFile pdfFile, + @RequestParam("angle") + @Parameter(description = "The angle by which to rotate the PDF file. This should be a multiple of 90.", example = "90", required = true) + Integer angle) throws IOException { + // Load the PDF document PDDocument document = PDDocument.load(pdfFile.getBytes()); 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 9cec9a46..039954bb 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 @@ -15,6 +15,8 @@ import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; import stirling.software.SPDF.utils.PdfUtils; import stirling.software.SPDF.utils.ProcessExecutor; @@ -54,8 +56,18 @@ public class ConvertOfficeController { } @PostMapping(consumes = "multipart/form-data", value = "/file-to-pdf") - public ResponseEntity processPdfWithOCR(@RequestPart(required = true, value = "fileInput") MultipartFile inputFile) throws IOException, InterruptedException { - + @Operation( + summary = "Convert a file to a PDF using OCR", + description = "This endpoint converts a given file to a PDF using Optical Character Recognition (OCR). The filename of the resulting PDF will be the original filename with '_convertedToPDF.pdf' appended." + ) + public ResponseEntity processPdfWithOCR( + @RequestPart(required = true, value = "fileInput") + @Parameter( + description = "The input file to be converted to a PDF file using OCR", + required = true + ) + MultipartFile inputFile + ) throws IOException, InterruptedException { // unused but can start server instance if startup time is to long // LibreOfficeListener.getInstance().start(); diff --git a/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertPDFToOffice.java b/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertPDFToOffice.java index 153e5fdf..6ac5b22d 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertPDFToOffice.java +++ b/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertPDFToOffice.java @@ -13,63 +13,60 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.media.Schema; import stirling.software.SPDF.utils.PDFToFile; + @RestController public class ConvertPDFToOffice { - + @PostMapping(consumes = "multipart/form-data", value = "/pdf-to-html") + @Operation(summary = "Convert PDF to HTML", description = "This endpoint converts a PDF file to HTML format.") + public ResponseEntity processPdfToHTML( + @RequestPart(required = true, value = "fileInput") @Parameter(description = "The input PDF file to be converted to HTML format", required = true) MultipartFile inputFile) + throws IOException, InterruptedException { + PDFToFile pdfToFile = new PDFToFile(); + return pdfToFile.processPdfToOfficeFormat(inputFile, "html", "writer_pdf_import"); + } - @PostMapping(consumes = "multipart/form-data", value = "/pdf-to-html") - public ResponseEntity processPdfToHTML(@RequestPart(required = true, value = "fileInput") MultipartFile inputFile) throws IOException, InterruptedException { - PDFToFile pdfToFile = new PDFToFile(); - return pdfToFile.processPdfToOfficeFormat(inputFile, "html", "writer_pdf_import"); - } + @PostMapping(consumes = "multipart/form-data", value = "/pdf-to-presentation") + @Operation(summary = "Convert PDF to Presentation format", description = "This endpoint converts a given PDF file to a Presentation format.") + public ResponseEntity processPdfToPresentation( + @RequestPart(required = true, value = "fileInput") @Parameter(description = "The input PDF file") MultipartFile inputFile, + @RequestParam("outputFormat") @Parameter(description = "The output Presentation format", schema = @Schema(allowableValues = { + "ppt", "pptx", "odp" })) String outputFormat) + throws IOException, InterruptedException { + PDFToFile pdfToFile = new PDFToFile(); + return pdfToFile.processPdfToOfficeFormat(inputFile, outputFormat, "impress_pdf_import"); + } - @PostMapping(consumes = "multipart/form-data", value = "/pdf-to-presentation") - @Operation(summary = "Convert PDF to Presentation format", - description = "This endpoint converts a given PDF file to a Presentation format.") - public ResponseEntity processPdfToPresentation( - @RequestPart(required = true, value = "fileInput") - @Parameter(description = "The input PDF file") - MultipartFile inputFile, - @RequestParam("outputFormat") - @Parameter(description = "The output Presentation format", schema = @Schema(allowableValues = {"ppt", "pptx", "odp"})) - String outputFormat) throws IOException, InterruptedException { - PDFToFile pdfToFile = new PDFToFile(); - return pdfToFile.processPdfToOfficeFormat(inputFile, outputFormat, "impress_pdf_import"); - } + @PostMapping(consumes = "multipart/form-data", value = "/pdf-to-text") + @Operation(summary = "Convert PDF to Text or RTF format", description = "This endpoint converts a given PDF file to Text or RTF format.") + public ResponseEntity processPdfToRTForTXT( + @RequestPart(required = true, value = "fileInput") @Parameter(description = "The input PDF file") MultipartFile inputFile, + @RequestParam("outputFormat") @Parameter(description = "The output Text or RTF format", schema = @Schema(allowableValues = { + "rtf", "txt:Text" })) String outputFormat) + throws IOException, InterruptedException { + PDFToFile pdfToFile = new PDFToFile(); + return pdfToFile.processPdfToOfficeFormat(inputFile, outputFormat, "writer_pdf_import"); + } - @PostMapping(consumes = "multipart/form-data", value = "/pdf-to-text") - @Operation(summary = "Convert PDF to Text or RTF format", - description = "This endpoint converts a given PDF file to Text or RTF format.") - public ResponseEntity processPdfToRTForTXT( - @RequestPart(required = true, value = "fileInput") - @Parameter(description = "The input PDF file") - MultipartFile inputFile, - @RequestParam("outputFormat") - @Parameter(description = "The output Text or RTF format", schema = @Schema(allowableValues = {"rtf", "txt:Text"})) - String outputFormat) throws IOException, InterruptedException { - PDFToFile pdfToFile = new PDFToFile(); - return pdfToFile.processPdfToOfficeFormat(inputFile, outputFormat, "writer_pdf_import"); - } + @PostMapping(consumes = "multipart/form-data", value = "/pdf-to-word") + @Operation(summary = "Convert PDF to Word document", description = "This endpoint converts a given PDF file to a Word document format.") + public ResponseEntity processPdfToWord( + @RequestPart(required = true, value = "fileInput") @Parameter(description = "The input PDF file") MultipartFile inputFile, + @RequestParam("outputFormat") @Parameter(description = "The output Word document format", schema = @Schema(allowableValues = { + "doc", "docx", "odt" })) String outputFormat) + throws IOException, InterruptedException { + PDFToFile pdfToFile = new PDFToFile(); + return pdfToFile.processPdfToOfficeFormat(inputFile, outputFormat, "writer_pdf_import"); + } - @PostMapping(consumes = "multipart/form-data", value = "/pdf-to-word") - @Operation(summary = "Convert PDF to Word document", - description = "This endpoint converts a given PDF file to a Word document format.") - public ResponseEntity processPdfToWord( - @RequestPart(required = true, value = "fileInput") - @Parameter(description = "The input PDF file") - MultipartFile inputFile, - @RequestParam("outputFormat") - @Parameter(description = "The output Word document format", schema = @Schema(allowableValues = {"doc", "docx", "odt"})) - String outputFormat) throws IOException, InterruptedException { - PDFToFile pdfToFile = new PDFToFile(); - return pdfToFile.processPdfToOfficeFormat(inputFile, outputFormat, "writer_pdf_import"); - } + @PostMapping(consumes = "multipart/form-data", value = "/pdf-to-xml") + @Operation(summary = "Convert PDF to XML", description = "This endpoint converts a PDF file to an XML file.") + public ResponseEntity processPdfToXML( + @RequestPart(required = true, value = "fileInput") @Parameter(description = "The input PDF file to be converted to an XML file", required = true) MultipartFile inputFile) + throws IOException, InterruptedException { - @PostMapping(consumes = "multipart/form-data", value = "/pdf-to-xml") - public ResponseEntity processPdfToXML(@RequestPart(required = true, value = "fileInput") MultipartFile inputFile) throws IOException, InterruptedException { - PDFToFile pdfToFile = new PDFToFile(); - return pdfToFile.processPdfToOfficeFormat(inputFile, "xml", "writer_pdf_import"); - } + PDFToFile pdfToFile = new PDFToFile(); + return pdfToFile.processPdfToOfficeFormat(inputFile, "xml", "writer_pdf_import"); + } } 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 e36b15c6..207ed0bb 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 @@ -12,14 +12,23 @@ import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; import stirling.software.SPDF.utils.PdfUtils; import stirling.software.SPDF.utils.ProcessExecutor; @RestController public class ConvertPDFToPDFA { - @PostMapping(consumes = "multipart/form-data", value = "/pdf-to-pdfa") - public ResponseEntity pdfToPdfA(@RequestPart(required = true, value = "fileInput") MultipartFile inputFile) throws IOException, InterruptedException { + @PostMapping(consumes = "multipart/form-data", value = "/pdf-to-pdfa") + @Operation( + summary = "Convert a PDF to a PDF/A", + description = "This endpoint converts a PDF file to a PDF/A file. PDF/A is a format designed for long-term archiving of digital documents." + ) + public ResponseEntity pdfToPdfA( + @RequestPart(required = true, value = "fileInput") + @Parameter(description = "The input PDF file to be converted to a PDF/A file", required = true) + MultipartFile inputFile) throws IOException, InterruptedException { // Save the uploaded file to a temporary location Path tempInputFile = Files.createTempFile("input_", ".pdf"); diff --git a/src/main/java/stirling/software/SPDF/controller/api/other/BlankPageController.java b/src/main/java/stirling/software/SPDF/controller/api/other/BlankPageController.java index e43fc60b..98d710d7 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/other/BlankPageController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/other/BlankPageController.java @@ -26,6 +26,8 @@ import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; import stirling.software.SPDF.utils.ImageFinder; import stirling.software.SPDF.utils.PdfUtils; import stirling.software.SPDF.utils.ProcessExecutor; @@ -33,11 +35,23 @@ import stirling.software.SPDF.utils.ProcessExecutor; @RestController public class BlankPageController { - @PostMapping(consumes = "multipart/form-data", value = "/remove-blanks") - public ResponseEntity removeBlankPages(@RequestPart(required = true, value = "fileInput") MultipartFile inputFile, - @RequestParam(defaultValue = "10", name = "threshold") int threshold, - @RequestParam(defaultValue = "99.9", name = "whitePercent") float whitePercent) throws IOException, InterruptedException { - PDDocument document = null; + @PostMapping(consumes = "multipart/form-data", value = "/remove-blanks") + @Operation( + summary = "Remove blank pages from a PDF file", + description = "This endpoint removes blank pages from a given PDF file. Users can specify the threshold and white percentage to tune the detection of blank pages." + ) + public ResponseEntity removeBlankPages( + @RequestPart(required = true, value = "fileInput") + @Parameter(description = "The input PDF file from which blank pages will be removed", required = true) + MultipartFile inputFile, + @RequestParam(defaultValue = "10", name = "threshold") + @Parameter(description = "The threshold value to determine blank pages", example = "10") + int threshold, + @RequestParam(defaultValue = "99.9", name = "whitePercent") + @Parameter(description = "The percentage of white color on a page to consider it as blank", example = "99.9") + float whitePercent) throws IOException, InterruptedException { + + PDDocument document = null; try { document = PDDocument.load(inputFile.getInputStream()); PDPageTree pages = document.getDocumentCatalog().getPages(); diff --git a/src/main/java/stirling/software/SPDF/controller/api/other/CompressController.java b/src/main/java/stirling/software/SPDF/controller/api/other/CompressController.java index 6a9a0d8d..89f00981 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/other/CompressController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/other/CompressController.java @@ -15,18 +15,36 @@ import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; import stirling.software.SPDF.utils.PdfUtils; import stirling.software.SPDF.utils.ProcessExecutor; - +import io.swagger.v3.oas.annotations.media.Schema; @RestController public class CompressController { private static final Logger logger = LoggerFactory.getLogger(CompressController.class); @PostMapping(consumes = "multipart/form-data", value = "/compress-pdf") - public ResponseEntity optimizePdf(@RequestPart(required = true, value = "fileInput") MultipartFile inputFile, @RequestParam("optimizeLevel") int optimizeLevel, - @RequestParam(name = "fastWebView", required = false) Boolean fastWebView, @RequestParam(name = "jbig2Lossy", required = false) Boolean jbig2Lossy) - throws IOException, InterruptedException { + @Operation( + summary = "Optimize PDF file", + description = "This endpoint accepts a PDF file and optimizes it based on the provided parameters." + ) + public ResponseEntity optimizePdf( + @RequestPart(required = true, value = "fileInput") + @Parameter(description = "The input PDF file to be optimized.", required = true) + MultipartFile inputFile, + @RequestParam("optimizeLevel") + @Parameter(description = "The level of optimization to apply to the PDF file. Higher values indicate greater compression but may reduce quality.", + schema = @Schema(allowableValues = {"0", "1", "2", "3"}), example = "1") + int optimizeLevel, + @RequestParam(name = "fastWebView", required = false) + @Parameter(description = "If true, optimize the PDF for fast web view. This increases the file size by about 25%.", example = "false") + Boolean fastWebView, + @RequestParam(name = "jbig2Lossy", required = false) + @Parameter(description = "If true, apply lossy JB2 compression to the PDF file.", example = "false") + Boolean jbig2Lossy) + throws IOException, InterruptedException { // Save the uploaded file to a temporary location Path tempInputFile = Files.createTempFile("input_", ".pdf"); diff --git a/src/main/java/stirling/software/SPDF/controller/api/other/OverlayImageController.java b/src/main/java/stirling/software/SPDF/controller/api/other/OverlayImageController.java index 4b0d4017..0bf8ce83 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/other/OverlayImageController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/other/OverlayImageController.java @@ -12,6 +12,8 @@ import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; import stirling.software.SPDF.utils.PdfUtils; @RestController @@ -20,8 +22,26 @@ public class OverlayImageController { private static final Logger logger = LoggerFactory.getLogger(OverlayImageController.class); @PostMapping(consumes = "multipart/form-data", value = "/add-image") - public ResponseEntity overlayImage(@RequestPart(required = true, value = "fileInput") MultipartFile pdfFile, @RequestParam("fileInput2") MultipartFile imageFile, - @RequestParam("x") float x, @RequestParam("y") float y, @RequestParam("everyPage") boolean everyPage) { + @Operation( + summary = "Overlay image onto a PDF file", + description = "This endpoint overlays an image onto a PDF file at the specified coordinates. The image can be overlaid on every page of the PDF if specified." + ) + public ResponseEntity overlayImage( + @RequestPart(required = true, value = "fileInput") + @Parameter(description = "The input PDF file to overlay the image onto.", required = true) + MultipartFile pdfFile, + @RequestParam("fileInput2") + @Parameter(description = "The image file to be overlaid onto the PDF.", required = true) + MultipartFile imageFile, + @RequestParam("x") + @Parameter(description = "The x-coordinate at which to place the top-left corner of the image.", example = "0") + float x, + @RequestParam("y") + @Parameter(description = "The y-coordinate at which to place the top-left corner of the image.", example = "0") + float y, + @RequestParam("everyPage") + @Parameter(description = "Whether to overlay the image onto every page of the PDF.", example = "false") + boolean everyPage) { try { byte[] pdfBytes = pdfFile.getBytes(); byte[] imageBytes = imageFile.getBytes(); diff --git a/src/main/java/stirling/software/SPDF/controller/api/other/RepairController.java b/src/main/java/stirling/software/SPDF/controller/api/other/RepairController.java index 08fe65c5..55cdf83e 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/other/RepairController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/other/RepairController.java @@ -14,6 +14,8 @@ import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; import stirling.software.SPDF.utils.PdfUtils; import stirling.software.SPDF.utils.ProcessExecutor; @@ -23,8 +25,14 @@ public class RepairController { private static final Logger logger = LoggerFactory.getLogger(RepairController.class); @PostMapping(consumes = "multipart/form-data", value = "/repair") - public ResponseEntity repairPdf(@RequestPart(required = true, value = "fileInput") MultipartFile inputFile) - throws IOException, InterruptedException { + @Operation( + summary = "Repair a PDF file", + description = "This endpoint repairs a given PDF file by running Ghostscript command. The PDF is first saved to a temporary location, repaired, read back, and then returned as a response." + ) + public ResponseEntity repairPdf( + @RequestPart(required = true, value = "fileInput") + @Parameter(description = "The input PDF file to be repaired", required = true) + MultipartFile inputFile) throws IOException, InterruptedException { // Save the uploaded file to a temporary location Path tempInputFile = Files.createTempFile("input_", ".pdf"); 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 3cbe9965..1872d6eb 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 @@ -14,8 +14,10 @@ import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; import stirling.software.SPDF.utils.PdfUtils; - +import io.swagger.v3.oas.annotations.media.Schema; @RestController public class PasswordController { @@ -23,23 +25,62 @@ public class PasswordController { @PostMapping(consumes = "multipart/form-data", value = "/remove-password") - public ResponseEntity compressPDF(@RequestPart(required = true, value = "fileInput") MultipartFile fileInput, @RequestParam(name = "password") String password) - throws IOException { + @Operation( + summary = "Remove password from a PDF file", + description = "This endpoint removes the password from a protected PDF file. Users need to provide the existing password." + ) + public ResponseEntity removePassword( + @RequestPart(required = true, value = "fileInput") + @Parameter(description = "The input PDF file from which the password should be removed", required = true) + MultipartFile fileInput, + @RequestParam(name = "password") + @Parameter(description = "The password of the PDF file", required = true) + String password) throws IOException { PDDocument document = PDDocument.load(fileInput.getBytes(), password); document.setAllSecurityToBeRemoved(true); return PdfUtils.pdfDocToWebResponse(document, fileInput.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_password_removed.pdf"); } @PostMapping(consumes = "multipart/form-data", value = "/add-password") - public ResponseEntity compressPDF(@RequestPart(required = true, value = "fileInput") MultipartFile fileInput, - @RequestParam(defaultValue = "", name = "password") String password, @RequestParam(defaultValue = "128", name = "keyLength") int keyLength, - @RequestParam(defaultValue = "false", name = "canAssembleDocument") boolean canAssembleDocument, - @RequestParam(defaultValue = "false", name = "canExtractContent") boolean canExtractContent, - @RequestParam(defaultValue = "false", name = "canExtractForAccessibility") boolean canExtractForAccessibility, - @RequestParam(defaultValue = "false", name = "canFillInForm") boolean canFillInForm, @RequestParam(defaultValue = "false", name = "canModify") boolean canModify, - @RequestParam(defaultValue = "false", name = "canModifyAnnotations") boolean canModifyAnnotations, - @RequestParam(defaultValue = "false", name = "canPrint") boolean canPrint, @RequestParam(defaultValue = "false", name = "canPrintFaithful") boolean canPrintFaithful) - throws IOException { + @Operation( + summary = "Add password to a PDF file", + description = "This endpoint adds password protection to a PDF file. Users can specify a set of permissions that should be applied to the file." + ) + public ResponseEntity addPassword( + @RequestPart(required = true, value = "fileInput") + @Parameter(description = "The input PDF file to which the password should be added", required = true) + MultipartFile fileInput, + @RequestParam(defaultValue = "", name = "password") + @Parameter(description = "The password to be added to the PDF file") + String password, + @RequestParam(defaultValue = "128", name = "keyLength") + @Parameter(description = "The length of the encryption key", schema = @Schema(allowableValues = {"40", "128", "256"})) + int keyLength, + @RequestParam(defaultValue = "false", name = "canAssembleDocument") + @Parameter(description = "Whether the document assembly is allowed", example = "false") + boolean canAssembleDocument, + @RequestParam(defaultValue = "false", name = "canExtractContent") + @Parameter(description = "Whether content extraction for accessibility is allowed", example = "false") + boolean canExtractContent, + @RequestParam(defaultValue = "false", name = "canExtractForAccessibility") + @Parameter(description = "Whether content extraction for accessibility is allowed", example = "false") + boolean canExtractForAccessibility, + @RequestParam(defaultValue = "false", name = "canFillInForm") + @Parameter(description = "Whether form filling is allowed", example = "false") + boolean canFillInForm, + @RequestParam(defaultValue = "false", name = "canModify") + @Parameter(description = "Whether the document modification is allowed", example = "false") + boolean canModify, + @RequestParam(defaultValue = "false", name = "canModifyAnnotations") + @Parameter(description = "Whether modification of annotations is allowed", example = "false") + boolean canModifyAnnotations, + @RequestParam(defaultValue = "false", name = "canPrint") + @Parameter(description = "Whether printing of the document is allowed", example = "false") + boolean canPrint, + @RequestParam(defaultValue = "false", name = "canPrintFaithful") + @Parameter(description = "Whether faithful printing is allowed", example = "false") + boolean canPrintFaithful + ) throws IOException { PDDocument document = PDDocument.load(fileInput.getBytes()); AccessPermission ap = new AccessPermission(); 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 88955197..46199c63 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 @@ -2,20 +2,13 @@ package stirling.software.SPDF.controller.api.security; import java.awt.Color; import java.io.IOException; -import java.util.ArrayList; -import java.util.List; import org.apache.pdfbox.pdmodel.PDDocument; -import org.apache.pdfbox.pdmodel.PDDocumentCatalog; import org.apache.pdfbox.pdmodel.PDPage; import org.apache.pdfbox.pdmodel.PDPageContentStream; import org.apache.pdfbox.pdmodel.font.PDFont; import org.apache.pdfbox.pdmodel.font.PDType1Font; import org.apache.pdfbox.pdmodel.graphics.state.PDExtendedGraphicsState; -import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotation; -import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationMarkup; -import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm; -import org.apache.pdfbox.pdmodel.interactive.form.PDField; import org.apache.pdfbox.util.Matrix; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; @@ -27,7 +20,6 @@ import org.springframework.web.multipart.MultipartFile; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import stirling.software.SPDF.utils.PdfUtils; -import stirling.software.SPDF.utils.WatermarkRemover; @RestController public class WatermarkController { @@ -102,61 +94,4 @@ public class WatermarkController { return PdfUtils.pdfDocToWebResponse(document, pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_watermarked.pdf"); } - - @PostMapping(consumes = "multipart/form-data", value = "/remove-watermark") - public ResponseEntity removeWatermark(@RequestPart(required = true, value = "fileInput") MultipartFile pdfFile, @RequestParam("watermarkText") String watermarkText) - throws Exception { - - // Load the input PDF - PDDocument document = PDDocument.load(pdfFile.getInputStream()); - - // Create a new PDF document for the output - PDDocument outputDocument = new PDDocument(); - - // Loop through the pages - int numPages = document.getNumberOfPages(); - for (int i = 0; i < numPages; i++) { - PDPage page = document.getPage(i); - - // Process the content stream to remove the watermark text - WatermarkRemover editor = new WatermarkRemover(watermarkText) { - }; - editor.processPage(page); - editor.processPage(page); - // Add the page to the output document - outputDocument.addPage(page); - } - - for (PDPage page : outputDocument.getPages()) { - List annotations = page.getAnnotations(); - List annotationsToRemove = new ArrayList<>(); - - for (PDAnnotation annotation : annotations) { - if (annotation instanceof PDAnnotationMarkup) { - PDAnnotationMarkup markup = (PDAnnotationMarkup) annotation; - String contents = markup.getContents(); - if (contents != null && contents.contains(watermarkText)) { - annotationsToRemove.add(markup); - } - } - } - - annotations.removeAll(annotationsToRemove); - } - PDDocumentCatalog catalog = outputDocument.getDocumentCatalog(); - PDAcroForm acroForm = catalog.getAcroForm(); - if (acroForm != null) { - List fields = acroForm.getFields(); - for (PDField field : fields) { - String fieldValue = field.getValueAsString(); - if (fieldValue.contains(watermarkText)) { - field.setValue(fieldValue.replace(watermarkText, "")); - } - } - } - - return PdfUtils.pdfDocToWebResponse(outputDocument, "removed.pdf"); - } - - } diff --git a/src/main/java/stirling/software/SPDF/controller/web/SecurityWebController.java b/src/main/java/stirling/software/SPDF/controller/web/SecurityWebController.java index 31f16563..9eb10267 100644 --- a/src/main/java/stirling/software/SPDF/controller/web/SecurityWebController.java +++ b/src/main/java/stirling/software/SPDF/controller/web/SecurityWebController.java @@ -34,13 +34,4 @@ public class SecurityWebController { model.addAttribute("currentPage", "add-watermark"); return "security/add-watermark"; } - - //WIP - @GetMapping("/remove-watermark") - @Hidden - public String removeWatermarkForm(Model model) { - model.addAttribute("currentPage", "remove-watermark"); - return "security/remove-watermark"; - } - } diff --git a/src/main/java/stirling/software/SPDF/utils/WatermarkRemover.java b/src/main/java/stirling/software/SPDF/utils/WatermarkRemover.java deleted file mode 100644 index 90b44e53..00000000 --- a/src/main/java/stirling/software/SPDF/utils/WatermarkRemover.java +++ /dev/null @@ -1,69 +0,0 @@ -package stirling.software.SPDF.utils; - -import java.io.IOException; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import org.apache.pdfbox.contentstream.PDFStreamEngine; -import org.apache.pdfbox.contentstream.operator.Operator; -import org.apache.pdfbox.cos.COSArray; -import org.apache.pdfbox.cos.COSBase; -import org.apache.pdfbox.cos.COSString; - -public class WatermarkRemover extends PDFStreamEngine { - - private final Pattern pattern; - private final String watermarkText; - - public WatermarkRemover(String watermarkText) { - this.watermarkText = watermarkText; - this.pattern = Pattern.compile(Pattern.quote(watermarkText)); - } - - @Override - protected void processOperator(Operator operator, List operands) throws IOException { - String operation = operator.getName(); - - boolean processText = false; - if ("Tj".equals(operation) || "TJ".equals(operation) || "'".equals(operation) || "\"".equals(operation)) { - processText = true; - } - - if (processText) { - for (int j = 0; j < operands.size(); ++j) { - COSBase operand = operands.get(j); - if (operand instanceof COSString) { - COSString cosString = (COSString) operand; - String string = cosString.getString(); - Matcher matcher = pattern.matcher(string); - if (matcher.find()) { - string = matcher.replaceAll(""); - cosString.setValue(string.getBytes()); - } - } else if (operand instanceof COSArray) { - COSArray array = (COSArray) operand; - for (int i = 0; i < array.size(); i++) { - COSBase item = array.get(i); - if (item instanceof COSString) { - COSString cosString = (COSString) item; - String string = cosString.getString(); - Matcher matcher = pattern.matcher(string); - if (matcher.find()) { - System.out.println("operation =" + operation); - System.out.println("1 =" + string); - string = matcher.replaceAll(""); - cosString.setValue(string.getBytes()); - array.set(i, cosString); - operands.set(j, array); - } - - } - } - } - - } - } - super.processOperator(operator, operands); - } -}