diff --git a/src/main/java/stirling/software/SPDF/controller/api/filters/FilterController.java b/src/main/java/stirling/software/SPDF/controller/api/filters/FilterController.java new file mode 100644 index 00000000..3a87d566 --- /dev/null +++ b/src/main/java/stirling/software/SPDF/controller/api/filters/FilterController.java @@ -0,0 +1,73 @@ +package stirling.software.SPDF.controller.api.filters; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestPart; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import stirling.software.SPDF.utils.PdfUtils; +import stirling.software.SPDF.utils.ProcessExecutor; +import stirling.software.SPDF.utils.WebResponseUtils; + +@RestController +@Tag(name = "Filter", description = "Filter APIs") +public class FilterController { + + @PostMapping(consumes = "multipart/form-data", value = "/contains-text") + @Operation(summary = "Checks if a PDF contains set text, returns true if does", description = "Input:PDF Output:Boolean Type:SISO") + public Boolean containsText( + @RequestPart(required = true, value = "fileInput") @Parameter(description = "The input PDF file to be converted to a PDF/A file", required = true) MultipartFile inputFile, + @Parameter(description = "The text to check for", required = true) String text, + @Parameter(description = "The page number to check for text on accepts 'All', ranges like '1-4'", required = false) String pageNumber) + throws IOException, InterruptedException { + PDDocument pdfDocument = PDDocument.load(inputFile.getInputStream()); + return PdfUtils.hasText(pdfDocument, pageNumber); + } + + @PostMapping(consumes = "multipart/form-data", value = "/contains-image") + @Operation(summary = "Checks if a PDF contains an image", description = "Input:PDF Output:Boolean Type:SISO") + public ResponseEntity containsImage( + @RequestPart(required = true, value = "fileInput") @Parameter(description = "The input PDF file to be converted to a PDF/A file", required = true) MultipartFile inputFile, + @Parameter(description = "The page number to check for image on accepts 'All', ranges like '1-4'", required = false) String pageNumber) + throws IOException, InterruptedException { + PDDocument pdfDocument = PDDocument.load(inputFile.getInputStream()); + return PdfUtils.hasImagesOnPage(null) + } + + @PostMapping(consumes = "multipart/form-data", value = "/page-count") + @Operation(summary = "Checks if a PDF is greater, less or equal to a setPageCount", description = "Input:PDF Output:Boolean Type:SISO") + public ResponseEntity pageCount( + @RequestPart(required = true, value = "fileInput") @Parameter(description = "The input PDF file to be converted to a PDF/A file", required = true) MultipartFile inputFile, + @Parameter(description = "Page COunt", required = true) String pageCount, + @Parameter(description = "Comparison type, accepts Greater, Equal, Less than", required = false) String comparitor) + throws IOException, InterruptedException { + return null; + } + + @PostMapping(consumes = "multipart/form-data", value = "/page-size") + @Operation(summary = "Checks if a PDF is a set size", description = "Input:PDF Output:Boolean Type:SISO") + public ResponseEntity pageSize( + @RequestPart(required = true, value = "fileInput") @Parameter(description = "The input PDF file to be converted to a PDF/A file", required = true) MultipartFile inputFile) + throws IOException, InterruptedException { + return null; + } + + @PostMapping(consumes = "multipart/form-data", value = "/page-rotation") + @Operation(summary = "Checks if a PDF is a set rotation", description = "Input:PDF Output:Boolean Type:SISO") + public ResponseEntity pageRotation( + @RequestPart(required = true, value = "fileInput") @Parameter(description = "The input PDF file to be converted to a PDF/A file", required = true) MultipartFile inputFile) + throws IOException, InterruptedException { + return null; + } +} diff --git a/src/main/java/stirling/software/SPDF/utils/PdfUtils.java b/src/main/java/stirling/software/SPDF/utils/PdfUtils.java index 7d7c8cec..f288f20d 100644 --- a/src/main/java/stirling/software/SPDF/utils/PdfUtils.java +++ b/src/main/java/stirling/software/SPDF/utils/PdfUtils.java @@ -42,16 +42,51 @@ public class PdfUtils { private static final Logger logger = LoggerFactory.getLogger(PdfUtils.class); + public boolean hasImageInFile(PDDocument pdfDocument, String text, String pagesToCheck) throws IOException { + PDFTextStripper textStripper = new PDFTextStripper(); + String pdfText = ""; + + if(pagesToCheck == null || pagesToCheck.equals("all")) { + pdfText = textStripper.getText(pdfDocument); + } else { + // remove whitespaces + pagesToCheck = pagesToCheck.replaceAll("\\s+", ""); + + String[] splitPoints = pagesToCheck.split(","); + for (String splitPoint : splitPoints) { + if (splitPoint.contains("-")) { + // Handle page ranges + String[] range = splitPoint.split("-"); + int startPage = Integer.parseInt(range[0]); + int endPage = Integer.parseInt(range[1]); + + for (int i = startPage; i <= endPage; i++) { + textStripper.setStartPage(i); + textStripper.setEndPage(i); + pdfText += textStripper.getText(pdfDocument); + } + } else { + // Handle individual page + int page = Integer.parseInt(splitPoint); + textStripper.setStartPage(page); + textStripper.setEndPage(page); + pdfText += textStripper.getText(pdfDocument); + } + } + } + + pdfDocument.close(); + + return pdfText.contains(text); + } + public static boolean hasImagesOnPage(PDPage page) throws IOException { ImageFinder imageFinder = new ImageFinder(page); imageFinder.processPage(page); return imageFinder.hasImages(); } - public static boolean hasTextOnPage(PdfPage page, String phrase) throws IOException { - String text = PdfTextExtractor.getTextFromPage(page, new SimpleTextExtractionStrategy()); - return text.contains(phrase); - } + public static boolean hasText(PDDocument document, String phrase) throws IOException { PDFTextStripper pdfStripper = new PDFTextStripper(); String text = pdfStripper.getText(document); @@ -59,6 +94,86 @@ public class PdfUtils { } + public boolean containsTextInFile(PDDocument pdfDocument, String text, String pagesToCheck) throws IOException { + PDFTextStripper textStripper = new PDFTextStripper(); + String pdfText = ""; + + if(pagesToCheck == null || pagesToCheck.equals("all")) { + pdfText = textStripper.getText(pdfDocument); + } else { + // remove whitespaces + pagesToCheck = pagesToCheck.replaceAll("\\s+", ""); + + String[] splitPoints = pagesToCheck.split(","); + for (String splitPoint : splitPoints) { + if (splitPoint.contains("-")) { + // Handle page ranges + String[] range = splitPoint.split("-"); + int startPage = Integer.parseInt(range[0]); + int endPage = Integer.parseInt(range[1]); + + for (int i = startPage; i <= endPage; i++) { + textStripper.setStartPage(i); + textStripper.setEndPage(i); + pdfText += textStripper.getText(pdfDocument); + } + } else { + // Handle individual page + int page = Integer.parseInt(splitPoint); + textStripper.setStartPage(page); + textStripper.setEndPage(page); + pdfText += textStripper.getText(pdfDocument); + } + } + } + + pdfDocument.close(); + + return pdfText.contains(text); + } + + + + + + public boolean pageCount(PDDocument pdfDocument, int pageCount, String comparator) throws IOException { + int actualPageCount = pdfDocument.getNumberOfPages(); + pdfDocument.close(); + + switch(comparator.toLowerCase()) { + case "greater": + return actualPageCount > pageCount; + case "equal": + return actualPageCount == pageCount; + case "less": + return actualPageCount < pageCount; + default: + throw new IllegalArgumentException("Invalid comparator. Only 'greater', 'equal', and 'less' are supported."); + } + } + + public boolean pageSize(PDDocument pdfDocument, String expectedPageSize) throws IOException { + PDPage firstPage = pdfDocument.getPage(0); + PDRectangle mediaBox = firstPage.getMediaBox(); + + float actualPageWidth = mediaBox.getWidth(); + float actualPageHeight = mediaBox.getHeight(); + + pdfDocument.close(); + + // Assumes the expectedPageSize is in the format "widthxheight", e.g. "595x842" for A4 + String[] dimensions = expectedPageSize.split("x"); + float expectedPageWidth = Float.parseFloat(dimensions[0]); + float expectedPageHeight = Float.parseFloat(dimensions[1]); + + // Checks if the actual page size matches the expected page size + return actualPageWidth == expectedPageWidth && actualPageHeight == expectedPageHeight; + } + + + + + 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))) { PDFRenderer pdfRenderer = new PDFRenderer(document); diff --git a/src/main/resources/messages_en_GB.properties b/src/main/resources/messages_en_GB.properties index 083bc8b6..391693f6 100644 --- a/src/main/resources/messages_en_GB.properties +++ b/src/main/resources/messages_en_GB.properties @@ -142,6 +142,8 @@ font=Font selectFillter=-- Select -- pageNum=Page Number +pipeline.title=Pipeline + pageLayout.title=Multi Page Layout pageLayout.header=Multi Page Layout pageLayout.pagesPerSheet=Pages per sheet: diff --git a/src/main/resources/static/js/pipeline.js b/src/main/resources/static/js/pipeline.js index a96c3f97..aa03ff23 100644 --- a/src/main/resources/static/js/pipeline.js +++ b/src/main/resources/static/js/pipeline.js @@ -120,24 +120,48 @@ let operationSettings = {}; fetch('v3/api-docs') .then(response => response.json()) .then(data => { - apiDocs = data.paths; let operationsDropdown = document.getElementById('operationsDropdown'); - const ignoreOperations = ["operationToIgnore", "operationToIgnore"]; // Add the operations you want to ignore here + const ignoreOperations = ["/handleData", "operationToIgnore"]; // Add the operations you want to ignore here operationsDropdown.innerHTML = ''; - Object.keys(apiDocs).forEach(operation => { - if (apiDocs[operation].hasOwnProperty('post')&& !ignoreOperations.includes(operation)) { - - let option = document.createElement('option'); - let operationWithoutSlash = operation.replace(/\//g, ''); // Remove slashes - option.textContent = operationWithoutSlash; - option.value = operation; // Keep the value with slashes for querying - operationsDropdown.appendChild(option); + let operationsByTag = {}; + + // Group operations by tags + Object.keys(data.paths).forEach(operationPath => { + let operation = data.paths[operationPath].post; + if (operation && !ignoreOperations.includes(operationPath)) { + let operationTag = operation.tags[0]; // This assumes each operation has exactly one tag + if (!operationsByTag[operationTag]) { + operationsByTag[operationTag] = []; + } + operationsByTag[operationTag].push(operationPath); } }); + + // Specify the order of tags + let tagOrder = ["General", "Security", "Convert", "Other", "Filter"]; + + // Create dropdown options + tagOrder.forEach(tag => { + if (operationsByTag[tag]) { + let group = document.createElement('optgroup'); + group.label = tag; + + operationsByTag[tag].forEach(operationPath => { + let option = document.createElement('option'); + let operationWithoutSlash = operationPath.replace(/\//g, ''); // Remove slashes + option.textContent = operationWithoutSlash; + option.value = operationPath; // Keep the value with slashes for querying + group.appendChild(option); + }); + + operationsDropdown.appendChild(group); + } + }); }); + document.getElementById('addOperationBtn').addEventListener('click', function() { let selectedOperation = document.getElementById('operationsDropdown').value; let pipelineList = document.getElementById('pipelineList'); diff --git a/src/main/resources/templates/pipeline.html b/src/main/resources/templates/pipeline.html index 53428fa8..7dd60c7f 100644 --- a/src/main/resources/templates/pipeline.html +++ b/src/main/resources/templates/pipeline.html @@ -3,7 +3,7 @@ th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org"> - +