From 91171727e4544b5a275e822b4ef5619e687590e5 Mon Sep 17 00:00:00 2001 From: Anthony Stirling <77850077+Frooodle@users.noreply.github.com> Date: Fri, 28 Apr 2023 23:18:10 +0100 Subject: [PATCH] rework for API, folder changes, easter eggs and fun --- Dockerfile | 11 +- DockerfileBase | 70 ++--- build.gradle | 4 +- scripts/init.sh | 9 + {pythonScripts => scripts}/split_photos.py | 7 +- .../software/SPDF/config/OpenApiConfig.java | 20 ++ .../SPDF/controller/MultiToolController.java | 20 -- .../SPDF/controller/PdfController.java | 25 -- .../controller/{ => api}/MergeController.java | 127 ++++---- .../RearrangePagesPDFController.java | 233 +++++++------- .../{ => api}/RotationController.java | 92 +++--- .../{ => api}/SplitPDFController.java | 268 ++++++++-------- .../converters/ConvertImgPDFController.java | 186 ++++++----- .../converters/ConvertOfficeController.java | 20 +- .../api/converters/ConvertPDFToOffice.java | 55 ++++ .../converters/ConvertPDFToPDFA.java | 19 +- .../{ => api}/other/CompressController.java | 162 +++++----- .../other/ExtractImageScansController.java | 40 +-- .../other/ExtractImagesController.java | 20 +- .../{ => api}/other/MetadataController.java | 17 +- .../{ => api}/other/OCRController.java | 38 +-- .../other/OverlayImageController.java | 82 +++-- .../security/PasswordController.java | 150 +++++---- .../security/WatermarkController.java | 293 +++++++++--------- .../converters/ConvertPDFToOffice.java | 86 ----- .../web/ConverterWebController.java | 86 +++++ .../controller/web/GeneralWebController.java | 62 ++++ .../controller/web/OtherWebController.java | 75 +++++ .../controller/web/SecurityWebController.java | 46 +++ .../software/SPDF/utils/PDFToFile.java | 3 +- .../software/SPDF/utils/PdfUtils.java | 8 +- .../resources/static/css/rainbow-mode.css | 35 +++ src/main/resources/static/rainbow.svg | 3 + .../resources/templates/fragments/common.html | 105 ++++--- .../templates/other/extract-image-scans.html | 25 ++ 35 files changed, 1345 insertions(+), 1157 deletions(-) create mode 100644 scripts/init.sh rename {pythonScripts => scripts}/split_photos.py (97%) create mode 100644 src/main/java/stirling/software/SPDF/config/OpenApiConfig.java delete mode 100644 src/main/java/stirling/software/SPDF/controller/MultiToolController.java delete mode 100644 src/main/java/stirling/software/SPDF/controller/PdfController.java rename src/main/java/stirling/software/SPDF/controller/{ => api}/MergeController.java (77%) rename src/main/java/stirling/software/SPDF/controller/{ => api}/RearrangePagesPDFController.java (80%) rename src/main/java/stirling/software/SPDF/controller/{ => api}/RotationController.java (69%) rename src/main/java/stirling/software/SPDF/controller/{ => api}/SplitPDFController.java (89%) rename src/main/java/stirling/software/SPDF/controller/{ => api}/converters/ConvertImgPDFController.java (76%) rename src/main/java/stirling/software/SPDF/controller/{ => api}/converters/ConvertOfficeController.java (82%) create mode 100644 src/main/java/stirling/software/SPDF/controller/api/converters/ConvertPDFToOffice.java rename src/main/java/stirling/software/SPDF/controller/{ => api}/converters/ConvertPDFToPDFA.java (77%) rename src/main/java/stirling/software/SPDF/controller/{ => api}/other/CompressController.java (82%) rename src/main/java/stirling/software/SPDF/controller/{ => api}/other/ExtractImageScansController.java (77%) rename src/main/java/stirling/software/SPDF/controller/{ => api}/other/ExtractImagesController.java (89%) rename src/main/java/stirling/software/SPDF/controller/{ => api}/other/MetadataController.java (93%) rename src/main/java/stirling/software/SPDF/controller/{ => api}/other/OCRController.java (77%) rename src/main/java/stirling/software/SPDF/controller/{ => api}/other/OverlayImageController.java (64%) rename src/main/java/stirling/software/SPDF/controller/{ => api}/security/PasswordController.java (65%) rename src/main/java/stirling/software/SPDF/controller/{ => api}/security/WatermarkController.java (84%) delete mode 100644 src/main/java/stirling/software/SPDF/controller/converters/ConvertPDFToOffice.java create mode 100644 src/main/java/stirling/software/SPDF/controller/web/ConverterWebController.java create mode 100644 src/main/java/stirling/software/SPDF/controller/web/GeneralWebController.java create mode 100644 src/main/java/stirling/software/SPDF/controller/web/OtherWebController.java create mode 100644 src/main/java/stirling/software/SPDF/controller/web/SecurityWebController.java create mode 100644 src/main/resources/static/css/rainbow-mode.css create mode 100644 src/main/resources/static/rainbow.svg diff --git a/Dockerfile b/Dockerfile index f2c478eb..50bbc809 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,9 +1,9 @@ # Build jbig2enc in a separate stage -FROM frooodle/stirling-pdf-base:beta +FROM frooodle/stirling-pdf-base:beta2 -# Create pythonScripts folder and copy local scripts -RUN mkdir /pythonScripts -COPY ./pythonScripts/* /pythonScripts/ +# Create scripts folder and copy local scripts +RUN mkdir /scripts +COPY ./scripts/* /scripts/ # Copy the application JAR file COPY build/libs/*.jar app.jar @@ -17,7 +17,8 @@ ENV APP_HOME_NAME="Stirling PDF" #ENV APP_NAVBAR_NAME="Stirling PDF" # Run the application -ENTRYPOINT java -jar /app.jar +ENTRYPOINT ["/scripts/init.sh"] +CMD ["java", "-jar", "/app.jar"] diff --git a/DockerfileBase b/DockerfileBase index 668b81f6..739b33b1 100644 --- a/DockerfileBase +++ b/DockerfileBase @@ -11,8 +11,8 @@ RUN apt-get update && \ pkg-config \ ca-certificates \ zlib1g-dev \ - make \ - g++ + make \ + g++ RUN git clone https://github.com/agl/jbig2enc && \ cd jbig2enc && \ @@ -21,10 +21,9 @@ RUN git clone https://github.com/agl/jbig2enc && \ make && \ make install -# Main stage -FROM openjdk:17-jdk-slim -# Install necessary dependencies +# Main stage +FROM openjdk:17-jdk-slim AS base RUN apt-get update && \ apt-get install -y --no-install-recommends \ libreoffice-core \ @@ -38,52 +37,27 @@ RUN apt-get update && \ pngquant \ unpaper \ ocrmypdf && \ - pip install --user --upgrade ocrmypdf && \ - pip3 install opencv-python-headless - -# Copy the jbig2enc binary from the builder stage -COPY --from=jbig2enc_builder /usr/local/bin/jbig2 /usr/local/bin/jbig2# Build jbig2enc in a separate stage -FROM debian:bullseye-slim as jbig2enc_builder + rm -rf /var/lib/apt/lists/* && \ + mkdir /usr/share/tesseract-ocr-original && \ + cp -r /usr/share/tesseract-ocr/* /usr/share/tesseract-ocr-original && \ + mv /usr/share/tesseract-ocr-original/4.00/tessdata/eng.traineddata /usr/share/tesseract-ocr-original/4.00/tessdata/english.traineddata && \ + rm -rf /usr/share/tesseract-ocr +# Python packages stage +FROM base AS python-packages RUN apt-get update && \ apt-get install -y --no-install-recommends \ - git \ - automake \ - autoconf \ - libtool \ - libleptonica-dev \ - pkg-config \ - ca-certificates \ + build-essential \ + libffi-dev \ + libssl-dev \ zlib1g-dev \ - make \ - g++ + libjpeg-dev && \ + pip install --upgrade pip && \ + pip install --no-cache-dir \ + opencv-python-headless && \ + rm -rf /var/lib/apt/lists/* -RUN git clone https://github.com/agl/jbig2enc && \ - cd jbig2enc && \ - ./autogen.sh && \ - ./configure && \ - make && \ - make install - -# Main stage -FROM openjdk:17-jdk-slim - -# Install necessary dependencies -RUN apt-get update && \ - apt-get install -y --no-install-recommends \ - libreoffice-core \ - libreoffice-common \ - libreoffice-writer \ - libreoffice-calc \ - libreoffice-impress \ - python3-uno \ - python3-pip \ - unoconv \ - pngquant \ - unpaper \ - ocrmypdf && \ - pip install --user --upgrade ocrmypdf && \ - pip3 install opencv-python-headless - -# Copy the jbig2enc binary from the builder stage +# Final stage: Copy necessary files from the previous stage +FROM base +COPY --from=python-packages /usr/local /usr/local COPY --from=jbig2enc_builder /usr/local/bin/jbig2 /usr/local/bin/jbig2 \ No newline at end of file diff --git a/build.gradle b/build.gradle index 1351ece7..0c785a3a 100644 --- a/build.gradle +++ b/build.gradle @@ -19,7 +19,9 @@ dependencies { // https://mvnrepository.com/artifact/org.apache.pdfbox/jbig2-imageio implementation group: 'org.apache.pdfbox', name: 'jbig2-imageio', version: '3.0.4' implementation 'commons-io:commons-io:2.11.0' - + + implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.1.0' + //general PDF implementation 'org.apache.pdfbox:pdfbox:2.0.28' diff --git a/scripts/init.sh b/scripts/init.sh new file mode 100644 index 00000000..b45bf45f --- /dev/null +++ b/scripts/init.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +# Copy the original tesseract-ocr files to the volume directory without overwriting existing files +echo "Copying original files without overwriting existing files" +mkdir -p /usr/share/tesseract-ocr +cp -rn /usr/share/tesseract-ocr-original/* /usr/share/tesseract-ocr + +# Run the main command +exec "$@" \ No newline at end of file diff --git a/pythonScripts/split_photos.py b/scripts/split_photos.py similarity index 97% rename from pythonScripts/split_photos.py rename to scripts/split_photos.py index ac854e24..13f44043 100644 --- a/pythonScripts/split_photos.py +++ b/scripts/split_photos.py @@ -85,7 +85,7 @@ def crop_borders(image, border_color, tolerance=30): return image[y:y+h, x:x+w] -def split_photos(input_file, output_directory, tolerance=30, min_area=10000, min_contour_area=500, angle_threshold=10, border_size=10): +def split_photos(input_file, output_directory, tolerance=30, min_area=10000, min_contour_area=500, angle_threshold=10, border_size=0): image = cv2.imread(input_file) background_color = estimate_background_color(image) @@ -121,7 +121,7 @@ if __name__ == "__main__": print(" [min_area] - Optional. Sets the minimum area threshold for a photo (default: 10000).") print(" [min_contour_area] - Optional. Sets the minimum contour area threshold for a photo (default: 500).") print(" [angle_threshold] - Optional. Sets the minimum absolute angle required for the image to be rotated (default: 10).") - print(" [border_size] - Optional. Sets the size of the border added and removed to prevent white borders in the output (default: 10).") + print(" [border_size] - Optional. Sets the size of the border added and removed to prevent white borders in the output (default: 0).") sys.exit(1) input_file = sys.argv[1] @@ -130,4 +130,5 @@ if __name__ == "__main__": min_area = int(sys.argv[4]) if len(sys.argv) > 4 else 8000 min_contour_area = int(sys.argv[5]) if len(sys.argv) > 5 else 500 angle_threshold = int(sys.argv[6]) if len(sys.argv) > 6 else 60 - split_photos(input_file, output_directory, tolerance=tolerance, min_area=min_area, min_contour_area=min_contour_area, angle_threshold=angle_threshold) + border_size = int(sys.argv[7]) if len(sys.argv) > 7 else 0 + split_photos(input_file, output_directory, tolerance=tolerance, min_area=min_area, min_contour_area=min_contour_area, angle_threshold=angle_threshold, border_size=border_size) diff --git a/src/main/java/stirling/software/SPDF/config/OpenApiConfig.java b/src/main/java/stirling/software/SPDF/config/OpenApiConfig.java new file mode 100644 index 00000000..6935f60f --- /dev/null +++ b/src/main/java/stirling/software/SPDF/config/OpenApiConfig.java @@ -0,0 +1,20 @@ +package stirling.software.SPDF.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import io.swagger.v3.oas.models.Components; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.info.Info; +import io.swagger.v3.oas.models.info.License; + +@Configuration +public class OpenApiConfig { + + @Bean + public OpenAPI customOpenAPI() { + return new OpenAPI().components(new Components()).info( + new Info().title("Your API Title").version("1.0.0").description("Your API Description").license(new License().name("Your License Name").url("Your License URL"))); + } + +} diff --git a/src/main/java/stirling/software/SPDF/controller/MultiToolController.java b/src/main/java/stirling/software/SPDF/controller/MultiToolController.java deleted file mode 100644 index cfe0ba72..00000000 --- a/src/main/java/stirling/software/SPDF/controller/MultiToolController.java +++ /dev/null @@ -1,20 +0,0 @@ -package stirling.software.SPDF.controller; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.stereotype.Controller; -import org.springframework.ui.Model; -import org.springframework.web.bind.annotation.GetMapping; - -@Controller -public class MultiToolController { - - private static final Logger logger = LoggerFactory.getLogger(MultiToolController.class); - - @GetMapping("/multi-tool") - public String multiToolForm(Model model) { - model.addAttribute("currentPage", "multi-tool"); - return "multi-tool"; - } - -} \ No newline at end of file diff --git a/src/main/java/stirling/software/SPDF/controller/PdfController.java b/src/main/java/stirling/software/SPDF/controller/PdfController.java deleted file mode 100644 index 90dc8229..00000000 --- a/src/main/java/stirling/software/SPDF/controller/PdfController.java +++ /dev/null @@ -1,25 +0,0 @@ -package stirling.software.SPDF.controller; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.stereotype.Controller; -import org.springframework.ui.Model; -import org.springframework.web.bind.annotation.GetMapping; - -@Controller -public class PdfController { - - private static final Logger logger = LoggerFactory.getLogger(PdfController.class); - - @GetMapping("/") - public String home(Model model) { - model.addAttribute("currentPage", "home"); - return "home"; - } - - @GetMapping("/home") - public String root(Model model) { - return "redirect:/"; - } - -} \ No newline at end of file diff --git a/src/main/java/stirling/software/SPDF/controller/MergeController.java b/src/main/java/stirling/software/SPDF/controller/api/MergeController.java similarity index 77% rename from src/main/java/stirling/software/SPDF/controller/MergeController.java rename to src/main/java/stirling/software/SPDF/controller/api/MergeController.java index c261c8e5..186656ff 100644 --- a/src/main/java/stirling/software/SPDF/controller/MergeController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/MergeController.java @@ -1,67 +1,62 @@ -package stirling.software.SPDF.controller; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import org.apache.pdfbox.pdmodel.PDDocument; -import org.apache.pdfbox.pdmodel.PDPage; -import org.apache.pdfbox.pdmodel.PDPageTree; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Controller; -import org.springframework.ui.Model; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.multipart.MultipartFile; - -import stirling.software.SPDF.utils.PdfUtils; - -@Controller -public class MergeController { - - private static final Logger logger = LoggerFactory.getLogger(MergeController.class); - - @GetMapping("/merge-pdfs") - public String hello(Model model) { - model.addAttribute("currentPage", "merge-pdfs"); - return "merge-pdfs"; - } - - private PDDocument mergeDocuments(List documents) throws IOException { - // Create a new empty document - PDDocument mergedDoc = new PDDocument(); - - // Iterate over the list of documents and add their pages to the merged document - for (PDDocument doc : documents) { - // Get all pages from the current document - PDPageTree pages = doc.getPages(); - // Iterate over the pages and add them to the merged document - for (PDPage page : pages) { - mergedDoc.addPage(page); - } - } - - // Return the merged document - return mergedDoc; - } - - @PostMapping("/merge-pdfs") - public ResponseEntity mergePdfs(@RequestParam("fileInput") MultipartFile[] files) throws IOException { - // Read the input PDF files into PDDocument objects - List documents = new ArrayList<>(); - - // Loop through the files array and read each file into a PDDocument - for (MultipartFile file : files) { - documents.add(PDDocument.load(file.getInputStream())); - } - - PDDocument mergedDoc = mergeDocuments(documents); - - // Return the merged PDF as a response - return PdfUtils.pdfDocToWebResponse(mergedDoc, files[0].getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_merged.pdf"); - } - +package stirling.software.SPDF.controller.api; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.pdfbox.pdmodel.PDPageTree; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.ResponseEntity; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +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.Hidden; +import stirling.software.SPDF.utils.PdfUtils; + +@RestController +public class MergeController { + + private static final Logger logger = LoggerFactory.getLogger(MergeController.class); + + private PDDocument mergeDocuments(List documents) throws IOException { + // Create a new empty document + PDDocument mergedDoc = new PDDocument(); + + // Iterate over the list of documents and add their pages to the merged document + for (PDDocument doc : documents) { + // Get all pages from the current document + PDPageTree pages = doc.getPages(); + // Iterate over the pages and add them to the merged document + for (PDPage page : pages) { + mergedDoc.addPage(page); + } + } + + // Return the merged document + return mergedDoc; + } + + @PostMapping(consumes = "multipart/form-data", value = "/merge-pdfs") + public ResponseEntity mergePdfs(@RequestPart(required = true, value = "fileInput") MultipartFile[] files) throws IOException { + // Read the input PDF files into PDDocument objects + List documents = new ArrayList<>(); + + // Loop through the files array and read each file into a PDDocument + for (MultipartFile file : files) { + documents.add(PDDocument.load(file.getInputStream())); + } + + PDDocument mergedDoc = mergeDocuments(documents); + + // Return the merged PDF as a response + return PdfUtils.pdfDocToWebResponse(mergedDoc, files[0].getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_merged.pdf"); + } + } \ No newline at end of file diff --git a/src/main/java/stirling/software/SPDF/controller/RearrangePagesPDFController.java b/src/main/java/stirling/software/SPDF/controller/api/RearrangePagesPDFController.java similarity index 80% rename from src/main/java/stirling/software/SPDF/controller/RearrangePagesPDFController.java rename to src/main/java/stirling/software/SPDF/controller/api/RearrangePagesPDFController.java index 053f3e26..5ea3b874 100644 --- a/src/main/java/stirling/software/SPDF/controller/RearrangePagesPDFController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/RearrangePagesPDFController.java @@ -1,121 +1,112 @@ -package stirling.software.SPDF.controller; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import org.apache.pdfbox.pdmodel.PDDocument; -import org.apache.pdfbox.pdmodel.PDPage; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Controller; -import org.springframework.ui.Model; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.multipart.MultipartFile; - -import stirling.software.SPDF.utils.PdfUtils; - -@Controller -public class RearrangePagesPDFController { - - private static final Logger logger = LoggerFactory.getLogger(RearrangePagesPDFController.class); - - @PostMapping("/remove-pages") - public ResponseEntity deletePages(@RequestParam("fileInput") MultipartFile pdfFile, @RequestParam("pagesToDelete") String pagesToDelete) throws IOException { - - PDDocument document = PDDocument.load(pdfFile.getBytes()); - - // Split the page order string into an array of page numbers or range of numbers - String[] pageOrderArr = pagesToDelete.split(","); - - List pagesToRemove = pageOrderToString(pageOrderArr, document.getNumberOfPages()); - - for (int i = pagesToRemove.size() - 1; i >= 0; i--) { - int pageIndex = pagesToRemove.get(i); - document.removePage(pageIndex); - } - return PdfUtils.pdfDocToWebResponse(document, pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_removed_pages.pdf"); - - } - - @GetMapping("/remove-pages") - public String pageDeleter(Model model) { - model.addAttribute("currentPage", "remove-pages"); - return "remove-pages"; - } - - private List pageOrderToString(String[] pageOrderArr, int totalPages) { - List newPageOrder = new ArrayList<>(); - // loop through the page order array - for (String element : pageOrderArr) { - // check if the element contains a range of pages - if (element.contains("-")) { - // split the range into start and end page - String[] range = element.split("-"); - int start = Integer.parseInt(range[0]); - int end = Integer.parseInt(range[1]); - // check if the end page is greater than total pages - if (end > totalPages) { - end = totalPages; - } - // loop through the range of pages - for (int j = start; j <= end; j++) { - // print the current index - newPageOrder.add(j - 1); - } - } else { - // if the element is a single page - newPageOrder.add(Integer.parseInt(element) - 1); - } - } - - return newPageOrder; - } - - @GetMapping("/pdf-organizer") - public String pageOrganizer(Model model) { - model.addAttribute("currentPage", "pdf-organizer"); - return "pdf-organizer"; - } - - @PostMapping("/rearrange-pages") - public ResponseEntity rearrangePages(@RequestParam("fileInput") MultipartFile pdfFile, @RequestParam("pageOrder") String pageOrder) { - try { - // Load the input PDF - PDDocument document = PDDocument.load(pdfFile.getInputStream()); - - // Split the page order string into an array of page numbers or range of numbers - String[] pageOrderArr = pageOrder.split(","); - // int[] newPageOrder = new int[pageOrderArr.length]; - int totalPages = document.getNumberOfPages(); - - List newPageOrder = pageOrderToString(pageOrderArr, totalPages); - - // Create a new list to hold the pages in the new order - List newPages = new ArrayList<>(); - for (int i = 0; i < newPageOrder.size(); i++) { - newPages.add(document.getPage(newPageOrder.get(i))); - } - - // Remove all the pages from the original document - for (int i = document.getNumberOfPages() - 1; i >= 0; i--) { - document.removePage(i); - } - - // Add the pages in the new order - for (PDPage page : newPages) { - document.addPage(page); - } - - return PdfUtils.pdfDocToWebResponse(document, pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_rearranged.pdf"); - } catch (IOException e) { - - logger.error("Failed rearranging documents", e); - return null; - } - } - -} +package stirling.software.SPDF.controller.api; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPage; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.ResponseEntity; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; +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.Hidden; +import stirling.software.SPDF.utils.PdfUtils; + +@RestController +public class RearrangePagesPDFController { + + private static final Logger logger = LoggerFactory.getLogger(RearrangePagesPDFController.class); + + @PostMapping(consumes = "multipart/form-data", value = "/remove-pages") + public ResponseEntity deletePages(@RequestPart(required = true, value = "fileInput") MultipartFile pdfFile, @RequestParam("pagesToDelete") String pagesToDelete) + throws IOException { + + PDDocument document = PDDocument.load(pdfFile.getBytes()); + + // Split the page order string into an array of page numbers or range of numbers + String[] pageOrderArr = pagesToDelete.split(","); + + List pagesToRemove = pageOrderToString(pageOrderArr, document.getNumberOfPages()); + + for (int i = pagesToRemove.size() - 1; i >= 0; i--) { + int pageIndex = pagesToRemove.get(i); + document.removePage(pageIndex); + } + return PdfUtils.pdfDocToWebResponse(document, pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_removed_pages.pdf"); + + } + + private List pageOrderToString(String[] pageOrderArr, int totalPages) { + List newPageOrder = new ArrayList<>(); + // loop through the page order array + for (String element : pageOrderArr) { + // check if the element contains a range of pages + if (element.contains("-")) { + // split the range into start and end page + String[] range = element.split("-"); + int start = Integer.parseInt(range[0]); + int end = Integer.parseInt(range[1]); + // check if the end page is greater than total pages + if (end > totalPages) { + end = totalPages; + } + // loop through the range of pages + for (int j = start; j <= end; j++) { + // print the current index + newPageOrder.add(j - 1); + } + } else { + // if the element is a single page + newPageOrder.add(Integer.parseInt(element) - 1); + } + } + + return newPageOrder; + } + + @PostMapping(consumes = "multipart/form-data", value = "/rearrange-pages") + public ResponseEntity rearrangePages(@RequestPart(required = true, value = "fileInput") MultipartFile pdfFile, @RequestParam("pageOrder") String pageOrder) { + try { + // Load the input PDF + PDDocument document = PDDocument.load(pdfFile.getInputStream()); + + // Split the page order string into an array of page numbers or range of numbers + String[] pageOrderArr = pageOrder.split(","); + // int[] newPageOrder = new int[pageOrderArr.length]; + int totalPages = document.getNumberOfPages(); + + List newPageOrder = pageOrderToString(pageOrderArr, totalPages); + + // Create a new list to hold the pages in the new order + List newPages = new ArrayList<>(); + for (int i = 0; i < newPageOrder.size(); i++) { + newPages.add(document.getPage(newPageOrder.get(i))); + } + + // Remove all the pages from the original document + for (int i = document.getNumberOfPages() - 1; i >= 0; i--) { + document.removePage(i); + } + + // Add the pages in the new order + for (PDPage page : newPages) { + document.addPage(page); + } + + return PdfUtils.pdfDocToWebResponse(document, pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_rearranged.pdf"); + } catch (IOException e) { + + logger.error("Failed rearranging documents", e); + return null; + } + } + +} diff --git a/src/main/java/stirling/software/SPDF/controller/RotationController.java b/src/main/java/stirling/software/SPDF/controller/api/RotationController.java similarity index 69% rename from src/main/java/stirling/software/SPDF/controller/RotationController.java rename to src/main/java/stirling/software/SPDF/controller/api/RotationController.java index f61fe40f..1a2ae4d4 100644 --- a/src/main/java/stirling/software/SPDF/controller/RotationController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/RotationController.java @@ -1,48 +1,44 @@ -package stirling.software.SPDF.controller; - -import java.io.IOException; - -import org.apache.pdfbox.pdmodel.PDDocument; -import org.apache.pdfbox.pdmodel.PDPage; -import org.apache.pdfbox.pdmodel.PDPageTree; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Controller; -import org.springframework.ui.Model; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.multipart.MultipartFile; - -import stirling.software.SPDF.utils.PdfUtils; - -@Controller -public class RotationController { - - private static final Logger logger = LoggerFactory.getLogger(RotationController.class); - - @PostMapping("/rotate-pdf") - public ResponseEntity rotatePDF(@RequestParam("fileInput") MultipartFile pdfFile, @RequestParam("angle") Integer angle) throws IOException { - - // Load the PDF document - PDDocument document = PDDocument.load(pdfFile.getBytes()); - - // Get the list of pages in the document - PDPageTree pages = document.getPages(); - - for (PDPage page : pages) { - page.setRotation(page.getRotation() + angle); - } - - return PdfUtils.pdfDocToWebResponse(document, pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_rotated.pdf"); - - } - - @GetMapping("/rotate-pdf") - public String rotatePdfForm(Model model) { - model.addAttribute("currentPage", "rotate-pdf"); - return "rotate-pdf"; - } - -} +package stirling.software.SPDF.controller.api; + +import java.io.IOException; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.pdfbox.pdmodel.PDPageTree; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.ResponseEntity; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; +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.Hidden; +import stirling.software.SPDF.utils.PdfUtils; + +@RestController +public class RotationController { + + private static final Logger logger = LoggerFactory.getLogger(RotationController.class); + + @PostMapping(consumes = "multipart/form-data", value = "/rotate-pdf") + public ResponseEntity rotatePDF(@RequestPart(required = true, value = "fileInput") MultipartFile pdfFile, @RequestParam("angle") Integer angle) throws IOException { + + // Load the PDF document + PDDocument document = PDDocument.load(pdfFile.getBytes()); + + // Get the list of pages in the document + PDPageTree pages = document.getPages(); + + for (PDPage page : pages) { + page.setRotation(page.getRotation() + angle); + } + + return PdfUtils.pdfDocToWebResponse(document, pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_rotated.pdf"); + + } + +} diff --git a/src/main/java/stirling/software/SPDF/controller/SplitPDFController.java b/src/main/java/stirling/software/SPDF/controller/api/SplitPDFController.java similarity index 89% rename from src/main/java/stirling/software/SPDF/controller/SplitPDFController.java rename to src/main/java/stirling/software/SPDF/controller/api/SplitPDFController.java index 37d49d63..51f8334d 100644 --- a/src/main/java/stirling/software/SPDF/controller/SplitPDFController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/SplitPDFController.java @@ -1,135 +1,133 @@ -package stirling.software.SPDF.controller; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.stream.Collectors; -import java.util.zip.ZipEntry; -import java.util.zip.ZipOutputStream; - -import org.apache.pdfbox.pdmodel.PDDocument; -import org.apache.pdfbox.pdmodel.PDPage; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.core.io.ByteArrayResource; -import org.springframework.core.io.Resource; -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Controller; -import org.springframework.ui.Model; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.multipart.MultipartFile; - -@Controller -public class SplitPDFController { - - private static final Logger logger = LoggerFactory.getLogger(SplitPDFController.class); - - @PostMapping("/split-pages") - public ResponseEntity splitPdf(@RequestParam("fileInput") MultipartFile file, @RequestParam("pages") String pages) throws IOException { - // parse user input - - // open the pdf document - InputStream inputStream = file.getInputStream(); - PDDocument document = PDDocument.load(inputStream); - - List pageNumbers = new ArrayList<>(); - pages = pages.replaceAll("\\s+", ""); // remove whitespaces - if (pages.toLowerCase().equals("all")) { - for (int i = 0; i < document.getNumberOfPages(); i++) { - pageNumbers.add(i); - } - } else { - List pageNumbersStr = new ArrayList<>(Arrays.asList(pages.split(","))); - if (!pageNumbersStr.contains(String.valueOf(document.getNumberOfPages()))) { - String lastpage = String.valueOf(document.getNumberOfPages()); - pageNumbersStr.add(lastpage); - } - for (String page : pageNumbersStr) { - if (page.contains("-")) { - String[] range = page.split("-"); - int start = Integer.parseInt(range[0]); - int end = Integer.parseInt(range[1]); - for (int i = start; i <= end; i++) { - pageNumbers.add(i); - } - } else { - pageNumbers.add(Integer.parseInt(page)); - } - } - } - - logger.info("Splitting PDF into pages: {}", pageNumbers.stream().map(String::valueOf).collect(Collectors.joining(","))); - - // split the document - List splitDocumentsBoas = new ArrayList<>(); - int currentPage = 0; - for (int pageNumber : pageNumbers) { - try (PDDocument splitDocument = new PDDocument()) { - for (int i = currentPage; i < pageNumber; i++) { - PDPage page = document.getPage(i); - splitDocument.addPage(page); - logger.debug("Adding page {} to split document", i); - } - currentPage = pageNumber; - logger.debug("Setting current page to {}", currentPage); - - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - splitDocument.save(baos); - - splitDocumentsBoas.add(baos); - } catch (Exception e) { - logger.error("Failed splitting documents and saving them", e); - throw e; - } - } - - // closing the original document - document.close(); - - Path zipFile = Files.createTempFile("split_documents", ".zip"); - - try (ZipOutputStream zipOut = new ZipOutputStream(Files.newOutputStream(zipFile))) { - // loop through the split documents and write them to the zip file - for (int i = 0; i < splitDocumentsBoas.size(); i++) { - String fileName = "split_document_" + (i + 1) + ".pdf"; - ByteArrayOutputStream baos = splitDocumentsBoas.get(i); - byte[] pdf = baos.toByteArray(); - - // Add PDF file to the zip - ZipEntry pdfEntry = new ZipEntry(fileName); - zipOut.putNextEntry(pdfEntry); - zipOut.write(pdf); - zipOut.closeEntry(); - - logger.info("Wrote split document {} to zip file", fileName); - } - } catch (Exception e) { - logger.error("Failed writing to zip", e); - throw e; - } - - logger.info("Successfully created zip file with split documents: {}", zipFile.toString()); - byte[] data = Files.readAllBytes(zipFile); - ByteArrayResource resource = new ByteArrayResource(data); - Files.delete(zipFile); - - // return the Resource in the response - return ResponseEntity.ok().header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + file.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_split.zip") - .contentType(MediaType.APPLICATION_OCTET_STREAM).contentLength(resource.contentLength()).body(resource); - } - - @GetMapping("/split-pdfs") - public String splitPdfForm(Model model) { - model.addAttribute("currentPage", "split-pdfs"); - return "split-pdfs"; - } -} +package stirling.software.SPDF.controller.api; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPage; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.io.ByteArrayResource; +import org.springframework.core.io.Resource; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; +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.Hidden; + +@RestController +public class SplitPDFController { + + private static final Logger logger = LoggerFactory.getLogger(SplitPDFController.class); + + @PostMapping(consumes = "multipart/form-data", value = "/split-pages") + public ResponseEntity splitPdf(@RequestPart(required = true, value = "fileInput") MultipartFile file, @RequestParam("pages") String pages) throws IOException { + // parse user input + + // open the pdf document + InputStream inputStream = file.getInputStream(); + PDDocument document = PDDocument.load(inputStream); + + List pageNumbers = new ArrayList<>(); + pages = pages.replaceAll("\\s+", ""); // remove whitespaces + if (pages.toLowerCase().equals("all")) { + for (int i = 0; i < document.getNumberOfPages(); i++) { + pageNumbers.add(i); + } + } else { + List pageNumbersStr = new ArrayList<>(Arrays.asList(pages.split(","))); + if (!pageNumbersStr.contains(String.valueOf(document.getNumberOfPages()))) { + String lastpage = String.valueOf(document.getNumberOfPages()); + pageNumbersStr.add(lastpage); + } + for (String page : pageNumbersStr) { + if (page.contains("-")) { + String[] range = page.split("-"); + int start = Integer.parseInt(range[0]); + int end = Integer.parseInt(range[1]); + for (int i = start; i <= end; i++) { + pageNumbers.add(i); + } + } else { + pageNumbers.add(Integer.parseInt(page)); + } + } + } + + logger.info("Splitting PDF into pages: {}", pageNumbers.stream().map(String::valueOf).collect(Collectors.joining(","))); + + // split the document + List splitDocumentsBoas = new ArrayList<>(); + int currentPage = 0; + for (int pageNumber : pageNumbers) { + try (PDDocument splitDocument = new PDDocument()) { + for (int i = currentPage; i < pageNumber; i++) { + PDPage page = document.getPage(i); + splitDocument.addPage(page); + logger.debug("Adding page {} to split document", i); + } + currentPage = pageNumber; + logger.debug("Setting current page to {}", currentPage); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + splitDocument.save(baos); + + splitDocumentsBoas.add(baos); + } catch (Exception e) { + logger.error("Failed splitting documents and saving them", e); + throw e; + } + } + + // closing the original document + document.close(); + + Path zipFile = Files.createTempFile("split_documents", ".zip"); + + try (ZipOutputStream zipOut = new ZipOutputStream(Files.newOutputStream(zipFile))) { + // loop through the split documents and write them to the zip file + for (int i = 0; i < splitDocumentsBoas.size(); i++) { + String fileName = "split_document_" + (i + 1) + ".pdf"; + ByteArrayOutputStream baos = splitDocumentsBoas.get(i); + byte[] pdf = baos.toByteArray(); + + // Add PDF file to the zip + ZipEntry pdfEntry = new ZipEntry(fileName); + zipOut.putNextEntry(pdfEntry); + zipOut.write(pdf); + zipOut.closeEntry(); + + logger.info("Wrote split document {} to zip file", fileName); + } + } catch (Exception e) { + logger.error("Failed writing to zip", e); + throw e; + } + + logger.info("Successfully created zip file with split documents: {}", zipFile.toString()); + byte[] data = Files.readAllBytes(zipFile); + ByteArrayResource resource = new ByteArrayResource(data); + Files.delete(zipFile); + + // return the Resource in the response + return ResponseEntity.ok().header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + file.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_split.zip") + .contentType(MediaType.APPLICATION_OCTET_STREAM).contentLength(resource.contentLength()).body(resource); + } + +} diff --git a/src/main/java/stirling/software/SPDF/controller/converters/ConvertImgPDFController.java b/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertImgPDFController.java similarity index 76% rename from src/main/java/stirling/software/SPDF/controller/converters/ConvertImgPDFController.java rename to src/main/java/stirling/software/SPDF/controller/api/converters/ConvertImgPDFController.java index f473b60d..ae14bd6e 100644 --- a/src/main/java/stirling/software/SPDF/controller/converters/ConvertImgPDFController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertImgPDFController.java @@ -1,97 +1,89 @@ -package stirling.software.SPDF.controller.converters; - -import java.io.IOException; - -import org.apache.pdfbox.rendering.ImageType; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.core.io.ByteArrayResource; -import org.springframework.core.io.Resource; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Controller; -import org.springframework.ui.Model; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.multipart.MultipartFile; - -import stirling.software.SPDF.utils.PdfUtils; - -@Controller -public class ConvertImgPDFController { - - private static final Logger logger = LoggerFactory.getLogger(ConvertImgPDFController.class); - - @PostMapping("/pdf-to-img") - public ResponseEntity convertToImage(@RequestParam("fileInput") MultipartFile file, @RequestParam("imageFormat") String imageFormat, - @RequestParam("singleOrMultiple") String singleOrMultiple, @RequestParam("colorType") String colorType, @RequestParam("dpi") String dpi) throws IOException { - - byte[] pdfBytes = file.getBytes(); - ImageType colorTypeResult = ImageType.RGB; - if ("greyscale".equals(colorType)) { - colorTypeResult = ImageType.GRAY; - } else if ("blackwhite".equals(colorType)) { - colorTypeResult = ImageType.BINARY; - } - // returns bytes for image - boolean singleImage = singleOrMultiple.equals("single"); - byte[] result = null; - try { - result = PdfUtils.convertFromPdf(pdfBytes, imageFormat.toUpperCase(), colorTypeResult, singleImage, Integer.valueOf(dpi)); - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (Exception e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - if (singleImage) { - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.parseMediaType(getMediaType(imageFormat))); - ResponseEntity response = new ResponseEntity<>(new ByteArrayResource(result), headers, HttpStatus.OK); - return response; - } else { - ByteArrayResource resource = new ByteArrayResource(result); - // return the Resource in the response - return ResponseEntity.ok() - .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + file.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_convertedToImages.zip") - .contentType(MediaType.APPLICATION_OCTET_STREAM).contentLength(resource.contentLength()).body(resource); - } - } - - @PostMapping("/img-to-pdf") - public ResponseEntity convertToPdf(@RequestParam("fileInput") MultipartFile[] file, @RequestParam(defaultValue = "false", name = "stretchToFit") boolean stretchToFit, - @RequestParam(defaultValue = "true", name = "autoRotate") boolean autoRotate) throws IOException { - // Convert the file to PDF and get the resulting bytes - System.out.println(stretchToFit); - byte[] bytes = PdfUtils.imageToPdf(file, stretchToFit, autoRotate); - return PdfUtils.bytesToWebResponse(bytes, file[0].getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_coverted.pdf"); - } - - @GetMapping("/img-to-pdf") - public String convertToPdfForm(Model model) { - model.addAttribute("currentPage", "img-to-pdf"); - return "convert/img-to-pdf"; - } - - private String getMediaType(String imageFormat) { - if (imageFormat.equalsIgnoreCase("PNG")) - return "image/png"; - else if (imageFormat.equalsIgnoreCase("JPEG") || imageFormat.equalsIgnoreCase("JPG")) - return "image/jpeg"; - else if (imageFormat.equalsIgnoreCase("GIF")) - return "image/gif"; - else - return "application/octet-stream"; - } - - @GetMapping("/pdf-to-img") - public String pdfToimgForm(Model model) { - model.addAttribute("currentPage", "pdf-to-img"); - return "convert/pdf-to-img"; - } - -} +package stirling.software.SPDF.controller.api.converters; + +import java.io.IOException; + +import org.apache.pdfbox.rendering.ImageType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.io.ByteArrayResource; +import org.springframework.core.io.Resource; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; +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.Hidden; +import stirling.software.SPDF.utils.PdfUtils; + +@RestController +public class ConvertImgPDFController { + + private static final Logger logger = LoggerFactory.getLogger(ConvertImgPDFController.class); + + @PostMapping(consumes = "multipart/form-data", value = "/pdf-to-img") + public ResponseEntity convertToImage(@RequestPart(required = true, value = "fileInput") MultipartFile file, @RequestParam("imageFormat") String imageFormat, + @RequestParam("singleOrMultiple") String singleOrMultiple, @RequestParam("colorType") String colorType, @RequestParam("dpi") String dpi) throws IOException { + + byte[] pdfBytes = file.getBytes(); + ImageType colorTypeResult = ImageType.RGB; + if ("greyscale".equals(colorType)) { + colorTypeResult = ImageType.GRAY; + } else if ("blackwhite".equals(colorType)) { + colorTypeResult = ImageType.BINARY; + } + // returns bytes for image + boolean singleImage = singleOrMultiple.equals("single"); + byte[] result = null; + try { + result = PdfUtils.convertFromPdf(pdfBytes, imageFormat.toUpperCase(), colorTypeResult, singleImage, Integer.valueOf(dpi)); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + if (singleImage) { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.parseMediaType(getMediaType(imageFormat))); + ResponseEntity response = new ResponseEntity<>(new ByteArrayResource(result), headers, HttpStatus.OK); + return response; + } else { + ByteArrayResource resource = new ByteArrayResource(result); + // return the Resource in the response + return ResponseEntity.ok() + .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + file.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_convertedToImages.zip") + .contentType(MediaType.APPLICATION_OCTET_STREAM).contentLength(resource.contentLength()).body(resource); + } + } + + @PostMapping(consumes = "multipart/form-data", value = "/img-to-pdf") + public ResponseEntity convertToPdf(@RequestPart(required = true, value = "fileInput") MultipartFile[] file, + @RequestParam(defaultValue = "false", name = "stretchToFit") boolean stretchToFit, @RequestParam(defaultValue = "true", name = "autoRotate") boolean autoRotate) + throws IOException { + // Convert the file to PDF and get the resulting bytes + System.out.println(stretchToFit); + byte[] bytes = PdfUtils.imageToPdf(file, stretchToFit, autoRotate); + return PdfUtils.bytesToWebResponse(bytes, file[0].getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_coverted.pdf"); + } + + private String getMediaType(String imageFormat) { + if (imageFormat.equalsIgnoreCase("PNG")) + return "image/png"; + else if (imageFormat.equalsIgnoreCase("JPEG") || imageFormat.equalsIgnoreCase("JPG")) + return "image/jpeg"; + else if (imageFormat.equalsIgnoreCase("GIF")) + return "image/gif"; + else + return "application/octet-stream"; + } + + +} diff --git a/src/main/java/stirling/software/SPDF/controller/converters/ConvertOfficeController.java b/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertOfficeController.java similarity index 82% rename from src/main/java/stirling/software/SPDF/controller/converters/ConvertOfficeController.java rename to src/main/java/stirling/software/SPDF/controller/api/converters/ConvertOfficeController.java index 03f04acd..f6a91cfa 100644 --- a/src/main/java/stirling/software/SPDF/controller/converters/ConvertOfficeController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertOfficeController.java @@ -1,4 +1,4 @@ -package stirling.software.SPDF.controller.converters; +package stirling.software.SPDF.controller.api.converters; import java.io.IOException; import java.nio.file.Files; @@ -10,17 +10,18 @@ import java.util.List; import org.apache.commons.io.FilenameUtils; import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestParam; +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.Hidden; import stirling.software.SPDF.utils.PdfUtils; import stirling.software.SPDF.utils.ProcessExecutor; -@Controller +@RestController public class ConvertOfficeController { public byte[] convertToPdf(MultipartFile inputFile) throws IOException, InterruptedException { @@ -50,20 +51,13 @@ public class ConvertOfficeController { return pdfBytes; } - - @GetMapping("/file-to-pdf") - public String convertToPdfForm(Model model) { - model.addAttribute("currentPage", "file-to-pdf"); - return "convert/file-to-pdf"; - } - private boolean isValidFileExtension(String fileExtension) { String extensionPattern = "^(?i)[a-z0-9]{2,4}$"; return fileExtension.matches(extensionPattern); } - @PostMapping("/file-to-pdf") - public ResponseEntity processPdfWithOCR(@RequestParam("fileInput") MultipartFile inputFile) throws IOException, InterruptedException { + @PostMapping(consumes = "multipart/form-data", value = "/file-to-pdf") + public ResponseEntity processPdfWithOCR(@RequestPart(required = true, value = "fileInput") MultipartFile inputFile) throws IOException, InterruptedException { // unused but can start server instance if startup time is to long // LibreOfficeListener.getInstance().start(); diff --git a/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertPDFToOffice.java b/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertPDFToOffice.java new file mode 100644 index 00000000..6dae6e76 --- /dev/null +++ b/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertPDFToOffice.java @@ -0,0 +1,55 @@ +package stirling.software.SPDF.controller.api.converters; + +import java.io.IOException; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RequestPart; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.servlet.ModelAndView; + +import io.swagger.v3.oas.annotations.Hidden; +import stirling.software.SPDF.utils.PDFToFile; + +@RestController +public class ConvertPDFToOffice { + + + + @PostMapping(consumes = "multipart/form-data", value = "/pdf-to-html") + public ResponseEntity processPdfToHTML(@RequestPart(required = true, value = "fileInput") MultipartFile inputFile) throws IOException, InterruptedException { + PDFToFile pdfToFile = new PDFToFile(); + return pdfToFile.processPdfToOfficeFormat(inputFile, "html", "writer_pdf_import"); + } + + @PostMapping(consumes = "multipart/form-data", value = "/pdf-to-presentation") + public ResponseEntity processPdfToPresentation(@RequestPart(required = true, value = "fileInput") MultipartFile inputFile, + @RequestParam("outputFormat") String outputFormat) throws IOException, InterruptedException { + PDFToFile pdfToFile = new PDFToFile(); + return pdfToFile.processPdfToOfficeFormat(inputFile, outputFormat, "impress_pdf_import"); + } + + @PostMapping(consumes = "multipart/form-data", value = "/pdf-to-text") + public ResponseEntity processPdfToRTForTXT(@RequestPart(required = true, value = "fileInput") MultipartFile inputFile, + @RequestParam("outputFormat") String outputFormat) throws IOException, InterruptedException { + PDFToFile pdfToFile = new PDFToFile(); + return pdfToFile.processPdfToOfficeFormat(inputFile, outputFormat, "writer_pdf_import"); + } + + @PostMapping(consumes = "multipart/form-data", value = "/pdf-to-word") + public ResponseEntity processPdfToWord(@RequestPart(required = true, value = "fileInput") MultipartFile inputFile, @RequestParam("outputFormat") String outputFormat) + throws IOException, InterruptedException { + PDFToFile pdfToFile = new PDFToFile(); + return pdfToFile.processPdfToOfficeFormat(inputFile, outputFormat, "writer_pdf_import"); + } + + @PostMapping(consumes = "multipart/form-data", value = "/pdf-to-xml") + public ResponseEntity processPdfToXML(@RequestPart(required = true, value = "fileInput") MultipartFile inputFile) throws IOException, InterruptedException { + PDFToFile pdfToFile = new PDFToFile(); + return pdfToFile.processPdfToOfficeFormat(inputFile, "xml", "writer_pdf_import"); + } + +} diff --git a/src/main/java/stirling/software/SPDF/controller/converters/ConvertPDFToPDFA.java b/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertPDFToPDFA.java similarity index 77% rename from src/main/java/stirling/software/SPDF/controller/converters/ConvertPDFToPDFA.java rename to src/main/java/stirling/software/SPDF/controller/api/converters/ConvertPDFToPDFA.java index 89776989..8d07084e 100644 --- a/src/main/java/stirling/software/SPDF/controller/converters/ConvertPDFToPDFA.java +++ b/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertPDFToPDFA.java @@ -1,4 +1,4 @@ -package stirling.software.SPDF.controller.converters; +package stirling.software.SPDF.controller.api.converters; import java.io.IOException; import java.nio.file.Files; @@ -7,21 +7,22 @@ import java.util.ArrayList; import java.util.List; import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestParam; +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.Hidden; import stirling.software.SPDF.utils.PdfUtils; import stirling.software.SPDF.utils.ProcessExecutor; -@Controller +@RestController public class ConvertPDFToPDFA { - @PostMapping("/pdf-to-pdfa") - public ResponseEntity pdfToPdfA(@RequestParam("fileInput") MultipartFile inputFile) throws IOException, InterruptedException { + @PostMapping(consumes = "multipart/form-data", value = "/pdf-to-pdfa") + public ResponseEntity pdfToPdfA(@RequestPart(required = true, value = "fileInput") MultipartFile inputFile) throws IOException, InterruptedException { // Save the uploaded file to a temporary location Path tempInputFile = Files.createTempFile("input_", ".pdf"); @@ -54,10 +55,4 @@ public class ConvertPDFToPDFA { return PdfUtils.bytesToWebResponse(pdfBytes, outputFilename); } - @GetMapping("/pdf-to-pdfa") - public String pdfToPdfAForm(Model model) { - model.addAttribute("currentPage", "pdf-to-pdfa"); - return "convert/pdf-to-pdfa"; - } - } diff --git a/src/main/java/stirling/software/SPDF/controller/other/CompressController.java b/src/main/java/stirling/software/SPDF/controller/api/other/CompressController.java similarity index 82% rename from src/main/java/stirling/software/SPDF/controller/other/CompressController.java rename to src/main/java/stirling/software/SPDF/controller/api/other/CompressController.java index 1b557d85..86304751 100644 --- a/src/main/java/stirling/software/SPDF/controller/other/CompressController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/other/CompressController.java @@ -1,83 +1,79 @@ -package stirling.software.SPDF.controller.other; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.List; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Controller; -import org.springframework.ui.Model; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.multipart.MultipartFile; - -import stirling.software.SPDF.utils.PdfUtils; -import stirling.software.SPDF.utils.ProcessExecutor; - -@Controller -public class CompressController { - - private static final Logger logger = LoggerFactory.getLogger(CompressController.class); - - @GetMapping("/compress-pdf") - public String compressPdfForm(Model model) { - model.addAttribute("currentPage", "compress-pdf"); - return "other/compress-pdf"; - } - - @PostMapping("/compress-pdf") - public ResponseEntity optimizePdf(@RequestParam("fileInput") MultipartFile inputFile, @RequestParam("optimizeLevel") int optimizeLevel, - @RequestParam(name = "fastWebView", required = false) Boolean fastWebView, @RequestParam(name = "jbig2Lossy", required = false) Boolean jbig2Lossy) - throws IOException, InterruptedException { - - // Save the uploaded file to a temporary location - Path tempInputFile = Files.createTempFile("input_", ".pdf"); - inputFile.transferTo(tempInputFile.toFile()); - - // Prepare the output file path - Path tempOutputFile = Files.createTempFile("output_", ".pdf"); - - // Prepare the OCRmyPDF command - List command = new ArrayList<>(); - command.add("ocrmypdf"); - command.add("--skip-text"); - command.add("--tesseract-timeout=0"); - command.add("--optimize"); - command.add(String.valueOf(optimizeLevel)); - command.add("--output-type"); - command.add("pdf"); - - if (fastWebView != null && fastWebView) { - long fileSize = inputFile.getSize(); - long fastWebViewSize = (long) (fileSize * 1.25); // 25% higher than file size - command.add("--fast-web-view"); - command.add(String.valueOf(fastWebViewSize)); - } - - if (jbig2Lossy != null && jbig2Lossy) { - command.add("--jbig2-lossy"); - } - - command.add(tempInputFile.toString()); - command.add(tempOutputFile.toString()); - - int returnCode = ProcessExecutor.getInstance(ProcessExecutor.Processes.OCR_MY_PDF).runCommandWithOutputHandling(command); - - // Read the optimized PDF file - byte[] pdfBytes = Files.readAllBytes(tempOutputFile); - - // Clean up the temporary files - Files.delete(tempInputFile); - Files.delete(tempOutputFile); - - // Return the optimized PDF as a response - String outputFilename = inputFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_Optimized.pdf"; - return PdfUtils.bytesToWebResponse(pdfBytes, outputFilename); - } - -} +package stirling.software.SPDF.controller.api.other; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.ResponseEntity; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; +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.Hidden; +import stirling.software.SPDF.utils.PdfUtils; +import stirling.software.SPDF.utils.ProcessExecutor; + +@RestController +public class CompressController { + + private static final Logger logger = LoggerFactory.getLogger(CompressController.class); + + @PostMapping(consumes = "multipart/form-data", value = "/compress-pdf") + public ResponseEntity optimizePdf(@RequestPart(required = true, value = "fileInput") MultipartFile inputFile, @RequestParam("optimizeLevel") int optimizeLevel, + @RequestParam(name = "fastWebView", required = false) Boolean fastWebView, @RequestParam(name = "jbig2Lossy", required = false) Boolean jbig2Lossy) + throws IOException, InterruptedException { + + // Save the uploaded file to a temporary location + Path tempInputFile = Files.createTempFile("input_", ".pdf"); + inputFile.transferTo(tempInputFile.toFile()); + + // Prepare the output file path + Path tempOutputFile = Files.createTempFile("output_", ".pdf"); + + // Prepare the OCRmyPDF command + List command = new ArrayList<>(); + command.add("ocrmypdf"); + command.add("--skip-text"); + command.add("--tesseract-timeout=0"); + command.add("--optimize"); + command.add(String.valueOf(optimizeLevel)); + command.add("--output-type"); + command.add("pdf"); + + if (fastWebView != null && fastWebView) { + long fileSize = inputFile.getSize(); + long fastWebViewSize = (long) (fileSize * 1.25); // 25% higher than file size + command.add("--fast-web-view"); + command.add(String.valueOf(fastWebViewSize)); + } + + if (jbig2Lossy != null && jbig2Lossy) { + command.add("--jbig2-lossy"); + } + + command.add(tempInputFile.toString()); + command.add(tempOutputFile.toString()); + + int returnCode = ProcessExecutor.getInstance(ProcessExecutor.Processes.OCR_MY_PDF).runCommandWithOutputHandling(command); + + // Read the optimized PDF file + byte[] pdfBytes = Files.readAllBytes(tempOutputFile); + + // Clean up the temporary files + Files.delete(tempInputFile); + Files.delete(tempOutputFile); + + // Return the optimized PDF as a response + String outputFilename = inputFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_Optimized.pdf"; + return PdfUtils.bytesToWebResponse(pdfBytes, outputFilename); + } + +} diff --git a/src/main/java/stirling/software/SPDF/controller/other/ExtractImageScansController.java b/src/main/java/stirling/software/SPDF/controller/api/other/ExtractImageScansController.java similarity index 77% rename from src/main/java/stirling/software/SPDF/controller/other/ExtractImageScansController.java rename to src/main/java/stirling/software/SPDF/controller/api/other/ExtractImageScansController.java index e3562697..3a9519c6 100644 --- a/src/main/java/stirling/software/SPDF/controller/other/ExtractImageScansController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/other/ExtractImageScansController.java @@ -1,10 +1,9 @@ -package stirling.software.SPDF.controller.other; +package stirling.software.SPDF.controller.api.other; import java.awt.image.BufferedImage; import java.io.ByteArrayInputStream; import java.io.FileOutputStream; import java.io.IOException; -import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardCopyOption; @@ -24,42 +23,35 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RequestPart; +import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; import org.springframework.web.servlet.ModelAndView; +import io.swagger.v3.oas.annotations.Hidden; import stirling.software.SPDF.utils.PdfUtils; import stirling.software.SPDF.utils.ProcessExecutor; -@Controller +@RestController public class ExtractImageScansController { private static final Logger logger = LoggerFactory.getLogger(ExtractImageScansController.class); - - @GetMapping("/extract-image-scans") - public ModelAndView extractImageScansForm() { - ModelAndView modelAndView = new ModelAndView("other/extract-image-scans"); - modelAndView.addObject("currentPage", "extract-image-scans"); - return modelAndView; - } - - @PostMapping("/extract-image-scans") - public ResponseEntity extractImageScans(@RequestParam("fileInput") MultipartFile inputFile, - @RequestParam(name = "angle_threshold", defaultValue = "5") int angleThreshold, - @RequestParam(name = "tolerance", defaultValue = "20") int tolerance, - @RequestParam(name = "min_area", defaultValue = "8000") int minArea, - @RequestParam(name = "min_contour_area", defaultValue = "500") int minContourArea) throws IOException, InterruptedException { + @PostMapping(consumes = "multipart/form-data", value = "/extract-image-scans") + public ResponseEntity extractImageScans(@RequestPart(required = true, value = "fileInput") MultipartFile inputFile, + @RequestParam(name = "angle_threshold", defaultValue = "5") int angleThreshold, @RequestParam(name = "tolerance", defaultValue = "20") int tolerance, + @RequestParam(name = "min_area", defaultValue = "8000") int minArea, @RequestParam(name = "min_contour_area", defaultValue = "500") int minContourArea, + @RequestParam(name = "border_size", defaultValue = "1") int borderSize) throws IOException, InterruptedException { String fileName = inputFile.getOriginalFilename(); String extension = fileName.substring(fileName.lastIndexOf(".") + 1); List images = new ArrayList<>(); - // Check if input file is a PDF + // Check if input file is a PDF if (extension.equalsIgnoreCase("pdf")) { // Load PDF document try (PDDocument document = PDDocument.load(new ByteArrayInputStream(inputFile.getBytes()))) { @@ -87,14 +79,14 @@ public class ExtractImageScansController { images.add(tempInputFile.toString()); } - List processedImageBytes = new ArrayList<>(); // Process each image for (int i = 0; i < images.size(); i++) { Path tempDir = Files.createTempDirectory("openCV_output"); - List command = new ArrayList<>(Arrays.asList("python3", "/pythonScripts/split_photos.py", images.get(i), tempDir.toString(), String.valueOf(angleThreshold), String.valueOf(tolerance),String.valueOf(minArea),String.valueOf(minContourArea))); + List command = new ArrayList<>(Arrays.asList("python3", "/scripts/split_photos.py", images.get(i), tempDir.toString(), String.valueOf(angleThreshold), + String.valueOf(tolerance), String.valueOf(minArea), String.valueOf(minContourArea), String.valueOf(borderSize))); // Run CLI command int returnCode = ProcessExecutor.getInstance(ProcessExecutor.Processes.PYTHON_OPENCV).runCommandWithOutputHandling(command); @@ -117,7 +109,7 @@ public class ExtractImageScansController { try (ZipOutputStream zipOut = new ZipOutputStream(new FileOutputStream(tempZipFile.toFile()))) { // Add processed images to the zip for (int i = 0; i < processedImageBytes.size(); i++) { - ZipEntry entry = new ZipEntry(fileName.replaceFirst("[.][^.]+$", "") + "_" + (i+1) + ".png"); + ZipEntry entry = new ZipEntry(fileName.replaceFirst("[.][^.]+$", "") + "_" + (i + 1) + ".png"); zipOut.putNextEntry(entry); zipOut.write(processedImageBytes.get(i)); zipOut.closeEntry(); @@ -135,11 +127,7 @@ public class ExtractImageScansController { byte[] imageBytes = processedImageBytes.get(0); return PdfUtils.bytesToWebResponse(imageBytes, fileName.replaceFirst("[.][^.]+$", "") + ".png", MediaType.IMAGE_PNG); } - } - - - } diff --git a/src/main/java/stirling/software/SPDF/controller/other/ExtractImagesController.java b/src/main/java/stirling/software/SPDF/controller/api/other/ExtractImagesController.java similarity index 89% rename from src/main/java/stirling/software/SPDF/controller/other/ExtractImagesController.java rename to src/main/java/stirling/software/SPDF/controller/api/other/ExtractImagesController.java index 9afe67c6..43228022 100644 --- a/src/main/java/stirling/software/SPDF/controller/other/ExtractImagesController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/other/ExtractImagesController.java @@ -1,4 +1,4 @@ -package stirling.software.SPDF.controller.other; +package stirling.software.SPDF.controller.api.other; import java.awt.Graphics2D; import java.awt.Image; @@ -20,22 +20,24 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; +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.Hidden; import stirling.software.SPDF.utils.PdfUtils; -@Controller +@RestController public class ExtractImagesController { private static final Logger logger = LoggerFactory.getLogger(ExtractImagesController.class); - @PostMapping("/extract-images") - public ResponseEntity extractImages(@RequestParam("fileInput") MultipartFile file, @RequestParam("format") String format) throws IOException { + @PostMapping(consumes = "multipart/form-data", value = "/extract-images") + public ResponseEntity extractImages(@RequestPart(required = true, value = "fileInput") MultipartFile file, @RequestParam("format") String format) throws IOException { System.out.println(System.currentTimeMillis() + "file=" + file.getName() + ", format=" + format); PDDocument document = PDDocument.load(file.getBytes()); @@ -96,14 +98,8 @@ public class ExtractImagesController { // Create ByteArrayResource from byte array byte[] zipContents = baos.toByteArray(); - + return PdfUtils.boasToWebResponse(baos, file.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_extracted-images.zip", MediaType.APPLICATION_OCTET_STREAM); } - @GetMapping("/extract-images") - public String extractImagesForm(Model model) { - model.addAttribute("currentPage", "extract-images"); - return "other/extract-images"; - } - } diff --git a/src/main/java/stirling/software/SPDF/controller/other/MetadataController.java b/src/main/java/stirling/software/SPDF/controller/api/other/MetadataController.java similarity index 93% rename from src/main/java/stirling/software/SPDF/controller/other/MetadataController.java rename to src/main/java/stirling/software/SPDF/controller/api/other/MetadataController.java index c139b885..43835c04 100644 --- a/src/main/java/stirling/software/SPDF/controller/other/MetadataController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/other/MetadataController.java @@ -1,4 +1,4 @@ -package stirling.software.SPDF.controller.other; +package stirling.software.SPDF.controller.api.other; import java.io.IOException; import java.text.ParseException; @@ -11,23 +11,20 @@ import org.apache.pdfbox.cos.COSName; import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocumentInformation; import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; +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.Hidden; import stirling.software.SPDF.utils.PdfUtils; -@Controller +@RestController public class MetadataController { - @GetMapping("/change-metadata") - public String addWatermarkForm(Model model) { - model.addAttribute("currentPage", "change-metadata"); - return "other/change-metadata"; - } private String checkUndefined(String entry) { // Check if the string is "undefined" @@ -40,8 +37,8 @@ public class MetadataController { } - @PostMapping("/update-metadata") - public ResponseEntity metadata(@RequestParam("fileInput") MultipartFile pdfFile, + @PostMapping(consumes = "multipart/form-data", value = "/update-metadata") + public ResponseEntity metadata(@RequestPart(required = true, value = "fileInput") MultipartFile pdfFile, @RequestParam(value = "deleteAll", required = false, defaultValue = "false") Boolean deleteAll, @RequestParam(value = "author", required = false) String author, @RequestParam(value = "creationDate", required = false) String creationDate, @RequestParam(value = "creator", required = false) String creator, @RequestParam(value = "keywords", required = false) String keywords, @RequestParam(value = "modificationDate", required = false) String modificationDate, diff --git a/src/main/java/stirling/software/SPDF/controller/other/OCRController.java b/src/main/java/stirling/software/SPDF/controller/api/other/OCRController.java similarity index 77% rename from src/main/java/stirling/software/SPDF/controller/other/OCRController.java rename to src/main/java/stirling/software/SPDF/controller/api/other/OCRController.java index 189b79d6..9a9b527c 100644 --- a/src/main/java/stirling/software/SPDF/controller/other/OCRController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/other/OCRController.java @@ -1,4 +1,4 @@ -package stirling.software.SPDF.controller.other; +package stirling.software.SPDF.controller.api.other; import java.io.File; import java.io.FileOutputStream; @@ -19,44 +19,30 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RequestPart; +import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; import org.springframework.web.servlet.ModelAndView; +import io.swagger.v3.oas.annotations.Hidden; import stirling.software.SPDF.utils.PdfUtils; import stirling.software.SPDF.utils.ProcessExecutor; -@Controller +@RestController public class OCRController { private static final Logger logger = LoggerFactory.getLogger(OCRController.class); - public List getAvailableTesseractLanguages() { - String tessdataDir = "/usr/share/tesseract-ocr/4.00/tessdata"; - File[] files = new File(tessdataDir).listFiles(); - if (files == null) { - return Collections.emptyList(); - } - return Arrays.stream(files).filter(file -> file.getName().endsWith(".traineddata")).map(file -> file.getName().replace(".traineddata", "")) - .filter(lang -> !lang.equalsIgnoreCase("osd")).collect(Collectors.toList()); - } - @GetMapping("/ocr-pdf") - public ModelAndView ocrPdfPage() { - ModelAndView modelAndView = new ModelAndView("other/ocr-pdf"); - modelAndView.addObject("languages", getAvailableTesseractLanguages()); - modelAndView.addObject("currentPage", "ocr-pdf"); - return modelAndView; - } - - @PostMapping("/ocr-pdf") - public ResponseEntity processPdfWithOCR(@RequestParam("fileInput") MultipartFile inputFile, @RequestParam("languages") List selectedLanguages, - @RequestParam(name = "sidecar", required = false) Boolean sidecar, @RequestParam(name = "deskew", required = false) Boolean deskew, - @RequestParam(name = "clean", required = false) Boolean clean, @RequestParam(name = "clean-final", required = false) Boolean cleanFinal, - @RequestParam(name = "ocrType", required = false) String ocrType) throws IOException, InterruptedException { + @PostMapping(consumes = "multipart/form-data", value = "/ocr-pdf") + public ResponseEntity processPdfWithOCR(@RequestPart(required = true, value = "fileInput") MultipartFile inputFile, + @RequestParam("languages") List selectedLanguages, @RequestParam(name = "sidecar", required = false) Boolean sidecar, + @RequestParam(name = "deskew", required = false) Boolean deskew, @RequestParam(name = "clean", required = false) Boolean clean, + @RequestParam(name = "clean-final", required = false) Boolean cleanFinal, @RequestParam(name = "ocrType", required = false) String ocrType) + throws IOException, InterruptedException { // --output-type pdfa if (selectedLanguages == null || selectedLanguages.size() < 1) { @@ -148,7 +134,7 @@ public class OCRController { Files.delete(tempZipFile); Files.delete(tempOutputFile); Files.delete(sidecarTextPath); - + // Return the zip file containing both the PDF and the text file return PdfUtils.bytesToWebResponse(pdfBytes, outputZipFilename, MediaType.APPLICATION_OCTET_STREAM); } else { diff --git a/src/main/java/stirling/software/SPDF/controller/other/OverlayImageController.java b/src/main/java/stirling/software/SPDF/controller/api/other/OverlayImageController.java similarity index 64% rename from src/main/java/stirling/software/SPDF/controller/other/OverlayImageController.java rename to src/main/java/stirling/software/SPDF/controller/api/other/OverlayImageController.java index bf0f2283..b31d8681 100644 --- a/src/main/java/stirling/software/SPDF/controller/other/OverlayImageController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/other/OverlayImageController.java @@ -1,43 +1,39 @@ -package stirling.software.SPDF.controller.other; - -import java.io.IOException; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Controller; -import org.springframework.ui.Model; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.multipart.MultipartFile; - -import stirling.software.SPDF.utils.PdfUtils; - -@Controller -public class OverlayImageController { - - private static final Logger logger = LoggerFactory.getLogger(OverlayImageController.class); - - @GetMapping("/add-image") - public String overlayImage(Model model) { - model.addAttribute("currentPage", "add-image"); - return "other/add-image"; - } - - @PostMapping("/add-image") - public ResponseEntity overlayImage(@RequestParam("fileInput") MultipartFile pdfFile, @RequestParam("fileInput2") MultipartFile imageFile, @RequestParam("x") float x, - @RequestParam("y") float y, @RequestParam("everyPage") boolean everyPage) { - try { - byte[] pdfBytes = pdfFile.getBytes(); - byte[] imageBytes = imageFile.getBytes(); - byte[] result = PdfUtils.overlayImage(pdfBytes, imageBytes, x, y, everyPage); - - return PdfUtils.bytesToWebResponse(result, pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_overlayed.pdf"); - } catch (IOException e) { - logger.error("Failed to add image to PDF", e); - return new ResponseEntity<>(HttpStatus.BAD_REQUEST); - } - } -} +package stirling.software.SPDF.controller.api.other; + +import java.io.IOException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; +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.Hidden; +import stirling.software.SPDF.utils.PdfUtils; + +@RestController +public class OverlayImageController { + + private static final Logger logger = LoggerFactory.getLogger(OverlayImageController.class); + + @PostMapping(consumes = "multipart/form-data", value = "/add-image") + public ResponseEntity overlayImage(@RequestPart(required = true, value = "fileInput") MultipartFile pdfFile, @RequestParam("fileInput2") MultipartFile imageFile, + @RequestParam("x") float x, @RequestParam("y") float y, @RequestParam("everyPage") boolean everyPage) { + try { + byte[] pdfBytes = pdfFile.getBytes(); + byte[] imageBytes = imageFile.getBytes(); + byte[] result = PdfUtils.overlayImage(pdfBytes, imageBytes, x, y, everyPage); + + return PdfUtils.bytesToWebResponse(result, pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_overlayed.pdf"); + } catch (IOException e) { + logger.error("Failed to add image to PDF", e); + return new ResponseEntity<>(HttpStatus.BAD_REQUEST); + } + } +} diff --git a/src/main/java/stirling/software/SPDF/controller/security/PasswordController.java b/src/main/java/stirling/software/SPDF/controller/api/security/PasswordController.java similarity index 65% rename from src/main/java/stirling/software/SPDF/controller/security/PasswordController.java rename to src/main/java/stirling/software/SPDF/controller/api/security/PasswordController.java index f86bcc06..2dafeb8d 100644 --- a/src/main/java/stirling/software/SPDF/controller/security/PasswordController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/security/PasswordController.java @@ -1,81 +1,69 @@ -package stirling.software.SPDF.controller.security; - -import java.io.IOException; - -import org.apache.pdfbox.pdmodel.PDDocument; -import org.apache.pdfbox.pdmodel.encryption.AccessPermission; -import org.apache.pdfbox.pdmodel.encryption.StandardProtectionPolicy; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Controller; -import org.springframework.ui.Model; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.multipart.MultipartFile; - -import stirling.software.SPDF.utils.PdfUtils; - -@Controller -public class PasswordController { - - private static final Logger logger = LoggerFactory.getLogger(PasswordController.class); - - @GetMapping("/add-password") - public String addPasswordForm(Model model) { - model.addAttribute("currentPage", "add-password"); - return "security/add-password"; - } - - @PostMapping("/remove-password") - public ResponseEntity compressPDF(@RequestParam("fileInput") MultipartFile fileInput, @RequestParam(name = "password") String password) throws IOException { - PDDocument document = PDDocument.load(fileInput.getBytes(), password); - document.setAllSecurityToBeRemoved(true); - return PdfUtils.pdfDocToWebResponse(document, fileInput.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_password_removed.pdf"); - } - - @PostMapping("/add-password") - public ResponseEntity compressPDF(@RequestParam("fileInput") MultipartFile fileInput, @RequestParam(defaultValue = "", name = "password") String password, - @RequestParam(defaultValue = "128", name = "keyLength") int keyLength, @RequestParam(defaultValue = "false", name = "canAssembleDocument") boolean canAssembleDocument, - @RequestParam(defaultValue = "false", name = "canExtractContent") boolean canExtractContent, - @RequestParam(defaultValue = "false", name = "canExtractForAccessibility") boolean canExtractForAccessibility, - @RequestParam(defaultValue = "false", name = "canFillInForm") boolean canFillInForm, @RequestParam(defaultValue = "false", name = "canModify") boolean canModify, - @RequestParam(defaultValue = "false", name = "canModifyAnnotations") boolean canModifyAnnotations, - @RequestParam(defaultValue = "false", name = "canPrint") boolean canPrint, @RequestParam(defaultValue = "false", name = "canPrintFaithful") boolean canPrintFaithful) - throws IOException { - - PDDocument document = PDDocument.load(fileInput.getBytes()); - AccessPermission ap = new AccessPermission(); - - ap.setCanAssembleDocument(!canAssembleDocument); - ap.setCanExtractContent(!canExtractContent); - ap.setCanExtractForAccessibility(!canExtractForAccessibility); - ap.setCanFillInForm(!canFillInForm); - ap.setCanModify(!canModify); - ap.setCanModifyAnnotations(!canModifyAnnotations); - ap.setCanPrint(!canPrint); - ap.setCanPrintFaithful(!canPrintFaithful); - StandardProtectionPolicy spp = new StandardProtectionPolicy(password, password, ap); - spp.setEncryptionKeyLength(keyLength); - - spp.setPermissions(ap); - - document.protect(spp); - - return PdfUtils.pdfDocToWebResponse(document, fileInput.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_passworded.pdf"); - } - - @GetMapping("/change-permissions") - public String permissionsForm(Model model) { - model.addAttribute("currentPage", "change-permissions"); - return "security/change-permissions"; - } - - @GetMapping("/remove-password") - public String removePasswordForm(Model model) { - model.addAttribute("currentPage", "remove-password"); - return "security/remove-password"; - } - -} +package stirling.software.SPDF.controller.api.security; + +import java.io.IOException; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.encryption.AccessPermission; +import org.apache.pdfbox.pdmodel.encryption.StandardProtectionPolicy; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.ResponseEntity; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; +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.Hidden; +import stirling.software.SPDF.utils.PdfUtils; + +@RestController +public class PasswordController { + + private static final Logger logger = LoggerFactory.getLogger(PasswordController.class); + + + @PostMapping(consumes = "multipart/form-data", value = "/remove-password") + public ResponseEntity compressPDF(@RequestPart(required = true, value = "fileInput") MultipartFile fileInput, @RequestParam(name = "password") String password) + throws IOException { + PDDocument document = PDDocument.load(fileInput.getBytes(), password); + document.setAllSecurityToBeRemoved(true); + return PdfUtils.pdfDocToWebResponse(document, fileInput.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_password_removed.pdf"); + } + + @PostMapping(consumes = "multipart/form-data", value = "/add-password") + public ResponseEntity compressPDF(@RequestPart(required = true, value = "fileInput") MultipartFile fileInput, + @RequestParam(defaultValue = "", name = "password") String password, @RequestParam(defaultValue = "128", name = "keyLength") int keyLength, + @RequestParam(defaultValue = "false", name = "canAssembleDocument") boolean canAssembleDocument, + @RequestParam(defaultValue = "false", name = "canExtractContent") boolean canExtractContent, + @RequestParam(defaultValue = "false", name = "canExtractForAccessibility") boolean canExtractForAccessibility, + @RequestParam(defaultValue = "false", name = "canFillInForm") boolean canFillInForm, @RequestParam(defaultValue = "false", name = "canModify") boolean canModify, + @RequestParam(defaultValue = "false", name = "canModifyAnnotations") boolean canModifyAnnotations, + @RequestParam(defaultValue = "false", name = "canPrint") boolean canPrint, @RequestParam(defaultValue = "false", name = "canPrintFaithful") boolean canPrintFaithful) + throws IOException { + + PDDocument document = PDDocument.load(fileInput.getBytes()); + AccessPermission ap = new AccessPermission(); + + ap.setCanAssembleDocument(!canAssembleDocument); + ap.setCanExtractContent(!canExtractContent); + ap.setCanExtractForAccessibility(!canExtractForAccessibility); + ap.setCanFillInForm(!canFillInForm); + ap.setCanModify(!canModify); + ap.setCanModifyAnnotations(!canModifyAnnotations); + ap.setCanPrint(!canPrint); + ap.setCanPrintFaithful(!canPrintFaithful); + StandardProtectionPolicy spp = new StandardProtectionPolicy(password, password, ap); + spp.setEncryptionKeyLength(keyLength); + + spp.setPermissions(ap); + + document.protect(spp); + + return PdfUtils.pdfDocToWebResponse(document, fileInput.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_passworded.pdf"); + } + + +} diff --git a/src/main/java/stirling/software/SPDF/controller/security/WatermarkController.java b/src/main/java/stirling/software/SPDF/controller/api/security/WatermarkController.java similarity index 84% rename from src/main/java/stirling/software/SPDF/controller/security/WatermarkController.java rename to src/main/java/stirling/software/SPDF/controller/api/security/WatermarkController.java index 74c952a9..7c9beb54 100644 --- a/src/main/java/stirling/software/SPDF/controller/security/WatermarkController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/security/WatermarkController.java @@ -1,150 +1,143 @@ -package stirling.software.SPDF.controller.security; - -import java.awt.Color; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import org.apache.pdfbox.pdmodel.PDDocument; -import org.apache.pdfbox.pdmodel.PDDocumentCatalog; -import org.apache.pdfbox.pdmodel.PDPage; -import org.apache.pdfbox.pdmodel.PDPageContentStream; -import org.apache.pdfbox.pdmodel.font.PDFont; -import org.apache.pdfbox.pdmodel.font.PDType1Font; -import org.apache.pdfbox.pdmodel.graphics.state.PDExtendedGraphicsState; -import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotation; -import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationMarkup; -import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm; -import org.apache.pdfbox.pdmodel.interactive.form.PDField; -import org.apache.pdfbox.util.Matrix; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Controller; -import org.springframework.ui.Model; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.multipart.MultipartFile; - -import stirling.software.SPDF.utils.PdfUtils; -import stirling.software.SPDF.utils.WatermarkRemover; - -@Controller -public class WatermarkController { - - @PostMapping("/add-watermark") - public ResponseEntity addWatermark(@RequestParam("fileInput") MultipartFile pdfFile, @RequestParam("watermarkText") String watermarkText, - @RequestParam(defaultValue = "30", name = "fontSize") float fontSize, @RequestParam(defaultValue = "0", name = "rotation") float rotation, - @RequestParam(defaultValue = "0.5", name = "opacity") float opacity, @RequestParam(defaultValue = "50", name = "widthSpacer") int widthSpacer, - @RequestParam(defaultValue = "50", name = "heightSpacer") int heightSpacer) throws IOException { - - // Load the input PDF - PDDocument document = PDDocument.load(pdfFile.getInputStream()); - - // Create a page in the document - for (PDPage page : document.getPages()) { - - // Get the page's content stream - PDPageContentStream contentStream = new PDPageContentStream(document, page, PDPageContentStream.AppendMode.APPEND, true); - - // Set transparency - PDExtendedGraphicsState graphicsState = new PDExtendedGraphicsState(); - graphicsState.setNonStrokingAlphaConstant(opacity); - contentStream.setGraphicsStateParameters(graphicsState); - - // Set font of watermark - PDFont font = PDType1Font.HELVETICA_BOLD; - contentStream.beginText(); - contentStream.setFont(font, fontSize); - contentStream.setNonStrokingColor(Color.LIGHT_GRAY); - - // Set size and location of watermark - float pageWidth = page.getMediaBox().getWidth(); - float pageHeight = page.getMediaBox().getHeight(); - float watermarkWidth = widthSpacer + font.getStringWidth(watermarkText) * fontSize / 1000; - float watermarkHeight = heightSpacer + fontSize; - int watermarkRows = (int) (pageHeight / watermarkHeight + 1); - int watermarkCols = (int) (pageWidth / watermarkWidth + 1); - - // Add the watermark text - for (int i = 0; i < watermarkRows; i++) { - for (int j = 0; j < watermarkCols; j++) { - contentStream.setTextMatrix(Matrix.getRotateInstance((float) Math.toRadians(rotation), j * watermarkWidth, i * watermarkHeight)); - contentStream.showTextWithPositioning(new Object[] { watermarkText }); - } - } - - contentStream.endText(); - - // Close the content stream - contentStream.close(); - } - return PdfUtils.pdfDocToWebResponse(document, pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_watermarked.pdf"); - } - - @GetMapping("/add-watermark") - public String addWatermarkForm(Model model) { - model.addAttribute("currentPage", "add-watermark"); - return "security/add-watermark"; - } - - @PostMapping("/remove-watermark") - public ResponseEntity removeWatermark(@RequestParam("fileInput") MultipartFile pdfFile, @RequestParam("watermarkText") String watermarkText) throws Exception { - - // Load the input PDF - PDDocument document = PDDocument.load(pdfFile.getInputStream()); - - // Create a new PDF document for the output - PDDocument outputDocument = new PDDocument(); - - // Loop through the pages - int numPages = document.getNumberOfPages(); - for (int i = 0; i < numPages; i++) { - PDPage page = document.getPage(i); - - // Process the content stream to remove the watermark text - WatermarkRemover editor = new WatermarkRemover(watermarkText) { - }; - editor.processPage(page); - editor.processPage(page); - // Add the page to the output document - outputDocument.addPage(page); - } - - for (PDPage page : outputDocument.getPages()) { - List annotations = page.getAnnotations(); - List annotationsToRemove = new ArrayList<>(); - - for (PDAnnotation annotation : annotations) { - if (annotation instanceof PDAnnotationMarkup) { - PDAnnotationMarkup markup = (PDAnnotationMarkup) annotation; - String contents = markup.getContents(); - if (contents != null && contents.contains(watermarkText)) { - annotationsToRemove.add(markup); - } - } - } - - annotations.removeAll(annotationsToRemove); - } - PDDocumentCatalog catalog = outputDocument.getDocumentCatalog(); - PDAcroForm acroForm = catalog.getAcroForm(); - if (acroForm != null) { - List fields = acroForm.getFields(); - for (PDField field : fields) { - String fieldValue = field.getValueAsString(); - if (fieldValue.contains(watermarkText)) { - field.setValue(fieldValue.replace(watermarkText, "")); - } - } - } - - return PdfUtils.pdfDocToWebResponse(outputDocument, "removed.pdf"); - } - - @GetMapping("/remove-watermark") - public String removeWatermarkForm(Model model) { - model.addAttribute("currentPage", "remove-watermark"); - return "security/remove-watermark"; - } - -} +package stirling.software.SPDF.controller.api.security; + +import java.awt.Color; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDDocumentCatalog; +import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.pdfbox.pdmodel.PDPageContentStream; +import org.apache.pdfbox.pdmodel.font.PDFont; +import org.apache.pdfbox.pdmodel.font.PDType1Font; +import org.apache.pdfbox.pdmodel.graphics.state.PDExtendedGraphicsState; +import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotation; +import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationMarkup; +import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm; +import org.apache.pdfbox.pdmodel.interactive.form.PDField; +import org.apache.pdfbox.util.Matrix; +import org.springframework.http.ResponseEntity; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; +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.Hidden; +import stirling.software.SPDF.utils.PdfUtils; +import stirling.software.SPDF.utils.WatermarkRemover; + +@RestController +public class WatermarkController { + + @PostMapping(consumes = "multipart/form-data", value = "/add-watermark") + public ResponseEntity addWatermark(@RequestPart(required = true, value = "fileInput") MultipartFile pdfFile, @RequestParam("watermarkText") String watermarkText, + @RequestParam(defaultValue = "30", name = "fontSize") float fontSize, @RequestParam(defaultValue = "0", name = "rotation") float rotation, + @RequestParam(defaultValue = "0.5", name = "opacity") float opacity, @RequestParam(defaultValue = "50", name = "widthSpacer") int widthSpacer, + @RequestParam(defaultValue = "50", name = "heightSpacer") int heightSpacer) throws IOException { + + // Load the input PDF + PDDocument document = PDDocument.load(pdfFile.getInputStream()); + + // Create a page in the document + for (PDPage page : document.getPages()) { + + // Get the page's content stream + PDPageContentStream contentStream = new PDPageContentStream(document, page, PDPageContentStream.AppendMode.APPEND, true); + + // Set transparency + PDExtendedGraphicsState graphicsState = new PDExtendedGraphicsState(); + graphicsState.setNonStrokingAlphaConstant(opacity); + contentStream.setGraphicsStateParameters(graphicsState); + + // Set font of watermark + PDFont font = PDType1Font.HELVETICA_BOLD; + contentStream.beginText(); + contentStream.setFont(font, fontSize); + contentStream.setNonStrokingColor(Color.LIGHT_GRAY); + + // Set size and location of watermark + float pageWidth = page.getMediaBox().getWidth(); + float pageHeight = page.getMediaBox().getHeight(); + float watermarkWidth = widthSpacer + font.getStringWidth(watermarkText) * fontSize / 1000; + float watermarkHeight = heightSpacer + fontSize; + int watermarkRows = (int) (pageHeight / watermarkHeight + 1); + int watermarkCols = (int) (pageWidth / watermarkWidth + 1); + + // Add the watermark text + for (int i = 0; i < watermarkRows; i++) { + for (int j = 0; j < watermarkCols; j++) { + contentStream.setTextMatrix(Matrix.getRotateInstance((float) Math.toRadians(rotation), j * watermarkWidth, i * watermarkHeight)); + contentStream.showTextWithPositioning(new Object[] { watermarkText }); + } + } + + contentStream.endText(); + + // Close the content stream + contentStream.close(); + } + return PdfUtils.pdfDocToWebResponse(document, pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_watermarked.pdf"); + } + + + @PostMapping(consumes = "multipart/form-data", value = "/remove-watermark") + public ResponseEntity removeWatermark(@RequestPart(required = true, value = "fileInput") MultipartFile pdfFile, @RequestParam("watermarkText") String watermarkText) + throws Exception { + + // Load the input PDF + PDDocument document = PDDocument.load(pdfFile.getInputStream()); + + // Create a new PDF document for the output + PDDocument outputDocument = new PDDocument(); + + // Loop through the pages + int numPages = document.getNumberOfPages(); + for (int i = 0; i < numPages; i++) { + PDPage page = document.getPage(i); + + // Process the content stream to remove the watermark text + WatermarkRemover editor = new WatermarkRemover(watermarkText) { + }; + editor.processPage(page); + editor.processPage(page); + // Add the page to the output document + outputDocument.addPage(page); + } + + for (PDPage page : outputDocument.getPages()) { + List annotations = page.getAnnotations(); + List annotationsToRemove = new ArrayList<>(); + + for (PDAnnotation annotation : annotations) { + if (annotation instanceof PDAnnotationMarkup) { + PDAnnotationMarkup markup = (PDAnnotationMarkup) annotation; + String contents = markup.getContents(); + if (contents != null && contents.contains(watermarkText)) { + annotationsToRemove.add(markup); + } + } + } + + annotations.removeAll(annotationsToRemove); + } + PDDocumentCatalog catalog = outputDocument.getDocumentCatalog(); + PDAcroForm acroForm = catalog.getAcroForm(); + if (acroForm != null) { + List fields = acroForm.getFields(); + for (PDField field : fields) { + String fieldValue = field.getValueAsString(); + if (fieldValue.contains(watermarkText)) { + field.setValue(fieldValue.replace(watermarkText, "")); + } + } + } + + return PdfUtils.pdfDocToWebResponse(outputDocument, "removed.pdf"); + } + + +} diff --git a/src/main/java/stirling/software/SPDF/controller/converters/ConvertPDFToOffice.java b/src/main/java/stirling/software/SPDF/controller/converters/ConvertPDFToOffice.java deleted file mode 100644 index dd0b9d5f..00000000 --- a/src/main/java/stirling/software/SPDF/controller/converters/ConvertPDFToOffice.java +++ /dev/null @@ -1,86 +0,0 @@ -package stirling.software.SPDF.controller.converters; - -import java.io.IOException; - -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.multipart.MultipartFile; -import org.springframework.web.servlet.ModelAndView; - -import stirling.software.SPDF.utils.PDFToFile; - -@Controller -public class ConvertPDFToOffice { - - @GetMapping("/pdf-to-html") - public ModelAndView pdfToHTML() { - ModelAndView modelAndView = new ModelAndView("convert/pdf-to-html"); - modelAndView.addObject("currentPage", "pdf-to-html"); - return modelAndView; - } - - @GetMapping("/pdf-to-presentation") - public ModelAndView pdfToPresentation() { - ModelAndView modelAndView = new ModelAndView("convert/pdf-to-presentation"); - modelAndView.addObject("currentPage", "pdf-to-presentation"); - return modelAndView; - } - - @GetMapping("/pdf-to-text") - public ModelAndView pdfToText() { - ModelAndView modelAndView = new ModelAndView("convert/pdf-to-text"); - modelAndView.addObject("currentPage", "pdf-to-text"); - return modelAndView; - } - - @GetMapping("/pdf-to-word") - public ModelAndView pdfToWord() { - ModelAndView modelAndView = new ModelAndView("convert/pdf-to-word"); - modelAndView.addObject("currentPage", "pdf-to-word"); - return modelAndView; - } - - @GetMapping("/pdf-to-xml") - public ModelAndView pdfToXML() { - ModelAndView modelAndView = new ModelAndView("convert/pdf-to-xml"); - modelAndView.addObject("currentPage", "pdf-to-xml"); - return modelAndView; - } - - @PostMapping("/pdf-to-html") - public ResponseEntity processPdfToHTML(@RequestParam("fileInput") MultipartFile inputFile) throws IOException, InterruptedException { - PDFToFile pdfToFile = new PDFToFile(); - return pdfToFile.processPdfToOfficeFormat(inputFile, "html", "writer_pdf_import"); - } - - @PostMapping("/pdf-to-presentation") - public ResponseEntity processPdfToPresentation(@RequestParam("fileInput") MultipartFile inputFile, @RequestParam("outputFormat") String outputFormat) - throws IOException, InterruptedException { - PDFToFile pdfToFile = new PDFToFile(); - return pdfToFile.processPdfToOfficeFormat(inputFile, outputFormat, "impress_pdf_import"); - } - - @PostMapping("/pdf-to-text") - public ResponseEntity processPdfToRTForTXT(@RequestParam("fileInput") MultipartFile inputFile, @RequestParam("outputFormat") String outputFormat) - throws IOException, InterruptedException { - PDFToFile pdfToFile = new PDFToFile(); - return pdfToFile.processPdfToOfficeFormat(inputFile, outputFormat, "writer_pdf_import"); - } - - @PostMapping("/pdf-to-word") - public ResponseEntity processPdfToWord(@RequestParam("fileInput") MultipartFile inputFile, @RequestParam("outputFormat") String outputFormat) - throws IOException, InterruptedException { - PDFToFile pdfToFile = new PDFToFile(); - return pdfToFile.processPdfToOfficeFormat(inputFile, outputFormat, "writer_pdf_import"); - } - - @PostMapping("/pdf-to-xml") - public ResponseEntity processPdfToXML(@RequestParam("fileInput") MultipartFile inputFile) throws IOException, InterruptedException { - PDFToFile pdfToFile = new PDFToFile(); - return pdfToFile.processPdfToOfficeFormat(inputFile, "xml", "writer_pdf_import"); - } - -} diff --git a/src/main/java/stirling/software/SPDF/controller/web/ConverterWebController.java b/src/main/java/stirling/software/SPDF/controller/web/ConverterWebController.java new file mode 100644 index 00000000..f110cdc8 --- /dev/null +++ b/src/main/java/stirling/software/SPDF/controller/web/ConverterWebController.java @@ -0,0 +1,86 @@ +package stirling.software.SPDF.controller.web; + +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.servlet.ModelAndView; + +import io.swagger.v3.oas.annotations.Hidden; + +@Controller +public class ConverterWebController { + + @GetMapping("/img-to-pdf") + @Hidden + public String convertImgToPdfForm(Model model) { + model.addAttribute("currentPage", "img-to-pdf"); + return "convert/img-to-pdf"; + } + + + @GetMapping("/pdf-to-img") + @Hidden + public String pdfToimgForm(Model model) { + model.addAttribute("currentPage", "pdf-to-img"); + return "convert/pdf-to-img"; + } + + @GetMapping("/file-to-pdf") + @Hidden + public String convertToPdfForm(Model model) { + model.addAttribute("currentPage", "file-to-pdf"); + return "convert/file-to-pdf"; + } + + + + //PDF TO...... + + @GetMapping("/pdf-to-html") + @Hidden + public ModelAndView pdfToHTML() { + ModelAndView modelAndView = new ModelAndView("convert/pdf-to-html"); + modelAndView.addObject("currentPage", "pdf-to-html"); + return modelAndView; + } + + @GetMapping("/pdf-to-presentation") + @Hidden + public ModelAndView pdfToPresentation() { + ModelAndView modelAndView = new ModelAndView("convert/pdf-to-presentation"); + modelAndView.addObject("currentPage", "pdf-to-presentation"); + return modelAndView; + } + + @GetMapping("/pdf-to-text") + @Hidden + public ModelAndView pdfToText() { + ModelAndView modelAndView = new ModelAndView("convert/pdf-to-text"); + modelAndView.addObject("currentPage", "pdf-to-text"); + return modelAndView; + } + + @GetMapping("/pdf-to-word") + @Hidden + public ModelAndView pdfToWord() { + ModelAndView modelAndView = new ModelAndView("convert/pdf-to-word"); + modelAndView.addObject("currentPage", "pdf-to-word"); + return modelAndView; + } + + @GetMapping("/pdf-to-xml") + @Hidden + public ModelAndView pdfToXML() { + ModelAndView modelAndView = new ModelAndView("convert/pdf-to-xml"); + modelAndView.addObject("currentPage", "pdf-to-xml"); + return modelAndView; + } + + + @GetMapping("/pdf-to-pdfa") + @Hidden + public String pdfToPdfAForm(Model model) { + model.addAttribute("currentPage", "pdf-to-pdfa"); + return "convert/pdf-to-pdfa"; + } +} diff --git a/src/main/java/stirling/software/SPDF/controller/web/GeneralWebController.java b/src/main/java/stirling/software/SPDF/controller/web/GeneralWebController.java new file mode 100644 index 00000000..d2bc4886 --- /dev/null +++ b/src/main/java/stirling/software/SPDF/controller/web/GeneralWebController.java @@ -0,0 +1,62 @@ +package stirling.software.SPDF.controller.web; + +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; + +import io.swagger.v3.oas.annotations.Hidden; + +@Controller +public class GeneralWebController { + @GetMapping("/merge-pdfs") + @Hidden + public String mergePdfForm(Model model) { + model.addAttribute("currentPage", "merge-pdfs"); + return "merge-pdfs"; + } + @GetMapping("/multi-tool") + @Hidden + public String multiToolForm(Model model) { + model.addAttribute("currentPage", "multi-tool"); + return "multi-tool"; + } + + @GetMapping("/") + public String home(Model model) { + model.addAttribute("currentPage", "home"); + return "home"; + } + + @GetMapping("/home") + public String root(Model model) { + return "redirect:/"; + } + + @GetMapping("/remove-pages") + @Hidden + public String pageDeleter(Model model) { + model.addAttribute("currentPage", "remove-pages"); + return "remove-pages"; + } + + @GetMapping("/pdf-organizer") + @Hidden + public String pageOrganizer(Model model) { + model.addAttribute("currentPage", "pdf-organizer"); + return "pdf-organizer"; + } + + @GetMapping("/rotate-pdf") + @Hidden + public String rotatePdfForm(Model model) { + model.addAttribute("currentPage", "rotate-pdf"); + return "rotate-pdf"; + } + + @GetMapping("/split-pdfs") + @Hidden + public String splitPdfForm(Model model) { + model.addAttribute("currentPage", "split-pdfs"); + return "split-pdfs"; + } +} diff --git a/src/main/java/stirling/software/SPDF/controller/web/OtherWebController.java b/src/main/java/stirling/software/SPDF/controller/web/OtherWebController.java new file mode 100644 index 00000000..549bd1fb --- /dev/null +++ b/src/main/java/stirling/software/SPDF/controller/web/OtherWebController.java @@ -0,0 +1,75 @@ +package stirling.software.SPDF.controller.web; + +import java.io.File; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.servlet.ModelAndView; + +import io.swagger.v3.oas.annotations.Hidden; + +@Controller +public class OtherWebController { + @GetMapping("/compress-pdf") + @Hidden + public String compressPdfForm(Model model) { + model.addAttribute("currentPage", "compress-pdf"); + return "other/compress-pdf"; + } + + @GetMapping("/extract-image-scans") + @Hidden + public ModelAndView extractImageScansForm() { + ModelAndView modelAndView = new ModelAndView("other/extract-image-scans"); + modelAndView.addObject("currentPage", "extract-image-scans"); + return modelAndView; + } + + @GetMapping("/extract-images") + @Hidden + public String extractImagesForm(Model model) { + model.addAttribute("currentPage", "extract-images"); + return "other/extract-images"; + } + + + @GetMapping("/change-metadata") + @Hidden + public String addWatermarkForm(Model model) { + model.addAttribute("currentPage", "change-metadata"); + return "other/change-metadata"; + } + + + public List getAvailableTesseractLanguages() { + String tessdataDir = "/usr/share/tesseract-ocr/4.00/tessdata"; + File[] files = new File(tessdataDir).listFiles(); + if (files == null) { + return Collections.emptyList(); + } + return Arrays.stream(files).filter(file -> file.getName().endsWith(".traineddata")).map(file -> file.getName().replace(".traineddata", "")) + .filter(lang -> !lang.equalsIgnoreCase("osd")).collect(Collectors.toList()); + } + + @GetMapping("/ocr-pdf") + @Hidden + public ModelAndView ocrPdfPage() { + ModelAndView modelAndView = new ModelAndView("other/ocr-pdf"); + modelAndView.addObject("languages", getAvailableTesseractLanguages()); + modelAndView.addObject("currentPage", "ocr-pdf"); + return modelAndView; + } + + + @GetMapping("/add-image") + @Hidden + public String overlayImage(Model model) { + model.addAttribute("currentPage", "add-image"); + return "other/add-image"; + } +} diff --git a/src/main/java/stirling/software/SPDF/controller/web/SecurityWebController.java b/src/main/java/stirling/software/SPDF/controller/web/SecurityWebController.java new file mode 100644 index 00000000..31f16563 --- /dev/null +++ b/src/main/java/stirling/software/SPDF/controller/web/SecurityWebController.java @@ -0,0 +1,46 @@ +package stirling.software.SPDF.controller.web; + +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; + +import io.swagger.v3.oas.annotations.Hidden; + +@Controller +public class SecurityWebController { + @GetMapping("/add-password") + @Hidden + public String addPasswordForm(Model model) { + model.addAttribute("currentPage", "add-password"); + return "security/add-password"; + } + @GetMapping("/change-permissions") + @Hidden + public String permissionsForm(Model model) { + model.addAttribute("currentPage", "change-permissions"); + return "security/change-permissions"; + } + + @GetMapping("/remove-password") + @Hidden + public String removePasswordForm(Model model) { + model.addAttribute("currentPage", "remove-password"); + return "security/remove-password"; + } + + @GetMapping("/add-watermark") + @Hidden + public String addWatermarkForm(Model model) { + model.addAttribute("currentPage", "add-watermark"); + return "security/add-watermark"; + } + + //WIP + @GetMapping("/remove-watermark") + @Hidden + public String removeWatermarkForm(Model model) { + model.addAttribute("currentPage", "remove-watermark"); + return "security/remove-watermark"; + } + +} diff --git a/src/main/java/stirling/software/SPDF/utils/PDFToFile.java b/src/main/java/stirling/software/SPDF/utils/PDFToFile.java index 4f52c97f..ea9308ea 100644 --- a/src/main/java/stirling/software/SPDF/utils/PDFToFile.java +++ b/src/main/java/stirling/software/SPDF/utils/PDFToFile.java @@ -58,7 +58,6 @@ public class PDFToFile { // Get output files List outputFiles = Arrays.asList(tempOutputDir.toFile().listFiles()); - if (outputFiles.size() == 1) { // Return single output file File outputFile = outputFiles.get(0); @@ -69,7 +68,7 @@ public class PDFToFile { fileBytes = FileUtils.readFileToByteArray(outputFile); } else { // Return output files in a ZIP archive - fileName = pdfBaseName + "To" + outputFormat + ".zip"; + fileName = pdfBaseName + "To" + outputFormat + ".zip"; ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); ZipOutputStream zipOutputStream = new ZipOutputStream(byteArrayOutputStream); diff --git a/src/main/java/stirling/software/SPDF/utils/PdfUtils.java b/src/main/java/stirling/software/SPDF/utils/PdfUtils.java index 446e73dd..e6a1a602 100644 --- a/src/main/java/stirling/software/SPDF/utils/PdfUtils.java +++ b/src/main/java/stirling/software/SPDF/utils/PdfUtils.java @@ -48,10 +48,10 @@ public class PdfUtils { } public static ResponseEntity boasToWebResponse(ByteArrayOutputStream baos, String docName, MediaType mediaType) throws IOException { - return PdfUtils.bytesToWebResponse(baos.toByteArray(), docName, mediaType ); + return PdfUtils.bytesToWebResponse(baos.toByteArray(), docName, mediaType); } - - public static ResponseEntity bytesToWebResponse(byte[] bytes, String docName, MediaType mediaType ) throws IOException { + + public static ResponseEntity bytesToWebResponse(byte[] bytes, String docName, MediaType mediaType) throws IOException { // Return the PDF as a response HttpHeaders headers = new HttpHeaders(); @@ -61,7 +61,7 @@ public class PdfUtils { headers.setContentDispositionFormData("attachment", encodedDocName); return new ResponseEntity<>(bytes, headers, HttpStatus.OK); } - + public static ResponseEntity bytesToWebResponse(byte[] bytes, String docName) throws IOException { return bytesToWebResponse(bytes, docName, MediaType.APPLICATION_PDF); } diff --git a/src/main/resources/static/css/rainbow-mode.css b/src/main/resources/static/css/rainbow-mode.css new file mode 100644 index 00000000..bd82dba1 --- /dev/null +++ b/src/main/resources/static/css/rainbow-mode.css @@ -0,0 +1,35 @@ +/* Rainbow Mode Styles */ +body { + background: linear-gradient(90deg, rgba(255,0,0,1) 0%, rgba(255,154,0,1) 10%, rgba(208,222,33,1) 20%, rgba(79,220,74,1) 30%, rgba(63,218,216,1) 40%, rgba(47,201,226,1) 50%, rgba(28,127,238,1) 60%, rgba(95,21,242,1) 70%, rgba(186,12,248,1) 80%, rgba(251,7,217,1) 90%, rgba(255,0,0,1) 100%); + color: #fff !important; +} + +.dark-card { + background: linear-gradient(90deg, rgba(255,0,0,1) 0%, rgba(255,154,0,1) 10%, rgba(208,222,33,1) 20%, rgba(79,220,74,1) 30%, rgba(63,218,216,1) 40%, rgba(47,201,226,1) 50%, rgba(28,127,238,1) 60%, rgba(95,21,242,1) 70%, rgba(186,12,248,1) 80%, rgba(251,7,217,1) 90%, rgba(255,0,0,1) 100%) !important; + color: white !important; +} +.jumbotron { + background: linear-gradient(90deg, rgba(255,0,0,1) 0%, rgba(255,154,0,1) 10%, rgba(208,222,33,1) 20%, rgba(79,220,74,1) 30%, rgba(63,218,216,1) 40%, rgba(47,201,226,1) 50%, rgba(28,127,238,1) 60%, rgba(95,21,242,1) 70%, rgba(186,12,248,1) 80%, rgba(251,7,217,1) 90%, rgba(255,0,0,1) 100%); + color: #fff !important; +} + +.list-group { + background: linear-gradient(90deg, rgba(255,0,0,1) 0%, rgba(255,154,0,1) 10%, rgba(208,222,33,1) 20%, rgba(79,220,74,1) 30%, rgba(63,218,216,1) 40%, rgba(47,201,226,1) 50%, rgba(28,127,238,1) 60%, rgba(95,21,242,1) 70%, rgba(186,12,248,1) 80%, rgba(251,7,217,1) 90%, rgba(255,0,0,1) 100%) !important; + color: fff !important; +} +.list-group-item { + background: linear-gradient(90deg, rgba(255,0,0,1) 0%, rgba(255,154,0,1) 10%, rgba(208,222,33,1) 20%, rgba(79,220,74,1) 30%, rgba(63,218,216,1) 40%, rgba(47,201,226,1) 50%, rgba(28,127,238,1) 60%, rgba(95,21,242,1) 70%, rgba(186,12,248,1) 80%, rgba(251,7,217,1) 90%, rgba(255,0,0,1) 100%) !important; + color: fff !important; +} +#support-section { + background: linear-gradient(90deg, rgba(255,0,0,1) 0%, rgba(255,154,0,1) 10%, rgba(208,222,33,1) 20%, rgba(79,220,74,1) 30%, rgba(63,218,216,1) 40%, rgba(47,201,226,1) 50%, rgba(28,127,238,1) 60%, rgba(95,21,242,1) 70%, rgba(186,12,248,1) 80%, rgba(251,7,217,1) 90%, rgba(255,0,0,1) 100%) !important; +} + + +#pages-container-wrapper { + --background-color: rgba(255, 255, 255, 0.046) !important; + --scroll-bar-color: #4c4c4c !important; + --scroll-bar-thumb: #d3d3d3 !important; + --scroll-bar-thumb-hover: #ffffff !important; +} + diff --git a/src/main/resources/static/rainbow.svg b/src/main/resources/static/rainbow.svg new file mode 100644 index 00000000..8e8aea78 --- /dev/null +++ b/src/main/resources/static/rainbow.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/main/resources/templates/fragments/common.html b/src/main/resources/templates/fragments/common.html index 85d5c5a7..6a8048a6 100644 --- a/src/main/resources/templates/fragments/common.html +++ b/src/main/resources/templates/fragments/common.html @@ -28,53 +28,78 @@ - + -
diff --git a/src/main/resources/templates/other/extract-image-scans.html b/src/main/resources/templates/other/extract-image-scans.html index fbcfe839..61291cf8 100644 --- a/src/main/resources/templates/other/extract-image-scans.html +++ b/src/main/resources/templates/other/extract-image-scans.html @@ -16,6 +16,31 @@
+
+ + + Sets the minimum absolute angle required for the image to be rotated (default: 10). +
+
+ + + Determines the range of color variation around the estimated background color (default: 30). +
+
+ + + Sets the minimum area threshold for a photo (default: 10000). +
+
+ + + Sets the minimum contour area threshold for a photo (default: 500). +
+
+ + + Sets the size of the border added and removed to prevent white borders in the output (default: 0). +