diff --git a/src/main/java/stirling/software/SPDF/config/EndpointConfiguration.java b/src/main/java/stirling/software/SPDF/config/EndpointConfiguration.java index 7f429756..686a6b38 100644 --- a/src/main/java/stirling/software/SPDF/config/EndpointConfiguration.java +++ b/src/main/java/stirling/software/SPDF/config/EndpointConfiguration.java @@ -65,6 +65,7 @@ public class EndpointConfiguration { 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"); diff --git a/src/main/java/stirling/software/SPDF/controller/api/other/ScalePagesController.java b/src/main/java/stirling/software/SPDF/controller/api/other/ScalePagesController.java new file mode 100644 index 00000000..8db0fa80 --- /dev/null +++ b/src/main/java/stirling/software/SPDF/controller/api/other/ScalePagesController.java @@ -0,0 +1,88 @@ +package stirling.software.SPDF.controller.api.other; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpHeaders; +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.RestController; +import org.springframework.web.multipart.MultipartFile; + +import com.itextpdf.kernel.geom.PageSize; +import com.itextpdf.kernel.geom.Rectangle; +import com.itextpdf.kernel.pdf.PdfDocument; +import com.itextpdf.kernel.pdf.PdfPage; +import com.itextpdf.kernel.pdf.PdfReader; +import com.itextpdf.kernel.pdf.PdfWriter; +import com.itextpdf.kernel.pdf.canvas.PdfCanvas; +import com.itextpdf.kernel.pdf.xobject.PdfFormXObject; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Schema; + +@RestController +public class ScalePagesController { + + private static final Logger logger = LoggerFactory.getLogger(ScalePagesController.class); + + @PostMapping(value = "/scale-pages", consumes = "multipart/form-data") + @Operation(summary = "Change the size of a PDF page/document", description = "This operation takes an input PDF file and the size to scale the pages to in the output PDF file.") + public ResponseEntity mergeMultiplePagesIntoOne( + @Parameter(description = "The input PDF file", required = true) @RequestParam("fileInput") MultipartFile file, + @Parameter(description = "The scale of pages in the output PDF. Acceptable values are A4.", required = true, schema = @Schema(type = "String", allowableValues = { "A4" })) @RequestParam("pageSize") String targetPageSize, + @Parameter(description = "The scale of the content on the pages of the output PDF. Acceptable values are floats.", required = true, schema = @Schema(type = "float")) @RequestParam("scaleFactor") float scaleFactor) + throws IOException { + + if (!targetPageSize.equals("A4")) { + throw new IllegalArgumentException("pageSize must be A4"); + } + + byte[] bytes = file.getBytes(); + PdfReader reader = new PdfReader(new ByteArrayInputStream(bytes)); + PdfDocument pdfDoc = new PdfDocument(reader); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + PdfWriter writer = new PdfWriter(baos); + PdfDocument outputPdf = new PdfDocument(writer); + + PageSize pageSize = new PageSize(PageSize.A4); // TODO: This (and all other PageSize.A4) need to be dynamically changed in response to targetPageSize + + int totalPages = pdfDoc.getNumberOfPages(); + + for (int i = 1; i <= totalPages; i++) { + PdfPage page = outputPdf.addNewPage(pageSize); + PdfCanvas pdfCanvas = new PdfCanvas(page); + + // Get the page and calculate scaling factors + Rectangle rect = pdfDoc.getPage(i).getPageSize(); + float scaleWidth = PageSize.A4.getWidth() / rect.getWidth(); + float scaleHeight = PageSize.A4.getHeight() / rect.getHeight(); + float scale = Math.min(scaleWidth, scaleHeight) * scaleFactor; + System.out.println("Scale: " + scale); + + PdfFormXObject formXObject = pdfDoc.getPage(i).copyAsFormXObject(outputPdf); + float x = (PageSize.A4.getWidth() - rect.getWidth() * scale) / 2; // Center Page + float y = (PageSize.A4.getHeight() - rect.getHeight() * scale) / 2; + + // Save the graphics state, apply the transformations, add the object, and then + // restore the graphics state + pdfCanvas.saveState(); + pdfCanvas.concatMatrix(scale, 0, 0, scale, x, y); + pdfCanvas.addXObject(formXObject, 0, 0); + pdfCanvas.restoreState(); + } + + outputPdf.close(); + byte[] pdfContent = baos.toByteArray(); + pdfDoc.close(); + return ResponseEntity.ok() + .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + file.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_modified.pdf\"") + .body(pdfContent); + } +} diff --git a/src/main/java/stirling/software/SPDF/controller/web/OtherWebController.java b/src/main/java/stirling/software/SPDF/controller/web/OtherWebController.java index e86fb764..95cef9e7 100644 --- a/src/main/java/stirling/software/SPDF/controller/web/OtherWebController.java +++ b/src/main/java/stirling/software/SPDF/controller/web/OtherWebController.java @@ -115,6 +115,11 @@ public class OtherWebController { return "other/multi-page-layout"; } - + @GetMapping("/scale-pages") + @Hidden + public String scalePagesFrom(Model model) { + model.addAttribute("currentPage", "scale-pages"); + return "other/scale-pages"; + } } diff --git a/src/main/resources/messages_en_GB.properties b/src/main/resources/messages_en_GB.properties index b4c5bca1..b8c634f2 100644 --- a/src/main/resources/messages_en_GB.properties +++ b/src/main/resources/messages_en_GB.properties @@ -131,6 +131,9 @@ home.certSign.desc=Signs a PDF with a Certificate/Key (PEM/P12) home.pageLayout.title=Multi-Page Layout home.pageLayout.desc=Merge multiple pages of a PDF document into a single page +home.scalePages.title=Adjust page-scale +home.scalePages.desc=Change the size of the pages of a PDF document + error.pdfPassword=The PDF Document is passworded and either the password was not provided or was incorrect downloadPdf=Download PDF @@ -144,6 +147,12 @@ pageLayout.header=Multi Page Layout pageLayout.pagesPerSheet=Pages per sheet: pageLayout.submit=Submit +scalePages.title=Adjust page-scale +scalePages.header=Adjust page-scale +scalePages.pageSize=Size of a page of the document. +scalePages.scaleFactor=Zoom level (crop) of a page. +scalePages.submit=Submit + certSign.title=Certificate Signing certSign.header=Sign a PDF with your certificate (Work in progress) certSign.selectPDF=Select a PDF File for Signing: diff --git a/src/main/resources/static/images/scale-pages.svg b/src/main/resources/static/images/scale-pages.svg new file mode 100644 index 00000000..bc505957 --- /dev/null +++ b/src/main/resources/static/images/scale-pages.svg @@ -0,0 +1,3 @@ + + + \ 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 fa75aa1a..275f99f1 100644 --- a/src/main/resources/templates/fragments/navbar.html +++ b/src/main/resources/templates/fragments/navbar.html @@ -157,6 +157,7 @@ function compareVersions(version1, version2) {
+
diff --git a/src/main/resources/templates/home.html b/src/main/resources/templates/home.html index 5d40ee9d..659220bf 100644 --- a/src/main/resources/templates/home.html +++ b/src/main/resources/templates/home.html @@ -137,7 +137,8 @@ filter: invert(0.2) sepia(2) saturate(50) hue-rotate(190deg);
- +
+