mirror of
				https://github.com/Frooodle/Stirling-PDF.git
				synced 2025-10-25 11:17:28 +02:00 
			
		
		
		
	
						commit
						a43c296eef
					
				| @ -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"); | ||||
|  | ||||
| @ -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); | ||||
| 	} | ||||
| } | ||||
| @ -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"; | ||||
|     } | ||||
|      | ||||
| } | ||||
|  | ||||
| @ -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:  | ||||
|  | ||||
							
								
								
									
										3
									
								
								src/main/resources/static/images/scale-pages.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								src/main/resources/static/images/scale-pages.svg
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-grid" viewBox="0 0 16 16"> | ||||
|   <path d="M1 2.5A1.5 1.5 0 0 1 2.5 1h3A1.5 1.5 0 0 1 7 2.5v3A1.5 1.5 0 0 1 5.5 7h-3A1.5 1.5 0 0 1 1 5.5v-3zM2.5 2a.5.5 0 0 0-.5.5v3a.5.5 0 0 0 .5.5h3a.5.5 0 0 0 .5-.5v-3a.5.5 0 0 0-.5-.5h-3zm6.5.5A1.5 1.5 0 0 1 10.5 1h3A1.5 1.5 0 0 1 15 2.5v3A1.5 1.5 0 0 1 13.5 7h-3A1.5 1.5 0 0 1 9 5.5v-3zm1.5-.5a.5.5 0 0 0-.5.5v3a.5.5 0 0 0 .5.5h3a.5.5 0 0 0 .5-.5v-3a.5.5 0 0 0-.5-.5h-3zM1 10.5A1.5 1.5 0 0 1 2.5 9h3A1.5 1.5 0 0 1 7 10.5v3A1.5 1.5 0 0 1 5.5 15h-3A1.5 1.5 0 0 1 1 13.5v-3zm1.5-.5a.5.5 0 0 0-.5.5v3a.5.5 0 0 0 .5.5h3a.5.5 0 0 0 .5-.5v-3a.5.5 0 0 0-.5-.5h-3zm6.5.5A1.5 1.5 0 0 1 10.5 9h3a1.5 1.5 0 0 1 1.5 1.5v3a1.5 1.5 0 0 1-1.5 1.5h-3A1.5 1.5 0 0 1 9 13.5v-3zm1.5-.5a.5.5 0 0 0-.5.5v3a.5.5 0 0 0 .5.5h3a.5.5 0 0 0 .5-.5v-3a.5.5 0 0 0-.5-.5h-3z"/> | ||||
| </svg> | ||||
| After Width: | Height: | Size: 880 B | 
| @ -157,6 +157,7 @@ function compareVersions(version1, version2) { | ||||
|         <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> | ||||
|  | ||||
| @ -137,7 +137,8 @@ filter: invert(0.2) sepia(2) saturate(50) hue-rotate(190deg); | ||||
|                  | ||||
|                 <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> | ||||
|  | ||||
							
								
								
									
										39
									
								
								src/main/resources/templates/other/scale-pages.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								src/main/resources/templates/other/scale-pages.html
									
									
									
									
									
										Normal file
									
								
							| @ -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> | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user