diff --git a/src/main/java/stirling/software/SPDF/config/EndpointConfiguration.java b/src/main/java/stirling/software/SPDF/config/EndpointConfiguration.java index 92580b43..24f2822d 100644 --- a/src/main/java/stirling/software/SPDF/config/EndpointConfiguration.java +++ b/src/main/java/stirling/software/SPDF/config/EndpointConfiguration.java @@ -1,200 +1,208 @@ -package stirling.software.SPDF.config; - -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.stereotype.Service; -@Service -public class EndpointConfiguration { - private static final Logger logger = LoggerFactory.getLogger(EndpointConfiguration.class); - private Map endpointStatuses = new ConcurrentHashMap<>(); - private Map> endpointGroups = new ConcurrentHashMap<>(); - - public EndpointConfiguration() { - init(); - processEnvironmentConfigs(); - } - - public void enableEndpoint(String endpoint) { - endpointStatuses.put(endpoint, true); - } - - public void disableEndpoint(String endpoint) { - if(!endpointStatuses.containsKey(endpoint) || endpointStatuses.get(endpoint) != false) { - logger.info("Disabling {}", endpoint); - endpointStatuses.put(endpoint, false); - } - } - - public boolean isEndpointEnabled(String endpoint) { - if (endpoint.startsWith("/")) { - endpoint = endpoint.substring(1); - } - return endpointStatuses.getOrDefault(endpoint, true); - } - - public void addEndpointToGroup(String group, String endpoint) { - endpointGroups.computeIfAbsent(group, k -> new HashSet<>()).add(endpoint); - } - - public void enableGroup(String group) { - Set endpoints = endpointGroups.get(group); - if (endpoints != null) { - for (String endpoint : endpoints) { - enableEndpoint(endpoint); - } - } - } - - public void disableGroup(String group) { - Set endpoints = endpointGroups.get(group); - if (endpoints != null) { - for (String endpoint : endpoints) { - disableEndpoint(endpoint); - } - } - } - - public void init() { - // Adding endpoints to "PageOps" group - addEndpointToGroup("PageOps", "remove-pages"); - addEndpointToGroup("PageOps", "merge-pdfs"); - addEndpointToGroup("PageOps", "split-pdfs"); - addEndpointToGroup("PageOps", "pdf-organizer"); - addEndpointToGroup("PageOps", "rotate-pdf"); - addEndpointToGroup("PageOps", "multi-page-layout"); - addEndpointToGroup("PageOps", "scale-pages"); - - // Adding endpoints to "Convert" group - addEndpointToGroup("Convert", "pdf-to-img"); - addEndpointToGroup("Convert", "img-to-pdf"); - addEndpointToGroup("Convert", "pdf-to-pdfa"); - addEndpointToGroup("Convert", "file-to-pdf"); - addEndpointToGroup("Convert", "xlsx-to-pdf"); - addEndpointToGroup("Convert", "pdf-to-word"); - addEndpointToGroup("Convert", "pdf-to-presentation"); - addEndpointToGroup("Convert", "pdf-to-text"); - addEndpointToGroup("Convert", "pdf-to-html"); - addEndpointToGroup("Convert", "pdf-to-xml"); - - // Adding endpoints to "Security" group - addEndpointToGroup("Security", "add-password"); - addEndpointToGroup("Security", "remove-password"); - addEndpointToGroup("Security", "change-permissions"); - addEndpointToGroup("Security", "add-watermark"); - addEndpointToGroup("Security", "cert-sign"); - - - - // Adding endpoints to "Other" group - addEndpointToGroup("Other", "ocr-pdf"); - addEndpointToGroup("Other", "add-image"); - addEndpointToGroup("Other", "compress-pdf"); - addEndpointToGroup("Other", "extract-images"); - addEndpointToGroup("Other", "change-metadata"); - addEndpointToGroup("Other", "extract-image-scans"); - addEndpointToGroup("Other", "sign"); - addEndpointToGroup("Other", "flatten"); - addEndpointToGroup("Other", "repair"); - addEndpointToGroup("Other", "remove-blanks"); - addEndpointToGroup("Other", "compare"); - - - - - - - - //CLI - addEndpointToGroup("CLI", "compress-pdf"); - addEndpointToGroup("CLI", "extract-image-scans"); - addEndpointToGroup("CLI", "remove-blanks"); - addEndpointToGroup("CLI", "repair"); - addEndpointToGroup("CLI", "pdf-to-pdfa"); - addEndpointToGroup("CLI", "file-to-pdf"); - addEndpointToGroup("CLI", "xlsx-to-pdf"); - addEndpointToGroup("CLI", "pdf-to-word"); - addEndpointToGroup("CLI", "pdf-to-presentation"); - addEndpointToGroup("CLI", "pdf-to-text"); - addEndpointToGroup("CLI", "pdf-to-html"); - addEndpointToGroup("CLI", "pdf-to-xml"); - addEndpointToGroup("CLI", "ocr-pdf"); - - //python - addEndpointToGroup("Python", "extract-image-scans"); - addEndpointToGroup("Python", "remove-blanks"); - - - - //openCV - addEndpointToGroup("OpenCV", "extract-image-scans"); - addEndpointToGroup("OpenCV", "remove-blanks"); - - //LibreOffice - addEndpointToGroup("LibreOffice", "repair"); - addEndpointToGroup("LibreOffice", "file-to-pdf"); - addEndpointToGroup("LibreOffice", "xlsx-to-pdf"); - addEndpointToGroup("LibreOffice", "pdf-to-word"); - addEndpointToGroup("LibreOffice", "pdf-to-presentation"); - addEndpointToGroup("LibreOffice", "pdf-to-text"); - addEndpointToGroup("LibreOffice", "pdf-to-html"); - addEndpointToGroup("LibreOffice", "pdf-to-xml"); - - - //OCRmyPDF - addEndpointToGroup("OCRmyPDF", "compress-pdf"); - addEndpointToGroup("OCRmyPDF", "pdf-to-pdfa"); - addEndpointToGroup("OCRmyPDF", "ocr-pdf"); - - //Java - addEndpointToGroup("Java", "merge-pdfs"); - addEndpointToGroup("Java", "remove-pages"); - addEndpointToGroup("Java", "split-pdfs"); - addEndpointToGroup("Java", "pdf-organizer"); - addEndpointToGroup("Java", "rotate-pdf"); - addEndpointToGroup("Java", "pdf-to-img"); - addEndpointToGroup("Java", "img-to-pdf"); - addEndpointToGroup("Java", "add-password"); - addEndpointToGroup("Java", "remove-password"); - addEndpointToGroup("Java", "change-permissions"); - addEndpointToGroup("Java", "add-watermark"); - addEndpointToGroup("Java", "add-image"); - addEndpointToGroup("Java", "extract-images"); - addEndpointToGroup("Java", "change-metadata"); - addEndpointToGroup("Java", "cert-sign"); - addEndpointToGroup("Java", "multi-page-layout"); - addEndpointToGroup("Java", "scale-pages"); - - - //Javascript - addEndpointToGroup("Javascript", "pdf-organizer"); - addEndpointToGroup("Javascript", "sign"); - addEndpointToGroup("Javascript", "compare"); - - } - - private void processEnvironmentConfigs() { - String endpointsToRemove = System.getenv("ENDPOINTS_TO_REMOVE"); - String groupsToRemove = System.getenv("GROUPS_TO_REMOVE"); - - if (endpointsToRemove != null) { - String[] endpoints = endpointsToRemove.split(","); - for (String endpoint : endpoints) { - disableEndpoint(endpoint.trim()); - } - } - - if (groupsToRemove != null) { - String[] groups = groupsToRemove.split(","); - for (String group : groups) { - disableGroup(group.trim()); - } - } - } - -} - +package stirling.software.SPDF.config; + +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; +@Service +public class EndpointConfiguration { + private static final Logger logger = LoggerFactory.getLogger(EndpointConfiguration.class); + private Map endpointStatuses = new ConcurrentHashMap<>(); + private Map> endpointGroups = new ConcurrentHashMap<>(); + + public EndpointConfiguration() { + init(); + processEnvironmentConfigs(); + } + + public void enableEndpoint(String endpoint) { + endpointStatuses.put(endpoint, true); + } + + public void disableEndpoint(String endpoint) { + if(!endpointStatuses.containsKey(endpoint) || endpointStatuses.get(endpoint) != false) { + logger.info("Disabling {}", endpoint); + endpointStatuses.put(endpoint, false); + } + } + + public boolean isEndpointEnabled(String endpoint) { + if (endpoint.startsWith("/")) { + endpoint = endpoint.substring(1); + } + return endpointStatuses.getOrDefault(endpoint, true); + } + + public void addEndpointToGroup(String group, String endpoint) { + endpointGroups.computeIfAbsent(group, k -> new HashSet<>()).add(endpoint); + } + + public void enableGroup(String group) { + Set endpoints = endpointGroups.get(group); + if (endpoints != null) { + for (String endpoint : endpoints) { + enableEndpoint(endpoint); + } + } + } + + public void disableGroup(String group) { + Set endpoints = endpointGroups.get(group); + if (endpoints != null) { + for (String endpoint : endpoints) { + disableEndpoint(endpoint); + } + } + } + + public void init() { + // Adding endpoints to "PageOps" group + addEndpointToGroup("PageOps", "remove-pages"); + addEndpointToGroup("PageOps", "merge-pdfs"); + addEndpointToGroup("PageOps", "split-pdfs"); + addEndpointToGroup("PageOps", "pdf-organizer"); + addEndpointToGroup("PageOps", "rotate-pdf"); + addEndpointToGroup("PageOps", "multi-page-layout"); + addEndpointToGroup("PageOps", "scale-pages"); + addEndpointToGroup("PageOps", "adjust-contrast"); + addEndpointToGroup("PageOps", "crop"); + addEndpointToGroup("PageOps", "auto-split-pdf"); + + // Adding endpoints to "Convert" group + addEndpointToGroup("Convert", "pdf-to-img"); + addEndpointToGroup("Convert", "img-to-pdf"); + addEndpointToGroup("Convert", "pdf-to-pdfa"); + addEndpointToGroup("Convert", "file-to-pdf"); + addEndpointToGroup("Convert", "xlsx-to-pdf"); + addEndpointToGroup("Convert", "pdf-to-word"); + addEndpointToGroup("Convert", "pdf-to-presentation"); + addEndpointToGroup("Convert", "pdf-to-text"); + addEndpointToGroup("Convert", "pdf-to-html"); + addEndpointToGroup("Convert", "pdf-to-xml"); + + // Adding endpoints to "Security" group + addEndpointToGroup("Security", "add-password"); + addEndpointToGroup("Security", "remove-password"); + addEndpointToGroup("Security", "change-permissions"); + addEndpointToGroup("Security", "add-watermark"); + addEndpointToGroup("Security", "cert-sign"); + addEndpointToGroup("Security", "sanitize-pdf"); + + + // Adding endpoints to "Other" group + addEndpointToGroup("Other", "ocr-pdf"); + addEndpointToGroup("Other", "add-image"); + addEndpointToGroup("Other", "compress-pdf"); + addEndpointToGroup("Other", "extract-images"); + addEndpointToGroup("Other", "change-metadata"); + addEndpointToGroup("Other", "extract-image-scans"); + addEndpointToGroup("Other", "sign"); + addEndpointToGroup("Other", "flatten"); + addEndpointToGroup("Other", "repair"); + addEndpointToGroup("Other", "remove-blanks"); + addEndpointToGroup("Other", "compare"); + addEndpointToGroup("Other", "add-page-numbers"); + addEndpointToGroup("Other", "auto-rename"); + + + + + //CLI + addEndpointToGroup("CLI", "compress-pdf"); + addEndpointToGroup("CLI", "extract-image-scans"); + addEndpointToGroup("CLI", "remove-blanks"); + addEndpointToGroup("CLI", "repair"); + addEndpointToGroup("CLI", "pdf-to-pdfa"); + addEndpointToGroup("CLI", "file-to-pdf"); + addEndpointToGroup("CLI", "xlsx-to-pdf"); + addEndpointToGroup("CLI", "pdf-to-word"); + addEndpointToGroup("CLI", "pdf-to-presentation"); + addEndpointToGroup("CLI", "pdf-to-text"); + addEndpointToGroup("CLI", "pdf-to-html"); + addEndpointToGroup("CLI", "pdf-to-xml"); + addEndpointToGroup("CLI", "ocr-pdf"); + + //python + addEndpointToGroup("Python", "extract-image-scans"); + addEndpointToGroup("Python", "remove-blanks"); + + + + //openCV + addEndpointToGroup("OpenCV", "extract-image-scans"); + addEndpointToGroup("OpenCV", "remove-blanks"); + + //LibreOffice + addEndpointToGroup("LibreOffice", "repair"); + addEndpointToGroup("LibreOffice", "file-to-pdf"); + addEndpointToGroup("LibreOffice", "xlsx-to-pdf"); + addEndpointToGroup("LibreOffice", "pdf-to-word"); + addEndpointToGroup("LibreOffice", "pdf-to-presentation"); + addEndpointToGroup("LibreOffice", "pdf-to-text"); + addEndpointToGroup("LibreOffice", "pdf-to-html"); + addEndpointToGroup("LibreOffice", "pdf-to-xml"); + + + //OCRmyPDF + addEndpointToGroup("OCRmyPDF", "compress-pdf"); + addEndpointToGroup("OCRmyPDF", "pdf-to-pdfa"); + addEndpointToGroup("OCRmyPDF", "ocr-pdf"); + + //Java + addEndpointToGroup("Java", "merge-pdfs"); + addEndpointToGroup("Java", "remove-pages"); + addEndpointToGroup("Java", "split-pdfs"); + addEndpointToGroup("Java", "pdf-organizer"); + addEndpointToGroup("Java", "rotate-pdf"); + addEndpointToGroup("Java", "pdf-to-img"); + addEndpointToGroup("Java", "img-to-pdf"); + addEndpointToGroup("Java", "add-password"); + addEndpointToGroup("Java", "remove-password"); + addEndpointToGroup("Java", "change-permissions"); + addEndpointToGroup("Java", "add-watermark"); + addEndpointToGroup("Java", "add-image"); + addEndpointToGroup("Java", "extract-images"); + addEndpointToGroup("Java", "change-metadata"); + addEndpointToGroup("Java", "cert-sign"); + addEndpointToGroup("Java", "multi-page-layout"); + addEndpointToGroup("Java", "scale-pages"); + addEndpointToGroup("Java", "add-page-numbers"); + addEndpointToGroup("Java", "auto-rename"); + addEndpointToGroup("Java", "auto-split-pdf"); + addEndpointToGroup("Java", "sanitize-pdf"); + addEndpointToGroup("Java", "crop"); + + //Javascript + addEndpointToGroup("Javascript", "pdf-organizer"); + addEndpointToGroup("Javascript", "sign"); + addEndpointToGroup("Javascript", "compare"); + addEndpointToGroup("Javascript", "adjust-contrast"); + + + } + + private void processEnvironmentConfigs() { + String endpointsToRemove = System.getenv("ENDPOINTS_TO_REMOVE"); + String groupsToRemove = System.getenv("GROUPS_TO_REMOVE"); + + if (endpointsToRemove != null) { + String[] endpoints = endpointsToRemove.split(","); + for (String endpoint : endpoints) { + disableEndpoint(endpoint.trim()); + } + } + + if (groupsToRemove != null) { + String[] groups = groupsToRemove.split(","); + for (String group : groups) { + disableGroup(group.trim()); + } + } + } + +} + diff --git a/src/main/java/stirling/software/SPDF/controller/api/other/PageNumbersController.java b/src/main/java/stirling/software/SPDF/controller/api/other/PageNumbersController.java index 3a4b5b1c..c1454a24 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/other/PageNumbersController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/other/PageNumbersController.java @@ -1,176 +1,176 @@ -package stirling.software.SPDF.controller.api.other; - -import java.io.IOException; -import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestParam; -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 io.swagger.v3.oas.annotations.tags.Tag; -import stirling.software.SPDF.utils.GeneralUtils; -import stirling.software.SPDF.utils.PdfUtils; -import stirling.software.SPDF.utils.WebResponseUtils; -import org.apache.pdfbox.pdmodel.*; -import org.apache.pdfbox.pdmodel.common.*; -import org.apache.pdfbox.pdmodel.PDPageContentStream.*; -import org.springframework.web.bind.annotation.*; -import org.springframework.http.*; -import org.springframework.web.multipart.MultipartFile; -import io.swagger.v3.oas.annotations.*; -import io.swagger.v3.oas.annotations.media.*; -import io.swagger.v3.oas.annotations.parameters.*; -import org.apache.pdfbox.pdmodel.font.PDType1Font; -import org.apache.tomcat.util.http.ResponseUtil; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.net.URLEncoder; -import java.util.List; - -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.multipart.MultipartFile; - -import com.itextpdf.io.font.constants.StandardFonts; -import com.itextpdf.kernel.font.PdfFont; -import com.itextpdf.kernel.font.PdfFontFactory; -import com.itextpdf.kernel.geom.Rectangle; -import com.itextpdf.kernel.pdf.PdfReader; -import com.itextpdf.kernel.pdf.PdfWriter; -import com.itextpdf.kernel.pdf.PdfDocument; -import com.itextpdf.kernel.pdf.PdfPage; -import com.itextpdf.kernel.pdf.canvas.PdfCanvas; -import com.itextpdf.layout.Canvas; -import com.itextpdf.layout.element.Paragraph; -import com.itextpdf.layout.properties.TextAlignment; - -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.media.Schema; - -import java.io.*; - -@RestController -@Tag(name = "Other", description = "Other APIs") -public class PageNumbersController { - - private static final Logger logger = LoggerFactory.getLogger(PageNumbersController.class); - - @PostMapping(value = "/add-page-numbers", consumes = "multipart/form-data") - @Operation(summary = "Add page numbers to a PDF document", description = "This operation takes an input PDF file and adds page numbers to it. Input:PDF Output:PDF Type:SISO") - public ResponseEntity addPageNumbers( - @Parameter(description = "The input PDF file", required = true) @RequestParam("fileInput") MultipartFile file, - @Parameter(description = "Custom margin: small/medium/large", required = true, schema = @Schema(type = "string", allowableValues = {"small", "medium", "large"})) @RequestParam("customMargin") String customMargin, - @Parameter(description = "Position: 1 of 9 positions", required = true, schema = @Schema(type = "integer", minimum = "1", maximum = "9")) @RequestParam("position") int position, - @Parameter(description = "Starting number", required = true, schema = @Schema(type = "integer", minimum = "1")) @RequestParam("startingNumber") int startingNumber, - @Parameter(description = "Which pages to number, default all", required = false, schema = @Schema(type = "string")) @RequestParam(value = "pagesToNumber", required = false) String pagesToNumber, - @Parameter(description = "Custom text: defaults to just number but can have things like \"Page {n} of {p}\"", required = false, schema = @Schema(type = "string")) @RequestParam(value = "customText", required = false) String customText) - throws IOException { - - byte[] fileBytes = file.getBytes(); - ByteArrayInputStream bais = new ByteArrayInputStream(fileBytes); - - int pageNumber = startingNumber; - float marginFactor; - switch (customMargin.toLowerCase()) { - case "small": - marginFactor = 0.01f; - break; - case "medium": - marginFactor = 0.025f; - break; - case "large": - marginFactor = 0.05f; - break; - case "x-large": - marginFactor = 0.1f; - break; - default: - marginFactor = 0.01f; - break; - } - - float fontSize = 12.0f; - - PdfReader reader = new PdfReader(bais); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - PdfWriter writer = new PdfWriter(baos); - - PdfDocument pdfDoc = new PdfDocument(reader, writer); - - List pagesToNumberList = GeneralUtils.parsePageList(pagesToNumber.split(","), pdfDoc.getNumberOfPages()); - - for (int i : pagesToNumberList) { - PdfPage page = pdfDoc.getPage(i+1); - Rectangle pageSize = page.getPageSize(); - PdfCanvas pdfCanvas = new PdfCanvas(page.newContentStreamAfter(), page.getResources(), pdfDoc); - - String text = customText != null ? customText.replace("{n}", String.valueOf(pageNumber)).replace("{total}", String.valueOf(pdfDoc.getNumberOfPages())) : String.valueOf(pageNumber); - - PdfFont font = PdfFontFactory.createFont(StandardFonts.HELVETICA); - float textWidth = font.getWidth(text, fontSize); - float textHeight = font.getAscent(text, fontSize) - font.getDescent(text, fontSize); - - float x, y; - TextAlignment alignment; - - int xGroup = (position - 1) % 3; - int yGroup = 2 - (position - 1) / 3; - - switch (xGroup) { - case 0: // left - x = pageSize.getLeft() + marginFactor * pageSize.getWidth(); - alignment = TextAlignment.LEFT; - break; - case 1: // center - x = pageSize.getLeft() + (pageSize.getWidth()) / 2; - alignment = TextAlignment.CENTER; - break; - default: // right - x = pageSize.getRight() - marginFactor * pageSize.getWidth(); - alignment = TextAlignment.RIGHT; - break; - } - - switch (yGroup) { - case 0: // bottom - y = pageSize.getBottom() + marginFactor * pageSize.getHeight(); - break; - case 1: // middle - y = pageSize.getBottom() + (pageSize.getHeight() ) / 2; - break; - default: // top - y = pageSize.getTop() - marginFactor * pageSize.getHeight(); - break; - } - - new Canvas(pdfCanvas, page.getPageSize()) - .showTextAligned(new Paragraph(text).setFont(font).setFontSize(fontSize), x, y, alignment); - - pageNumber++; - } - - - pdfDoc.close(); - byte[] resultBytes = baos.toByteArray(); - - return ResponseEntity.ok() - .header("Content-Type", "application/pdf; charset=UTF-8") - .header("Content-Disposition", "inline; filename=" + URLEncoder.encode(file.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_numbersAdded.pdf", "UTF-8")) - .body(resultBytes); - } - - - -} +package stirling.software.SPDF.controller.api.other; + +import java.io.IOException; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; +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 io.swagger.v3.oas.annotations.tags.Tag; +import stirling.software.SPDF.utils.GeneralUtils; +import stirling.software.SPDF.utils.PdfUtils; +import stirling.software.SPDF.utils.WebResponseUtils; +import org.apache.pdfbox.pdmodel.*; +import org.apache.pdfbox.pdmodel.common.*; +import org.apache.pdfbox.pdmodel.PDPageContentStream.*; +import org.springframework.web.bind.annotation.*; +import org.springframework.http.*; +import org.springframework.web.multipart.MultipartFile; +import io.swagger.v3.oas.annotations.*; +import io.swagger.v3.oas.annotations.media.*; +import io.swagger.v3.oas.annotations.parameters.*; +import org.apache.pdfbox.pdmodel.font.PDType1Font; +import org.apache.tomcat.util.http.ResponseUtil; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.net.URLEncoder; +import java.util.List; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.multipart.MultipartFile; + +import com.itextpdf.io.font.constants.StandardFonts; +import com.itextpdf.kernel.font.PdfFont; +import com.itextpdf.kernel.font.PdfFontFactory; +import com.itextpdf.kernel.geom.Rectangle; +import com.itextpdf.kernel.pdf.PdfReader; +import com.itextpdf.kernel.pdf.PdfWriter; +import com.itextpdf.kernel.pdf.PdfDocument; +import com.itextpdf.kernel.pdf.PdfPage; +import com.itextpdf.kernel.pdf.canvas.PdfCanvas; +import com.itextpdf.layout.Canvas; +import com.itextpdf.layout.element.Paragraph; +import com.itextpdf.layout.properties.TextAlignment; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Schema; + +import java.io.*; + +@RestController +@Tag(name = "Other", description = "Other APIs") +public class PageNumbersController { + + private static final Logger logger = LoggerFactory.getLogger(PageNumbersController.class); + + @PostMapping(value = "/add-page-numbers", consumes = "multipart/form-data") + @Operation(summary = "Add page numbers to a PDF document", description = "This operation takes an input PDF file and adds page numbers to it. Input:PDF Output:PDF Type:SISO") + public ResponseEntity addPageNumbers( + @Parameter(description = "The input PDF file", required = true) @RequestParam("fileInput") MultipartFile file, + @Parameter(description = "Custom margin: small/medium/large", required = true, schema = @Schema(type = "string", allowableValues = {"small", "medium", "large"})) @RequestParam("customMargin") String customMargin, + @Parameter(description = "Position: 1 of 9 positions", required = true, schema = @Schema(type = "integer", minimum = "1", maximum = "9")) @RequestParam("position") int position, + @Parameter(description = "Starting number", required = true, schema = @Schema(type = "integer", minimum = "1")) @RequestParam("startingNumber") int startingNumber, + @Parameter(description = "Which pages to number, default all", required = false, schema = @Schema(type = "string")) @RequestParam(value = "pagesToNumber", required = false) String pagesToNumber, + @Parameter(description = "Custom text: defaults to just number but can have things like \"Page {n} of {p}\"", required = false, schema = @Schema(type = "string")) @RequestParam(value = "customText", required = false) String customText) + throws IOException { + + byte[] fileBytes = file.getBytes(); + ByteArrayInputStream bais = new ByteArrayInputStream(fileBytes); + + int pageNumber = startingNumber; + float marginFactor; + switch (customMargin.toLowerCase()) { + case "small": + marginFactor = 0.02f; + break; + case "medium": + marginFactor = 0.035f; + break; + case "large": + marginFactor = 0.05f; + break; + case "x-large": + marginFactor = 0.1f; + break; + default: + marginFactor = 0.035f; + break; + } + + float fontSize = 12.0f; + + PdfReader reader = new PdfReader(bais); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + PdfWriter writer = new PdfWriter(baos); + + PdfDocument pdfDoc = new PdfDocument(reader, writer); + + List pagesToNumberList = GeneralUtils.parsePageList(pagesToNumber.split(","), pdfDoc.getNumberOfPages()); + + for (int i : pagesToNumberList) { + PdfPage page = pdfDoc.getPage(i+1); + Rectangle pageSize = page.getPageSize(); + PdfCanvas pdfCanvas = new PdfCanvas(page.newContentStreamAfter(), page.getResources(), pdfDoc); + + String text = customText != null ? customText.replace("{n}", String.valueOf(pageNumber)).replace("{total}", String.valueOf(pdfDoc.getNumberOfPages())) : String.valueOf(pageNumber); + + PdfFont font = PdfFontFactory.createFont(StandardFonts.HELVETICA); + float textWidth = font.getWidth(text, fontSize); + float textHeight = font.getAscent(text, fontSize) - font.getDescent(text, fontSize); + + float x, y; + TextAlignment alignment; + + int xGroup = (position - 1) % 3; + int yGroup = 2 - (position - 1) / 3; + + switch (xGroup) { + case 0: // left + x = pageSize.getLeft() + marginFactor * pageSize.getWidth(); + alignment = TextAlignment.LEFT; + break; + case 1: // center + x = pageSize.getLeft() + (pageSize.getWidth()) / 2; + alignment = TextAlignment.CENTER; + break; + default: // right + x = pageSize.getRight() - marginFactor * pageSize.getWidth(); + alignment = TextAlignment.RIGHT; + break; + } + + switch (yGroup) { + case 0: // bottom + y = pageSize.getBottom() + marginFactor * pageSize.getHeight(); + break; + case 1: // middle + y = pageSize.getBottom() + (pageSize.getHeight() ) / 2; + break; + default: // top + y = pageSize.getTop() - marginFactor * pageSize.getHeight(); + break; + } + + new Canvas(pdfCanvas, page.getPageSize()) + .showTextAligned(new Paragraph(text).setFont(font).setFontSize(fontSize), x, y, alignment); + + pageNumber++; + } + + + pdfDoc.close(); + byte[] resultBytes = baos.toByteArray(); + + return ResponseEntity.ok() + .header("Content-Type", "application/pdf; charset=UTF-8") + .header("Content-Disposition", "inline; filename=" + URLEncoder.encode(file.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_numbersAdded.pdf", "UTF-8")) + .body(resultBytes); + } + + + +} 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 new file mode 100644 index 00000000..ccc19b09 --- /dev/null +++ b/src/main/java/stirling/software/SPDF/controller/api/security/SanitizeController.java @@ -0,0 +1,140 @@ +package stirling.software.SPDF.controller.api.security; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.pdfbox.pdmodel.PDResources; +import org.apache.pdfbox.pdmodel.PDPageTree; +import org.apache.pdfbox.pdmodel.common.PDMetadata; +import org.apache.pdfbox.pdmodel.common.PDStream; +import org.apache.pdfbox.pdmodel.interactive.action.*; +import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotation; +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.apache.pdfbox.pdmodel.interactive.form.PDNonTerminalField; +import org.apache.pdfbox.pdmodel.interactive.form.PDTerminalField; +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 io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Schema; +import stirling.software.SPDF.utils.WebResponseUtils; + +import java.io.IOException; +import java.io.InputStream; + +@RestController +public class SanitizeController { + + @PostMapping(consumes = "multipart/form-data", value = "/sanitize-pdf") + @Operation(summary = "Sanitize a PDF file", + description = "This endpoint processes a PDF file and removes specific elements based on the provided options. Input:PDF Output:PDF Type:SISO") + public ResponseEntity sanitizePDF( + @RequestPart(required = true, value = "fileInput") + @Parameter(description = "The input PDF file to be sanitized") + MultipartFile inputFile, + @RequestParam(name = "removeJavaScript", required = false, defaultValue = "true") + @Parameter(description = "Remove JavaScript actions from the PDF if set to true") + Boolean removeJavaScript, + @RequestParam(name = "removeEmbeddedFiles", required = false, defaultValue = "true") + @Parameter(description = "Remove embedded files from the PDF if set to true") + Boolean removeEmbeddedFiles, + @RequestParam(name = "removeMetadata", required = false, defaultValue = "true") + @Parameter(description = "Remove metadata from the PDF if set to true") + Boolean removeMetadata, + @RequestParam(name = "removeLinks", required = false, defaultValue = "true") + @Parameter(description = "Remove links from the PDF if set to true") + Boolean removeLinks, + @RequestParam(name = "removeFonts", required = false, defaultValue = "true") + @Parameter(description = "Remove fonts from the PDF if set to true") + Boolean removeFonts) throws IOException { + + try (PDDocument document = PDDocument.load(inputFile.getInputStream())) { + if (removeJavaScript) { + sanitizeJavaScript(document); + } + + if (removeEmbeddedFiles) { + sanitizeEmbeddedFiles(document); + } + + if (removeMetadata) { + sanitizeMetadata(document); + } + + if (removeLinks) { + sanitizeLinks(document); + } + + if (removeFonts) { + sanitizeFonts(document); + } + + return WebResponseUtils.pdfDocToWebResponse(document, inputFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_sanitized.pdf"); + } + } + private void sanitizeJavaScript(PDDocument document) throws IOException { + for (PDPage page : document.getPages()) { + for (PDAnnotation annotation : page.getAnnotations()) { + if (annotation instanceof PDAnnotationWidget) { + PDAnnotationWidget widget = (PDAnnotationWidget) annotation; + PDAction action = widget.getAction(); + if (action instanceof PDActionJavaScript) { + widget.setAction(null); + } + } + } + PDAcroForm acroForm = document.getDocumentCatalog().getAcroForm(); + if (acroForm != null) { + for (PDField field : acroForm.getFields()) { + if (field.getActions().getF() instanceof PDActionJavaScript) { + field.getActions().setF(null); + } + } + } + } + } + + private void sanitizeEmbeddedFiles(PDDocument document) { + PDPageTree allPages = document.getPages(); + + for (PDPage page : allPages) { + PDResources res = page.getResources(); + + // Remove embedded files from the PDF + res.getCOSObject().removeItem(COSName.getPDFName("EmbeddedFiles")); + } + } + + + private void sanitizeMetadata(PDDocument document) { + PDMetadata metadata = document.getDocumentCatalog().getMetadata(); + if (metadata != null) { + document.getDocumentCatalog().setMetadata(null); + } + } + + + + private void sanitizeLinks(PDDocument document) throws IOException { + for (PDPage page : document.getPages()) { + for (PDAnnotation annotation : page.getAnnotations()) { + if (annotation instanceof PDAnnotationLink) { + PDAction action = ((PDAnnotationLink) annotation).getAction(); + if (action instanceof PDActionLaunch || action instanceof PDActionURI) { + ((PDAnnotationLink) annotation).setAction(null); + } + } + } + } + } + + private void sanitizeFonts(PDDocument document) { + for (PDPage page : document.getPages()) { + page.getResources().getCOSObject().removeItem(COSName.getPDFName("Font")); + } + } + +} 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 0dbb226b..54f88618 100644 --- a/src/main/java/stirling/software/SPDF/controller/web/SecurityWebController.java +++ b/src/main/java/stirling/software/SPDF/controller/web/SecurityWebController.java @@ -1,46 +1,53 @@ -package stirling.software.SPDF.controller.web; - -import org.springframework.stereotype.Controller; -import org.springframework.ui.Model; -import org.springframework.web.bind.annotation.GetMapping; - -import io.swagger.v3.oas.annotations.Hidden; -import io.swagger.v3.oas.annotations.tags.Tag; - -@Controller -@Tag(name = "Security", description = "Security APIs") -public class SecurityWebController { - @GetMapping("/add-password") - @Hidden - public String addPasswordForm(Model model) { - model.addAttribute("currentPage", "add-password"); - return "security/add-password"; - } - @GetMapping("/change-permissions") - @Hidden - public String permissionsForm(Model model) { - model.addAttribute("currentPage", "change-permissions"); - return "security/change-permissions"; - } - - @GetMapping("/remove-password") - @Hidden - public String removePasswordForm(Model model) { - model.addAttribute("currentPage", "remove-password"); - return "security/remove-password"; - } - - @GetMapping("/add-watermark") - @Hidden - public String addWatermarkForm(Model model) { - model.addAttribute("currentPage", "add-watermark"); - return "security/add-watermark"; - } - - @GetMapping("/cert-sign") - @Hidden - public String certSignForm(Model model) { - model.addAttribute("currentPage", "cert-sign"); - return "security/cert-sign"; - } -} +package stirling.software.SPDF.controller.web; + +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; + +import io.swagger.v3.oas.annotations.Hidden; +import io.swagger.v3.oas.annotations.tags.Tag; + +@Controller +@Tag(name = "Security", description = "Security APIs") +public class SecurityWebController { + @GetMapping("/add-password") + @Hidden + public String addPasswordForm(Model model) { + model.addAttribute("currentPage", "add-password"); + return "security/add-password"; + } + @GetMapping("/change-permissions") + @Hidden + public String permissionsForm(Model model) { + model.addAttribute("currentPage", "change-permissions"); + return "security/change-permissions"; + } + + @GetMapping("/remove-password") + @Hidden + public String removePasswordForm(Model model) { + model.addAttribute("currentPage", "remove-password"); + return "security/remove-password"; + } + + @GetMapping("/add-watermark") + @Hidden + public String addWatermarkForm(Model model) { + model.addAttribute("currentPage", "add-watermark"); + return "security/add-watermark"; + } + + @GetMapping("/cert-sign") + @Hidden + public String certSignForm(Model model) { + model.addAttribute("currentPage", "cert-sign"); + return "security/cert-sign"; + } + + @GetMapping("/sanitize-pdf") + @Hidden + public String sanitizeForm(Model model) { + model.addAttribute("currentPage", ""); + return "security/sanitize-pdf"; + } +} diff --git a/src/main/resources/messages_en_GB.properties b/src/main/resources/messages_en_GB.properties index 0da26ea7..c16aa404 100644 --- a/src/main/resources/messages_en_GB.properties +++ b/src/main/resources/messages_en_GB.properties @@ -152,6 +152,10 @@ home.crop.desc=Crop a PDF to reduce its size (maintains text!) home.autoSplitPDF.title=Auto Split Pages home.autoSplitPDF.desc=Auto Split Scanned PDF with physical scanned page splitter QR Code +home.sanitizePdf.title=Sanitize +home.sanitizePdf.desc=Remove scripts and other elements from PDF files + + error.pdfPassword=The PDF Document is passworded and either the password was not provided or was incorrect downloadPdf=Download PDF @@ -159,6 +163,49 @@ text=Text font=Font selectFillter=-- Select -- pageNum=Page Number +sizes.small=Small +sizes.medium=Medium +sizes.large=Large +sizes.x-large=X-Large + +sanitizePDF.title=Sanitize PDF +sanitizePDF.header=Sanitize a PDF file +sanitizePDF.selectText.1=Remove JavaScript actions +sanitizePDF.selectText.2=Remove embedded files +sanitizePDF.selectText.3=Remove metadata +sanitizePDF.selectText.4=Remove links +sanitizePDF.selectText.5=Remove fonts +sanitizePDF.submit=Sanitize PDF + +addPageNumbers.title=Add Page Numbers +addPageNumbers.header=Add Page Numbers +addPageNumbers.selectText.1=Select PDF file: +addPageNumbers.selectText.2=Margin Size +addPageNumbers.selectText.3=Position +addPageNumbers.selectText.4=Starting Number +addPageNumbers.selectText.5=Pages to Number +addPageNumbers.selectText.6=Custom Text +addPageNumbers.submit=Add Page Numbers + +auto-rename.title=Auto Rename +auto-rename.header=Auto Rename PDF +auto-rename.submit=Auto Rename + +adjustContrast.title=Adjust Contrast +adjustContrast.header=Adjust Contrast +adjustContrast.contrast=Contrast: +adjustContrast.brightness=Brightness: +adjustContrast.saturation=Saturation: +adjustContrast.download=Download + +crop.title=Crop +crop.header=Crop Image +crop.submit=Submit + +autoSplitPDF.title=Auto Split PDF +autoSplitPDF.header=Auto Split PDF +autoSplitPDF.submit=Submit + pipeline.title=Pipeline diff --git a/src/main/resources/static/images/sanitize.svg b/src/main/resources/static/images/sanitize.svg new file mode 100644 index 00000000..fc4dd2f9 --- /dev/null +++ b/src/main/resources/static/images/sanitize.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/resources/templates/crop.html b/src/main/resources/templates/crop.html index b3abeba3..a0e958ea 100644 --- a/src/main/resources/templates/crop.html +++ b/src/main/resources/templates/crop.html @@ -1,105 +1,103 @@ - - - - - - - -
-
-
-

-
-
-
-

-
- - - - - - -
- - - -
-
-
-
-
-
- - \ No newline at end of file + + + + + + + +
+
+
+

+
+
+
+

+
+
+ + + + + +
+ + + +
+
+
+
+
+
+ + diff --git a/src/main/resources/templates/fragments/navbar.html b/src/main/resources/templates/fragments/navbar.html index 18425f11..84a1e371 100644 --- a/src/main/resources/templates/fragments/navbar.html +++ b/src/main/resources/templates/fragments/navbar.html @@ -40,7 +40,7 @@ --> -