Merge branch 'main' into dependabot/gradle/org.springframework.boot-spring-boot-starter-web-3.1.0
This commit is contained in:
commit
602df08df5
37 changed files with 733 additions and 400 deletions
|
@ -65,6 +65,7 @@ public class EndpointConfiguration {
|
||||||
addEndpointToGroup("PageOps", "pdf-organizer");
|
addEndpointToGroup("PageOps", "pdf-organizer");
|
||||||
addEndpointToGroup("PageOps", "rotate-pdf");
|
addEndpointToGroup("PageOps", "rotate-pdf");
|
||||||
addEndpointToGroup("PageOps", "multi-page-layout");
|
addEndpointToGroup("PageOps", "multi-page-layout");
|
||||||
|
addEndpointToGroup("PageOps", "scale-pages");
|
||||||
|
|
||||||
// Adding endpoints to "Convert" group
|
// Adding endpoints to "Convert" group
|
||||||
addEndpointToGroup("Convert", "pdf-to-img");
|
addEndpointToGroup("Convert", "pdf-to-img");
|
||||||
|
@ -164,7 +165,7 @@ public class EndpointConfiguration {
|
||||||
addEndpointToGroup("Java", "change-metadata");
|
addEndpointToGroup("Java", "change-metadata");
|
||||||
addEndpointToGroup("Java", "cert-sign");
|
addEndpointToGroup("Java", "cert-sign");
|
||||||
addEndpointToGroup("Java", "multi-page-layout");
|
addEndpointToGroup("Java", "multi-page-layout");
|
||||||
|
addEndpointToGroup("Java", "scale-pages");
|
||||||
|
|
||||||
|
|
||||||
//Javascript
|
//Javascript
|
||||||
|
|
|
@ -17,7 +17,7 @@ import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.Parameter;
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
import stirling.software.SPDF.utils.PdfUtils;
|
import stirling.software.SPDF.utils.WebResponseUtils;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
public class MergeController {
|
public class MergeController {
|
||||||
|
@ -65,7 +65,7 @@ public class MergeController {
|
||||||
|
|
||||||
|
|
||||||
// Return the merged PDF as a response
|
// Return the merged PDF as a response
|
||||||
ResponseEntity<byte[]> response = PdfUtils.pdfDocToWebResponse(mergedDoc, files[0].getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_merged.pdf");
|
ResponseEntity<byte[]> response = WebResponseUtils.pdfDocToWebResponse(mergedDoc, files[0].getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_merged.pdf");
|
||||||
|
|
||||||
for (PDDocument doc : documents) {
|
for (PDDocument doc : documents) {
|
||||||
// Close the document after processing
|
// Close the document after processing
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package stirling.software.SPDF.controller.api.other;
|
package stirling.software.SPDF.controller.api;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
|
@ -6,7 +6,6 @@ import java.io.IOException;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.http.HttpHeaders;
|
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
@ -25,6 +24,7 @@ import com.itextpdf.kernel.pdf.xobject.PdfFormXObject;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.Parameter;
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import stirling.software.SPDF.utils.WebResponseUtils;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
public class MultiPageLayoutController {
|
public class MultiPageLayoutController {
|
||||||
|
@ -92,9 +92,8 @@ public class MultiPageLayoutController {
|
||||||
outputPdf.close();
|
outputPdf.close();
|
||||||
byte[] pdfContent = baos.toByteArray();
|
byte[] pdfContent = baos.toByteArray();
|
||||||
pdfDoc.close();
|
pdfDoc.close();
|
||||||
return ResponseEntity.ok()
|
|
||||||
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"modifiedDocument.pdf\"")
|
return WebResponseUtils.bytesToWebResponse(pdfContent, file.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_layoutChanged.pdf");
|
||||||
.body(pdfContent);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -2,6 +2,8 @@ package stirling.software.SPDF.controller.api;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import stirling.software.SPDF.utils.WebResponseUtils;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
@ -18,7 +20,6 @@ import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.Parameter;
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
import stirling.software.SPDF.utils.PdfUtils;
|
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
public class RearrangePagesPDFController {
|
public class RearrangePagesPDFController {
|
||||||
|
@ -48,7 +49,7 @@ public class RearrangePagesPDFController {
|
||||||
int pageIndex = pagesToRemove.get(i);
|
int pageIndex = pagesToRemove.get(i);
|
||||||
document.removePage(pageIndex);
|
document.removePage(pageIndex);
|
||||||
}
|
}
|
||||||
return PdfUtils.pdfDocToWebResponse(document, pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_removed_pages.pdf");
|
return WebResponseUtils.pdfDocToWebResponse(document, pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_removed_pages.pdf");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -239,7 +240,7 @@ public class RearrangePagesPDFController {
|
||||||
document.addPage(page);
|
document.addPage(page);
|
||||||
}
|
}
|
||||||
|
|
||||||
return PdfUtils.pdfDocToWebResponse(document, pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_rearranged.pdf");
|
return WebResponseUtils.pdfDocToWebResponse(document, pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_rearranged.pdf");
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
logger.error("Failed rearranging documents", e);
|
logger.error("Failed rearranging documents", e);
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -16,7 +16,7 @@ import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.Parameter;
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
import stirling.software.SPDF.utils.PdfUtils;
|
import stirling.software.SPDF.utils.WebResponseUtils;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
public class RotationController {
|
public class RotationController {
|
||||||
|
@ -46,7 +46,7 @@ public class RotationController {
|
||||||
page.setRotation(page.getRotation() + angle);
|
page.setRotation(page.getRotation() + angle);
|
||||||
}
|
}
|
||||||
|
|
||||||
return PdfUtils.pdfDocToWebResponse(document, pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_rotated.pdf");
|
return WebResponseUtils.pdfDocToWebResponse(document, pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_rotated.pdf");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,87 @@
|
||||||
|
package stirling.software.SPDF.controller.api;
|
||||||
|
|
||||||
|
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;
|
||||||
|
import stirling.software.SPDF.utils.WebResponseUtils;
|
||||||
|
|
||||||
|
@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 WebResponseUtils.bytesToWebResponse(pdfContent, file.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_scaled.pdf");
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,6 +21,7 @@ import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.Parameter;
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import stirling.software.SPDF.utils.PdfUtils;
|
import stirling.software.SPDF.utils.PdfUtils;
|
||||||
|
import stirling.software.SPDF.utils.WebResponseUtils;
|
||||||
@RestController
|
@RestController
|
||||||
public class ConvertImgPDFController {
|
public class ConvertImgPDFController {
|
||||||
|
|
||||||
|
@ -98,7 +99,7 @@ public class ConvertImgPDFController {
|
||||||
boolean autoRotate) throws IOException {
|
boolean autoRotate) throws IOException {
|
||||||
// Convert the file to PDF and get the resulting bytes
|
// Convert the file to PDF and get the resulting bytes
|
||||||
byte[] bytes = PdfUtils.imageToPdf(file, stretchToFit, autoRotate, colorType);
|
byte[] bytes = PdfUtils.imageToPdf(file, stretchToFit, autoRotate, colorType);
|
||||||
return PdfUtils.bytesToWebResponse(bytes, file[0].getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_coverted.pdf");
|
return WebResponseUtils.bytesToWebResponse(bytes, file[0].getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_coverted.pdf");
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getMediaType(String imageFormat) {
|
private String getMediaType(String imageFormat) {
|
||||||
|
|
|
@ -17,8 +17,8 @@ import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.Parameter;
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
import stirling.software.SPDF.utils.PdfUtils;
|
|
||||||
import stirling.software.SPDF.utils.ProcessExecutor;
|
import stirling.software.SPDF.utils.ProcessExecutor;
|
||||||
|
import stirling.software.SPDF.utils.WebResponseUtils;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
public class ConvertOfficeController {
|
public class ConvertOfficeController {
|
||||||
|
@ -72,7 +72,7 @@ public class ConvertOfficeController {
|
||||||
// LibreOfficeListener.getInstance().start();
|
// LibreOfficeListener.getInstance().start();
|
||||||
|
|
||||||
byte[] pdfByteArray = convertToPdf(inputFile);
|
byte[] pdfByteArray = convertToPdf(inputFile);
|
||||||
return PdfUtils.bytesToWebResponse(pdfByteArray, inputFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_convertedToPDF.pdf");
|
return WebResponseUtils.bytesToWebResponse(pdfByteArray, inputFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_convertedToPDF.pdf");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,8 +14,8 @@ import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.Parameter;
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
import stirling.software.SPDF.utils.PdfUtils;
|
|
||||||
import stirling.software.SPDF.utils.ProcessExecutor;
|
import stirling.software.SPDF.utils.ProcessExecutor;
|
||||||
|
import stirling.software.SPDF.utils.WebResponseUtils;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
public class ConvertPDFToPDFA {
|
public class ConvertPDFToPDFA {
|
||||||
|
@ -58,7 +58,7 @@ public class ConvertPDFToPDFA {
|
||||||
|
|
||||||
// Return the optimized PDF as a response
|
// Return the optimized PDF as a response
|
||||||
String outputFilename = inputFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_PDFA.pdf";
|
String outputFilename = inputFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_PDFA.pdf";
|
||||||
return PdfUtils.bytesToWebResponse(pdfBytes, outputFilename);
|
return WebResponseUtils.bytesToWebResponse(pdfBytes, outputFilename);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,9 +28,9 @@ import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.Parameter;
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
import stirling.software.SPDF.utils.ImageFinder;
|
import stirling.software.SPDF.pdf.ImageFinder;
|
||||||
import stirling.software.SPDF.utils.PdfUtils;
|
|
||||||
import stirling.software.SPDF.utils.ProcessExecutor;
|
import stirling.software.SPDF.utils.ProcessExecutor;
|
||||||
|
import stirling.software.SPDF.utils.WebResponseUtils;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
public class BlankPageController {
|
public class BlankPageController {
|
||||||
|
@ -109,7 +109,7 @@ public class BlankPageController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return PdfUtils.pdfDocToWebResponse(document, inputFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_blanksRemoved.pdf");
|
return WebResponseUtils.pdfDocToWebResponse(document, inputFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_blanksRemoved.pdf");
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
|
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
|
||||||
|
|
|
@ -31,8 +31,9 @@ import org.springframework.web.multipart.MultipartFile;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.Parameter;
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import stirling.software.SPDF.utils.PdfUtils;
|
import stirling.software.SPDF.utils.GeneralUtils;
|
||||||
import stirling.software.SPDF.utils.ProcessExecutor;
|
import stirling.software.SPDF.utils.ProcessExecutor;
|
||||||
|
import stirling.software.SPDF.utils.WebResponseUtils;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
public class CompressController {
|
public class CompressController {
|
||||||
|
@ -55,7 +56,7 @@ public class CompressController {
|
||||||
Long expectedOutputSize = 0L;
|
Long expectedOutputSize = 0L;
|
||||||
boolean autoMode = false;
|
boolean autoMode = false;
|
||||||
if (expectedOutputSizeString != null && expectedOutputSizeString.length() > 1 ) {
|
if (expectedOutputSizeString != null && expectedOutputSizeString.length() > 1 ) {
|
||||||
expectedOutputSize = PdfUtils.convertSizeToBytes(expectedOutputSizeString);
|
expectedOutputSize = GeneralUtils.convertSizeToBytes(expectedOutputSizeString);
|
||||||
autoMode = true;
|
autoMode = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -224,7 +225,7 @@ public class CompressController {
|
||||||
|
|
||||||
// Return the optimized PDF as a response
|
// Return the optimized PDF as a response
|
||||||
String outputFilename = inputFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_Optimized.pdf";
|
String outputFilename = inputFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_Optimized.pdf";
|
||||||
return PdfUtils.bytesToWebResponse(pdfBytes, outputFilename);
|
return WebResponseUtils.bytesToWebResponse(pdfBytes, outputFilename);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,8 +31,8 @@ import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.Parameter;
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
import stirling.software.SPDF.utils.PdfUtils;
|
|
||||||
import stirling.software.SPDF.utils.ProcessExecutor;
|
import stirling.software.SPDF.utils.ProcessExecutor;
|
||||||
|
import stirling.software.SPDF.utils.WebResponseUtils;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
public class ExtractImageScansController {
|
public class ExtractImageScansController {
|
||||||
|
@ -147,11 +147,11 @@ public class ExtractImageScansController {
|
||||||
// Clean up the temporary zip file
|
// Clean up the temporary zip file
|
||||||
Files.delete(tempZipFile);
|
Files.delete(tempZipFile);
|
||||||
|
|
||||||
return PdfUtils.bytesToWebResponse(zipBytes, outputZipFilename, MediaType.APPLICATION_OCTET_STREAM);
|
return WebResponseUtils.bytesToWebResponse(zipBytes, outputZipFilename, MediaType.APPLICATION_OCTET_STREAM);
|
||||||
} else {
|
} else {
|
||||||
// Return the processed image as a response
|
// Return the processed image as a response
|
||||||
byte[] imageBytes = processedImageBytes.get(0);
|
byte[] imageBytes = processedImageBytes.get(0);
|
||||||
return PdfUtils.bytesToWebResponse(imageBytes, fileName.replaceFirst("[.][^.]+$", "") + ".png", MediaType.IMAGE_PNG);
|
return WebResponseUtils.bytesToWebResponse(imageBytes, fileName.replaceFirst("[.][^.]+$", "") + ".png", MediaType.IMAGE_PNG);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@ import org.springframework.web.multipart.MultipartFile;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.Parameter;
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import stirling.software.SPDF.utils.PdfUtils;
|
import stirling.software.SPDF.utils.WebResponseUtils;
|
||||||
@RestController
|
@RestController
|
||||||
public class ExtractImagesController {
|
public class ExtractImagesController {
|
||||||
|
|
||||||
|
@ -106,7 +106,7 @@ public class ExtractImagesController {
|
||||||
// Create ByteArrayResource from byte array
|
// Create ByteArrayResource from byte array
|
||||||
byte[] zipContents = baos.toByteArray();
|
byte[] zipContents = baos.toByteArray();
|
||||||
|
|
||||||
return PdfUtils.boasToWebResponse(baos, filename + "_extracted-images.zip", MediaType.APPLICATION_OCTET_STREAM);
|
return WebResponseUtils.boasToWebResponse(baos, filename + "_extracted-images.zip", MediaType.APPLICATION_OCTET_STREAM);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.Parameter;
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
import stirling.software.SPDF.utils.PdfUtils;
|
import stirling.software.SPDF.utils.WebResponseUtils;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
public class MetadataController {
|
public class MetadataController {
|
||||||
|
@ -159,7 +159,7 @@ public class MetadataController {
|
||||||
info.setTrapped(trapped);
|
info.setTrapped(trapped);
|
||||||
|
|
||||||
document.setDocumentInformation(info);
|
document.setDocumentInformation(info);
|
||||||
return PdfUtils.pdfDocToWebResponse(document, pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_metadata.pdf");
|
return WebResponseUtils.pdfDocToWebResponse(document, pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_metadata.pdf");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,8 +27,8 @@ import org.springframework.web.multipart.MultipartFile;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.Parameter;
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import stirling.software.SPDF.utils.PdfUtils;
|
|
||||||
import stirling.software.SPDF.utils.ProcessExecutor;
|
import stirling.software.SPDF.utils.ProcessExecutor;
|
||||||
|
import stirling.software.SPDF.utils.WebResponseUtils;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
public class OCRController {
|
public class OCRController {
|
||||||
|
@ -189,11 +189,11 @@ public class OCRController {
|
||||||
Files.delete(sidecarTextPath);
|
Files.delete(sidecarTextPath);
|
||||||
|
|
||||||
// Return the zip file containing both the PDF and the text file
|
// Return the zip file containing both the PDF and the text file
|
||||||
return PdfUtils.bytesToWebResponse(zipBytes, outputZipFilename, MediaType.APPLICATION_OCTET_STREAM);
|
return WebResponseUtils.bytesToWebResponse(zipBytes, outputZipFilename, MediaType.APPLICATION_OCTET_STREAM);
|
||||||
} else {
|
} else {
|
||||||
// Return the OCR processed PDF as a response
|
// Return the OCR processed PDF as a response
|
||||||
Files.delete(tempOutputFile);
|
Files.delete(tempOutputFile);
|
||||||
return PdfUtils.bytesToWebResponse(pdfBytes, outputFilename);
|
return WebResponseUtils.bytesToWebResponse(pdfBytes, outputFilename);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@ import org.springframework.web.multipart.MultipartFile;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.Parameter;
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
import stirling.software.SPDF.utils.PdfUtils;
|
import stirling.software.SPDF.utils.PdfUtils;
|
||||||
|
import stirling.software.SPDF.utils.WebResponseUtils;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
public class OverlayImageController {
|
public class OverlayImageController {
|
||||||
|
@ -47,7 +48,7 @@ public class OverlayImageController {
|
||||||
byte[] imageBytes = imageFile.getBytes();
|
byte[] imageBytes = imageFile.getBytes();
|
||||||
byte[] result = PdfUtils.overlayImage(pdfBytes, imageBytes, x, y, everyPage);
|
byte[] result = PdfUtils.overlayImage(pdfBytes, imageBytes, x, y, everyPage);
|
||||||
|
|
||||||
return PdfUtils.bytesToWebResponse(result, pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_overlayed.pdf");
|
return WebResponseUtils.bytesToWebResponse(result, pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_overlayed.pdf");
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
logger.error("Failed to add image to PDF", e);
|
logger.error("Failed to add image to PDF", e);
|
||||||
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
|
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
|
||||||
|
|
|
@ -16,8 +16,8 @@ import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.Parameter;
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
import stirling.software.SPDF.utils.PdfUtils;
|
|
||||||
import stirling.software.SPDF.utils.ProcessExecutor;
|
import stirling.software.SPDF.utils.ProcessExecutor;
|
||||||
|
import stirling.software.SPDF.utils.WebResponseUtils;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
public class RepairController {
|
public class RepairController {
|
||||||
|
@ -60,7 +60,7 @@ public class RepairController {
|
||||||
|
|
||||||
// Return the optimized PDF as a response
|
// Return the optimized PDF as a response
|
||||||
String outputFilename = inputFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_repaired.pdf";
|
String outputFilename = inputFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_repaired.pdf";
|
||||||
return PdfUtils.bytesToWebResponse(pdfBytes, outputFilename);
|
return WebResponseUtils.bytesToWebResponse(pdfBytes, outputFilename);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,8 @@ package stirling.software.SPDF.controller.api.security;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import stirling.software.SPDF.utils.WebResponseUtils;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
@ -51,7 +53,6 @@ import com.itextpdf.signatures.SignatureUtil;
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.Parameter;
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
import stirling.software.SPDF.utils.PdfUtils;
|
|
||||||
@RestController
|
@RestController
|
||||||
public class CertSignController {
|
public class CertSignController {
|
||||||
|
|
||||||
|
@ -239,7 +240,7 @@ public class CertSignController {
|
||||||
System.out.println("Signed PDF size: " + signedPdf.size());
|
System.out.println("Signed PDF size: " + signedPdf.size());
|
||||||
|
|
||||||
System.out.println("PDF signed = " + isPdfSigned(signedPdf.toByteArray()));
|
System.out.println("PDF signed = " + isPdfSigned(signedPdf.toByteArray()));
|
||||||
return PdfUtils.bytesToWebResponse(signedPdf.toByteArray(), "example.pdf");
|
return WebResponseUtils.bytesToWebResponse(signedPdf.toByteArray(), "example.pdf");
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isPdfSigned(byte[] pdfData) throws IOException {
|
public boolean isPdfSigned(byte[] pdfData) throws IOException {
|
||||||
|
|
|
@ -17,7 +17,7 @@ import org.springframework.web.multipart.MultipartFile;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.Parameter;
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import stirling.software.SPDF.utils.PdfUtils;
|
import stirling.software.SPDF.utils.WebResponseUtils;
|
||||||
@RestController
|
@RestController
|
||||||
public class PasswordController {
|
public class PasswordController {
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ public class PasswordController {
|
||||||
String password) throws IOException {
|
String password) throws IOException {
|
||||||
PDDocument document = PDDocument.load(fileInput.getBytes(), password);
|
PDDocument document = PDDocument.load(fileInput.getBytes(), password);
|
||||||
document.setAllSecurityToBeRemoved(true);
|
document.setAllSecurityToBeRemoved(true);
|
||||||
return PdfUtils.pdfDocToWebResponse(document, fileInput.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_password_removed.pdf");
|
return WebResponseUtils.pdfDocToWebResponse(document, fileInput.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_password_removed.pdf");
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping(consumes = "multipart/form-data", value = "/add-password")
|
@PostMapping(consumes = "multipart/form-data", value = "/add-password")
|
||||||
|
@ -105,7 +105,7 @@ public class PasswordController {
|
||||||
|
|
||||||
document.protect(spp);
|
document.protect(spp);
|
||||||
|
|
||||||
return PdfUtils.pdfDocToWebResponse(document, fileInput.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_passworded.pdf");
|
return WebResponseUtils.pdfDocToWebResponse(document, fileInput.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_passworded.pdf");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.Parameter;
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
import stirling.software.SPDF.utils.PdfUtils;
|
import stirling.software.SPDF.utils.WebResponseUtils;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
public class WatermarkController {
|
public class WatermarkController {
|
||||||
|
@ -91,7 +91,7 @@ public class WatermarkController {
|
||||||
// Close the content stream
|
// Close the content stream
|
||||||
contentStream.close();
|
contentStream.close();
|
||||||
}
|
}
|
||||||
return PdfUtils.pdfDocToWebResponse(document, pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_watermarked.pdf");
|
return WebResponseUtils.pdfDocToWebResponse(document, pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_watermarked.pdf");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -115,6 +115,11 @@ public class OtherWebController {
|
||||||
return "other/multi-page-layout";
|
return "other/multi-page-layout";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping("/scale-pages")
|
||||||
|
@Hidden
|
||||||
|
public String scalePagesFrom(Model model) {
|
||||||
|
model.addAttribute("currentPage", "scale-pages");
|
||||||
|
return "other/scale-pages";
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package stirling.software.SPDF.utils;
|
package stirling.software.SPDF.pdf;
|
||||||
|
|
||||||
import java.awt.geom.Point2D;
|
import java.awt.geom.Point2D;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
30
src/main/java/stirling/software/SPDF/utils/GeneralUtils.java
Normal file
30
src/main/java/stirling/software/SPDF/utils/GeneralUtils.java
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
package stirling.software.SPDF.utils;
|
||||||
|
|
||||||
|
public class GeneralUtils {
|
||||||
|
|
||||||
|
public static Long convertSizeToBytes(String sizeStr) {
|
||||||
|
if (sizeStr == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
sizeStr = sizeStr.trim().toUpperCase();
|
||||||
|
try {
|
||||||
|
if (sizeStr.endsWith("KB")) {
|
||||||
|
return (long) (Double.parseDouble(sizeStr.substring(0, sizeStr.length() - 2)) * 1024);
|
||||||
|
} else if (sizeStr.endsWith("MB")) {
|
||||||
|
return (long) (Double.parseDouble(sizeStr.substring(0, sizeStr.length() - 2)) * 1024 * 1024);
|
||||||
|
} else if (sizeStr.endsWith("GB")) {
|
||||||
|
return (long) (Double.parseDouble(sizeStr.substring(0, sizeStr.length() - 2)) * 1024 * 1024 * 1024);
|
||||||
|
} else if (sizeStr.endsWith("B")) {
|
||||||
|
return Long.parseLong(sizeStr.substring(0, sizeStr.length() - 1));
|
||||||
|
} else {
|
||||||
|
// Input string does not have a valid format, handle this case
|
||||||
|
}
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
// The numeric part of the input string cannot be parsed, handle this case
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
package stirling.software.SPDF.utils;
|
||||||
|
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
|
||||||
|
public class ImageProcessingUtils {
|
||||||
|
|
||||||
|
static BufferedImage convertColorType(BufferedImage sourceImage, String colorType) {
|
||||||
|
BufferedImage convertedImage;
|
||||||
|
switch (colorType) {
|
||||||
|
case "greyscale":
|
||||||
|
convertedImage = new BufferedImage(sourceImage.getWidth(), sourceImage.getHeight(), BufferedImage.TYPE_BYTE_GRAY);
|
||||||
|
convertedImage.getGraphics().drawImage(sourceImage, 0, 0, null);
|
||||||
|
break;
|
||||||
|
case "blackwhite":
|
||||||
|
convertedImage = new BufferedImage(sourceImage.getWidth(), sourceImage.getHeight(), BufferedImage.TYPE_BYTE_BINARY);
|
||||||
|
convertedImage.getGraphics().drawImage(sourceImage, 0, 0, null);
|
||||||
|
break;
|
||||||
|
default: // full color
|
||||||
|
convertedImage = sourceImage;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return convertedImage;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
package stirling.software.SPDF.utils;
|
||||||
|
|
||||||
|
public class PDFManipulationUtils {
|
||||||
|
|
||||||
|
}
|
|
@ -92,6 +92,6 @@ public class PDFToFile {
|
||||||
if (tempOutputDir != null)
|
if (tempOutputDir != null)
|
||||||
FileUtils.deleteDirectory(tempOutputDir.toFile());
|
FileUtils.deleteDirectory(tempOutputDir.toFile());
|
||||||
}
|
}
|
||||||
return PdfUtils.bytesToWebResponse(fileBytes, fileName, MediaType.APPLICATION_OCTET_STREAM);
|
return WebResponseUtils.bytesToWebResponse(fileBytes, fileName, MediaType.APPLICATION_OCTET_STREAM);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,8 +8,6 @@ import java.io.File;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.net.URLEncoder;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.security.KeyPair;
|
import java.security.KeyPair;
|
||||||
import java.security.KeyStore;
|
import java.security.KeyStore;
|
||||||
|
@ -37,39 +35,12 @@ import org.apache.pdfbox.rendering.ImageType;
|
||||||
import org.apache.pdfbox.rendering.PDFRenderer;
|
import org.apache.pdfbox.rendering.PDFRenderer;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.http.HttpHeaders;
|
|
||||||
import org.springframework.http.HttpStatus;
|
|
||||||
import org.springframework.http.MediaType;
|
|
||||||
import org.springframework.http.ResponseEntity;
|
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
public class PdfUtils {
|
public class PdfUtils {
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(PdfUtils.class);
|
private static final Logger logger = LoggerFactory.getLogger(PdfUtils.class);
|
||||||
|
|
||||||
public static ResponseEntity<byte[]> boasToWebResponse(ByteArrayOutputStream baos, String docName) throws IOException {
|
|
||||||
return PdfUtils.bytesToWebResponse(baos.toByteArray(), docName);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ResponseEntity<byte[]> boasToWebResponse(ByteArrayOutputStream baos, String docName, MediaType mediaType) throws IOException {
|
|
||||||
return PdfUtils.bytesToWebResponse(baos.toByteArray(), docName, mediaType);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ResponseEntity<byte[]> bytesToWebResponse(byte[] bytes, String docName, MediaType mediaType) throws IOException {
|
|
||||||
|
|
||||||
// Return the PDF as a response
|
|
||||||
HttpHeaders headers = new HttpHeaders();
|
|
||||||
headers.setContentType(mediaType);
|
|
||||||
headers.setContentLength(bytes.length);
|
|
||||||
String encodedDocName = URLEncoder.encode(docName, StandardCharsets.UTF_8.toString()).replaceAll("\\+", "%20");
|
|
||||||
headers.setContentDispositionFormData("attachment", encodedDocName);
|
|
||||||
return new ResponseEntity<>(bytes, headers, HttpStatus.OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ResponseEntity<byte[]> bytesToWebResponse(byte[] bytes, String docName) throws IOException {
|
|
||||||
return bytesToWebResponse(bytes, docName, MediaType.APPLICATION_PDF);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static byte[] convertFromPdf(byte[] inputStream, String imageType, ImageType colorType, boolean singleImage, int DPI, String filename) throws IOException, Exception {
|
public static byte[] convertFromPdf(byte[] inputStream, String imageType, ImageType colorType, boolean singleImage, int DPI, String filename) throws IOException, Exception {
|
||||||
try (PDDocument document = PDDocument.load(new ByteArrayInputStream(inputStream))) {
|
try (PDDocument document = PDDocument.load(new ByteArrayInputStream(inputStream))) {
|
||||||
PDFRenderer pdfRenderer = new PDFRenderer(document);
|
PDFRenderer pdfRenderer = new PDFRenderer(document);
|
||||||
|
@ -134,7 +105,7 @@ public class PdfUtils {
|
||||||
int numPages = reader.getNumImages(true);
|
int numPages = reader.getNumImages(true);
|
||||||
for (int i = 0; i < numPages; i++) {
|
for (int i = 0; i < numPages; i++) {
|
||||||
BufferedImage pageImage = reader.read(i);
|
BufferedImage pageImage = reader.read(i);
|
||||||
BufferedImage convertedImage = convertColorType(pageImage, colorType);
|
BufferedImage convertedImage = ImageProcessingUtils.convertColorType(pageImage, colorType);
|
||||||
PDImageXObject pdImage = LosslessFactory.createFromImage(doc, convertedImage);
|
PDImageXObject pdImage = LosslessFactory.createFromImage(doc, convertedImage);
|
||||||
addImageToDocument(doc, pdImage, stretchToFit, autoRotate);
|
addImageToDocument(doc, pdImage, stretchToFit, autoRotate);
|
||||||
}
|
}
|
||||||
|
@ -147,7 +118,7 @@ public class PdfUtils {
|
||||||
fos.write(buffer, 0, len);
|
fos.write(buffer, 0, len);
|
||||||
}
|
}
|
||||||
BufferedImage image = ImageIO.read(imageFile);
|
BufferedImage image = ImageIO.read(imageFile);
|
||||||
BufferedImage convertedImage = convertColorType(image, colorType);
|
BufferedImage convertedImage = ImageProcessingUtils.convertColorType(image, colorType);
|
||||||
PDImageXObject pdImage;
|
PDImageXObject pdImage;
|
||||||
if (contentType != null && (contentType.equals("image/jpeg"))) {
|
if (contentType != null && (contentType.equals("image/jpeg"))) {
|
||||||
pdImage = JPEGFactory.createFromImage(doc, convertedImage);
|
pdImage = JPEGFactory.createFromImage(doc, convertedImage);
|
||||||
|
@ -170,24 +141,6 @@ public class PdfUtils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static BufferedImage convertColorType(BufferedImage sourceImage, String colorType) {
|
|
||||||
BufferedImage convertedImage;
|
|
||||||
switch (colorType) {
|
|
||||||
case "greyscale":
|
|
||||||
convertedImage = new BufferedImage(sourceImage.getWidth(), sourceImage.getHeight(), BufferedImage.TYPE_BYTE_GRAY);
|
|
||||||
convertedImage.getGraphics().drawImage(sourceImage, 0, 0, null);
|
|
||||||
break;
|
|
||||||
case "blackwhite":
|
|
||||||
convertedImage = new BufferedImage(sourceImage.getWidth(), sourceImage.getHeight(), BufferedImage.TYPE_BYTE_BINARY);
|
|
||||||
convertedImage.getGraphics().drawImage(sourceImage, 0, 0, null);
|
|
||||||
break;
|
|
||||||
default: // full color
|
|
||||||
convertedImage = sourceImage;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return convertedImage;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void addImageToDocument(PDDocument doc, PDImageXObject image, boolean stretchToFit, boolean autoRotate) throws IOException {
|
private static void addImageToDocument(PDDocument doc, PDImageXObject image, boolean stretchToFit, boolean autoRotate) throws IOException {
|
||||||
boolean imageIsLandscape = image.getWidth() > image.getHeight();
|
boolean imageIsLandscape = image.getWidth() > image.getHeight();
|
||||||
PDRectangle pageSize = PDRectangle.A4;
|
PDRectangle pageSize = PDRectangle.A4;
|
||||||
|
@ -224,33 +177,6 @@ public class PdfUtils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static X509Certificate[] loadCertificateChainFromKeystore(InputStream keystoreInputStream, String keystorePassword) throws Exception {
|
|
||||||
KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
|
|
||||||
keystore.load(keystoreInputStream, keystorePassword.toCharArray());
|
|
||||||
|
|
||||||
String alias = keystore.aliases().nextElement();
|
|
||||||
Certificate[] certChain = keystore.getCertificateChain(alias);
|
|
||||||
X509Certificate[] x509CertChain = new X509Certificate[certChain.length];
|
|
||||||
|
|
||||||
for (int i = 0; i < certChain.length; i++) {
|
|
||||||
x509CertChain[i] = (X509Certificate) certChain[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
return x509CertChain;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static KeyPair loadKeyPairFromKeystore(InputStream keystoreInputStream, String keystorePassword) throws Exception {
|
|
||||||
KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
|
|
||||||
keystore.load(keystoreInputStream, keystorePassword.toCharArray());
|
|
||||||
|
|
||||||
String alias = keystore.aliases().nextElement();
|
|
||||||
PrivateKey privateKey = (PrivateKey) keystore.getKey(alias, keystorePassword.toCharArray());
|
|
||||||
Certificate cert = keystore.getCertificate(alias);
|
|
||||||
PublicKey publicKey = cert.getPublicKey();
|
|
||||||
|
|
||||||
return new KeyPair(publicKey, privateKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static byte[] overlayImage(byte[] pdfBytes, byte[] imageBytes, float x, float y, boolean everyPage) throws IOException {
|
public static byte[] overlayImage(byte[] pdfBytes, byte[] imageBytes, float x, float y, boolean everyPage) throws IOException {
|
||||||
|
|
||||||
PDDocument document = PDDocument.load(new ByteArrayInputStream(pdfBytes));
|
PDDocument document = PDDocument.load(new ByteArrayInputStream(pdfBytes));
|
||||||
|
@ -282,41 +208,7 @@ public class PdfUtils {
|
||||||
return baos.toByteArray();
|
return baos.toByteArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ResponseEntity<byte[]> pdfDocToWebResponse(PDDocument document, String docName) throws IOException {
|
|
||||||
|
|
||||||
// Open Byte Array and save document to it
|
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
|
||||||
document.save(baos);
|
|
||||||
// Close the document
|
|
||||||
document.close();
|
|
||||||
|
|
||||||
return PdfUtils.boasToWebResponse(baos, docName);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Long convertSizeToBytes(String sizeStr) {
|
|
||||||
if (sizeStr == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
sizeStr = sizeStr.trim().toUpperCase();
|
|
||||||
try {
|
|
||||||
if (sizeStr.endsWith("KB")) {
|
|
||||||
return (long) (Double.parseDouble(sizeStr.substring(0, sizeStr.length() - 2)) * 1024);
|
|
||||||
} else if (sizeStr.endsWith("MB")) {
|
|
||||||
return (long) (Double.parseDouble(sizeStr.substring(0, sizeStr.length() - 2)) * 1024 * 1024);
|
|
||||||
} else if (sizeStr.endsWith("GB")) {
|
|
||||||
return (long) (Double.parseDouble(sizeStr.substring(0, sizeStr.length() - 2)) * 1024 * 1024 * 1024);
|
|
||||||
} else if (sizeStr.endsWith("B")) {
|
|
||||||
return Long.parseLong(sizeStr.substring(0, sizeStr.length() - 1));
|
|
||||||
} else {
|
|
||||||
// Input string does not have a valid format, handle this case
|
|
||||||
}
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
// The numeric part of the input string cannot be parsed, handle this case
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
package stirling.software.SPDF.utils;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
|
||||||
|
public class WebResponseUtils {
|
||||||
|
|
||||||
|
public static ResponseEntity<byte[]> boasToWebResponse(ByteArrayOutputStream baos, String docName) throws IOException {
|
||||||
|
return WebResponseUtils.bytesToWebResponse(baos.toByteArray(), docName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ResponseEntity<byte[]> boasToWebResponse(ByteArrayOutputStream baos, String docName, MediaType mediaType) throws IOException {
|
||||||
|
return WebResponseUtils.bytesToWebResponse(baos.toByteArray(), docName, mediaType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ResponseEntity<byte[]> bytesToWebResponse(byte[] bytes, String docName, MediaType mediaType) throws IOException {
|
||||||
|
|
||||||
|
// Return the PDF as a response
|
||||||
|
HttpHeaders headers = new HttpHeaders();
|
||||||
|
headers.setContentType(mediaType);
|
||||||
|
headers.setContentLength(bytes.length);
|
||||||
|
String encodedDocName = URLEncoder.encode(docName, StandardCharsets.UTF_8.toString()).replaceAll("\\+", "%20");
|
||||||
|
headers.setContentDispositionFormData("attachment", encodedDocName);
|
||||||
|
return new ResponseEntity<>(bytes, headers, HttpStatus.OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ResponseEntity<byte[]> bytesToWebResponse(byte[] bytes, String docName) throws IOException {
|
||||||
|
return bytesToWebResponse(bytes, docName, MediaType.APPLICATION_PDF);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ResponseEntity<byte[]> pdfDocToWebResponse(PDDocument document, String docName) throws IOException {
|
||||||
|
|
||||||
|
// Open Byte Array and save document to it
|
||||||
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
document.save(baos);
|
||||||
|
// Close the document
|
||||||
|
document.close();
|
||||||
|
|
||||||
|
return boasToWebResponse(baos, docName);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -131,7 +131,10 @@ home.certSign.desc=Signs a PDF with a Certificate/Key (PEM/P12)
|
||||||
home.pageLayout.title=Multi-Page Layout
|
home.pageLayout.title=Multi-Page Layout
|
||||||
home.pageLayout.desc=Merge multiple pages of a PDF document into a single page
|
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
|
downloadPdf=Download PDF
|
||||||
text=Text
|
text=Text
|
||||||
|
@ -144,6 +147,12 @@ pageLayout.header=Multi Page Layout
|
||||||
pageLayout.pagesPerSheet=Pages per sheet:
|
pageLayout.pagesPerSheet=Pages per sheet:
|
||||||
pageLayout.submit=Submit
|
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.title=Certificate Signing
|
||||||
certSign.header=Sign a PDF with your certificate (Work in progress)
|
certSign.header=Sign a PDF with your certificate (Work in progress)
|
||||||
certSign.selectPDF=Select a PDF File for Signing:
|
certSign.selectPDF=Select a PDF File for Signing:
|
||||||
|
|
|
@ -131,6 +131,11 @@ home.certSign.desc=Podpisz dokument PDF za pomocą certyfikatu/klucza prywatnego
|
||||||
home.pageLayout.title=Układ wielu stron
|
home.pageLayout.title=Układ wielu stron
|
||||||
home.pageLayout.desc=Scal wiele stron dokumentu PDF w jedną stronę
|
home.pageLayout.desc=Scal wiele stron dokumentu PDF w jedną stronę
|
||||||
|
|
||||||
|
home.scalePages.title=Dopasuj rozmiar stron
|
||||||
|
home.scalePages.desc=Dopasuj rozmiar stron wybranego dokumentu PDF
|
||||||
|
|
||||||
|
error.pdfPassword=Dokument PDF jest zabezpieczony hasłem, musisz podać prawidłowe hasło.
|
||||||
|
|
||||||
downloadPdf=Pobierz PDF
|
downloadPdf=Pobierz PDF
|
||||||
text=Tekst
|
text=Tekst
|
||||||
font=Czcionka
|
font=Czcionka
|
||||||
|
@ -142,6 +147,12 @@ pageLayout.header=Układ wielu stron
|
||||||
pageLayout.pagesPerSheet=Stron na jednym arkuszu:
|
pageLayout.pagesPerSheet=Stron na jednym arkuszu:
|
||||||
pageLayout.submit=Wykonaj
|
pageLayout.submit=Wykonaj
|
||||||
|
|
||||||
|
scalePages.title=Dopasuj rozmiar stron
|
||||||
|
scalePages.header=Dopasuj rozmiar stron
|
||||||
|
scalePages.pageSize=Rozmiar stron dokumentu:
|
||||||
|
scalePages.scaleFactor=Poziom powiększenia (przycięcia) stron:
|
||||||
|
scalePages.submit=Wykonaj
|
||||||
|
|
||||||
certSign.title=Podpisywanie certyfikatem
|
certSign.title=Podpisywanie certyfikatem
|
||||||
certSign.header=Podpisz dokument PDF certyfikatem prywatnym (moduł w budowie)
|
certSign.header=Podpisz dokument PDF certyfikatem prywatnym (moduł w budowie)
|
||||||
certSign.selectPDF=Wybierz dokument PDF do podpisania:
|
certSign.selectPDF=Wybierz dokument PDF do podpisania:
|
||||||
|
|
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-arrows-fullscreen" viewBox="0 0 16 16">
|
||||||
|
<path fill-rule="evenodd" d="M5.828 10.172a.5.5 0 0 0-.707 0l-4.096 4.096V11.5a.5.5 0 0 0-1 0v3.975a.5.5 0 0 0 .5.5H4.5a.5.5 0 0 0 0-1H1.732l4.096-4.096a.5.5 0 0 0 0-.707zm4.344 0a.5.5 0 0 1 .707 0l4.096 4.096V11.5a.5.5 0 1 1 1 0v3.975a.5.5 0 0 1-.5.5H11.5a.5.5 0 0 1 0-1h2.768l-4.096-4.096a.5.5 0 0 1 0-.707zm0-4.344a.5.5 0 0 0 .707 0l4.096-4.096V4.5a.5.5 0 1 0 1 0V.525a.5.5 0 0 0-.5-.5H11.5a.5.5 0 0 0 0 1h2.768l-4.096 4.096a.5.5 0 0 0 0 .707zm-4.344 0a.5.5 0 0 1-.707 0L1.025 1.732V4.5a.5.5 0 0 1-1 0V.525a.5.5 0 0 1 .5-.5H4.5a.5.5 0 0 1 0 1H1.732l4.096 4.096a.5.5 0 0 1 0 .707z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 730 B |
|
@ -103,7 +103,7 @@ margin-top: 0;
|
||||||
<div id="content-wrap">
|
<div id="content-wrap">
|
||||||
|
|
||||||
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
|
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
|
||||||
|
<div th:insert="~{fragments/errorBanner.html :: errorBanner}"></div>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
|
|
||||||
<div id="support-section">
|
<div id="support-section">
|
||||||
|
|
|
@ -220,267 +220,212 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||||
|
|
||||||
<th:block th:fragment="fileSelector(name, multiple)" th:with="accept=${accept} ?: '*/*', inputText=${inputText} ?: #{pdfPrompt}, remoteCall=${remoteCall} ?: 'true', notRequired=${notRequired} ?: false">
|
<th:block th:fragment="fileSelector(name, multiple)" th:with="accept=${accept} ?: '*/*', inputText=${inputText} ?: #{pdfPrompt}, remoteCall=${remoteCall} ?: 'true', notRequired=${notRequired} ?: false">
|
||||||
<script>
|
<script>
|
||||||
$(document).ready(function() {
|
function showErrorBanner(message, stackTrace) {
|
||||||
$('form').submit(async function(event) {
|
const errorContainer = document.getElementById("errorContainer");
|
||||||
const boredWaiting = localStorage.getItem('boredWaiting');
|
errorContainer.style.display = "block"; // Display the banner
|
||||||
if (boredWaiting === 'enabled') {
|
document.querySelector("#errorContainer .alert-heading").textContent = "Error";
|
||||||
$('#show-game-btn').show();
|
document.querySelector("#errorContainer p").textContent = message;
|
||||||
}
|
document.querySelector("#traceContent").textContent = stackTrace;
|
||||||
var processing = "Processing..."
|
}
|
||||||
var submitButtonText = $('#submitBtn').text()
|
|
||||||
|
$(document).ready(function () {
|
||||||
$('#submitBtn').text('Processing...');
|
$('form').submit(async function (event) {
|
||||||
console.log("start download code")
|
event.preventDefault();
|
||||||
var files = $('#fileInput-input')[0].files;
|
|
||||||
var url = this.action;
|
const url = this.action;
|
||||||
console.log(url)
|
const files = $('#fileInput-input')[0].files;
|
||||||
event.preventDefault(); // Prevent the default form handling behavior
|
const formData = new FormData(this);
|
||||||
|
const override = $('#override').val() || '';
|
||||||
|
|
||||||
/* Check if ${multiple} is false */
|
$('#submitBtn').text('Processing...');
|
||||||
var multiple = [[${multiple}]] || false;
|
|
||||||
var override = $('#override').val() || '';
|
try {
|
||||||
console.log("override=" + override)
|
if (override === 'multi' || files.length > 1 && override !== 'single') {
|
||||||
|
await submitMultiPdfForm(url, files);
|
||||||
if([[${remoteCall}]] === true) {
|
} else {
|
||||||
if (override === 'multi' || (!multiple && files.length > 1) && override !== 'single' ) {
|
const downloadDetails = await handleSingleDownload(url, formData);
|
||||||
console.log("multi parallel download")
|
|
||||||
await submitMultiPdfForm(event,url);
|
|
||||||
} else {
|
|
||||||
console.log("start single download")
|
|
||||||
|
|
||||||
// Get the selected download option from localStorage
|
|
||||||
const downloadOption = localStorage.getItem('downloadOption');
|
|
||||||
|
|
||||||
var formData = new FormData($('form')[0]);
|
|
||||||
|
|
||||||
// Send the request to the server using the fetch() API
|
|
||||||
const response = await fetch(url, {
|
|
||||||
method: 'POST',
|
|
||||||
body: formData
|
|
||||||
});
|
|
||||||
try {
|
|
||||||
if (!response) {
|
|
||||||
throw new Error('Received null response for file ' + i);
|
|
||||||
}
|
|
||||||
console.log("load single download")
|
|
||||||
|
|
||||||
|
|
||||||
// Extract the filename from the Content-Disposition header, if present
|
|
||||||
let filename = null;
|
|
||||||
const contentDispositionHeader = response.headers.get('Content-Disposition');
|
|
||||||
console.log(contentDispositionHeader)
|
|
||||||
if (contentDispositionHeader && contentDispositionHeader.indexOf('attachment') !== -1) {
|
|
||||||
filename = decodeURIComponent(contentDispositionHeader.split('filename=')[1].replace(/"/g, ''));
|
|
||||||
} else {
|
|
||||||
// If the Content-Disposition header is not present or does not contain the filename, use a default filename
|
|
||||||
filename = 'download';
|
|
||||||
}
|
|
||||||
console.log("filename=" + filename)
|
|
||||||
|
|
||||||
|
|
||||||
const contentType = response.headers.get('Content-Type');
|
|
||||||
console.log("contentType=" + contentType)
|
|
||||||
// Check if the response is a PDF or an image
|
|
||||||
if (contentType.includes('pdf') || contentType.includes('image')) {
|
|
||||||
const blob = await response.blob();
|
|
||||||
console.log("pdf/image")
|
|
||||||
|
|
||||||
// Perform the appropriate action based on the download option
|
|
||||||
if (downloadOption === 'sameWindow') {
|
|
||||||
console.log("same window")
|
|
||||||
|
|
||||||
// Open the file in the same window
|
|
||||||
window.location.href = URL.createObjectURL(blob);
|
|
||||||
} else if (downloadOption === 'newWindow') {
|
|
||||||
console.log("new window")
|
|
||||||
|
|
||||||
// Open the file in a new window
|
|
||||||
window.open(URL.createObjectURL(blob), '_blank');
|
|
||||||
} else {
|
|
||||||
console.log("else save")
|
|
||||||
|
|
||||||
// Download the file
|
|
||||||
const link = document.createElement('a');
|
|
||||||
link.href = URL.createObjectURL(blob);
|
|
||||||
link.download = filename;
|
|
||||||
link.click();
|
|
||||||
}
|
|
||||||
} else if (contentType.includes('json')) {
|
|
||||||
// Handle the JSON response
|
|
||||||
const json = await response.json();
|
|
||||||
// Format the error message
|
|
||||||
const errorMessage = JSON.stringify(json, null, 2);
|
|
||||||
// Display the error message in an alert
|
|
||||||
alert(`An error occurred: ${errorMessage}`);
|
|
||||||
} else {
|
|
||||||
const blob = await response.blob()
|
|
||||||
console.log("else save 2 zip")
|
|
||||||
|
|
||||||
// For ZIP files or other file types, just download the file
|
|
||||||
const link = document.createElement('a');
|
|
||||||
link.href = URL.createObjectURL(blob);
|
|
||||||
link.download = filename;
|
|
||||||
link.click();
|
|
||||||
}
|
|
||||||
} catch(error) {
|
|
||||||
console.log("error listener")
|
|
||||||
|
|
||||||
// Extract the error message and stack trace from the response
|
|
||||||
const errorMessage = error.message;
|
|
||||||
const stackTrace = error.stack;
|
|
||||||
|
|
||||||
// Create an error message to display to the user
|
|
||||||
const message = `${errorMessage}\n\n${stackTrace}`;
|
|
||||||
|
|
||||||
$('#submitBtn').text(submitButtonText);
|
|
||||||
|
|
||||||
// Display the error message to the user
|
|
||||||
alert(message);
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
//Offline do nothing and let other scripts handle everything
|
|
||||||
|
|
||||||
}
|
|
||||||
$('#submitBtn').text(submitButtonText);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
async function submitMultiPdfForm(event, url) {
|
|
||||||
// Get the selected PDF files
|
|
||||||
let files = $('#fileInput-input')[0].files;
|
|
||||||
|
|
||||||
// Get the existing form data
|
// Determine the download option from localStorage
|
||||||
let formData = new FormData($('form')[0]);
|
const downloadOption = localStorage.getItem('downloadOption');
|
||||||
formData.delete('fileInput');
|
|
||||||
|
|
||||||
// Show the progress bar
|
// Handle the download action according to the selected option
|
||||||
$('#progressBarContainer').show();
|
handleDownloadAction(downloadOption, downloadDetails.blob, downloadDetails.filename);
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize the progress bar
|
$('#submitBtn').text('Submit');
|
||||||
let progressBar = $('#progressBar');
|
} catch (error) {
|
||||||
progressBar.css('width', '0%');
|
handleDownloadError(error);
|
||||||
progressBar.attr('aria-valuenow', 0);
|
$('#submitBtn').text('Submit');
|
||||||
progressBar.attr('aria-valuemax', files.length);
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// Check the flag in localStorage, default to 4
|
function handleDownloadAction(downloadOption, blob, filename) {
|
||||||
const zipThreshold = parseInt(localStorage.getItem('zipThreshold'), 10) || 4;
|
const url = URL.createObjectURL(blob);
|
||||||
const zipFiles = files.length > zipThreshold;
|
|
||||||
|
switch (downloadOption) {
|
||||||
|
case 'sameWindow':
|
||||||
|
// Open the file in the same window
|
||||||
|
window.location.href = url;
|
||||||
|
break;
|
||||||
|
case 'newWindow':
|
||||||
|
// Open the file in a new window
|
||||||
|
window.open(url, '_blank');
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// Download the file
|
||||||
|
const link = document.createElement('a');
|
||||||
|
link.href = url;
|
||||||
|
link.download = filename;
|
||||||
|
link.click();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize JSZip instance if needed
|
async function handleSingleDownload(url, formData) {
|
||||||
let jszip = null;
|
const response = await fetch(url, {
|
||||||
if (zipFiles) {
|
method: 'POST',
|
||||||
jszip = new JSZip();
|
body: formData
|
||||||
}
|
});
|
||||||
|
|
||||||
// Submit each PDF file in parallel
|
if (!response.ok) {
|
||||||
let promises = [];
|
throw new Error(`HTTP error! status: ${response.status}`);
|
||||||
for (let i = 0; i < files.length; i++) {
|
} else {
|
||||||
let promise = new Promise(async function(resolve, reject) {
|
const blob = await response.blob();
|
||||||
let fileFormData = new FormData();
|
const filename = getFilenameFromContentDisposition(response.headers.get('Content-Disposition'));
|
||||||
fileFormData.append('fileInput', files[i]);
|
return { blob, filename };
|
||||||
for (let pair of formData.entries()) {
|
}
|
||||||
fileFormData.append(pair[0], pair[1]);
|
}
|
||||||
}
|
function getFilenameFromContentDisposition(contentDisposition) {
|
||||||
console.log(fileFormData);
|
let filename;
|
||||||
|
|
||||||
try {
|
if (contentDisposition && contentDisposition.indexOf('attachment') !== -1) {
|
||||||
let response = await fetch(url, {
|
filename = decodeURIComponent(contentDisposition.split('filename=')[1].replace(/"/g, ''));
|
||||||
method: 'POST',
|
} else {
|
||||||
body: fileFormData
|
// If the Content-Disposition header is not present or does not contain the filename, use a default filename
|
||||||
});
|
filename = 'download';
|
||||||
|
}
|
||||||
|
|
||||||
if (!response) {
|
return filename;
|
||||||
throw new Error('Received null response for file ' + i);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error(`Error submitting request for file ${i}: ${response.status} ${response.statusText}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
let contentDisposition = response.headers.get('content-disposition');
|
|
||||||
let fileName = "file.pdf"
|
|
||||||
if (!contentDisposition) {
|
|
||||||
//throw new Error('Content-Disposition header not found for file ' + i);
|
|
||||||
} else {
|
|
||||||
fileName = decodeURIComponent(contentDisposition.split('filename=')[1].replace(/"/g, ''));
|
|
||||||
}
|
|
||||||
console.log('Received response for file ' + i + ': ' + response);
|
|
||||||
|
|
||||||
|
|
||||||
let blob = await response.blob();
|
async function handlePdfOrImageResponse(response, filename) {
|
||||||
if (zipFiles) {
|
const downloadOption = localStorage.getItem('downloadOption');
|
||||||
// Add the file to the ZIP archive
|
const blob = await response.blob();
|
||||||
jszip.file(fileName, blob);
|
const url = URL.createObjectURL(blob);
|
||||||
resolve();
|
if (downloadOption === 'sameWindow') {
|
||||||
} else {
|
window.location.href = url;
|
||||||
// Download the file directly
|
} else if (downloadOption === 'newWindow') {
|
||||||
let url = window.URL.createObjectURL(blob);
|
window.open(url, '_blank');
|
||||||
let a = document.createElement('a');
|
} else {
|
||||||
a.href = url;
|
downloadFile(url, filename);
|
||||||
a.download = fileName;
|
}
|
||||||
document.body.appendChild(a);
|
}
|
||||||
a.click();
|
|
||||||
a.remove();
|
|
||||||
resolve();
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error submitting request for file ' + i + ': ' + error);
|
|
||||||
|
|
||||||
// Set default values or fallbacks for error properties
|
async function handleJsonResponse(response) {
|
||||||
let status = error && error.status || 500;
|
const json = await response.json();
|
||||||
let statusText = error && error.statusText || 'Internal Server Error';
|
const errorMessage = JSON.stringify(json, null, 2);
|
||||||
let message = error && error.message || 'An error occurred while processing your request.';
|
if(errorMessage.toLowerCase().includes('the password is incorrect') || errorMessage.toLowerCase().includes('Password is not provided')){
|
||||||
|
alert('[[#{error.pdfPassword}]]');
|
||||||
|
} else {
|
||||||
|
showErrorBanner(json.error + ':' + json.message, json.trace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Reject the Promise to signal that the request has failed
|
async function handleOtherResponse(response, filename) {
|
||||||
reject();
|
const blob = await response.blob();
|
||||||
// Redirect to error page with Spring Boot error parameters
|
const url = URL.createObjectURL(blob);
|
||||||
let url = '/error?status=' + status + '&error=' + encodeURIComponent(statusText) + '&message=' + encodeURIComponent(message);
|
downloadFile(url, filename);
|
||||||
window.location.href = url;
|
}
|
||||||
}
|
function handleDownloadError(error) {
|
||||||
});
|
const errorMessage = error.message;
|
||||||
|
showErrorBanner(errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
// Update the progress bar as each request finishes
|
let urls = []; // An array to hold all the URLs
|
||||||
promise.then(function() {
|
|
||||||
updateProgressBar(progressBar, files);
|
|
||||||
});
|
|
||||||
|
|
||||||
promises.push(promise);
|
function downloadFile(blob, filename) {
|
||||||
}
|
const url = URL.createObjectURL(blob);
|
||||||
|
const a = document.createElement('a');
|
||||||
|
a.href = url;
|
||||||
|
a.download = filename;
|
||||||
|
a.click();
|
||||||
|
urls.push(url); // Store the URL so it doesn't get garbage collected too soon
|
||||||
|
}
|
||||||
|
|
||||||
// Wait for all requests to finish
|
|
||||||
try {
|
async function submitMultiPdfForm(url, files) {
|
||||||
await Promise.all(promises);
|
const zipThreshold = parseInt(localStorage.getItem('zipThreshold'), 10) || 4;
|
||||||
} catch (error) {
|
const zipFiles = files.length > zipThreshold;
|
||||||
console.error('Error while uploading files: ' + error);
|
let jszip = null;
|
||||||
}
|
let progressBar = $('#progressBar');
|
||||||
|
progressBar.css('width', '0%');
|
||||||
|
progressBar.attr('aria-valuenow', 0);
|
||||||
|
progressBar.attr('aria-valuemax', Array.from(files).length);
|
||||||
|
if (zipFiles) {
|
||||||
|
jszip = new JSZip();
|
||||||
|
}
|
||||||
|
|
||||||
// Update the progress bar
|
// Get existing form data
|
||||||
progressBar.css('width', '100%');
|
let formData = new FormData($('form')[0]);
|
||||||
progressBar.attr('aria-valuenow', files.length);
|
formData.delete('fileInput');
|
||||||
|
|
||||||
// After all requests are finished, download the ZIP file if needed
|
const CONCURRENCY_LIMIT = 8;
|
||||||
if (zipFiles) {
|
const chunks = [];
|
||||||
try {
|
for (let i = 0; i < Array.from(files).length; i += CONCURRENCY_LIMIT) {
|
||||||
let content = await jszip.generateAsync({ type: "blob" });
|
chunks.push(Array.from(files).slice(i, i + CONCURRENCY_LIMIT));
|
||||||
let url = window.URL.createObjectURL(content);
|
}
|
||||||
let a = document.createElement('a');
|
|
||||||
a.href = url;
|
for (const chunk of chunks) {
|
||||||
a.download = "files.zip";
|
const promises = chunk.map(async file => {
|
||||||
document.body.appendChild(a);
|
let fileFormData = new FormData();
|
||||||
a.click();
|
fileFormData.append('fileInput', file);
|
||||||
a.remove();
|
|
||||||
} catch (error) {
|
// Add other form data
|
||||||
console.error('Error generating ZIP file: ' + error);
|
for (let pair of formData.entries()) {
|
||||||
}
|
fileFormData.append(pair[0], pair[1]);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
try {
|
||||||
|
const downloadDetails = await handleSingleDownload(url, fileFormData);
|
||||||
|
if (zipFiles) {
|
||||||
|
jszip.file(downloadDetails.filename, downloadDetails.blob);
|
||||||
|
} else {
|
||||||
|
downloadFile(downloadDetails.blob, downloadDetails.filename);
|
||||||
|
}
|
||||||
|
updateProgressBar(progressBar, Array.from(files).length);
|
||||||
|
} catch (error) {
|
||||||
|
handleDownloadError(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
await Promise.all(promises);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (zipFiles) {
|
||||||
|
try {
|
||||||
|
const content = await jszip.generateAsync({ type: "blob" });
|
||||||
|
downloadFile(content, "files.zip");
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error generating ZIP file: ' + error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function updateProgressBar(progressBar, files) {
|
function updateProgressBar(progressBar, files) {
|
||||||
let progress = ((progressBar.attr('aria-valuenow') / files.length) * 100) + (100 / files.length);
|
let progress = ((progressBar.attr('aria-valuenow') / files.length) * 100) + (100 / files.length);
|
||||||
progressBar.css('width', progress + '%');
|
progressBar.css('width', progress + '%');
|
||||||
progressBar.attr('aria-valuenow', parseInt(progressBar.attr('aria-valuenow')) + 1);
|
progressBar.attr('aria-valuenow', parseInt(progressBar.attr('aria-valuenow')) + 1);
|
||||||
}
|
}
|
||||||
|
window.addEventListener('unload', () => {
|
||||||
|
for (const url of urls) {
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="custom-file-chooser">
|
<div class="custom-file-chooser">
|
||||||
|
|
225
src/main/resources/templates/fragments/errorBannerPerPage.html
Normal file
225
src/main/resources/templates/fragments/errorBannerPerPage.html
Normal file
|
@ -0,0 +1,225 @@
|
||||||
|
<th:block th:fragment="errorBannerPerPage">
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
#github-button,
|
||||||
|
#discord-button {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 1rem 2rem;
|
||||||
|
background-color: #008CBA;
|
||||||
|
color: #fff;
|
||||||
|
text-align: center;
|
||||||
|
text-decoration: none;
|
||||||
|
border-radius: 3rem;
|
||||||
|
transition: all 0.3s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
#github-button:hover,
|
||||||
|
#discord-button:hover {
|
||||||
|
background-color: #005b7f;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div id="errorContainer" class="alert alert-danger alert-dismissible fade show" role="alert" style="display: none;">
|
||||||
|
<h4 class="alert-heading">Error</h4>
|
||||||
|
<p></p>
|
||||||
|
<button type="button" class="btn btn-danger" onclick="toggletrace()">Show Stack Trace</button>
|
||||||
|
<button type="button" class="btn btn-secondary" onclick="copytrace()">Copy Stack Trace</button>
|
||||||
|
<button type="button" class="btn btn-info" onclick="showHelp()">Help</button>
|
||||||
|
<button type="button" class="close" data-dismiss="alert" aria-label="Close" onclick="dismissError()">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
<!-- Stack trace section -->
|
||||||
|
<div id="trace" style="max-height: 0; overflow: hidden;">
|
||||||
|
<div style="background-color: #f8d7da; border: 1px solid #f5c6cb; border-radius: 3px; padding: 10px; margin-top: 5px;">
|
||||||
|
<pre id="traceContent"></pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Help Modal -->
|
||||||
|
<style scoped>
|
||||||
|
#errorContainer {
|
||||||
|
margin: 20px; /* adjust this value as needed */
|
||||||
|
}
|
||||||
|
|
||||||
|
#helpModalDialog {
|
||||||
|
width: 90%;
|
||||||
|
max-width: 800px;
|
||||||
|
}
|
||||||
|
#helpModal h1 {
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 10%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#helpModal p {
|
||||||
|
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#helpModal .button:hover {
|
||||||
|
background-color: #005b7f;
|
||||||
|
}
|
||||||
|
|
||||||
|
#helpModal .features-container {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(21rem, 3fr));
|
||||||
|
gap: 25px 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#helpModal .feature-card {
|
||||||
|
border: 1px solid rgba(0, 0, 0, .125);
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
padding: 1.25rem;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
#helpModal .feature-card .card-text {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#support-section {
|
||||||
|
background-color: #f9f9f9;
|
||||||
|
padding: 4rem;
|
||||||
|
margin-top: 1rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#support-section h1 {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#support-section p {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#button-group {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
#github-button, #discord-button {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 1rem 2rem;
|
||||||
|
margin: 1rem;
|
||||||
|
background-color: #008CBA;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
text-align: center;
|
||||||
|
text-decoration: none;
|
||||||
|
border-radius: 3rem;
|
||||||
|
transition: all 0.3s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
#github-button:hover, #discord-button:hover, #home-button:hover {
|
||||||
|
background-color: #005b7f;
|
||||||
|
}
|
||||||
|
|
||||||
|
#home-button {
|
||||||
|
display: block;
|
||||||
|
width: 200px;
|
||||||
|
height: 50px;
|
||||||
|
margin: 2em auto;
|
||||||
|
background-color: #008CBA;
|
||||||
|
color: white;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 50px;
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: bold;
|
||||||
|
border-radius: 25px;
|
||||||
|
transition: all 0.3s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div class="modal fade" id="helpModal" tabindex="-1" role="dialog" aria-labelledby="helpModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-dialog-centered" role="document" id="helpModalDialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="helpModalLabel">Help</h5>
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div id="support-section">
|
||||||
|
<h1 class="display-2">Oops!</h1>
|
||||||
|
<p class="lead">Sorry for the issue!.</p>
|
||||||
|
<br>
|
||||||
|
<h2>Need help / Found an issue?</h2>
|
||||||
|
<p>If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord:</p>
|
||||||
|
<div id="button-group">
|
||||||
|
<a href="https://github.com/Frooodle/Stirling-PDF/issues" id="github-button" target="_blank">GitHub - Submit a ticket</a>
|
||||||
|
<a href="https://discord.gg/Cn8pWhQRxZ" id="discord-button" target="_blank">Discord - Submit Support post</a>
|
||||||
|
</div>
|
||||||
|
<a href="/" id="home-button">Go to Homepage</a>
|
||||||
|
<a data-dismiss="modal" id="home-button">Close</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
var traceVisible = false;
|
||||||
|
|
||||||
|
function toggletrace() {
|
||||||
|
var traceDiv = document.getElementById("trace");
|
||||||
|
if (!traceVisible) {
|
||||||
|
traceDiv.style.maxHeight = "500px";
|
||||||
|
traceVisible = true;
|
||||||
|
} else {
|
||||||
|
traceDiv.style.maxHeight = "0px";
|
||||||
|
traceVisible = false;
|
||||||
|
}
|
||||||
|
adjustContainerHeight();
|
||||||
|
}
|
||||||
|
|
||||||
|
function copytrace() {
|
||||||
|
var flip = false
|
||||||
|
if(!traceVisible) {
|
||||||
|
toggletrace()
|
||||||
|
flip = true
|
||||||
|
}
|
||||||
|
var traceContent = document.getElementById("traceContent");
|
||||||
|
var range = document.createRange();
|
||||||
|
range.selectNode(traceContent);
|
||||||
|
window.getSelection().removeAllRanges();
|
||||||
|
window.getSelection().addRange(range);
|
||||||
|
document.execCommand("copy");
|
||||||
|
window.getSelection().removeAllRanges();
|
||||||
|
if(flip){
|
||||||
|
toggletrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function dismissError() {
|
||||||
|
var errorContainer = document.getElementById("errorContainer");
|
||||||
|
errorContainer.style.display = "none";
|
||||||
|
errorContainer.style.height ="0";
|
||||||
|
}
|
||||||
|
|
||||||
|
function adjustContainerHeight() {
|
||||||
|
var errorContainer = document.getElementById("errorContainer");
|
||||||
|
var traceDiv = document.getElementById("trace");
|
||||||
|
if (traceVisible) {
|
||||||
|
errorContainer.style.height = errorContainer.scrollHeight - traceDiv.scrollHeight + traceDiv.offsetHeight + "px";
|
||||||
|
} else {
|
||||||
|
errorContainer.style.height = "auto";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function showHelp() {
|
||||||
|
$('#helpModal').modal('show');
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</th:block>
|
|
@ -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 ( '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 ( '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 ( '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>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
|
@ -349,8 +350,8 @@ function compareVersions(version1, version2) {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
</nav>
|
</nav>
|
||||||
<div th:insert="~{fragments/errorBanner.html :: errorBanner}"></div>
|
|
||||||
|
<div th:insert="~{fragments/errorBannerPerPage.html :: errorBannerPerPage}"></div>
|
||||||
<div class="modal fade" id="settingsModal" tabindex="-1" role="dialog" aria-labelledby="settingsModalLabel" aria-hidden="true">
|
<div class="modal fade" id="settingsModal" tabindex="-1" role="dialog" aria-labelledby="settingsModalLabel" aria-hidden="true">
|
||||||
<div class="modal-dialog modal-dialog-centered" role="document">
|
<div class="modal-dialog modal-dialog-centered" role="document">
|
||||||
<div class="modal-content dark-card">
|
<div class="modal-content dark-card">
|
||||||
|
|
|
@ -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='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='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>
|
<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=#{scalePages.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 a new issue