Skip to content

Commit

Permalink
Merge pull request #221 from LaserKaspar/main
Browse files Browse the repository at this point in the history
Scale Pages
  • Loading branch information
Frooodle committed May 31, 2023
2 parents 005b158 + 6015591 commit a43c296
Show file tree
Hide file tree
Showing 8 changed files with 149 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ public void init() {
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");
Expand Down
Original file line number Diff line number Diff line change
@@ -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<byte[]> 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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,11 @@ public String multiPageLayoutForm(Model model) {
return "other/multi-page-layout";
}


@GetMapping("/scale-pages")
@Hidden
public String scalePagesFrom(Model model) {
model.addAttribute("currentPage", "scale-pages");
return "other/scale-pages";
}

}
9 changes: 9 additions & 0 deletions src/main/resources/messages_en_GB.properties
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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:
Expand Down
3 changes: 3 additions & 0 deletions src/main/resources/static/images/scale-pages.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/main/resources/templates/fragments/navbar.html
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@
<div th:replace="~{fragments/navbarEntry :: navbarEntry ( 'rotate-pdf', 'images/arrow-clockwise.svg', 'home.rotate.title', 'home.rotate.desc')}"></div>
<div th:replace="~{fragments/navbarEntry :: navbarEntry ( 'remove-pages', 'images/file-earmark-x.svg', 'home.removePages.title', 'home.removePages.desc')}"></div>
<div th:replace="~{fragments/navbarEntry :: navbarEntry ( 'multi-page-layout', 'images/page-layout.svg', 'home.pageLayout.title', 'home.pageLayout.desc')}"></div>
<div th:replace="~{fragments/navbarEntry :: navbarEntry ( 'scale-pages', 'images/scale-pages.svg', 'home.scalePages.title', 'home.scalePages.desc')}"></div>

</div>
</li>
Expand Down
3 changes: 2 additions & 1 deletion src/main/resources/templates/home.html
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,8 @@ <h1 class="display-4" th:text="${@appName}"></h1>

<div th:replace="~{fragments/card :: card(id='cert-sign', cardTitle=#{home.certSign.title}, cardText=#{home.certSign.desc}, cardLink='cert-sign', svgPath='images/award.svg')}"></div>
<div th:replace="~{fragments/card :: card(id='multi-page-layout', cardTitle=#{home.pageLayout.title}, cardText=#{home.pageLayout.desc}, cardLink='multi-page-layout', svgPath='images/page-layout.svg')}"></div>

<div th:replace="~{fragments/card :: card(id='scale-pages', cardTitle=#{home.scalePages.title}, cardText=#{home.scalePages.desc}, cardLink='scale-pages', svgPath='images/scale-pages.svg')}"></div>



<script>
Expand Down
39 changes: 39 additions & 0 deletions src/main/resources/templates/other/scale-pages.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<!DOCTYPE html>
<html th:lang="${#locale.toString()}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">


<th:block th:insert="~{fragments/common :: head(title=#{pageLayout.title})}"></th:block>


<body>
<div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
<br> <br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6">
<h2 th:text="#{scalePages.header}"></h2>
<form id="scalePagesFrom" th:action="@{scale-pages}" method="post" enctype="multipart/form-data">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
<div class="form-group">
<label for="pageSize" th:text="#{scalePages.pageSize}"></label>
<select id="pageSize" name="pageSize" required>
<option value="A4">A4</option>
</select>
</div>
<div class="form-group">
<label for="scaleFactor" th:text="#{scalePages.scaleFactor}"></label>
<input type="number" id="scaleFactor" name="scaleFactor" step="any" min="0" value="1">
</div>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{scalePages.submit}"></button>
</form>
</div>
</div>
</div>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
</div>
</body>

</html>

0 comments on commit a43c296

Please sign in to comment.