This commit is contained in:
Anthony Stirling 2023-09-13 00:46:30 +01:00
parent 2c0fb33548
commit 82b641458f
23 changed files with 121 additions and 100 deletions

View file

@ -39,63 +39,69 @@ public class MultiPageLayoutController {
)
public ResponseEntity<byte[]> mergeMultiplePagesIntoOne(@ModelAttribute MergeMultiplePagesRequest request)
throws IOException {
int pagesPerSheet = request.getPagesPerSheet();
MultipartFile file = request.getFileInput();
if (pagesPerSheet != 2 && pagesPerSheet != 3 && pagesPerSheet != (int) Math.sqrt(pagesPerSheet) * Math.sqrt(pagesPerSheet)) {
throw new IllegalArgumentException("pagesPerSheet must be 2, 3 or a perfect square");
}
int cols = pagesPerSheet == 2 || pagesPerSheet == 3 ? pagesPerSheet : (int) Math.sqrt(pagesPerSheet);
int rows = pagesPerSheet == 2 || pagesPerSheet == 3 ? 1 : (int) Math.sqrt(pagesPerSheet);
int pagesPerSheet = request.getPagesPerSheet();
MultipartFile file = request.getFileInput();
PDDocument sourceDocument = PDDocument.load(file.getInputStream());
PDDocument newDocument = new PDDocument();
PDPage newPage = new PDPage(PDRectangle.A4);
newDocument.addPage(newPage);
if (pagesPerSheet != 2 && pagesPerSheet != 3 && pagesPerSheet != (int) Math.sqrt(pagesPerSheet) * Math.sqrt(pagesPerSheet)) {
throw new IllegalArgumentException("pagesPerSheet must be 2, 3 or a perfect square");
}
int totalPages = sourceDocument.getNumberOfPages();
float cellWidth = newPage.getMediaBox().getWidth() / cols;
float cellHeight = newPage.getMediaBox().getHeight() / rows;
int cols = pagesPerSheet == 2 || pagesPerSheet == 3 ? pagesPerSheet : (int) Math.sqrt(pagesPerSheet);
int rows = pagesPerSheet == 2 || pagesPerSheet == 3 ? 1 : (int) Math.sqrt(pagesPerSheet);
PDPageContentStream contentStream = new PDPageContentStream(newDocument, newPage, PDPageContentStream.AppendMode.APPEND, true, true);
PDDocument sourceDocument = PDDocument.load(file.getInputStream());
PDDocument newDocument = new PDDocument();
PDPage newPage = new PDPage(PDRectangle.A4);
newDocument.addPage(newPage);
LayerUtility layerUtility = new LayerUtility(newDocument);
int totalPages = sourceDocument.getNumberOfPages();
float cellWidth = newPage.getMediaBox().getWidth() / cols;
float cellHeight = newPage.getMediaBox().getHeight() / rows;
for (int i = 0; i < totalPages; i++) {
PDPage sourcePage = sourceDocument.getPage(i);
System.out.println("Reading page " + (i+1));
PDRectangle rect = sourcePage.getMediaBox();
float scaleWidth = cellWidth / rect.getWidth();
float scaleHeight = cellHeight / rect.getHeight();
float scale = Math.min(scaleWidth, scaleHeight);
System.out.println("Scale for page " + (i+1) + ": " + scale);
PDPageContentStream contentStream = new PDPageContentStream(newDocument, newPage, PDPageContentStream.AppendMode.APPEND, true, true);
LayerUtility layerUtility = new LayerUtility(newDocument);
for (int i = 0; i < totalPages; i++) {
if (i != 0 && i % pagesPerSheet == 0) {
// Close the current content stream and create a new page and content stream
contentStream.close();
newPage = new PDPage(PDRectangle.A4);
newDocument.addPage(newPage);
contentStream = new PDPageContentStream(newDocument, newPage, PDPageContentStream.AppendMode.APPEND, true, true);
}
PDPage sourcePage = sourceDocument.getPage(i);
PDRectangle rect = sourcePage.getMediaBox();
float scaleWidth = cellWidth / rect.getWidth();
float scaleHeight = cellHeight / rect.getHeight();
float scale = Math.min(scaleWidth, scaleHeight);
int adjustedPageIndex = i % pagesPerSheet; // This will reset the index for every new page
int rowIndex = adjustedPageIndex / cols;
int colIndex = adjustedPageIndex % cols;
float x = colIndex * cellWidth + (cellWidth - rect.getWidth() * scale) / 2;
float y = newPage.getMediaBox().getHeight() - ((rowIndex + 1) * cellHeight - (cellHeight - rect.getHeight() * scale) / 2);
contentStream.saveGraphicsState();
contentStream.transform(Matrix.getTranslateInstance(x, y));
contentStream.transform(Matrix.getScaleInstance(scale, scale));
PDFormXObject formXObject = layerUtility.importPageAsForm(sourceDocument, i);
contentStream.drawForm(formXObject);
contentStream.restoreGraphicsState();
}
int rowIndex = i / cols;
int colIndex = i % cols;
contentStream.close(); // Close the final content stream
sourceDocument.close();
float x = colIndex * cellWidth + (cellWidth - rect.getWidth() * scale) / 2;
float y = newPage.getMediaBox().getHeight() - ((rowIndex + 1) * cellHeight - (cellHeight - rect.getHeight() * scale) / 2);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
newDocument.save(baos);
newDocument.close();
contentStream.saveGraphicsState();
contentStream.transform(Matrix.getTranslateInstance(x, y));
contentStream.transform(Matrix.getScaleInstance(scale, scale));
PDFormXObject formXObject = layerUtility.importPageAsForm(sourceDocument, i);
contentStream.drawForm(formXObject);
contentStream.restoreGraphicsState();
}
contentStream.close();
sourceDocument.close();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
newDocument.save(baos);
newDocument.close();
byte[] result = baos.toByteArray();
return WebResponseUtils.bytesToWebResponse(result, file.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_layoutChanged.pdf");
}

View file

@ -186,7 +186,8 @@ public class RearrangePagesPDFController {
} else {
newPageOrder = GeneralUtils.parsePageList(pageOrderArr, totalPages);
}
logger.info("newPageOrder = " +newPageOrder);
logger.info("totalPages = " +totalPages);
// Create a new list to hold the pages in the new order
List<PDPage> newPages = new ArrayList<>();
for (int i = 0; i < newPageOrder.size(); i++) {

View file

@ -36,7 +36,7 @@ public class ScalePagesController {
@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. Input:PDF Output:PDF Type:SISO")
public ResponseEntity<byte[]> scalePages(@ModelAttribute ScalePagesRequest request) throws IOException {
MultipartFile file = request.getFileInput();
String targetPDRectangle = request.getTargetPDRectangle();
String targetPDRectangle = request.getPageSize();
float scaleFactor = request.getScaleFactor();
Map<String, PDRectangle> sizeMap = new HashMap<>();

View file

@ -35,7 +35,7 @@ public class AutoRenameController {
@Operation(summary = "Extract header from PDF file", description = "This endpoint accepts a PDF file and attempts to extract its title or header based on heuristics. Input:PDF Output:PDF Type:SISO")
public ResponseEntity<byte[]> extractHeader(@ModelAttribute ExtractHeaderRequest request) throws Exception {
MultipartFile file = request.getFileInput();
Boolean useFirstTextAsFallback = request.getUseFirstTextAsFallback();
Boolean useFirstTextAsFallback = request.isUseFirstTextAsFallback();
PDDocument document = PDDocument.load(file.getInputStream());
PDFTextStripper reader = new PDFTextStripper() {

View file

@ -48,7 +48,7 @@ public class CompressController {
public ResponseEntity<byte[]> optimizePdf(@ModelAttribute OptimizePdfRequest request) throws Exception {
MultipartFile inputFile = request.getFileInput();
Integer optimizeLevel = request.getOptimizeLevel();
String expectedOutputSizeString = request.getExpectedOutputSizeString();
String expectedOutputSizeString = request.getExpectedOutputSize();
if(expectedOutputSizeString == null && optimizeLevel == null) {

View file

@ -48,7 +48,7 @@ public class MetadataController {
MultipartFile pdfFile = request.getFileInput();
// Extract metadata information
Boolean deleteAll = request.getDeleteAll();
Boolean deleteAll = request.isDeleteAll();
String author = request.getAuthor();
String creationDate = request.getCreationDate();
String creator = request.getCreator();
@ -61,7 +61,9 @@ public class MetadataController {
// Extract additional custom parameters
Map<String, String> allRequestParams = request.getAllRequestParams();
if(allRequestParams == null) {
allRequestParams = new java.util.HashMap<String, String>();
}
// Load the PDF file into a PDDocument
PDDocument document = PDDocument.load(pdfFile.getBytes());

View file

@ -53,14 +53,14 @@ public class OCRController {
description = "This endpoint processes a PDF file using OCR (Optical Character Recognition). Users can specify languages, sidecar, deskew, clean, cleanFinal, ocrType, ocrRenderType, and removeImagesAfter options. Input:PDF Output:PDF Type:SI-Conditional")
public ResponseEntity<byte[]> processPdfWithOCR(@ModelAttribute ProcessPdfWithOcrRequest request) throws IOException, InterruptedException {
MultipartFile inputFile = request.getFileInput();
List<String> selectedLanguages = request.getSelectedLanguages();
Boolean sidecar = request.getSidecar();
Boolean deskew = request.getDeskew();
Boolean clean = request.getClean();
Boolean cleanFinal = request.getCleanFinal();
List<String> selectedLanguages = request.getLanguages();
Boolean sidecar = request.isSidecar();
Boolean deskew = request.isDeskew();
Boolean clean = request.isClean();
Boolean cleanFinal = request.isCleanFinal();
String ocrType = request.getOcrType();
String ocrRenderType = request.getOcrRenderType();
Boolean removeImagesAfter = request.getRemoveImagesAfter();
Boolean removeImagesAfter = request.isRemoveImagesAfter();
// --output-type pdfa
if (selectedLanguages == null || selectedLanguages.isEmpty()) {
throw new IOException("Please select at least one language.");

View file

@ -75,7 +75,7 @@ public class CertSignController {
MultipartFile certFile = request.getCertFile();
MultipartFile p12File = request.getP12File();
String password = request.getPassword();
Boolean showSignature = request.getShowSignature();
Boolean showSignature = request.isShowSignature();
String reason = request.getReason();
String location = request.getLocation();
String name = request.getName();

View file

@ -39,11 +39,11 @@ public class SanitizeController {
description = "This endpoint processes a PDF file and removes specific elements based on the provided options. Input:PDF Output:PDF Type:SISO")
public ResponseEntity<byte[]> sanitizePDF(@ModelAttribute SanitizePdfRequest request) throws IOException {
MultipartFile inputFile = request.getFileInput();
Boolean removeJavaScript = request.getRemoveJavaScript();
Boolean removeEmbeddedFiles = request.getRemoveEmbeddedFiles();
Boolean removeMetadata = request.getRemoveMetadata();
Boolean removeLinks = request.getRemoveLinks();
Boolean removeFonts = request.getRemoveFonts();
boolean removeJavaScript = request.isRemoveJavaScript();
boolean removeEmbeddedFiles = request.isRemoveEmbeddedFiles();
boolean removeMetadata = request.isRemoveMetadata();
boolean removeLinks = request.isRemoveLinks();
boolean removeFonts = request.isRemoveFonts();
try (PDDocument document = PDDocument.load(inputFile.getInputStream())) {
if (removeJavaScript) {

View file

@ -148,6 +148,14 @@ public class GeneralWebController {
}
@GetMapping("/scale-pages")
@Hidden
public String scalePagesFrom(Model model) {
model.addAttribute("currentPage", "scale-pages");
return "scale-pages";
}
@Autowired
private ResourceLoader resourceLoader;

View file

@ -27,7 +27,7 @@ public class OtherWebController {
@GetMapping("/extract-image-scans")
@Hidden
public ModelAndView extractImageScansForm() {
ModelAndView modelAndView = new ModelAndView("other/extract-image-scans");
ModelAndView modelAndView = new ModelAndView("misc/extract-image-scans");
modelAndView.addObject("currentPage", "extract-image-scans");
return modelAndView;
}
@ -90,7 +90,7 @@ public class OtherWebController {
@GetMapping("/ocr-pdf")
@Hidden
public ModelAndView ocrPdfPage() {
ModelAndView modelAndView = new ModelAndView("other/ocr-pdf");
ModelAndView modelAndView = new ModelAndView("misc/ocr-pdf");
List<String> languages = getAvailableTesseractLanguages();
Collections.sort(languages);
modelAndView.addObject("languages", languages);
@ -127,13 +127,7 @@ public class OtherWebController {
return "misc/remove-blanks";
}
@GetMapping("/scale-pages")
@Hidden
public String scalePagesFrom(Model model) {
model.addAttribute("currentPage", "scale-pages");
return "misc/scale-pages";
}
@GetMapping("/auto-crop")
@Hidden
public String autoCropForm(Model model) {

View file

@ -0,0 +1,16 @@
package stirling.software.SPDF.model.api;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode(callSuper=true)
public class PDFWithPageSize extends PDFFile {
@Schema(description = "The scale of pages in the output PDF. Acceptable values are A0-A6, LETTER, LEGAL.",
allowableValues = {
"A0", "A1", "A2", "A3", "A4", "A5", "A6", "LETTER", "LEGAL"
})
private String pageSize;
}

View file

@ -4,17 +4,11 @@ import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import stirling.software.SPDF.model.api.PDFFile;
import stirling.software.SPDF.model.api.PDFWithPageSize;
@Data
@EqualsAndHashCode(callSuper=true)
public class ScalePagesRequest extends PDFFile {
@Schema(description = "The scale of pages in the output PDF. Acceptable values are A0-A10, B0-B9, LETTER, TABLOID, LEDGER, LEGAL, EXECUTIVE.",
allowableValues = {
"A0", "A1", "A2", "A3", "A4", "A5", "A6", "A7", "A8", "A9", "A10", "B0", "B1", "B2", "B3", "B4",
"B5", "B6", "B7", "B8", "B9", "LETTER", "TABLOID", "LEDGER", "LEGAL", "EXECUTIVE"
})
private String targetPDRectangle;
public class ScalePagesRequest extends PDFWithPageSize {
@Schema(description = "The scale of the content on the pages of the output PDF. Acceptable values are floats.")
private float scaleFactor;

View file

@ -10,5 +10,5 @@ import stirling.software.SPDF.model.api.PDFFile;
public class ExtractHeaderRequest extends PDFFile {
@Schema(description = "Flag indicating whether to use the first text as a fallback if no suitable title is found. Defaults to false.", required = false, defaultValue = "false")
private Boolean useFirstTextAsFallback;
private boolean useFirstTextAsFallback;
}

View file

@ -12,7 +12,7 @@ import stirling.software.SPDF.model.api.PDFFile;
public class MetadataRequest extends PDFFile {
@Schema(description = "Delete all metadata if set to true")
private Boolean deleteAll;
private boolean deleteAll;
@Schema(description = "The author of the document")
private String author;

View file

@ -14,5 +14,5 @@ public class OptimizePdfRequest extends PDFFile {
private Integer optimizeLevel;
@Schema(description = "The expected output size, e.g. '100MB', '25KB', etc.")
private String expectedOutputSizeString;
private String expectedOutputSize;
}

View file

@ -12,19 +12,19 @@ import stirling.software.SPDF.model.api.PDFFile;
public class ProcessPdfWithOcrRequest extends PDFFile {
@Schema(description = "List of languages to use in OCR processing")
private List<String> selectedLanguages;
private List<String> languages;
@Schema(description = "Include OCR text in a sidecar text file if set to true")
private Boolean sidecar;
private boolean sidecar;
@Schema(description = "Deskew the input file if set to true")
private Boolean deskew;
private boolean deskew;
@Schema(description = "Clean the input file if set to true")
private Boolean clean;
private boolean clean;
@Schema(description = "Clean the final output if set to true")
private Boolean cleanFinal;
private boolean cleanFinal;
@Schema(description = "Specify the OCR type, e.g., 'skip-text', 'force-ocr', or 'Normal'", allowableValues = {"skip-text", "force-ocr", "Normal"})
private String ocrType;
@ -33,5 +33,5 @@ public class ProcessPdfWithOcrRequest extends PDFFile {
private String ocrRenderType = "hocr";
@Schema(description = "Remove images from the output PDF if set to true")
private Boolean removeImagesAfter;
private boolean removeImagesAfter;
}

View file

@ -10,17 +10,17 @@ import stirling.software.SPDF.model.api.PDFFile;
public class SanitizePdfRequest extends PDFFile {
@Schema(description = "Remove JavaScript actions from the PDF", defaultValue = "false")
private Boolean removeJavaScript;
private boolean removeJavaScript;
@Schema(description = "Remove embedded files from the PDF", defaultValue = "false")
private Boolean removeEmbeddedFiles;
private boolean removeEmbeddedFiles;
@Schema(description = "Remove metadata from the PDF", defaultValue = "false")
private Boolean removeMetadata;
private boolean removeMetadata;
@Schema(description = "Remove links from the PDF", defaultValue = "false")
private Boolean removeLinks;
private boolean removeLinks;
@Schema(description = "Remove fonts from the PDF", defaultValue = "false")
private Boolean removeFonts;
private boolean removeFonts;
}

View file

@ -27,7 +27,7 @@ public class SignPDFWithCertRequest extends PDFFile {
private String password;
@Schema(description = "Whether to visually show the signature in the PDF file")
private Boolean showSignature;
private boolean showSignature;
@Schema(description = "The reason for signing the PDF")
private String reason;

View file

@ -18,7 +18,7 @@
<input type="hidden" id="customMode" name="customMode" value="">
<div class="mb-3">
<label for="pageOrder" th:text="#{pageOrderPrompt}"></label>
<input type="text" class="form-control" id="pageOrder" name="pageOrder" placeholder="(e.g. 1,2,8 or 4,7,12-16 or 2n-1)" required>
<input type="text" class="form-control" id="pageOrder" name="pageNumbers" placeholder="(e.g. 1,2,8 or 4,7,12-16 or 2n-1)" required>
</div>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{pageExtracter.submit}"></button>

View file

@ -14,7 +14,7 @@
<div class="row justify-content-center">
<div class="col-md-6">
<h2 th:text="#{scalePages.header}"></h2>
<form id="scalePagesFrom" th:action="@{api/v1/misc/scale-pages}" method="post" enctype="multipart/form-data">
<form id="scalePagesFrom" th:action="@{api/v1/general/scale-pages}" method="post" enctype="multipart/form-data">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
<div class="mb-3">
<label for="pageSize" th:text="#{scalePages.pageSize}"></label>

View file

@ -40,7 +40,7 @@
const formData = new FormData(event.target);
fetch('get-info-on-pdf', {
fetch('api/v1/security/get-info-on-pdf', {
method: 'POST',
body: formData
})