From 833b3c45c6d382874176d75f2f9cc0f14c7c532e Mon Sep 17 00:00:00 2001 From: Anthony Stirling <77850077+Frooodle@users.noreply.github.com> Date: Tue, 26 Nov 2024 20:50:35 +0000 Subject: [PATCH] Removal of Ghostscript to use qpdf and tesseract directly (#2338) * navbar fix multi tool and compress location * release notes and ghostscript removal * cleanups * formatting * update docs * more * more * docs * release bump * Hardening suggestions for Stirling-PDF / ghostscript (#2339) * Protect `readLine()` against DoS * Sanitized user-provided file names in HTTP multipart uploads --------- Co-authored-by: pixeebot[bot] <104101892+pixeebot[bot]@users.noreply.github.com> --------- Co-authored-by: pixeebot[bot] <104101892+pixeebot[bot]@users.noreply.github.com> --- DeveloperGuide.md | 4 +- Dockerfile | 2 +- Dockerfile-fat | 2 +- Endpoint-groups.md | 10 +- HowToUseOCR.md | 7 +- LocalRunGuide.md | 11 +- README.md | 8 +- Version-groups.md | 2 +- build.gradle | 3 +- cucumber/features/external.feature | 6 +- scripts/replace_translation_line.sh | 4 +- .../SPDF/config/EndpointConfiguration.java | 19 +- .../SPDF/config/ExternalAppDepConfig.java | 9 +- .../api/converters/ConvertPDFToPDFA.java | 118 +++-- .../api/converters/ExtractCSVController.java | 14 +- .../api/misc/CompressController.java | 289 +++++------ .../api/misc/FakeScanControllerWIP.java | 2 +- .../controller/api/misc/OCRController.java | 322 ++++++------ .../controller/api/misc/RepairController.java | 13 +- .../api/security/CertSignController.java | 35 +- .../controller/web/HomeWebController.java | 5 + .../SPDF/model/ApplicationProperties.java | 40 +- .../model/api/misc/OptimizePdfRequest.java | 11 + .../api/misc/ProcessPdfWithOcrRequest.java | 15 - .../service/MetricsAggregatorService.java | 12 +- .../software/SPDF/service/PostHogService.java | 22 +- .../software/SPDF/utils/ProcessExecutor.java | 46 +- src/main/resources/messages_ar_AR.properties | 6 +- src/main/resources/messages_az_AZ.properties | 6 +- src/main/resources/messages_bg_BG.properties | 6 +- src/main/resources/messages_ca_CA.properties | 6 +- src/main/resources/messages_cs_CZ.properties | 6 +- src/main/resources/messages_da_DK.properties | 6 +- src/main/resources/messages_de_DE.properties | 6 +- src/main/resources/messages_el_GR.properties | 6 +- src/main/resources/messages_en_GB.properties | 14 +- src/main/resources/messages_en_US.properties | 6 +- src/main/resources/messages_es_ES.properties | 6 +- src/main/resources/messages_eu_ES.properties | 6 +- src/main/resources/messages_fr_FR.properties | 6 +- src/main/resources/messages_ga_IE.properties | 6 +- src/main/resources/messages_hi_IN.properties | 6 +- src/main/resources/messages_hr_HR.properties | 6 +- src/main/resources/messages_hu_HU.properties | 6 +- src/main/resources/messages_id_ID.properties | 6 +- src/main/resources/messages_it_IT.properties | 6 +- src/main/resources/messages_ja_JP.properties | 6 +- src/main/resources/messages_ko_KR.properties | 6 +- src/main/resources/messages_nl_NL.properties | 6 +- src/main/resources/messages_no_NB.properties | 6 +- src/main/resources/messages_pl_PL.properties | 6 +- src/main/resources/messages_pt_BR.properties | 6 +- src/main/resources/messages_pt_PT.properties | 6 +- src/main/resources/messages_ro_RO.properties | 6 +- src/main/resources/messages_ru_RU.properties | 6 +- src/main/resources/messages_sk_SK.properties | 6 +- .../resources/messages_sr_LATN_RS.properties | 6 +- src/main/resources/messages_sv_SE.properties | 6 +- src/main/resources/messages_th_TH.properties | 6 +- src/main/resources/messages_tr_TR.properties | 6 +- src/main/resources/messages_uk_UA.properties | 6 +- src/main/resources/messages_vi_VN.properties | 6 +- src/main/resources/messages_zh_CN.properties | 6 +- src/main/resources/messages_zh_TW.properties | 6 +- src/main/resources/settings.yml.template | 6 +- .../resources/templates/fragments/footer.html | 1 + .../templates/misc/compress-pdf.html | 9 +- .../resources/templates/misc/ocr-pdf.html | 21 - src/main/resources/templates/releases.html | 473 ++++++++++++++++++ 69 files changed, 1106 insertions(+), 665 deletions(-) create mode 100644 src/main/resources/templates/releases.html diff --git a/DeveloperGuide.md b/DeveloperGuide.md index d5705fb71..66b1751e1 100644 --- a/DeveloperGuide.md +++ b/DeveloperGuide.md @@ -11,7 +11,7 @@ Stirling-PDF is built using: - Spring Boot + Thymeleaf - PDFBox - LibreOffice -- OcrMyPdf +- qpdf - HTML, CSS, JavaScript - Docker - PDF.js @@ -243,7 +243,7 @@ To run Stirling-PDF locally: Important notes: -- Local testing doesn't include features that depend on external tools like OCRmyPDF, LibreOffice, or Python scripts. +- Local testing doesn't include features that depend on external tools like qpdf, LibreOffice, or Python scripts. - There are currently no automated unit tests. All testing is done manually through the UI or API calls. (You are welcome to add JUnits!) - Always verify your changes in the full Docker environment before submitting pull requests, as some integrations and features will only work in the complete setup. diff --git a/Dockerfile b/Dockerfile index 424257e16..08ef76644 100644 --- a/Dockerfile +++ b/Dockerfile @@ -30,6 +30,7 @@ RUN echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/main" | tee -a /et tini \ bash \ curl \ + qpdf \ shadow \ su-exec \ openssl \ @@ -40,7 +41,6 @@ RUN echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/main" | tee -a /et # pdftohtml poppler-utils \ # OCR MY PDF (unpaper for descew and other advanced features) - ocrmypdf \ tesseract-ocr-data-eng \ # CV py3-opencv \ diff --git a/Dockerfile-fat b/Dockerfile-fat index 2c6ef7625..d34c7daa4 100644 --- a/Dockerfile-fat +++ b/Dockerfile-fat @@ -55,7 +55,7 @@ RUN echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/main" | tee -a /et # pdftohtml poppler-utils \ # OCR MY PDF (unpaper for descew and other advanced featues) - ocrmypdf \ + qpdf \ tesseract-ocr-data-eng \ font-terminus font-dejavu font-noto font-noto-cjk font-awesome font-noto-extra \ # CV diff --git a/Endpoint-groups.md b/Endpoint-groups.md index 0c31c5daa..5b32bbc4b 100644 --- a/Endpoint-groups.md +++ b/Endpoint-groups.md @@ -1,4 +1,4 @@ -| Operation | PageOps | Convert | Security | Other | CLI | Python | OpenCV | LibreOffice | OCRmyPDF | Java | Javascript | Unoconv | Ghostscript | +| Operation | PageOps | Convert | Security | Other | CLI | Python | OpenCV | LibreOffice | qpdf | Java | Javascript | Unoconv | tesseract | | ------------------- | ------- | ------- | -------- | ----- | --- | ------ | ------ | ----------- | -------- | ---- | ---------- | ------- | ----------- | | adjust-contrast | ✔️ | | | | | | | | | | ✔️ | | | | auto-split-pdf | ✔️ | | | | | | | | | ✔️ | | | | @@ -16,7 +16,7 @@ | img-to-pdf | | ✔️ | | | | | | | | ✔️ | | | | | pdf-to-html | | ✔️ | | | ✔️ | | | ✔️ | | | | | | | pdf-to-img | | ✔️ | | | | ✔️ | | | | ✔️ | | | | -| pdf-to-pdfa | | ✔️ | | | ✔️ | | | | ✔️ | | | | ✔️ | +| pdf-to-pdfa | | ✔️ | | | ✔️ | | | | ✔️ | | | | | | pdf-to-markdown | | ✔️ | | | | | | | | ✔️ | | | | | pdf-to-presentation | | ✔️ | | | ✔️ | | | ✔️ | | | | | | | pdf-to-text | | ✔️ | | | ✔️ | | | ✔️ | | | | | | @@ -34,13 +34,13 @@ | auto-rename | | | | ✔️ | | | | | | ✔️ | | | | | change-metadata | | | | ✔️ | | | | | | ✔️ | | | | | compare | | | | ✔️ | | | | | | | ✔️ | | | -| compress-pdf | | | | ✔️ | ✔️ | | | | ✔️ | | | | ✔️ | +| compress-pdf | | | | ✔️ | ✔️ | | | | ✔️ | | | | | | extract-image-scans | | | | ✔️ | ✔️ | ✔️ | ✔️ | | | | | | | | extract-images | | | | ✔️ | | | | | | ✔️ | | | | | flatten | | | | ✔️ | | | | | | | ✔️ | | | | get-info-on-pdf | | | | ✔️ | | | | | | ✔️ | | | | -| ocr-pdf | | | | ✔️ | ✔️ | | | | ✔️ | | | | | +| ocr-pdf | | | | ✔️ | ✔️ | | | | | | | | ✔ | | remove-blanks | | | | ✔️ | ✔️ | ✔️ | ✔️ | | | | | | | -| repair | | | | ✔️ | ✔️ | | | ✔️ | | | | | ✔️ | +| repair | | | | ✔️ | ✔️ | | | ✔️ | ✔ | | | | | | show-javascript | | | | ✔️ | | | | | | | ✔️ | | | | sign | | | | ✔️ | | | | | | | ✔️ | | | diff --git a/HowToUseOCR.md b/HowToUseOCR.md index 6f168111d..0a5cc94cc 100644 --- a/HowToUseOCR.md +++ b/HowToUseOCR.md @@ -8,7 +8,7 @@ The paths have changed for the tessdata locations on new Docker images. Please u ## How does the OCR Work -Stirling-PDF uses [OCRmyPDF](https://github.com/ocrmypdf/OCRmyPDF), which in turn uses Tesseract for its text recognition. All credit goes to them for this awesome work! +Stirling-PDF uses Tesseract for its text recognition. All credit goes to them for this awesome work! ## Language Packs @@ -52,8 +52,6 @@ Add the following to your existing Docker run command: ### Non-Docker Setup -If you are not using Docker, you need to install the OCR components, including the `ocrmypdf` app. You can see the [OCRmyPDF install guide](https://ocrmypdf.readthedocs.io/en/latest/installation.html). - For Debian-based systems, install languages with this command: ```bash @@ -83,8 +81,7 @@ rpm -qa | grep tesseract-langpack | sed 's/tesseract-langpack-//g' For Windows: -Ensure ocrmypdf in installed with -``pip install ocrmypdf`` +You must ensure tesseract is installed Additional languages must be downloaded manually: Download desired .traineddata files from tessdata or tessdata_fast diff --git a/LocalRunGuide.md b/LocalRunGuide.md index 027c7b226..124cff9b7 100644 --- a/LocalRunGuide.md +++ b/LocalRunGuide.md @@ -68,7 +68,7 @@ nix-env -iA nixpkgs.jbig2enc ### Step 3: Install Additional Software -Next we need to install LibreOffice for conversions, ocrmypdf for OCR, and OpenCV for pattern recognition functionality. +Next we need to install LibreOffice for conversions, qpdf for OCR, and OpenCV for pattern recognition functionality. Install the following software: @@ -81,27 +81,27 @@ Install the following software: - unoconv - pngquant - unpaper -- ocrmypdf +- qpdf - opencv-python-headless For Debian-based systems, you can use the following command: ```bash -sudo apt-get install -y libreoffice-writer libreoffice-calc libreoffice-impress unpaper ocrmypdf +sudo apt-get install -y libreoffice-writer libreoffice-calc libreoffice-impress unpaper qpdf pip3 install uno opencv-python-headless unoconv pngquant WeasyPrint --break-system-packages ``` For Fedora: ```bash -sudo dnf install -y libreoffice-writer libreoffice-calc libreoffice-impress unpaper ocrmypdf +sudo dnf install -y libreoffice-writer libreoffice-calc libreoffice-impress unpaper qpdf pip3 install uno opencv-python-headless unoconv pngquant WeasyPrint ``` For Nix: ```bash -nix-env -iA nixpkgs.unpaper nixpkgs.libreoffice nixpkgs.ocrmypdf nixpkgs.poppler_utils +nix-env -iA nixpkgs.unpaper nixpkgs.libreoffice nixpkgs.qpdf nixpkgs.poppler_utils pip3 install uno opencv-python-headless unoconv pngquant WeasyPrint ``` @@ -146,7 +146,6 @@ The easiest method is to use the language packs provided by your repositories. S 1. Download the desired language pack(s) by selecting the `.traineddata` file(s) for the language(s) you need. 2. Place the `.traineddata` files in the Tesseract tessdata directory: `/usr/share/tessdata` -3. Please view [OCRmyPDF install guide](https://ocrmypdf.readthedocs.io/en/latest/installation.html) for more info. **IMPORTANT:** DO NOT REMOVE EXISTING `eng.traineddata`, IT'S REQUIRED. diff --git a/README.md b/README.md index b0aedb944..042ae32ae 100644 --- a/README.md +++ b/README.md @@ -79,15 +79,15 @@ All files and PDFs exist either exclusively on the client side, reside in server - Detect and remove blank pages - Compare two PDFs and show differences in text - Add images to PDFs -- Compress PDFs to decrease their filesize (using OCRMyPDF) +- Compress PDFs to decrease their filesize (using qpdf) - Extract images from PDF - Remove images from PDF - Extract images from scans - Remove annotations - Add page numbers - Auto rename file by detecting PDF header text -- OCR on PDF (using OCRMyPDF) -- PDF/A conversion (using OCRMyPDF) +- OCR on PDF (using tesseract) +- PDF/A conversion (using libreoffice) - Edit metadata - Flatten PDFs - Get all information on a PDF to view or export as JSON @@ -102,7 +102,7 @@ A demo of the app is available [here](https://stirlingpdf.io). - Spring Boot + Thymeleaf - [PDFBox](https://github.com/apache/pdfbox/tree/trunk) - [LibreOffice](https://www.libreoffice.org/discover/libreoffice/) for advanced conversions -- [OcrMyPdf](https://github.com/ocrmypdf/OCRmyPDF) +- [qpdf](https://github.com/qpdf/qpdf) - HTML, CSS, JavaScript - Docker - [PDF.js](https://github.com/mozilla/pdf.js) diff --git a/Version-groups.md b/Version-groups.md index 93c5b4b0e..e7f5536c0 100644 --- a/Version-groups.md +++ b/Version-groups.md @@ -8,7 +8,7 @@ The 'Fat' container contains all those found in 'Full' with security jar along w | Libre | | ✔️ | | Python | | ✔️ | | OpenCV | | ✔️ | -| OCRmyPDF | | ✔️ | +| qpdf | | ✔️ | | Operation | Ultra-Lite | Full | | ---------------------- | ---------- | ---- | diff --git a/build.gradle b/build.gradle index 28219dada..fdfb5c8f8 100644 --- a/build.gradle +++ b/build.gradle @@ -24,7 +24,8 @@ ext { } group = "stirling.software" -version = "0.34.0" +version = "0.35.0" + java { // 17 is lowest but we support and recommend 21 diff --git a/cucumber/features/external.feature b/cucumber/features/external.feature index 58c0a859c..081d7f0e3 100644 --- a/cucumber/features/external.feature +++ b/cucumber/features/external.feature @@ -145,7 +145,7 @@ Feature: API Validation And the response file should have extension ".pdf" And the response file should have size greater than 100 - @compress @ghostscript @positive + @compress @qpdf @positive Scenario: Compress Given I use an example file at "exampleFiles/ghost3.pdf" as parameter "fileInput" And the request data includes @@ -156,7 +156,7 @@ Feature: API Validation And the response file should have extension ".pdf" And the response file should have size greater than 100 - @compress @ghostscript @positive + @compress @qpdf @positive Scenario: Compress Given I use an example file at "exampleFiles/ghost2.pdf" as parameter "fileInput" And the request data includes @@ -169,7 +169,7 @@ Feature: API Validation And the response file should have size greater than 100 - @compress @ghostscript @positive + @compress @qpdf @positive Scenario: Compress Given I use an example file at "exampleFiles/ghost1.pdf" as parameter "fileInput" And the request data includes diff --git a/scripts/replace_translation_line.sh b/scripts/replace_translation_line.sh index d5161d36a..75f217a87 100644 --- a/scripts/replace_translation_line.sh +++ b/scripts/replace_translation_line.sh @@ -1,8 +1,8 @@ #!/bin/bash translation_key="pdfToPDFA.credit" -old_value="OCRmyPDF" -new_value="ghostscript" +old_value="qpdf" +new_value="liibreoffice" for file in ../src/main/resources/messages_*.properties; do sed -i "/^$translation_key=/s/$old_value/$new_value/" "$file" diff --git a/src/main/java/stirling/software/SPDF/config/EndpointConfiguration.java b/src/main/java/stirling/software/SPDF/config/EndpointConfiguration.java index 64f56045b..3ce6f2bb8 100644 --- a/src/main/java/stirling/software/SPDF/config/EndpointConfiguration.java +++ b/src/main/java/stirling/software/SPDF/config/EndpointConfiguration.java @@ -188,7 +188,7 @@ public class EndpointConfiguration { addEndpointToGroup("OpenCV", "extract-image-scans"); // LibreOffice - addEndpointToGroup("LibreOffice", "repair"); + addEndpointToGroup("qpdf", "repair"); addEndpointToGroup("LibreOffice", "file-to-pdf"); addEndpointToGroup("LibreOffice", "pdf-to-word"); addEndpointToGroup("LibreOffice", "pdf-to-presentation"); @@ -199,10 +199,11 @@ public class EndpointConfiguration { // Unoconv addEndpointToGroup("Unoconv", "file-to-pdf"); - // OCRmyPDF - addEndpointToGroup("OCRmyPDF", "compress-pdf"); - addEndpointToGroup("OCRmyPDF", "pdf-to-pdfa"); - addEndpointToGroup("OCRmyPDF", "ocr-pdf"); + // qpdf + addEndpointToGroup("qpdf", "compress-pdf"); + addEndpointToGroup("qpdf", "pdf-to-pdfa"); + + addEndpointToGroup("tesseract", "ocr-pdf"); // Java addEndpointToGroup("Java", "merge-pdfs"); @@ -248,10 +249,10 @@ public class EndpointConfiguration { addEndpointToGroup("Javascript", "compare"); addEndpointToGroup("Javascript", "adjust-contrast"); - // Ghostscript dependent endpoints - addEndpointToGroup("Ghostscript", "compress-pdf"); - addEndpointToGroup("Ghostscript", "pdf-to-pdfa"); - addEndpointToGroup("Ghostscript", "repair"); + // qpdf dependent endpoints + addEndpointToGroup("qpdf", "compress-pdf"); + addEndpointToGroup("qpdf", "pdf-to-pdfa"); + addEndpointToGroup("qpdf", "repair"); // Weasyprint dependent endpoints addEndpointToGroup("Weasyprint", "html-to-pdf"); diff --git a/src/main/java/stirling/software/SPDF/config/ExternalAppDepConfig.java b/src/main/java/stirling/software/SPDF/config/ExternalAppDepConfig.java index f8ab43ad5..8ed708ea3 100644 --- a/src/main/java/stirling/software/SPDF/config/ExternalAppDepConfig.java +++ b/src/main/java/stirling/software/SPDF/config/ExternalAppDepConfig.java @@ -37,12 +37,13 @@ public class ExternalAppDepConfig { private final Map> commandToGroupMapping = new HashMap<>() { { - put("gs", List.of("Ghostscript")); put("soffice", List.of("LibreOffice")); - put("ocrmypdf", List.of("OCRmyPDF")); put("weasyprint", List.of("Weasyprint")); put("pdftohtml", List.of("Pdftohtml")); put("unoconv", List.of("Unoconv")); + put("qpdf", List.of("qpdf")); + put("tesseract", List.of("tesseract")); + } }; @@ -97,9 +98,9 @@ public class ExternalAppDepConfig { public void checkDependencies() { // Check core dependencies - checkDependencyAndDisableGroup("gs"); + checkDependencyAndDisableGroup("tesseract"); checkDependencyAndDisableGroup("soffice"); - checkDependencyAndDisableGroup("ocrmypdf"); + checkDependencyAndDisableGroup("qpdf"); checkDependencyAndDisableGroup("weasyprint"); checkDependencyAndDisableGroup("pdftohtml"); checkDependencyAndDisableGroup("unoconv"); diff --git a/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertPDFToPDFA.java b/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertPDFToPDFA.java index c437c4cb0..c72ddf0ff 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertPDFToPDFA.java +++ b/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertPDFToPDFA.java @@ -1,12 +1,13 @@ package stirling.software.SPDF.controller.api.converters; -import java.io.FileOutputStream; -import java.io.OutputStream; +import java.io.File; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; +import org.apache.commons.io.FileUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.MediaType; @@ -37,59 +38,90 @@ public class ConvertPDFToPDFA { @Operation( summary = "Convert a PDF to a PDF/A", description = - "This endpoint converts a PDF file to a PDF/A file. PDF/A is a format designed for long-term archiving of digital documents. Input:PDF Output:PDF Type:SISO") + "This endpoint converts a PDF file to a PDF/A file using LibreOffice. PDF/A is a format designed for long-term archiving of digital documents. Input:PDF Output:PDF Type:SISO") public ResponseEntity pdfToPdfA(@ModelAttribute PdfToPdfARequest request) throws Exception { MultipartFile inputFile = request.getFileInput(); String outputFormat = request.getOutputFormat(); - // Convert MultipartFile to byte[] - byte[] pdfBytes = inputFile.getBytes(); - - // Save the uploaded file to a temporary location - Path tempInputFile = Files.createTempFile("input_", ".pdf"); - try (OutputStream outputStream = new FileOutputStream(tempInputFile.toFile())) { - outputStream.write(pdfBytes); + // Validate input file type + if (!"application/pdf".equals(inputFile.getContentType())) { + logger.error("Invalid input file type: {}", inputFile.getContentType()); + throw new IllegalArgumentException("Input file must be a PDF"); } - // Prepare the output file path - Path tempOutputFile = Files.createTempFile("output_", ".pdf"); - - // Prepare the ghostscript command - List command = new ArrayList<>(); - command.add("gs"); - command.add("-dPDFA=" + ("pdfa".equals(outputFormat) ? "2" : "1")); - command.add("-dNOPAUSE"); - command.add("-dBATCH"); - command.add("-sColorConversionStrategy=sRGB"); - command.add("-sDEVICE=pdfwrite"); - command.add("-dPDFACompatibilityPolicy=2"); - command.add("-o"); - command.add(tempOutputFile.toString()); - command.add(tempInputFile.toString()); - - ProcessExecutorResult returnCode = - ProcessExecutor.getInstance(ProcessExecutor.Processes.GHOSTSCRIPT) - .runCommandWithOutputHandling(command); - - if (returnCode.getRc() != 0) { - logger.info( - outputFormat + " conversion failed with return code: " + returnCode.getRc()); + // Get the original filename without extension + String originalFileName = Filenames.toSimpleFileName(inputFile.getOriginalFilename()); + if (originalFileName == null || originalFileName.trim().isEmpty()) { + originalFileName = "output.pdf"; } + String baseFileName = + originalFileName.contains(".") + ? originalFileName.substring(0, originalFileName.lastIndexOf('.')) + : originalFileName; + + Path tempInputFile = null; + Path tempOutputDir = null; + byte[] fileBytes; try { - byte[] pdfBytesOutput = Files.readAllBytes(tempOutputFile); - // Return the optimized PDF as a response - String outputFilename = - Filenames.toSimpleFileName(inputFile.getOriginalFilename()) - .replaceFirst("[.][^.]+$", "") - + "_PDFA.pdf"; + // Save uploaded file to temp location + tempInputFile = Files.createTempFile("input_", ".pdf"); + inputFile.transferTo(tempInputFile); + + // Create temp output directory + tempOutputDir = Files.createTempDirectory("output_"); + + // Determine PDF/A filter based on requested format + String pdfFilter = + "pdfa".equals(outputFormat) + ? "writer_pdf_Export:{'SelectPdfVersion':{'Value':'2'}}:writer_pdf_Export" + : "writer_pdf_Export:{'SelectPdfVersion':{'Value':'1'}}:writer_pdf_Export"; + + // Prepare LibreOffice command + List command = + new ArrayList<>( + Arrays.asList( + "soffice", + "--headless", + "--nologo", + "--convert-to", + "pdf:" + pdfFilter, + "--outdir", + tempOutputDir.toString(), + tempInputFile.toString())); + + ProcessExecutorResult returnCode = + ProcessExecutor.getInstance(ProcessExecutor.Processes.LIBRE_OFFICE) + .runCommandWithOutputHandling(command); + + if (returnCode.getRc() != 0) { + logger.error("PDF/A conversion failed with return code: {}", returnCode.getRc()); + throw new RuntimeException("PDF/A conversion failed"); + } + + // Get the output file + File[] outputFiles = tempOutputDir.toFile().listFiles(); + if (outputFiles == null || outputFiles.length != 1) { + throw new RuntimeException( + "Expected exactly one output file but found " + + (outputFiles == null ? "none" : outputFiles.length)); + } + + fileBytes = FileUtils.readFileToByteArray(outputFiles[0]); + String outputFilename = baseFileName + "_PDFA.pdf"; + return WebResponseUtils.bytesToWebResponse( - pdfBytesOutput, outputFilename, MediaType.APPLICATION_PDF); + fileBytes, outputFilename, MediaType.APPLICATION_PDF); + } finally { - // Clean up the temporary files - Files.deleteIfExists(tempInputFile); - Files.deleteIfExists(tempOutputFile); + // Clean up temporary files + if (tempInputFile != null) { + Files.deleteIfExists(tempInputFile); + } + if (tempOutputDir != null) { + FileUtils.deleteDirectory(tempOutputDir.toFile()); + } } } } diff --git a/src/main/java/stirling/software/SPDF/controller/api/converters/ExtractCSVController.java b/src/main/java/stirling/software/SPDF/controller/api/converters/ExtractCSVController.java index a6415bfc4..f86b7aa3e 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/converters/ExtractCSVController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/converters/ExtractCSVController.java @@ -20,7 +20,7 @@ import org.springframework.web.bind.annotation.RestController; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; -import stirling.software.SPDF.controller.api.CropController; + import stirling.software.SPDF.model.api.extract.PDFFilePage; import stirling.software.SPDF.pdf.FlexibleCSVWriter; import technology.tabula.ObjectExtractor; @@ -37,11 +37,15 @@ public class ExtractCSVController { private static final Logger logger = LoggerFactory.getLogger(ExtractCSVController.class); @PostMapping(value = "/pdf/csv", consumes = "multipart/form-data") - @Operation(summary = "Extracts a CSV document from a PDF", description = "This operation takes an input PDF file and returns CSV file of whole page. Input:PDF Output:CSV Type:SISO") + @Operation( + summary = "Extracts a CSV document from a PDF", + description = + "This operation takes an input PDF file and returns CSV file of whole page. Input:PDF Output:CSV Type:SISO") public ResponseEntity PdfToCsv(@ModelAttribute PDFFilePage form) throws Exception { StringWriter writer = new StringWriter(); try (PDDocument document = Loader.loadPDF(form.getFileInput().getBytes())) { - CSVFormat format = CSVFormat.EXCEL.builder().setEscape('"').setQuoteMode(QuoteMode.ALL).build(); + CSVFormat format = + CSVFormat.EXCEL.builder().setEscape('"').setQuoteMode(QuoteMode.ALL).build(); Writer csvWriter = new FlexibleCSVWriter(format); SpreadsheetExtractionAlgorithm sea = new SpreadsheetExtractionAlgorithm(); try (ObjectExtractor extractor = new ObjectExtractor(document)) { @@ -56,8 +60,8 @@ public class ExtractCSVController { ContentDisposition.builder("attachment") .filename( form.getFileInput() - .getOriginalFilename() - .replaceFirst("[.][^.]+$", "") + .getOriginalFilename() + .replaceFirst("[.][^.]+$", "") + "_extracted.csv") .build()); headers.setContentType(MediaType.parseMediaType("text/csv")); diff --git a/src/main/java/stirling/software/SPDF/controller/api/misc/CompressController.java b/src/main/java/stirling/software/SPDF/controller/api/misc/CompressController.java index 3f55a4f51..723bd371f 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/misc/CompressController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/misc/CompressController.java @@ -10,7 +10,6 @@ import java.util.List; import javax.imageio.ImageIO; -import org.apache.commons.io.FileUtils; import org.apache.pdfbox.Loader; import org.apache.pdfbox.cos.COSName; import org.apache.pdfbox.pdmodel.PDDocument; @@ -53,6 +52,54 @@ public class CompressController { this.pdfDocumentFactory = pdfDocumentFactory; } + private void compressImagesInPDF(Path pdfFile, double initialScaleFactor) throws Exception { + byte[] fileBytes = Files.readAllBytes(pdfFile); + try (PDDocument doc = Loader.loadPDF(fileBytes)) { + double scaleFactor = initialScaleFactor; + + for (PDPage page : doc.getPages()) { + PDResources res = page.getResources(); + if (res != null && res.getXObjectNames() != null) { + for (COSName name : res.getXObjectNames()) { + PDXObject xobj = res.getXObject(name); + if (xobj instanceof PDImageXObject) { + PDImageXObject image = (PDImageXObject) xobj; + BufferedImage bufferedImage = image.getImage(); + + int newWidth = (int) (bufferedImage.getWidth() * scaleFactor); + int newHeight = (int) (bufferedImage.getHeight() * scaleFactor); + + if (newWidth == 0 || newHeight == 0) { + continue; + } + + Image scaledImage = + bufferedImage.getScaledInstance( + newWidth, newHeight, Image.SCALE_SMOOTH); + + BufferedImage scaledBufferedImage = + new BufferedImage( + newWidth, newHeight, BufferedImage.TYPE_INT_RGB); + scaledBufferedImage.getGraphics().drawImage(scaledImage, 0, 0, null); + + ByteArrayOutputStream compressedImageStream = + new ByteArrayOutputStream(); + ImageIO.write(scaledBufferedImage, "jpeg", compressedImageStream); + byte[] imageBytes = compressedImageStream.toByteArray(); + compressedImageStream.close(); + + PDImageXObject compressedImage = + PDImageXObject.createFromByteArray( + doc, imageBytes, image.getCOSObject().toString()); + res.put(name, compressedImage); + } + } + } + } + doc.save(pdfFile.toString()); + } + } + @PostMapping(consumes = "multipart/form-data", value = "/compress-pdf") @Operation( summary = "Optimize PDF file", @@ -75,209 +122,92 @@ public class CompressController { autoMode = true; } - // Save the uploaded file to a temporary location Path tempInputFile = Files.createTempFile("input_", ".pdf"); inputFile.transferTo(tempInputFile.toFile()); long inputFileSize = Files.size(tempInputFile); - // Prepare the output file path - Path tempOutputFile = null; byte[] pdfBytes; try { tempOutputFile = Files.createTempFile("output_", ".pdf"); - // Determine initial optimization level based on expected size reduction, only if in - // autoMode + if (autoMode) { double sizeReductionRatio = expectedOutputSize / (double) inputFileSize; - if (sizeReductionRatio > 0.7) { - optimizeLevel = 1; - } else if (sizeReductionRatio > 0.5) { - optimizeLevel = 2; - } else if (sizeReductionRatio > 0.35) { - optimizeLevel = 3; - } else { - optimizeLevel = 3; - } + optimizeLevel = determineOptimizeLevel(sizeReductionRatio); } boolean sizeMet = false; - while (!sizeMet && optimizeLevel <= 4) { - // Prepare the Ghostscript command - List command = new ArrayList<>(); - command.add("gs"); - command.add("-sDEVICE=pdfwrite"); - command.add("-dCompatibilityLevel=1.5"); + while (!sizeMet && optimizeLevel <= 9) { - switch (optimizeLevel) { - case 1: - command.add("-dPDFSETTINGS=/prepress"); - break; - case 2: - command.add("-dPDFSETTINGS=/printer"); - break; - case 3: - command.add("-dPDFSETTINGS=/ebook"); - break; - case 4: - command.add("-dPDFSETTINGS=/screen"); - break; - default: - command.add("-dPDFSETTINGS=/default"); + // Apply additional image compression for levels 6-9 + if (optimizeLevel >= 6) { + // Calculate scale factor based on optimization level + double scaleFactor = + switch (optimizeLevel) { + case 6 -> 0.9; // 90% of original size + case 7 -> 0.8; // 80% of original size + case 8 -> 0.65; // 70% of original size + case 9 -> 0.5; // 60% of original size + default -> 1.0; + }; + compressImagesInPDF(tempInputFile, scaleFactor); } - command.add("-dNOPAUSE"); - command.add("-dQUIET"); - command.add("-dBATCH"); - command.add("-sOutputFile=" + tempOutputFile.toString()); + // Run QPDF optimization + List command = new ArrayList<>(); + command.add("qpdf"); + if (request.getNormalize()) { + command.add("--normalize-content=y"); + } + if (request.getLinearize()) { + command.add("--linearize"); + } + command.add("--optimize-images"); + command.add("--recompress-flate"); + command.add("--compression-level=" + optimizeLevel); + command.add("--compress-streams=y"); + command.add("--object-streams=generate"); command.add(tempInputFile.toString()); + command.add(tempOutputFile.toString()); - ProcessExecutorResult returnCode = - ProcessExecutor.getInstance(ProcessExecutor.Processes.GHOSTSCRIPT) - .runCommandWithOutputHandling(command); + ProcessExecutorResult returnCode = null; + try { + returnCode = + ProcessExecutor.getInstance(ProcessExecutor.Processes.QPDF) + .runCommandWithOutputHandling(command); + } catch (Exception e) { + if (returnCode != null && returnCode.getRc() != 3) { + throw e; + } + } - // Check if file size is within expected size or not auto mode so instantly finish + // Check if file size is within expected size or not auto mode long outputFileSize = Files.size(tempOutputFile); if (outputFileSize <= expectedOutputSize || !autoMode) { sizeMet = true; } else { - // Increase optimization level for next iteration - optimizeLevel++; - if (autoMode && optimizeLevel > 4) { - logger.info("Skipping level 5 due to bad results in auto mode"); + optimizeLevel = + incrementOptimizeLevel( + optimizeLevel, outputFileSize, expectedOutputSize); + if (autoMode && optimizeLevel > 9) { + logger.info("Maximum compression level reached in auto mode"); sizeMet = true; - } else { - logger.info( - "Increasing ghostscript optimisation level to " + optimizeLevel); } } } - if (expectedOutputSize != null && autoMode) { - long outputFileSize = Files.size(tempOutputFile); - byte[] fileBytes = Files.readAllBytes(tempOutputFile); - if (outputFileSize > expectedOutputSize) { - try (PDDocument doc = Loader.loadPDF(fileBytes)) { - long previousFileSize = 0; - double scaleFactorConst = 0.9f; - double scaleFactor = 0.9f; - while (true) { - for (PDPage page : doc.getPages()) { - PDResources res = page.getResources(); - if (res != null && res.getXObjectNames() != null) { - for (COSName name : res.getXObjectNames()) { - PDXObject xobj = res.getXObject(name); - if (xobj != null && xobj instanceof PDImageXObject) { - PDImageXObject image = (PDImageXObject) xobj; - - // Get the image in BufferedImage format - BufferedImage bufferedImage = image.getImage(); - - // Calculate the new dimensions - int newWidth = - (int) - (bufferedImage.getWidth() - * scaleFactorConst); - int newHeight = - (int) - (bufferedImage.getHeight() - * scaleFactorConst); - - // If the new dimensions are zero, skip this iteration - if (newWidth == 0 || newHeight == 0) { - continue; - } - - // Otherwise, proceed with the scaling - Image scaledImage = - bufferedImage.getScaledInstance( - newWidth, - newHeight, - Image.SCALE_SMOOTH); - - // Convert the scaled image back to a BufferedImage - BufferedImage scaledBufferedImage = - new BufferedImage( - newWidth, - newHeight, - BufferedImage.TYPE_INT_RGB); - scaledBufferedImage - .getGraphics() - .drawImage(scaledImage, 0, 0, null); - - // Compress the scaled image - ByteArrayOutputStream compressedImageStream = - new ByteArrayOutputStream(); - ImageIO.write( - scaledBufferedImage, - "jpeg", - compressedImageStream); - byte[] imageBytes = compressedImageStream.toByteArray(); - compressedImageStream.close(); - - PDImageXObject compressedImage = - PDImageXObject.createFromByteArray( - doc, - imageBytes, - image.getCOSObject().toString()); - - // Replace the image in the resources with the - // compressed - // version - res.put(name, compressedImage); - } - } - } - } - - // save the document to tempOutputFile again - doc.save(tempOutputFile.toString()); - - long currentSize = Files.size(tempOutputFile); - // Check if the overall PDF size is still larger than expectedOutputSize - if (currentSize > expectedOutputSize) { - // Log the current file size and scaleFactor - - logger.info( - "Current file size: " - + FileUtils.byteCountToDisplaySize(currentSize)); - logger.info("Current scale factor: " + scaleFactor); - - // The file is still too large, reduce scaleFactor and try again - scaleFactor *= 0.9f; // reduce scaleFactor by 10% - // Avoid scaleFactor being too small, causing the image to shrink to - // 0 - if (scaleFactor < 0.2f || previousFileSize == currentSize) { - throw new RuntimeException( - "Could not reach the desired size without excessively degrading image quality, lowest size recommended is " - + FileUtils.byteCountToDisplaySize(currentSize) - + ", " - + currentSize - + " bytes"); - } - previousFileSize = currentSize; - } else { - // The file is small enough, break the loop - break; - } - } - } - } - } // Read the optimized PDF file pdfBytes = Files.readAllBytes(tempOutputFile); Path finalFile = tempOutputFile; + // Check if optimized file is larger than the original if (pdfBytes.length > inputFileSize) { - // Log the occurrence logger.warn( "Optimized file is larger than the original. Returning the original file instead."); - - // Read the original file again finalFile = tempInputFile; } - // Return the optimized PDF as a response + String outputFilename = Filenames.toSimpleFileName(inputFile.getOriginalFilename()) .replaceFirst("[.][^.]+$", "") @@ -286,10 +216,31 @@ public class CompressController { pdfDocumentFactory.load(finalFile.toFile()), outputFilename); } finally { - // Clean up the temporary files - // deleted by multipart file handler deu to transferTo? - // Files.deleteIfExists(tempInputFile); Files.deleteIfExists(tempOutputFile); } } + + private int determineOptimizeLevel(double sizeReductionRatio) { + if (sizeReductionRatio > 0.9) return 1; + if (sizeReductionRatio > 0.8) return 2; + if (sizeReductionRatio > 0.7) return 3; + if (sizeReductionRatio > 0.6) return 4; + if (sizeReductionRatio > 0.5) return 5; + if (sizeReductionRatio > 0.4) return 6; + if (sizeReductionRatio > 0.3) return 7; + if (sizeReductionRatio > 0.2) return 8; + return 9; + } + + private int incrementOptimizeLevel(int currentLevel, long currentSize, long targetSize) { + double currentRatio = currentSize / (double) targetSize; + logger.info("Current compression ratio: {}", String.format("%.2f", currentRatio)); + + if (currentRatio > 2.0) { + return Math.min(9, currentLevel + 3); + } else if (currentRatio > 1.5) { + return Math.min(9, currentLevel + 2); + } + return Math.min(9, currentLevel + 1); + } } diff --git a/src/main/java/stirling/software/SPDF/controller/api/misc/FakeScanControllerWIP.java b/src/main/java/stirling/software/SPDF/controller/api/misc/FakeScanControllerWIP.java index d4c4ce05b..7206d1cdd 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/misc/FakeScanControllerWIP.java +++ b/src/main/java/stirling/software/SPDF/controller/api/misc/FakeScanControllerWIP.java @@ -58,7 +58,7 @@ public class FakeScanControllerWIP { @Operation( summary = "Repair a PDF file", description = - "This endpoint repairs a given PDF file by running Ghostscript command. The PDF is first saved to a temporary location, repaired, read back, and then returned as a response.") + "This endpoint repairs a given PDF file by running qpdf command. The PDF is first saved to a temporary location, repaired, read back, and then returned as a response.") public ResponseEntity fakeScan(@ModelAttribute PDFFile request) throws IOException { MultipartFile inputFile = request.getFileInput(); diff --git a/src/main/java/stirling/software/SPDF/controller/api/misc/OCRController.java b/src/main/java/stirling/software/SPDF/controller/api/misc/OCRController.java index 96cabb60d..b2601ec55 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/misc/OCRController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/misc/OCRController.java @@ -1,19 +1,31 @@ package stirling.software.SPDF.controller.api.misc; -import java.io.ByteArrayInputStream; +import io.github.pixee.security.BoundedLineReader; +import io.github.pixee.security.Filenames; +import java.awt.image.BufferedImage; +import java.io.BufferedReader; import java.io.File; -import java.io.FileOutputStream; +import java.io.FileInputStream; import java.io.IOException; +import java.io.InputStreamReader; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.Comparator; import java.util.List; import java.util.stream.Collectors; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; +import javax.imageio.ImageIO; + +import org.apache.pdfbox.multipdf.PDFMergerUtility; +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.pdfbox.rendering.PDFRenderer; +import org.apache.pdfbox.text.PDFTextStripper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; @@ -23,24 +35,29 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; -import io.github.pixee.security.Filenames; -import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.extern.slf4j.Slf4j; import stirling.software.SPDF.model.ApplicationProperties; import stirling.software.SPDF.model.api.misc.ProcessPdfWithOcrRequest; import stirling.software.SPDF.service.CustomPDDocumentFactory; -import stirling.software.SPDF.utils.ProcessExecutor; -import stirling.software.SPDF.utils.ProcessExecutor.ProcessExecutorResult; -import stirling.software.SPDF.utils.WebResponseUtils; @RestController @RequestMapping("/api/v1/misc") @Tag(name = "Misc", description = "Miscellaneous APIs") +@Slf4j public class OCRController { - @Autowired ApplicationProperties applicationProperties; + @Autowired private ApplicationProperties applicationProperties; + private final CustomPDDocumentFactory pdfDocumentFactory; + + @Autowired + public OCRController(CustomPDDocumentFactory pdfDocumentFactory) { + this.pdfDocumentFactory = pdfDocumentFactory; + } + + /** Gets the list of available Tesseract languages from the tessdata directory */ public List getAvailableTesseractLanguages() { String tessdataDir = applicationProperties.getSystem().getTessdataDir(); File[] files = new File(tessdataDir).listFiles(); @@ -54,196 +71,161 @@ public class OCRController { .collect(Collectors.toList()); } - private final CustomPDDocumentFactory pdfDocumentFactory; - - @Autowired - public OCRController(CustomPDDocumentFactory pdfDocumentFactory) { - this.pdfDocumentFactory = pdfDocumentFactory; - } - @PostMapping(consumes = "multipart/form-data", value = "/ocr-pdf") - @Operation( - summary = "Process a PDF file with OCR", - description = - "This endpoint processes a PDF file using OCR (Optical Character Recognition). Users can specify languages, sidecar, deskew, clean, cleanFinal, ocrType, ocrRenderType, and removeImagesAfter options. Input:PDF Output:PDF Type:SI-Conditional") public ResponseEntity processPdfWithOCR( @ModelAttribute ProcessPdfWithOcrRequest request) throws IOException, InterruptedException { MultipartFile inputFile = request.getFileInput(); - List selectedLanguages = request.getLanguages(); - Boolean sidecar = request.isSidecar(); - Boolean deskew = request.isDeskew(); - Boolean clean = request.isClean(); - Boolean cleanFinal = request.isCleanFinal(); + List languages = request.getLanguages(); String ocrType = request.getOcrType(); - String ocrRenderType = request.getOcrRenderType(); - Boolean removeImagesAfter = request.isRemoveImagesAfter(); - // --output-type pdfa - if (selectedLanguages == null || selectedLanguages.isEmpty()) { - throw new IOException("Please select at least one language."); - } - if (!"hocr".equals(ocrRenderType) && !"sandwich".equals(ocrRenderType)) { - throw new IOException("ocrRenderType wrong"); - } + Path tempDir = Files.createTempDirectory("ocr_process"); + Path tempInputFile = tempDir.resolve("input.pdf"); + Path tempOutputDir = tempDir.resolve("output"); + Path tempImagesDir = tempDir.resolve("images"); + Path finalOutputFile = tempDir.resolve("final_output.pdf"); - // Get available Tesseract languages - List availableLanguages = getAvailableTesseractLanguages(); - - // Validate selected languages - selectedLanguages = - selectedLanguages.stream().filter(availableLanguages::contains).toList(); - - if (selectedLanguages.isEmpty()) { - throw new IOException("None of the selected languages are valid."); - } - // Save the uploaded file to a temporary location - Path tempInputFile = Files.createTempFile("input_", ".pdf"); - Path tempOutputFile = Files.createTempFile("output_", ".pdf"); - Path sidecarTextPath = null; + Files.createDirectories(tempOutputDir); + Files.createDirectories(tempImagesDir); try { + // Save input file inputFile.transferTo(tempInputFile.toFile()); + PDFMergerUtility merger = new PDFMergerUtility(); + merger.setDestinationFileName(finalOutputFile.toString()); - // Run OCR Command - String languageOption = String.join("+", selectedLanguages); + try (PDDocument document = pdfDocumentFactory.load(tempInputFile.toFile())) { + PDFRenderer pdfRenderer = new PDFRenderer(document); + int pageCount = document.getNumberOfPages(); - List command = - new ArrayList<>( - Arrays.asList( - "ocrmypdf", - "--verbose", - "2", - "--output-type", - "pdf", - "--pdf-renderer", - ocrRenderType)); + for (int pageNum = 0; pageNum < pageCount; pageNum++) { + PDPage page = document.getPage(pageNum); + boolean hasText = false; - if (sidecar != null && sidecar) { - sidecarTextPath = Files.createTempFile("sidecar", ".txt"); - command.add("--sidecar"); - command.add(sidecarTextPath.toString()); - } + // Check for existing text + try (PDDocument tempDoc = new PDDocument()) { + tempDoc.addPage(page); + PDFTextStripper stripper = new PDFTextStripper(); + hasText = !stripper.getText(tempDoc).trim().isEmpty(); + } - if (deskew != null && deskew) { - command.add("--deskew"); - } - if (clean != null && clean) { - command.add("--clean"); - } - if (cleanFinal != null && cleanFinal) { - command.add("--clean-final"); - } - if (ocrType != null && !"".equals(ocrType)) { - if ("skip-text".equals(ocrType)) { - command.add("--skip-text"); - } else if ("force-ocr".equals(ocrType)) { - command.add("--force-ocr"); - } else if ("Normal".equals(ocrType)) { + boolean shouldOcr = + switch (ocrType) { + case "skip-text" -> !hasText; + case "force-ocr" -> true; + default -> true; + }; - } - } + Path pageOutputPath = + tempOutputDir.resolve(String.format("page_%d.pdf", pageNum)); - command.addAll( - Arrays.asList( - "--language", - languageOption, - tempInputFile.toString(), - tempOutputFile.toString())); + if (shouldOcr) { + // Convert page to image + BufferedImage image = pdfRenderer.renderImageWithDPI(pageNum, 300); + Path imagePath = + tempImagesDir.resolve(String.format("page_%d.png", pageNum)); + ImageIO.write(image, "png", imagePath.toFile()); - // Run CLI command - ProcessExecutorResult result = - ProcessExecutor.getInstance(ProcessExecutor.Processes.OCR_MY_PDF) - .runCommandWithOutputHandling(command); - if (result.getRc() != 0 - && result.getMessages().contains("multiprocessing/synchronize.py") - && result.getMessages() - .contains("OSError: [Errno 38] Function not implemented")) { - command.add("--jobs"); - command.add("1"); - result = - ProcessExecutor.getInstance(ProcessExecutor.Processes.OCR_MY_PDF) - .runCommandWithOutputHandling(command); - } + // Build OCR command + List command = new ArrayList<>(); + command.add("tesseract"); + command.add(imagePath.toString()); + command.add( + tempOutputDir + .resolve(String.format("page_%d", pageNum)) + .toString()); + command.add("-l"); + command.add(String.join("+", languages)); + command.add("pdf"); // Always output PDF - // Remove images from the OCR processed PDF if the flag is set to true - if (removeImagesAfter != null && removeImagesAfter) { - Path tempPdfWithoutImages = Files.createTempFile("output_", "_no_images.pdf"); + ProcessBuilder pb = new ProcessBuilder(command); + Process process = pb.start(); - List gsCommand = - Arrays.asList( - "gs", - "-sDEVICE=pdfwrite", - "-dFILTERIMAGE", - "-o", - tempPdfWithoutImages.toString(), - tempOutputFile.toString()); + // Capture any error output + try (BufferedReader reader = + new BufferedReader( + new InputStreamReader(process.getErrorStream()))) { + String line; + while ((line = BoundedLineReader.readLine(reader, 5_000_000)) != null) { + log.debug("Tesseract: {}", line); + } + } - ProcessExecutor.getInstance(ProcessExecutor.Processes.GHOSTSCRIPT) - .runCommandWithOutputHandling(gsCommand); - tempOutputFile = tempPdfWithoutImages; - } - // Read the OCR processed PDF file - byte[] pdfBytes = pdfDocumentFactory.loadToBytes(tempOutputFile.toFile()); + int exitCode = process.waitFor(); + if (exitCode != 0) { + throw new RuntimeException( + "Tesseract failed with exit code: " + exitCode); + } - // Return the OCR processed PDF as a response - String outputFilename = - Filenames.toSimpleFileName(inputFile.getOriginalFilename()) - .replaceFirst("[.][^.]+$", "") - + "_OCR.pdf"; - - if (sidecar != null && sidecar) { - // Create a zip file containing both the PDF and the text file - String outputZipFilename = - Filenames.toSimpleFileName(inputFile.getOriginalFilename()) - .replaceFirst("[.][^.]+$", "") - + "_OCR.zip"; - Path tempZipFile = Files.createTempFile("output_", ".zip"); - - try (ZipOutputStream zipOut = - new ZipOutputStream(new FileOutputStream(tempZipFile.toFile()))) { - // Add PDF file to the zip - ZipEntry pdfEntry = new ZipEntry(outputFilename); - zipOut.putNextEntry(pdfEntry); - try (ByteArrayInputStream pdfInputStream = new ByteArrayInputStream(pdfBytes)) { - byte[] buffer = new byte[1024]; - int length; - while ((length = pdfInputStream.read(buffer)) != -1) { - zipOut.write(buffer, 0, length); + // Add OCR'd PDF to merger + merger.addSource(pageOutputPath.toFile()); + } else { + // Save original page without OCR + try (PDDocument pageDoc = new PDDocument()) { + pageDoc.addPage(page); + pageDoc.save(pageOutputPath.toFile()); + merger.addSource(pageOutputPath.toFile()); } } - zipOut.closeEntry(); - - // Add text file to the zip - ZipEntry txtEntry = new ZipEntry(outputFilename.replace(".pdf", ".txt")); - zipOut.putNextEntry(txtEntry); - Files.copy(sidecarTextPath, zipOut); - zipOut.closeEntry(); } - - byte[] zipBytes = Files.readAllBytes(tempZipFile); - - // Clean up the temporary zip file - Files.deleteIfExists(tempZipFile); - Files.deleteIfExists(tempOutputFile); - Files.deleteIfExists(sidecarTextPath); - - // Return the zip file containing both the PDF and the text file - return WebResponseUtils.bytesToWebResponse( - zipBytes, outputZipFilename, MediaType.APPLICATION_OCTET_STREAM); - } else { - // Return the OCR processed PDF as a response - Files.deleteIfExists(tempOutputFile); - return WebResponseUtils.bytesToWebResponse(pdfBytes, outputFilename); } + + // Merge all pages into final PDF + merger.mergeDocuments(null); + + // Read the final PDF file + byte[] pdfContent = Files.readAllBytes(finalOutputFile); + String outputFilename = + Filenames.toSimpleFileName(inputFile.getOriginalFilename()).replaceFirst("[.][^.]+$", "") + "_OCR.pdf"; + + return ResponseEntity.ok() + .header( + "Content-Disposition", + "attachment; filename=\"" + outputFilename + "\"") + .contentType(MediaType.APPLICATION_PDF) + .body(pdfContent); + } finally { - // Clean up the temporary files - Files.deleteIfExists(tempOutputFile); - // Comment out as transferTo makes multipart handle cleanup - // Files.deleteIfExists(tempInputFile); - if (sidecarTextPath != null) { - Files.deleteIfExists(sidecarTextPath); + // Clean up temporary files + deleteDirectory(tempDir); + } + } + + private void addFileToZip(File file, String filename, ZipOutputStream zipOut) + throws IOException { + if (!file.exists()) { + log.warn("File {} does not exist, skipping", file); + return; + } + + try (FileInputStream fis = new FileInputStream(file)) { + ZipEntry zipEntry = new ZipEntry(filename); + zipOut.putNextEntry(zipEntry); + + byte[] buffer = new byte[1024]; + int length; + while ((length = fis.read(buffer)) >= 0) { + zipOut.write(buffer, 0, length); } + + zipOut.closeEntry(); + } + } + + private void deleteDirectory(Path directory) { + try { + Files.walk(directory) + .sorted(Comparator.reverseOrder()) + .forEach( + path -> { + try { + Files.delete(path); + } catch (IOException e) { + log.error("Error deleting {}: {}", path, e.getMessage()); + } + }); + } catch (IOException e) { + log.error("Error walking directory {}: {}", directory, e.getMessage()); } } } diff --git a/src/main/java/stirling/software/SPDF/controller/api/misc/RepairController.java b/src/main/java/stirling/software/SPDF/controller/api/misc/RepairController.java index be0827cc9..7f79fcba0 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/misc/RepairController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/misc/RepairController.java @@ -44,7 +44,7 @@ public class RepairController { @Operation( summary = "Repair a PDF file", description = - "This endpoint repairs a given PDF file by running Ghostscript command. The PDF is first saved to a temporary location, repaired, read back, and then returned as a response. Input:PDF Output:PDF Type:SISO") + "This endpoint repairs a given PDF file by running qpdf command. The PDF is first saved to a temporary location, repaired, read back, and then returned as a response. Input:PDF Output:PDF Type:SISO") public ResponseEntity repairPdf(@ModelAttribute PDFFile request) throws IOException, InterruptedException { MultipartFile inputFile = request.getFileInput(); @@ -56,14 +56,15 @@ public class RepairController { try { List command = new ArrayList<>(); - command.add("gs"); - command.add("-o"); - command.add(tempOutputFile.toString()); - command.add("-sDEVICE=pdfwrite"); + command.add("qpdf"); + command.add("--replace-input"); // Automatically fixes problems it can + command.add("--qdf"); // Linearizes and normalizes PDF structure + command.add("--object-streams=disable"); // Can help with some corruptions command.add(tempInputFile.toString()); + command.add(tempOutputFile.toString()); ProcessExecutorResult returnCode = - ProcessExecutor.getInstance(ProcessExecutor.Processes.GHOSTSCRIPT) + ProcessExecutor.getInstance(ProcessExecutor.Processes.QPDF) .runCommandWithOutputHandling(command); // Read the optimized PDF file diff --git a/src/main/java/stirling/software/SPDF/controller/api/security/CertSignController.java b/src/main/java/stirling/software/SPDF/controller/api/security/CertSignController.java index cb5eef104..27262febd 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/security/CertSignController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/security/CertSignController.java @@ -98,10 +98,10 @@ public class CertSignController { public CreateSignature(KeyStore keystore, char[] pin) throws KeyStoreException, - UnrecoverableKeyException, - NoSuchAlgorithmException, - IOException, - CertificateException { + UnrecoverableKeyException, + NoSuchAlgorithmException, + IOException, + CertificateException { super(keystore, pin); ClassPathResource resource = new ClassPathResource("static/images/signature.png"); try (InputStream is = resource.getInputStream()) { @@ -160,7 +160,8 @@ public class CertSignController { extState.setNonStrokingAlphaConstant(0.5f); cs.setGraphicsStateParameters(extState); cs.transform(Matrix.getScaleInstance(0.08f, 0.08f)); - PDImageXObject img = PDImageXObject.createFromFileByExtension(logoFile, doc); + PDImageXObject img = + PDImageXObject.createFromFileByExtension(logoFile, doc); cs.drawImage(img, 100, 0); cs.restoreGraphicsState(); } @@ -208,7 +209,10 @@ public class CertSignController { } @PostMapping(consumes = "multipart/form-data", value = "/cert-sign") - @Operation(summary = "Sign PDF with a Digital Certificate", description = "This endpoint accepts a PDF file, a digital certificate and related information to sign the PDF. It then returns the digitally signed PDF file. Input:PDF Output:PDF Type:SISO") + @Operation( + summary = "Sign PDF with a Digital Certificate", + description = + "This endpoint accepts a PDF file, a digital certificate and related information to sign the PDF. It then returns the digitally signed PDF file. Input:PDF Output:PDF Type:SISO") public ResponseEntity signPDFWithCert(@ModelAttribute SignPDFWithCertRequest request) throws Exception { MultipartFile pdf = request.getFileInput(); @@ -238,7 +242,7 @@ public class CertSignController { PrivateKey privateKey = getPrivateKeyFromPEM(privateKeyFile.getBytes(), password); Certificate cert = (Certificate) getCertificateFromPEM(certFile.getBytes()); ks.setKeyEntry( - "alias", privateKey, password.toCharArray(), new Certificate[] { cert }); + "alias", privateKey, password.toCharArray(), new Certificate[] {cert}); break; case "PKCS12": ks = KeyStore.getInstance("PKCS12"); @@ -310,19 +314,22 @@ public class CertSignController { private PrivateKey getPrivateKeyFromPEM(byte[] pemBytes, String password) throws IOException, OperatorCreationException, PKCSException { - try (PEMParser pemParser = new PEMParser(new InputStreamReader(new ByteArrayInputStream(pemBytes)))) { + try (PEMParser pemParser = + new PEMParser(new InputStreamReader(new ByteArrayInputStream(pemBytes)))) { Object pemObject = pemParser.readObject(); JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC"); PrivateKeyInfo pkInfo; if (pemObject instanceof PKCS8EncryptedPrivateKeyInfo) { - InputDecryptorProvider decProv = new JceOpenSSLPKCS8DecryptorProviderBuilder() - .build(password.toCharArray()); + InputDecryptorProvider decProv = + new JceOpenSSLPKCS8DecryptorProviderBuilder().build(password.toCharArray()); pkInfo = ((PKCS8EncryptedPrivateKeyInfo) pemObject).decryptPrivateKeyInfo(decProv); } else if (pemObject instanceof PEMEncryptedKeyPair) { - PEMDecryptorProvider decProv = new JcePEMDecryptorProviderBuilder().build(password.toCharArray()); - pkInfo = ((PEMEncryptedKeyPair) pemObject) - .decryptKeyPair(decProv) - .getPrivateKeyInfo(); + PEMDecryptorProvider decProv = + new JcePEMDecryptorProviderBuilder().build(password.toCharArray()); + pkInfo = + ((PEMEncryptedKeyPair) pemObject) + .decryptKeyPair(decProv) + .getPrivateKeyInfo(); } else { pkInfo = ((PEMKeyPair) pemObject).getPrivateKeyInfo(); } diff --git a/src/main/java/stirling/software/SPDF/controller/web/HomeWebController.java b/src/main/java/stirling/software/SPDF/controller/web/HomeWebController.java index 5ade56681..41dca2fd7 100644 --- a/src/main/java/stirling/software/SPDF/controller/web/HomeWebController.java +++ b/src/main/java/stirling/software/SPDF/controller/web/HomeWebController.java @@ -55,6 +55,11 @@ public class HomeWebController { return "licenses"; } + @GetMapping("/releases") + public String getReleaseNotes(Model model) { + return "releases"; + } + @GetMapping("/") public String home(Model model) { model.addAttribute("currentPage", "home"); diff --git a/src/main/java/stirling/software/SPDF/model/ApplicationProperties.java b/src/main/java/stirling/software/SPDF/model/ApplicationProperties.java index 83df30ae0..6d4d0a7f4 100644 --- a/src/main/java/stirling/software/SPDF/model/ApplicationProperties.java +++ b/src/main/java/stirling/software/SPDF/model/ApplicationProperties.java @@ -320,12 +320,20 @@ public class ApplicationProperties { public static class SessionLimit { private int libreOfficeSessionLimit; private int pdfToHtmlSessionLimit; - private int ocrMyPdfSessionLimit; private int pythonOpenCvSessionLimit; - private int ghostScriptSessionLimit; private int weasyPrintSessionLimit; private int installAppSessionLimit; private int calibreSessionLimit; + private int qpdfSessionLimit; + private int tesseractSessionLimit; + + public int getQpdfSessionLimit() { + return qpdfSessionLimit > 0 ? qpdfSessionLimit : 2; + } + + public int getTesseractSessionLimit() { + return tesseractSessionLimit > 0 ? tesseractSessionLimit : 1; + } public int getLibreOfficeSessionLimit() { return libreOfficeSessionLimit > 0 ? libreOfficeSessionLimit : 1; @@ -335,18 +343,10 @@ public class ApplicationProperties { return pdfToHtmlSessionLimit > 0 ? pdfToHtmlSessionLimit : 1; } - public int getOcrMyPdfSessionLimit() { - return ocrMyPdfSessionLimit > 0 ? ocrMyPdfSessionLimit : 2; - } - public int getPythonOpenCvSessionLimit() { return pythonOpenCvSessionLimit > 0 ? pythonOpenCvSessionLimit : 8; } - public int getGhostScriptSessionLimit() { - return ghostScriptSessionLimit > 0 ? ghostScriptSessionLimit : 16; - } - public int getWeasyPrintSessionLimit() { return weasyPrintSessionLimit > 0 ? weasyPrintSessionLimit : 16; } @@ -364,12 +364,20 @@ public class ApplicationProperties { public static class TimeoutMinutes { private long libreOfficeTimeoutMinutes; private long pdfToHtmlTimeoutMinutes; - private long ocrMyPdfTimeoutMinutes; private long pythonOpenCvTimeoutMinutes; - private long ghostScriptTimeoutMinutes; private long weasyPrintTimeoutMinutes; private long installAppTimeoutMinutes; private long calibreTimeoutMinutes; + private long tesseractTimeoutMinutes; + private long qpdfTimeoutMinutes; + + public long getTesseractTimeoutMinutes() { + return tesseractTimeoutMinutes > 0 ? tesseractTimeoutMinutes : 30; + } + + public long getQpdfTimeoutMinutes() { + return qpdfTimeoutMinutes > 0 ? qpdfTimeoutMinutes : 30; + } public long getLibreOfficeTimeoutMinutes() { return libreOfficeTimeoutMinutes > 0 ? libreOfficeTimeoutMinutes : 30; @@ -379,18 +387,10 @@ public class ApplicationProperties { return pdfToHtmlTimeoutMinutes > 0 ? pdfToHtmlTimeoutMinutes : 20; } - public long getOcrMyPdfTimeoutMinutes() { - return ocrMyPdfTimeoutMinutes > 0 ? ocrMyPdfTimeoutMinutes : 30; - } - public long getPythonOpenCvTimeoutMinutes() { return pythonOpenCvTimeoutMinutes > 0 ? pythonOpenCvTimeoutMinutes : 30; } - public long getGhostScriptTimeoutMinutes() { - return ghostScriptTimeoutMinutes > 0 ? ghostScriptTimeoutMinutes : 30; - } - public long getWeasyPrintTimeoutMinutes() { return weasyPrintTimeoutMinutes > 0 ? weasyPrintTimeoutMinutes : 30; } diff --git a/src/main/java/stirling/software/SPDF/model/api/misc/OptimizePdfRequest.java b/src/main/java/stirling/software/SPDF/model/api/misc/OptimizePdfRequest.java index 96a787f31..3cd7f7be4 100644 --- a/src/main/java/stirling/software/SPDF/model/api/misc/OptimizePdfRequest.java +++ b/src/main/java/stirling/software/SPDF/model/api/misc/OptimizePdfRequest.java @@ -18,4 +18,15 @@ public class OptimizePdfRequest extends PDFFile { @Schema(description = "The expected output size, e.g. '100MB', '25KB', etc.") private String expectedOutputSize; + + @Schema( + description = "Whether to linearize the PDF for faster web viewing. Default is false.", + defaultValue = "false") + private Boolean linearize = false; + + @Schema( + description = + "Whether to normalize the PDF content for better compatibility. Default is true.", + defaultValue = "true") + private Boolean normalize = true; } diff --git a/src/main/java/stirling/software/SPDF/model/api/misc/ProcessPdfWithOcrRequest.java b/src/main/java/stirling/software/SPDF/model/api/misc/ProcessPdfWithOcrRequest.java index 7d3de3e6c..e26741c94 100644 --- a/src/main/java/stirling/software/SPDF/model/api/misc/ProcessPdfWithOcrRequest.java +++ b/src/main/java/stirling/software/SPDF/model/api/misc/ProcessPdfWithOcrRequest.java @@ -15,18 +15,6 @@ public class ProcessPdfWithOcrRequest extends PDFFile { @Schema(description = "List of languages to use in OCR processing") private List languages; - @Schema(description = "Include OCR text in a sidecar text file if set to true") - private boolean sidecar; - - @Schema(description = "Deskew the input file if set to true") - private boolean deskew; - - @Schema(description = "Clean the input file if set to true") - private boolean clean; - - @Schema(description = "Clean the final output if set to true") - private boolean cleanFinal; - @Schema( description = "Specify the OCR type, e.g., 'skip-text', 'force-ocr', or 'Normal'", allowableValues = {"skip-text", "force-ocr", "Normal"}) @@ -37,7 +25,4 @@ public class ProcessPdfWithOcrRequest extends PDFFile { allowableValues = {"hocr", "sandwich"}, defaultValue = "hocr") private String ocrRenderType = "hocr"; - - @Schema(description = "Remove images from the output PDF if set to true") - private boolean removeImagesAfter; } diff --git a/src/main/java/stirling/software/SPDF/service/MetricsAggregatorService.java b/src/main/java/stirling/software/SPDF/service/MetricsAggregatorService.java index 084480c18..9cfacdd14 100644 --- a/src/main/java/stirling/software/SPDF/service/MetricsAggregatorService.java +++ b/src/main/java/stirling/software/SPDF/service/MetricsAggregatorService.java @@ -34,17 +34,15 @@ public class MetricsAggregatorService { counter -> { String method = counter.getId().getTag("method"); String uri = counter.getId().getTag("uri"); - + // Skip if either method or uri is null if (method == null || uri == null) { return; } - - String key = String.format( - "http_requests_%s_%s", - method, - uri.replace("/", "_") - ); + + String key = + String.format( + "http_requests_%s_%s", method, uri.replace("/", "_")); double currentCount = counter.count(); double lastCount = lastSentMetrics.getOrDefault(key, 0.0); diff --git a/src/main/java/stirling/software/SPDF/service/PostHogService.java b/src/main/java/stirling/software/SPDF/service/PostHogService.java index 5046d6cd0..a2d3d485f 100644 --- a/src/main/java/stirling/software/SPDF/service/PostHogService.java +++ b/src/main/java/stirling/software/SPDF/service/PostHogService.java @@ -31,7 +31,7 @@ public class PostHogService { private final ApplicationProperties applicationProperties; private final UserServiceInterface userService; private final Environment env; - + @Autowired public PostHogService( PostHog postHog, @@ -71,16 +71,16 @@ public class PostHogService { Map metrics = new HashMap<>(); try { - //Application version - metrics.put("app_version", appVersion); - String deploymentType = "JAR"; // default - if ("true".equalsIgnoreCase(env.getProperty("BROWSER_OPEN"))) { - deploymentType = "EXE"; - } else if (isRunningInDocker()) { - deploymentType = "DOCKER"; - } - metrics.put("deployment_type", deploymentType); - + // Application version + metrics.put("app_version", appVersion); + String deploymentType = "JAR"; // default + if ("true".equalsIgnoreCase(env.getProperty("BROWSER_OPEN"))) { + deploymentType = "EXE"; + } else if (isRunningInDocker()) { + deploymentType = "DOCKER"; + } + metrics.put("deployment_type", deploymentType); + // System info metrics.put("os_name", System.getProperty("os.name")); metrics.put("os_version", System.getProperty("os.version")); diff --git a/src/main/java/stirling/software/SPDF/utils/ProcessExecutor.java b/src/main/java/stirling/software/SPDF/utils/ProcessExecutor.java index 0947714fd..e99cd70a9 100644 --- a/src/main/java/stirling/software/SPDF/utils/ProcessExecutor.java +++ b/src/main/java/stirling/software/SPDF/utils/ProcessExecutor.java @@ -29,12 +29,12 @@ public class ProcessExecutor { public enum Processes { LIBRE_OFFICE, PDFTOHTML, - OCR_MY_PDF, PYTHON_OPENCV, - GHOSTSCRIPT, WEASYPRINT, INSTALL_APP, - CALIBRE + CALIBRE, + TESSERACT, + QPDF } private static final Map instances = new ConcurrentHashMap<>(); @@ -59,21 +59,11 @@ public class ProcessExecutor { .getProcessExecutor() .getSessionLimit() .getPdfToHtmlSessionLimit(); - case OCR_MY_PDF -> - applicationProperties - .getProcessExecutor() - .getSessionLimit() - .getOcrMyPdfSessionLimit(); case PYTHON_OPENCV -> applicationProperties .getProcessExecutor() .getSessionLimit() .getPythonOpenCvSessionLimit(); - case GHOSTSCRIPT -> - applicationProperties - .getProcessExecutor() - .getSessionLimit() - .getGhostScriptSessionLimit(); case WEASYPRINT -> applicationProperties .getProcessExecutor() @@ -84,6 +74,16 @@ public class ProcessExecutor { .getProcessExecutor() .getSessionLimit() .getInstallAppSessionLimit(); + case TESSERACT -> + applicationProperties + .getProcessExecutor() + .getSessionLimit() + .getTesseractSessionLimit(); + case QPDF -> + applicationProperties + .getProcessExecutor() + .getSessionLimit() + .getQpdfSessionLimit(); case CALIBRE -> applicationProperties .getProcessExecutor() @@ -103,21 +103,11 @@ public class ProcessExecutor { .getProcessExecutor() .getTimeoutMinutes() .getPdfToHtmlTimeoutMinutes(); - case OCR_MY_PDF -> - applicationProperties - .getProcessExecutor() - .getTimeoutMinutes() - .getOcrMyPdfTimeoutMinutes(); case PYTHON_OPENCV -> applicationProperties .getProcessExecutor() .getTimeoutMinutes() .getPythonOpenCvTimeoutMinutes(); - case GHOSTSCRIPT -> - applicationProperties - .getProcessExecutor() - .getTimeoutMinutes() - .getGhostScriptTimeoutMinutes(); case WEASYPRINT -> applicationProperties .getProcessExecutor() @@ -128,6 +118,16 @@ public class ProcessExecutor { .getProcessExecutor() .getTimeoutMinutes() .getInstallAppTimeoutMinutes(); + case TESSERACT -> + applicationProperties + .getProcessExecutor() + .getTimeoutMinutes() + .getTesseractTimeoutMinutes(); + case QPDF -> + applicationProperties + .getProcessExecutor() + .getTimeoutMinutes() + .getQpdfTimeoutMinutes(); case CALIBRE -> applicationProperties .getProcessExecutor() diff --git a/src/main/resources/messages_ar_AR.properties b/src/main/resources/messages_ar_AR.properties index e8b7ecdf4..efc2c007f 100644 --- a/src/main/resources/messages_ar_AR.properties +++ b/src/main/resources/messages_ar_AR.properties @@ -868,7 +868,7 @@ ocr.selectText.10=وضع التعرف الضوئي على الحروف ocr.selectText.11=إزالة الصور بعد التعرف الضوئي على الحروف (يزيل كل الصور، يكون مفيدًا فقط إذا كان جزءًا من خطوة التحويل) ocr.selectText.12=نوع العرض (متقدم) ocr.help=يرجى قراءة هذه الوثائق حول كيفية استخدام هذا للغات أخرى و/أو الاستخدام ليس في Docker -ocr.credit=تستخدم هذه الخدمة OCRmyPDF و Tesseract للتعرف الضوئي على الحروف. +ocr.credit=تستخدم هذه الخدمة qpdf و Tesseract للتعرف الضوئي على الحروف. ocr.submit=معالجة PDF باستخدام OCR @@ -892,7 +892,7 @@ fileToPDF.submit=تحويل إلى PDF #compress compress.title=ضغط compress.header=ضغط ملف PDF -compress.credit=تستخدم هذه الخدمة OCRmyPDF لضغط / تحسين PDF. +compress.credit=تستخدم هذه الخدمة qpdf لضغط / تحسين PDF. compress.selectText.1=الوضع اليدوي - من 1 إلى 4 compress.selectText.2=مستوى التحسين: compress.selectText.3=4 (رهيب للصور النصية) @@ -1112,7 +1112,7 @@ changeMetadata.submit=تغيير #pdfToPDFA pdfToPDFA.title=PDF إلى PDF/A pdfToPDFA.header=PDF إلى PDF/A -pdfToPDFA.credit=تستخدم هذه الخدمة ghostscript لتحويل PDF/A. +pdfToPDFA.credit=تستخدم هذه الخدمة qpdf لتحويل PDF/A. pdfToPDFA.submit=تحويل pdfToPDFA.tip=لا يعمل حاليًا لمدخلات متعددة في وقت واحد pdfToPDFA.outputFormat=تنسيق الإخراج diff --git a/src/main/resources/messages_az_AZ.properties b/src/main/resources/messages_az_AZ.properties index 52372d0ff..cc33decdf 100644 --- a/src/main/resources/messages_az_AZ.properties +++ b/src/main/resources/messages_az_AZ.properties @@ -868,7 +868,7 @@ ocr.selectText.10=OCR Mode ocr.selectText.11=Remove images after OCR (Removes ALL images, only useful if part of conversion step) ocr.selectText.12=Render Type (Advanced) ocr.help=Please read this documentation on how to use this for other languages and/or use not in docker -ocr.credit=This service uses OCRmyPDF and Tesseract for OCR. +ocr.credit=This service uses qpdf and Tesseract for OCR. ocr.submit=Process PDF with OCR @@ -892,7 +892,7 @@ fileToPDF.submit=PDF-ə Çevir #compress compress.title=Compress compress.header=Compress PDF -compress.credit=This service uses Ghostscript for PDF Compress/Optimisation. +compress.credit=This service uses qpdf for PDF Compress/Optimisation. compress.selectText.1=Manual Mode - From 1 to 4 compress.selectText.2=Optimization level: compress.selectText.3=4 (Terrible for text images) @@ -1112,7 +1112,7 @@ changeMetadata.submit=Change #pdfToPDFA pdfToPDFA.title=PDF To PDF/A pdfToPDFA.header=PDF To PDF/A -pdfToPDFA.credit=This service uses ghostscript for PDF/A conversion +pdfToPDFA.credit=This service uses qpdf for PDF/A conversion pdfToPDFA.submit=Convert pdfToPDFA.tip=Currently does not work for multiple inputs at once pdfToPDFA.outputFormat=Output format diff --git a/src/main/resources/messages_bg_BG.properties b/src/main/resources/messages_bg_BG.properties index 451992c68..e0f38f816 100644 --- a/src/main/resources/messages_bg_BG.properties +++ b/src/main/resources/messages_bg_BG.properties @@ -868,7 +868,7 @@ ocr.selectText.10=OCR режим ocr.selectText.11=Премахване на изображения след OCR (Премахва ВСИЧКИ изображения, полезно само ако е част от стъпката на преобразуване) ocr.selectText.12=Тип изобразяване (Разширен) ocr.help=Моля, прочетете тази документация за това как да използвате това за други езици и/или да не използвате в docker -ocr.credit=Тази услуга използва OCRmyPDF и Tesseract за OCR. +ocr.credit=Тази услуга използва qpdf и Tesseract за OCR. ocr.submit=Обработка на PDF чрез OCR @@ -892,7 +892,7 @@ fileToPDF.submit=Преобразуване към PDF #compress compress.title=Компресиране compress.header=Компресиране на PDF -compress.credit=Тази услуга използва Ghostscript за PDF компресиране/оптимизиране. +compress.credit=Тази услуга използва qpdf за PDF компресиране/оптимизиране. compress.selectText.1=Ръчен режим - от 1 до 4 compress.selectText.2=Ниво на оптимизация: compress.selectText.3=4 (Ужасно за текстови изображения) @@ -1112,7 +1112,7 @@ changeMetadata.submit=Промени #pdfToPDFA pdfToPDFA.title=PDF към PDF/A pdfToPDFA.header=PDF към PDF/A -pdfToPDFA.credit=Тази услуга използва ghostscript за PDF/A преобразуване. +pdfToPDFA.credit=Тази услуга използва qpdf за PDF/A преобразуване. pdfToPDFA.submit=Преобразуване pdfToPDFA.tip=В момента не работи за няколко входа наведнъж pdfToPDFA.outputFormat=Изходен формат diff --git a/src/main/resources/messages_ca_CA.properties b/src/main/resources/messages_ca_CA.properties index a39445654..466740f29 100644 --- a/src/main/resources/messages_ca_CA.properties +++ b/src/main/resources/messages_ca_CA.properties @@ -868,7 +868,7 @@ ocr.selectText.10=Mode OCR ocr.selectText.11=Elimina Imatges després de l'OCR (Elimina TOTES les imatges, útil si forma part d'un procés de conversió) ocr.selectText.12=Tipus de Renderització (Avançat) ocr.help=Llegeix aquesta documentació sobre com utilitzar-la per a altres idiomes i/o no utilitzar-la a Docker -ocr.credit=Aquest servei fa servir OCRmyPDF i Tesseract per a OCR. +ocr.credit=Aquest servei fa servir qpdf i Tesseract per a OCR. ocr.submit=Processa PDF amb OCR @@ -892,7 +892,7 @@ fileToPDF.submit=Converteix a PDF #compress compress.title=Comprimir compress.header=Comprimir PDF -compress.credit=Aquest servei utilitza Ghostscript per a la compressió/optimització de PDF. +compress.credit=Aquest servei utilitza qpdf per a la compressió/optimització de PDF. compress.selectText.1=Mode manual: de l'1 al 4 compress.selectText.2=Nivell d'optimització: compress.selectText.3=4 (terrible per a imatges de text) @@ -1112,7 +1112,7 @@ changeMetadata.submit=Canvia #pdfToPDFA pdfToPDFA.title=PDF a PDF/A pdfToPDFA.header=PDF a PDF/A -pdfToPDFA.credit=Utilitza Ghostscript per a la conversió a PDF/A +pdfToPDFA.credit=Utilitza qpdf per a la conversió a PDF/A pdfToPDFA.submit=Converteix pdfToPDFA.tip=Actualment no funciona per a múltiples entrades al mateix temps pdfToPDFA.outputFormat=Format de sortida diff --git a/src/main/resources/messages_cs_CZ.properties b/src/main/resources/messages_cs_CZ.properties index cad100e60..61d2f16ca 100644 --- a/src/main/resources/messages_cs_CZ.properties +++ b/src/main/resources/messages_cs_CZ.properties @@ -868,7 +868,7 @@ ocr.selectText.10=Režim OCR ocr.selectText.11=Odstranit obrázky po OCR (Odstraní VŠECHNY obrázky, užitečné pouze jako součást kroku konverze) ocr.selectText.12=Typ vykreslení (Pokročilé) ocr.help=Prosím, přečtěte si tuto dokumentaci o použití pro jiné jazyky a/nebo použití mimo Docker -ocr.credit=Tato služba používá OCRmyPDF a Tesseract pro OCR. +ocr.credit=Tato služba používá qpdf a Tesseract pro OCR. ocr.submit=Zpracovat PDF s OCR @@ -892,7 +892,7 @@ fileToPDF.submit=Převést na PDF #compress compress.title=Komprese compress.header=Komprimovat PDF -compress.credit=Tato služba používá Ghostscript pro kompresi/optimalizaci PDF. +compress.credit=Tato služba používá qpdf pro kompresi/optimalizaci PDF. compress.selectText.1=Ruční režim - Od 1 do 4 compress.selectText.2=Úroveň optimalizace: compress.selectText.3=4 (Hrozné pro textové obrázky) @@ -1112,7 +1112,7 @@ changeMetadata.submit=Změnit #pdfToPDFA pdfToPDFA.title=PDF na PDF/A pdfToPDFA.header=PDF na PDF/A -pdfToPDFA.credit=Tato služba používá ghostscript pro konverzi do formátu PDF/A +pdfToPDFA.credit=Tato služba používá qpdf pro konverzi do formátu PDF/A pdfToPDFA.submit=Převést pdfToPDFA.tip=V současné době nepracuje pro více vstupů najednou pdfToPDFA.outputFormat=Výstupní formát diff --git a/src/main/resources/messages_da_DK.properties b/src/main/resources/messages_da_DK.properties index a8a50d746..45caaf7ef 100644 --- a/src/main/resources/messages_da_DK.properties +++ b/src/main/resources/messages_da_DK.properties @@ -868,7 +868,7 @@ ocr.selectText.10=OCR-tilstand ocr.selectText.11=Fjern billeder efter OCR (Fjerner ALLE billeder, kun nyttigt hvis det er en del af konverteringstrinnet) ocr.selectText.12=Renderingstype (Avanceret) ocr.help=Læs venligst denne dokumentation om, hvordan man bruger dette til andre sprog og/eller brug uden for docker -ocr.credit=Denne tjeneste bruger OCRmyPDF og Tesseract til OCR. +ocr.credit=Denne tjeneste bruger qpdf og Tesseract til OCR. ocr.submit=Behandl PDF med OCR @@ -892,7 +892,7 @@ fileToPDF.submit=Konvertér til PDF #compress compress.title=Komprimer compress.header=Komprimer PDF -compress.credit=Denne tjeneste bruger Ghostscript til PDF Komprimering/Optimering. +compress.credit=Denne tjeneste bruger qpdf til PDF Komprimering/Optimering. compress.selectText.1=Manuel Tilstand - Fra 1 til 4 compress.selectText.2=Optimeringsniveau: compress.selectText.3=4 (Forfærdelig for tekstbilleder) @@ -1112,7 +1112,7 @@ changeMetadata.submit=Ændre #pdfToPDFA pdfToPDFA.title=PDF Til PDF/A pdfToPDFA.header=PDF Til PDF/A -pdfToPDFA.credit=Denne tjeneste bruger ghostscript til PDF/A-konvertering +pdfToPDFA.credit=Denne tjeneste bruger qpdf til PDF/A-konvertering pdfToPDFA.submit=Konvertér pdfToPDFA.tip=Fungerer i øjeblikket ikke for flere input på én gang pdfToPDFA.outputFormat=Outputformat diff --git a/src/main/resources/messages_de_DE.properties b/src/main/resources/messages_de_DE.properties index c84938476..bc499996c 100644 --- a/src/main/resources/messages_de_DE.properties +++ b/src/main/resources/messages_de_DE.properties @@ -868,7 +868,7 @@ ocr.selectText.10=OCR-Modus ocr.selectText.11=Bilder nach OCR entfernen (Entfernt ALLE Bilder, nur sinnvoll, wenn Teil des Konvertierungsschritts) ocr.selectText.12=Rendertyp (Erweitert) ocr.help=Bitte lesen Sie diese Dokumentation, um zu erfahren, wie Sie dies für andere Sprachen verwenden und/oder nicht in Docker verwenden können -ocr.credit=Dieser Dienst verwendet OCRmyPDF und Tesseract für OCR. +ocr.credit=Dieser Dienst verwendet qpdf und Tesseract für OCR. ocr.submit=PDF mit OCR verarbeiten @@ -892,7 +892,7 @@ fileToPDF.submit=In PDF konvertieren #compress compress.title=Komprimieren compress.header=PDF komprimieren -compress.credit=Dieser Dienst verwendet Ghostscript für die PDF-Komprimierung/-Optimierung. +compress.credit=Dieser Dienst verwendet qpdf für die PDF-Komprimierung/-Optimierung. compress.selectText.1=Manueller Modus – Von 1 bis 4 compress.selectText.2=Optimierungsstufe: compress.selectText.3=4 (Schrecklich für Textbilder) @@ -1112,7 +1112,7 @@ changeMetadata.submit=Ändern #pdfToPDFA pdfToPDFA.title=PDF zu PDF/A pdfToPDFA.header=PDF zu PDF/A -pdfToPDFA.credit=Dieser Dienst verwendet ghostscript für die PDF/A-Konvertierung +pdfToPDFA.credit=Dieser Dienst verwendet qpdf für die PDF/A-Konvertierung pdfToPDFA.submit=Konvertieren pdfToPDFA.tip=Dieser Dienst kann nur einzelne Eingangsdateien verarbeiten. pdfToPDFA.outputFormat=Ausgabeformat diff --git a/src/main/resources/messages_el_GR.properties b/src/main/resources/messages_el_GR.properties index ce8002fec..68a75f013 100644 --- a/src/main/resources/messages_el_GR.properties +++ b/src/main/resources/messages_el_GR.properties @@ -868,7 +868,7 @@ ocr.selectText.10=Λειτουργία OCR ocr.selectText.11=Κατάργηση εικόνων μετά το OCR (Καταργεί ΟΛΕΣ τις εικόνες, είναι χρήσιμο μόνο αν αποτελεί μέρος του βήματος μετατροπής) ocr.selectText.12=Τύπος απόδοσης (Για προχωρημένους) ocr.help=Διαβάστε αυτήν την τεκμηρίωση σχετικά με τον τρόπο χρήσης αυτής για άλλες γλώσσες ή/και μη χρήσης σε docker -ocr.credit=Αυτή η υπηρεσία χρησιμοποιεί OCRmyPDF και Tesseract για OCR. +ocr.credit=Αυτή η υπηρεσία χρησιμοποιεί qpdf και Tesseract για OCR. ocr.submit=Επεξεργασία PDF με OCR @@ -892,7 +892,7 @@ fileToPDF.submit=Μετατροπή σε PDF #compress compress.title=Συμπίεση compress.header=Συμπίεση PDF -compress.credit=Αυτή η υπηρεσία χρησιμοποιεί Ghostscript για PDF Συμπίεση/Βελτιστοποίηση. +compress.credit=Αυτή η υπηρεσία χρησιμοποιεί qpdf για PDF Συμπίεση/Βελτιστοποίηση. compress.selectText.1=Χειροκίνητη Λειτουργία - Από 1 έως 4 compress.selectText.2=Επίπεδο Βελτιστοποίησης: compress.selectText.3=4 (Πολύ κακό για εικόνες κειμένου) @@ -1112,7 +1112,7 @@ changeMetadata.submit=Αλλαγή #pdfToPDFA pdfToPDFA.title=PDF σε PDF/A pdfToPDFA.header=PDF σε PDF/A -pdfToPDFA.credit=Αυτή η υπηρεσία χρησιμοποιεί ghostscript για PDF/A μετατροπή +pdfToPDFA.credit=Αυτή η υπηρεσία χρησιμοποιεί qpdf για PDF/A μετατροπή pdfToPDFA.submit=Μετατροπή pdfToPDFA.tip=Προς το παρόν δεν λειτουργεί για πολλαπλές εισόδους ταυτόχρονα pdfToPDFA.outputFormat=Εξόδος αναμορφώσεων diff --git a/src/main/resources/messages_en_GB.properties b/src/main/resources/messages_en_GB.properties index 15c3a153f..8a471ade7 100644 --- a/src/main/resources/messages_en_GB.properties +++ b/src/main/resources/messages_en_GB.properties @@ -868,7 +868,7 @@ ocr.selectText.10=OCR Mode ocr.selectText.11=Remove images after OCR (Removes ALL images, only useful if part of conversion step) ocr.selectText.12=Render Type (Advanced) ocr.help=Please read this documentation on how to use this for other languages and/or use not in docker -ocr.credit=This service uses OCRmyPDF and Tesseract for OCR. +ocr.credit=This service uses qpdf and Tesseract for OCR. ocr.submit=Process PDF with OCR @@ -892,7 +892,7 @@ fileToPDF.submit=Convert to PDF #compress compress.title=Compress compress.header=Compress PDF -compress.credit=This service uses Ghostscript for PDF Compress/Optimisation. +compress.credit=This service uses qpdf for PDF Compress/Optimisation. compress.selectText.1=Manual Mode - From 1 to 4 compress.selectText.2=Optimization level: compress.selectText.3=4 (Terrible for text images) @@ -1112,7 +1112,7 @@ changeMetadata.submit=Change #pdfToPDFA pdfToPDFA.title=PDF To PDF/A pdfToPDFA.header=PDF To PDF/A -pdfToPDFA.credit=This service uses ghostscript for PDF/A conversion +pdfToPDFA.credit=This service uses qpdf for PDF/A conversion pdfToPDFA.submit=Convert pdfToPDFA.tip=Currently does not work for multiple inputs at once pdfToPDFA.outputFormat=Output format @@ -1260,3 +1260,11 @@ splitByChapters.desc.2=Bookmark Level: Choose the level of bookmarks to use for splitByChapters.desc.3=Include Metadata: If checked, the original PDF's metadata will be included in each split PDF. splitByChapters.desc.4=Allow Duplicates: If checked, allows multiple bookmarks on the same page to create separate PDFs. splitByChapters.submit=Split PDF + + +#release notes +releases.footer=Releases +releases.title=Release Notes +releases.header=Release Notes +releases.current.version=Current Release +releases.note=Release notes are only available in English diff --git a/src/main/resources/messages_en_US.properties b/src/main/resources/messages_en_US.properties index 8b3f459e5..71a383485 100644 --- a/src/main/resources/messages_en_US.properties +++ b/src/main/resources/messages_en_US.properties @@ -868,7 +868,7 @@ ocr.selectText.10=OCR Mode ocr.selectText.11=Remove images after OCR (Removes ALL images, only useful if part of conversion step) ocr.selectText.12=Render Type (Advanced) ocr.help=Please read this documentation on how to use this for other languages and/or use not in docker -ocr.credit=This service uses OCRmyPDF and Tesseract for OCR. +ocr.credit=This service uses qpdf and Tesseract for OCR. ocr.submit=Process PDF with OCR @@ -892,7 +892,7 @@ fileToPDF.submit=Convert to PDF #compress compress.title=Compress compress.header=Compress PDF -compress.credit=This service uses Ghostscript for PDF Compress/Optimisation. +compress.credit=This service uses qpdf for PDF Compress/Optimisation. compress.selectText.1=Manual Mode - From 1 to 4 compress.selectText.2=Optimization level: compress.selectText.3=4 (Terrible for text images) @@ -1112,7 +1112,7 @@ changeMetadata.submit=Change #pdfToPDFA pdfToPDFA.title=PDF To PDF/A pdfToPDFA.header=PDF To PDF/A -pdfToPDFA.credit=This service uses ghostscript for PDF/A conversion +pdfToPDFA.credit=This service uses qpdf for PDF/A conversion pdfToPDFA.submit=Convert pdfToPDFA.tip=Currently does not work for multiple inputs at once pdfToPDFA.outputFormat=Output format diff --git a/src/main/resources/messages_es_ES.properties b/src/main/resources/messages_es_ES.properties index ba622a00c..f39da0d1a 100644 --- a/src/main/resources/messages_es_ES.properties +++ b/src/main/resources/messages_es_ES.properties @@ -868,7 +868,7 @@ ocr.selectText.10=Modo OCR ocr.selectText.11=Eliminar imágenes después de OCR (Elimina TODAS las imágenes, solo es útil si es parte del paso de conversión) ocr.selectText.12=Tipo de procesamiento (avanzado) ocr.help=Lea esta documentación sobre cómo usar esto para otros idiomas y/o no usarlo en Docker -ocr.credit=Este servicio utiliza OCRmyPDF y Tesseract para OCR +ocr.credit=Este servicio utiliza qpdf y Tesseract para OCR ocr.submit=Procesar PDF con OCR @@ -892,7 +892,7 @@ fileToPDF.submit=Convertir a PDF #compress compress.title=Comprimir compress.header=Comprimir PDF -compress.credit=Este servicio utiliza Ghostscript para compresión/optimización de PDF +compress.credit=Este servicio utiliza qpdf para compresión/optimización de PDF compress.selectText.1=Modo manual - De 1 a 4 compress.selectText.2=Nivel de optimización: compress.selectText.3=4 (Terrible para imágenes de texto) @@ -1112,7 +1112,7 @@ changeMetadata.submit=Cambiar #pdfToPDFA pdfToPDFA.title=PDF a PDF/A pdfToPDFA.header=PDF a PDF/A -pdfToPDFA.credit=Este servicio usa ghostscript para la conversión a PDF/A +pdfToPDFA.credit=Este servicio usa qpdf para la conversión a PDF/A pdfToPDFA.submit=Convertir pdfToPDFA.tip=Actualmente no funciona para múltiples entrada a la vez pdfToPDFA.outputFormat=Formato de salida diff --git a/src/main/resources/messages_eu_ES.properties b/src/main/resources/messages_eu_ES.properties index 03db337b2..fa9502207 100644 --- a/src/main/resources/messages_eu_ES.properties +++ b/src/main/resources/messages_eu_ES.properties @@ -868,7 +868,7 @@ ocr.selectText.10=OCR modua ocr.selectText.11=Irudiak ezabatu OCR-ren ondoren (Irudi GUZTIAK ezabatzen ditu, bakarrik da erabilgarri bihurketa urratsaren parte baldin bada) ocr.selectText.12=Prozesaketa-mota (aurreratua) ocr.help=Irakurri honen erabilerari buruzko dokumentazioa beste hizkuntza batzuetarako eta/edo ez erabili Docker-en -ocr.credit=Zerbitzu honek OCRmyPDF eta OCR-rako Tesseract erabiltzen ditu +ocr.credit=Zerbitzu honek qpdf eta OCR-rako Tesseract erabiltzen ditu ocr.submit=PDF prozesatu OCR-rekin @@ -892,7 +892,7 @@ fileToPDF.submit=PDF bihurtu #compress compress.title=Konprimatu compress.header=PDFa konprimatu -compress.credit=Zerbitzu honek Ghostscript erabiltzen du PDFak komprimatzeko/optimizatzeko +compress.credit=Zerbitzu honek qpdf erabiltzen du PDFak komprimatzeko/optimizatzeko compress.selectText.1=Eskuz 1etik 4ra compress.selectText.2=Optimizazio maila: compress.selectText.3=4 (Izugarria testu-irudietarako) @@ -1112,7 +1112,7 @@ changeMetadata.submit=Aldatu #pdfToPDFA pdfToPDFA.title=PDFa PDF/A bihurtu pdfToPDFA.header=PDFa PDF/A bihurtu -pdfToPDFA.credit=Zerbitzu honek ghostscript erabiltzen du PDFak PDF/A bihurtzeko +pdfToPDFA.credit=Zerbitzu honek qpdf erabiltzen du PDFak PDF/A bihurtzeko pdfToPDFA.submit=Bihurtu pdfToPDFA.tip=Currently does not work for multiple inputs at once pdfToPDFA.outputFormat=Output format diff --git a/src/main/resources/messages_fr_FR.properties b/src/main/resources/messages_fr_FR.properties index d5a89907b..ac8c73015 100644 --- a/src/main/resources/messages_fr_FR.properties +++ b/src/main/resources/messages_fr_FR.properties @@ -868,7 +868,7 @@ ocr.selectText.10=Mode OCR ocr.selectText.11=Supprimer les images après l'OCR (Supprime TOUTES les images, utile uniquement si elles font partie de l'étape de conversion) ocr.selectText.12=Type de rendu (avancé) ocr.help=Veuillez lire cette documentation pour savoir comment utiliser l'OCR pour d'autres langues ou une utilisation hors Docker : -ocr.credit=Ce service utilise OCRmyPDF et Tesseract pour l'OCR. +ocr.credit=Ce service utilise qpdf et Tesseract pour l'OCR. ocr.submit=Traiter @@ -892,7 +892,7 @@ fileToPDF.submit=Convertir #compress compress.title=Compresser un PDF compress.header=Compresser un PDF (lorsque c'est possible!) -compress.credit=Ce service utilise Ghostscript pour la compression et l'optimisation des PDF. +compress.credit=Ce service utilise qpdf pour la compression et l'optimisation des PDF. compress.selectText.1=Mode manuel – de 1 à 4 compress.selectText.2=Niveau d'optimisation compress.selectText.3=4 (terrible pour les images textuelles) @@ -1112,7 +1112,7 @@ changeMetadata.submit=Modifier #pdfToPDFA pdfToPDFA.title=PDF en PDF/A pdfToPDFA.header=PDF en PDF/A -pdfToPDFA.credit=Ce service utilise ghostscript pour la conversion en PDF/A. +pdfToPDFA.credit=Ce service utilise qpdf pour la conversion en PDF/A. pdfToPDFA.submit=Convertir pdfToPDFA.tip=Ne fonctionne actuellement pas pour plusieurs entrées à la fois pdfToPDFA.outputFormat=Format de sortie diff --git a/src/main/resources/messages_ga_IE.properties b/src/main/resources/messages_ga_IE.properties index 83f9bf919..a695187a0 100644 --- a/src/main/resources/messages_ga_IE.properties +++ b/src/main/resources/messages_ga_IE.properties @@ -868,7 +868,7 @@ ocr.selectText.10=Mód OCR ocr.selectText.11=Bain íomhánna tar éis OCR (Bain GACH íomhá, ní úsáideach ach amháin má tá siad mar chuid den chéim tiontaithe) ocr.selectText.12=Cineál Rindreála (Ardleibhéal) ocr.help=Léigh le do thoil an doiciméadú seo ar conas é seo a úsáid do theangacha eile agus/nó úsáid nach bhfuil i ndugairí -ocr.credit=Úsáideann an tseirbhís seo OCRmyPDF agus Tesseract le haghaidh OCR. +ocr.credit=Úsáideann an tseirbhís seo qpdf agus Tesseract le haghaidh OCR. ocr.submit=Próiseáil PDF le OCR @@ -892,7 +892,7 @@ fileToPDF.submit=Tiontaigh go PDF #compress compress.title=Comhbhrúigh compress.header=Comhbhrúigh PDF -compress.credit=Úsáideann an tseirbhís seo Ghostscript le haghaidh Comhbhrú/Optimization PDF. +compress.credit=Úsáideann an tseirbhís seo qpdf le haghaidh Comhbhrú/Optimization PDF. compress.selectText.1=Mód Láimhe - Ó 1 go 4 compress.selectText.2=Leibhéal optamaithe: compress.selectText.3=4 (Uafásach le haghaidh íomhánna téacs) @@ -1112,7 +1112,7 @@ changeMetadata.submit=Athrú #pdfToPDFA pdfToPDFA.title=PDF Go PDF/A pdfToPDFA.header=PDF Go PDF/A -pdfToPDFA.credit=Úsáideann an tseirbhís seo ghostscript chun PDF/A a thiontú +pdfToPDFA.credit=Úsáideann an tseirbhís seo qpdf chun PDF/A a thiontú pdfToPDFA.submit=Tiontaigh pdfToPDFA.tip=Faoi láthair ní oibríonn sé le haghaidh ionchuir iolracha ag an am céanna pdfToPDFA.outputFormat=Formáid aschuir diff --git a/src/main/resources/messages_hi_IN.properties b/src/main/resources/messages_hi_IN.properties index 927415154..9d28f4f55 100644 --- a/src/main/resources/messages_hi_IN.properties +++ b/src/main/resources/messages_hi_IN.properties @@ -868,7 +868,7 @@ ocr.selectText.10=OCR मोड ocr.selectText.11=OCR के बाद छवियां हटाएँ (सभी छवियां हटाएँ, केवल परिवर्तन चरण का हिस्सा होता है) ocr.selectText.12=रेंडर टाइप (उन्नत) ocr.help=कृपया इस डॉक्यूमेंटेशन को पढ़ें कि इसे अन्य भाषाओं के लिए कैसे उपयोग किया जाता है और/या डॉकर में नहीं हैं -ocr.credit=इस सेवा में OCRmyPDF और टेसरेक्ट का उपयोग होता है। +ocr.credit=इस सेवा में qpdf और टेसरेक्ट का उपयोग होता है। ocr.submit=OCR के साथ PDF प्रोसेस करें @@ -892,7 +892,7 @@ fileToPDF.submit=पीडीएफ़ में बदलें #compress compress.title=संकुचित करें compress.header=PDF को संकुचित करें -compress.credit=यह सेवा PDF संकुचन/अनुकूलन के लिए Ghostscript का उपयोग करती है। +compress.credit=यह सेवा PDF संकुचन/अनुकूलन के लिए qpdf का उपयोग करती है। compress.selectText.1=मैनुअल मोड - 1 से 4 तक compress.selectText.2=अनुकूलन स्तर: compress.selectText.3=4 (पाठ छवियों के लिए अत्यधिक) @@ -1112,7 +1112,7 @@ changeMetadata.submit=बदलें #pdfToPDFA pdfToPDFA.title=PDF से PDF/A में pdfToPDFA.header=PDF से PDF/A में -pdfToPDFA.credit=इस सेवा में PDF/A परिवर्तन के लिए ghostscript का उपयोग किया जाता है। +pdfToPDFA.credit=इस सेवा में PDF/A परिवर्तन के लिए qpdf का उपयोग किया जाता है। pdfToPDFA.submit=परिवर्तित करें pdfToPDFA.tip=यह सैकड़ों प्रविष्टियाँ एक ही समय में काम करते हैं pdfToPDFA.outputFormat=आउटपुट फॉर्मेट diff --git a/src/main/resources/messages_hr_HR.properties b/src/main/resources/messages_hr_HR.properties index dd57bd232..a07f621bd 100644 --- a/src/main/resources/messages_hr_HR.properties +++ b/src/main/resources/messages_hr_HR.properties @@ -868,7 +868,7 @@ ocr.selectText.10=OCR način ocr.selectText.11=Ukloni slike nakon OCR-a (Uklanja SVE slike, korisno samo ako je dio koraka konverzije) ocr.selectText.12=Vrsta iscrtavanja (napredno) ocr.help=Pročitajte ovu dokumentaciju o tome kako ovo koristiti za druge jezike i/ili koristiti ne u dockeru -ocr.credit=Ova usluga koristi OCRmyPDF i Tesseract za OCR. +ocr.credit=Ova usluga koristi qpdf i Tesseract za OCR. ocr.submit=Obradi PDF sa OCR-om @@ -892,7 +892,7 @@ fileToPDF.submit=Pretvori u PDF #compress compress.title=Komprimirajte compress.header=Komprimirajte PDF -compress.credit=Ova usluga koristi Ghostscript za komprimiranje / optimizaciju PDF-a. +compress.credit=Ova usluga koristi qpdf za komprimiranje / optimizaciju PDF-a. compress.selectText.1=Ručni režim - Od 1 do 4 compress.selectText.2=Nivo optimizacije: compress.selectText.3=4 (Užasno za tekstualne slike) @@ -1112,7 +1112,7 @@ changeMetadata.submit=Promijeniti #pdfToPDFA pdfToPDFA.title=PDF u PDF/A pdfToPDFA.header=PDF u PDF/A -pdfToPDFA.credit=Ova usluga koristi ghostscript za PDF/A pretvorbu +pdfToPDFA.credit=Ova usluga koristi qpdf za PDF/A pretvorbu pdfToPDFA.submit=Pretvoriti pdfToPDFA.tip=Trenutno ne radi za više unosa odjednom pdfToPDFA.outputFormat=Izlazni format diff --git a/src/main/resources/messages_hu_HU.properties b/src/main/resources/messages_hu_HU.properties index 3afc38ba6..233c2e5b0 100644 --- a/src/main/resources/messages_hu_HU.properties +++ b/src/main/resources/messages_hu_HU.properties @@ -868,7 +868,7 @@ ocr.selectText.10=OCR mód ocr.selectText.11=Képek eltávolítása OCR után (Az ÖSSZES kép eltávolítása, csak akkor hasznos, ha a konverzió része) ocr.selectText.12=Render típusa (Speciális) ocr.help=Kérjük, olvassa el ezt a dokumentációt az egyéb nyelvek használatához és/vagy a nem Docker-es használathoz. -ocr.credit=Ez a szolgáltatás az OCRmyPDF és a Tesseract OCR használatával működik. +ocr.credit=Ez a szolgáltatás az qpdf és a Tesseract OCR használatával működik. ocr.submit=PDF feldolgozása OCR-rel @@ -892,7 +892,7 @@ fileToPDF.submit=Konvertálás PDF dokumentummá #compress compress.title=Tömörítés compress.header=PDF tömörítése -compress.credit=Ez a szolgáltatás a Ghostscript-et használja a PDF tömörítéséhez/optimalizálásához. +compress.credit=Ez a szolgáltatás a qpdf-et használja a PDF tömörítéséhez/optimalizálásához. compress.selectText.1=Kézi mód - 1-től 4-ig compress.selectText.2=Optimalizálási szint: compress.selectText.3=4 (nem ajánlott a szöveges képekhez) @@ -1112,7 +1112,7 @@ changeMetadata.submit=Módosítás #pdfToPDFA pdfToPDFA.title=PDF >> PDF/A pdfToPDFA.header=PDF >> PDF/A -pdfToPDFA.credit=Ez a szolgáltatás az ghostscript-t használja a PDF/A konverzióhoz +pdfToPDFA.credit=Ez a szolgáltatás az qpdf-t használja a PDF/A konverzióhoz pdfToPDFA.submit=Konvertálás pdfToPDFA.tip=Jelenleg egyszerre több fájl nem működik ezzel a funkcióval pdfToPDFA.outputFormat=Kimeneti formátum diff --git a/src/main/resources/messages_id_ID.properties b/src/main/resources/messages_id_ID.properties index a9497ba3a..2dead1a08 100644 --- a/src/main/resources/messages_id_ID.properties +++ b/src/main/resources/messages_id_ID.properties @@ -868,7 +868,7 @@ ocr.selectText.10=Mode OCR ocr.selectText.11=Hapus gambar setelah OCR (Menghapus Semua gambar, hanya berguna jika merupakan bagian dari langkah konversi) ocr.selectText.12=Jenis Render (Lanjutan) ocr.help=Silakan baca dokumentasi ini tentang cara menggunakan ini untuk bahasa lain dan/atau penggunaan yang tidak ada di docker -ocr.credit=Layanan ini menggunakan OCRmyPDF dan Tesseract untuk OCR. +ocr.credit=Layanan ini menggunakan qpdf dan Tesseract untuk OCR. ocr.submit=Memproses PDF dengan OCR @@ -892,7 +892,7 @@ fileToPDF.submit=Konversi ke PDF #compress compress.title=Kompres compress.header=Kompres PDF -compress.credit=Layanan ini menggunakan Ghostscript untuk Kompresi/Optimalisasi PDF. +compress.credit=Layanan ini menggunakan qpdf untuk Kompresi/Optimalisasi PDF. compress.selectText.1=Mode Manual - Dari 1 hingga 4 compress.selectText.2=Tingkat Optimalisasi: compress.selectText.3=4 (Buruk untuk gambar teks) @@ -1112,7 +1112,7 @@ changeMetadata.submit=Ganti #pdfToPDFA pdfToPDFA.title=PDF Ke PDF/A pdfToPDFA.header=PDF ke PDF/A -pdfToPDFA.credit=Layanan ini menggunakan ghostscript untuk konversi PDF/A. +pdfToPDFA.credit=Layanan ini menggunakan qpdf untuk konversi PDF/A. pdfToPDFA.submit=Konversi pdfToPDFA.tip=Saat ini tidak dapat digunakan untuk beberapa input sekaligus pdfToPDFA.outputFormat=Format keluaran diff --git a/src/main/resources/messages_it_IT.properties b/src/main/resources/messages_it_IT.properties index 697e9fec5..c4179703a 100644 --- a/src/main/resources/messages_it_IT.properties +++ b/src/main/resources/messages_it_IT.properties @@ -868,7 +868,7 @@ ocr.selectText.10=Modalità OCR ocr.selectText.11=Rimuovi immagini dopo la scansione (Rimuove TUTTE le immagini, utile solo come parte del processo di conversione) ocr.selectText.12=Modalità di rendering (avanzato) ocr.help=Per favore leggi la documentazione su come usare il programma per altri linguaggi e/o uso non in Docker -ocr.credit=Questo servizio utilizza OCRmyPDF e Tesseract per l'OCR. +ocr.credit=Questo servizio utilizza qpdf e Tesseract per l'OCR. ocr.submit=Scansiona testo nel PDF con OCR @@ -892,7 +892,7 @@ fileToPDF.submit=Converti in PDF #compress compress.title=Comprimi compress.header=Comprimi PDF -compress.credit=Questo servizio utilizza Ghostscript per la compressione/ottimizzazione dei PDF. +compress.credit=Questo servizio utilizza qpdf per la compressione/ottimizzazione dei PDF. compress.selectText.1=Modalità manuale - Da 1 a 4 compress.selectText.2=Livello di ottimizzazione: compress.selectText.3=4 (Terribile per le immagini di testo) @@ -1112,7 +1112,7 @@ changeMetadata.submit=Cambia proprietà #pdfToPDFA pdfToPDFA.title=Da PDF a PDF/A pdfToPDFA.header=Da PDF a PDF/A -pdfToPDFA.credit=Questo servizio utilizza Ghostscript per la conversione in PDF/A. +pdfToPDFA.credit=Questo servizio utilizza qpdf per la conversione in PDF/A. pdfToPDFA.submit=Converti pdfToPDFA.tip=Attualmente non funziona per più input contemporaneamente pdfToPDFA.outputFormat=Formato di output diff --git a/src/main/resources/messages_ja_JP.properties b/src/main/resources/messages_ja_JP.properties index 0b9f67d61..bb232b88e 100644 --- a/src/main/resources/messages_ja_JP.properties +++ b/src/main/resources/messages_ja_JP.properties @@ -868,7 +868,7 @@ ocr.selectText.10=OCRモード ocr.selectText.11=OCR後に画像を削除する (すべての画像を削除します。変換ステップの一部である場合にのみ有効です)。 ocr.selectText.12=レンダリングタイプ (高度) ocr.help=他の言語でこれを使用する方法やDocker以外で使用する方法についてはこのドキュメントをお読みください。 -ocr.credit=本サービスにはOCRにOCRmyPDFとTesseractを使用しています。 +ocr.credit=本サービスにはOCRにqpdfとTesseractを使用しています。 ocr.submit=OCRでPDFを処理する @@ -892,7 +892,7 @@ fileToPDF.submit=PDFを変換 #compress compress.title=圧縮 compress.header=PDFを圧縮 -compress.credit=本サービスはPDFの圧縮/最適化にGhostscriptを使用しています。 +compress.credit=本サービスはPDFの圧縮/最適化にqpdfを使用しています。 compress.selectText.1=手動モード - 1 から 4 compress.selectText.2=品質レベル: compress.selectText.3=4 (テキスト画像は最悪) @@ -1112,7 +1112,7 @@ changeMetadata.submit=変更 #pdfToPDFA pdfToPDFA.title=PDFをPDF/Aに変換 pdfToPDFA.header=PDFをPDF/Aに変換 -pdfToPDFA.credit=本サービスはPDF/Aの変換にghostscriptを使用しています。 +pdfToPDFA.credit=本サービスはPDF/Aの変換にqpdfを使用しています。 pdfToPDFA.submit=変換 pdfToPDFA.tip=現在、一度に複数の入力に対して機能しません pdfToPDFA.outputFormat=Output format diff --git a/src/main/resources/messages_ko_KR.properties b/src/main/resources/messages_ko_KR.properties index 5fe2c08b6..a2f3fcd4a 100644 --- a/src/main/resources/messages_ko_KR.properties +++ b/src/main/resources/messages_ko_KR.properties @@ -868,7 +868,7 @@ ocr.selectText.10=OCR 모드 ocr.selectText.11=OCR 후 이미지 제거(모든 이미지 제거, 변환 단계의 일부인 경우에만 유용) ocr.selectText.12=렌더 유형(고급) ocr.help=다른 언어 또는 Docker에 포함되지 않은 언어에 대해 사용하는 방법에 대해서는 이 문서를 참조합니다. -ocr.credit=이 서비스는 OCR에 OCRmyPDF와 Tesseract를 사용합니다. +ocr.credit=이 서비스는 OCR에 qpdf와 Tesseract를 사용합니다. ocr.submit=인식 @@ -892,7 +892,7 @@ fileToPDF.submit=PDF로 변환 #compress compress.title=압축 compress.header=PDF 압축 -compress.credit=이 서비스는 PDF 압축 및 최적화를 위해 Ghostscript를 사용합니다. +compress.credit=이 서비스는 PDF 압축 및 최적화를 위해 qpdf를 사용합니다. compress.selectText.1=수동 모드 - 1에서 4 compress.selectText.2=최적화 수준: compress.selectText.3=4 (텍스트 이미지에 적합하지 않음) @@ -1112,7 +1112,7 @@ changeMetadata.submit=변경 #pdfToPDFA pdfToPDFA.title=PDF를 PDF/A로 pdfToPDFA.header=PDF 문서를 PDF/A로 변환 -pdfToPDFA.credit=이 서비스는 PDF/A 변환을 위해 ghostscript 문서를 사용합니다. +pdfToPDFA.credit=이 서비스는 PDF/A 변환을 위해 qpdf 문서를 사용합니다. pdfToPDFA.submit=변환 pdfToPDFA.tip=현재 한 번에 여러 입력에 대해 작동하지 않습니다. pdfToPDFA.outputFormat=출력 형식 diff --git a/src/main/resources/messages_nl_NL.properties b/src/main/resources/messages_nl_NL.properties index b305eb3fb..6e03b932d 100644 --- a/src/main/resources/messages_nl_NL.properties +++ b/src/main/resources/messages_nl_NL.properties @@ -868,7 +868,7 @@ ocr.selectText.10=OCR-modus ocr.selectText.11=Verwijder afbeeldingen na OCR (Verwijdert ALLE afbeeldingen, alleen nuttig als onderdeel van conversiestap) ocr.selectText.12=Weergave Type (Geavanceerd) ocr.help=Lees deze documentatie over hoe dit te gebruiken voor andere talen en/of gebruik buiten docker -ocr.credit=Deze dienst maakt gebruik van OCRmyPDF en Tesseract voor OCR. +ocr.credit=Deze dienst maakt gebruik van qpdf en Tesseract voor OCR. ocr.submit=Verwerk PDF met OCR @@ -892,7 +892,7 @@ fileToPDF.submit=Omzetten naar PDF #compress compress.title=Comprimeren compress.header=PDF comprimeren -compress.credit=Deze functie gebruikt Ghostscript voor PDF Compressie/Optimalisatie. +compress.credit=Deze functie gebruikt qpdf voor PDF Compressie/Optimalisatie. compress.selectText.1=Handmatige modus - Van 1 tot 4 compress.selectText.2=Optimalisatieniveau: compress.selectText.3=4 (Verschrikkelijk voor tekstafbeeldingen) @@ -1112,7 +1112,7 @@ changeMetadata.submit=Wijzigen #pdfToPDFA pdfToPDFA.title=PDF naar PDF/A pdfToPDFA.header=PDF naar PDF/A -pdfToPDFA.credit=Deze service gebruikt ghostscript voor PDF/A-conversie +pdfToPDFA.credit=Deze service gebruikt qpdf voor PDF/A-conversie pdfToPDFA.submit=Converteren pdfToPDFA.tip=Werkt momenteel niet voor meerdere inputs tegelijkertijd. pdfToPDFA.outputFormat=Uitvoerindeling diff --git a/src/main/resources/messages_no_NB.properties b/src/main/resources/messages_no_NB.properties index 6804d5d5d..37bfec9d2 100644 --- a/src/main/resources/messages_no_NB.properties +++ b/src/main/resources/messages_no_NB.properties @@ -868,7 +868,7 @@ ocr.selectText.10=OCR-modus ocr.selectText.11=Fjern bilder etter OCR (Fjerner ALLE bilder, kun nyttig hvis det er en del av konverteringsprosessen) ocr.selectText.12=Renderingstype (Avansert) ocr.help=Vennligst les denne dokumentasjonen for hvordan du bruker dette for andre språk og/eller bruk utenfor Docker. -ocr.credit=Denne tjenesten bruker OCRmyPDF og Tesseract for OCR. +ocr.credit=Denne tjenesten bruker qpdf og Tesseract for OCR. ocr.submit=Behandle PDF med OCR @@ -892,7 +892,7 @@ fileToPDF.submit=Konverter til PDF #compress compress.title=Komprimer compress.header=Komprimer PDF -compress.credit=Denne tjenesten bruker Ghostscript for PDF-komprimering/optimisering. +compress.credit=Denne tjenesten bruker qpdf for PDF-komprimering/optimisering. compress.selectText.1=Manuell modus - Fra 1 til 4 compress.selectText.2=Optimeringsnivå: compress.selectText.3=4 (Dårlig for tekstbilder) @@ -1112,7 +1112,7 @@ changeMetadata.submit=Endre #pdfToPDFA pdfToPDFA.title=PDF til PDF/A pdfToPDFA.header=PDF til PDF/A -pdfToPDFA.credit=Denne tjenesten bruker ghostscript for PDF/A-konvertering +pdfToPDFA.credit=Denne tjenesten bruker qpdf for PDF/A-konvertering pdfToPDFA.submit=Konverter pdfToPDFA.tip=Fungere for øyeblikket ikke for flere innganger samtidig pdfToPDFA.outputFormat=Utdataformat diff --git a/src/main/resources/messages_pl_PL.properties b/src/main/resources/messages_pl_PL.properties index daa952cec..2f1679af0 100755 --- a/src/main/resources/messages_pl_PL.properties +++ b/src/main/resources/messages_pl_PL.properties @@ -868,7 +868,7 @@ ocr.selectText.10=Tryb OCR ocr.selectText.11=Usuń obrazy po OCR (usuwa wszystkie obrazy, przydatne tylko, jeśli jest częścią etapu konwersji) ocr.selectText.12=Typ renderowania (zaawansowany) ocr.help=Przeczytaj tę dokumentację, aby dowiedzieć się, jak używać tego w innych językach i/lub nie używać docker -ocr.credit=Ta usługa używa OCRmyPDF i Tesseract do OCR. +ocr.credit=Ta usługa używa qpdf i Tesseract do OCR. ocr.submit=Przetwarzaj PDF za pomocą OCR @@ -892,7 +892,7 @@ fileToPDF.submit=Konwertuj na PDF #compress compress.title=Kompresuj compress.header=Kompresuj PDF -compress.credit=Ta usługa używa Ghostscript do kompresji/optymalizacji PDF. +compress.credit=Ta usługa używa qpdf do kompresji/optymalizacji PDF. compress.selectText.1=Tryb ręczny - Od 1 do 4 compress.selectText.2=Poziom optymalizacji: compress.selectText.3=4 (Duże dla obrazów tekstowych) @@ -1112,7 +1112,7 @@ changeMetadata.submit=Zmień #pdfToPDFA pdfToPDFA.title=PDF na PDF/A pdfToPDFA.header=PDF na PDF/A -pdfToPDFA.credit=Ta usługa używa ghostscript do konwersji PDF/A +pdfToPDFA.credit=Ta usługa używa qpdf do konwersji PDF/A pdfToPDFA.submit=Konwertuj pdfToPDFA.tip=Tylko jeden plik na raz pdfToPDFA.outputFormat=Format wyjściowy: diff --git a/src/main/resources/messages_pt_BR.properties b/src/main/resources/messages_pt_BR.properties index 5d3e2b858..32e34a974 100644 --- a/src/main/resources/messages_pt_BR.properties +++ b/src/main/resources/messages_pt_BR.properties @@ -868,7 +868,7 @@ ocr.selectText.10=Modo OCR ocr.selectText.11=Remover imagens após o OCR (remove TODAS as imagens, útil apenas como parte do processo de conversão) ocr.selectText.12=Tipo de Renderização (avançado) ocr.help=Por favor, leia a documentação sobre como usar isso para outros idiomas e/ou fora do ambiente Docker -ocr.credit=Este serviço usa OCRmyPDF e Tesseract para OCR. +ocr.credit=Este serviço usa qpdf e Tesseract para OCR. ocr.submit=Processar PDF com OCR @@ -892,7 +892,7 @@ fileToPDF.submit=Converter para PDF #compress compress.title=Comprimir compress.header=Comprimir PDF -compress.credit=Este serviço usa o Ghostscript para compressão/otimização de PDF. +compress.credit=Este serviço usa o qpdf para compressão/otimização de PDF. compress.selectText.1=Modo Manual - De 1 a 4 compress.selectText.2=Nível de Otimização: compress.selectText.3=4 (Pior para imagens de texto) @@ -1112,7 +1112,7 @@ changeMetadata.submit=Alterar #pdfToPDFA pdfToPDFA.title=PDF para PDF/A pdfToPDFA.header=PDF para PDF/A -pdfToPDFA.credit=Este serviço usa ghostscript para conversão de PDF/A +pdfToPDFA.credit=Este serviço usa qpdf para conversão de PDF/A pdfToPDFA.submit=Converter pdfToPDFA.tip=Atualmente não funciona para múltiplas entradas ao mesmo tempo pdfToPDFA.outputFormat=Formato de saída diff --git a/src/main/resources/messages_pt_PT.properties b/src/main/resources/messages_pt_PT.properties index 395f50936..76de21b4d 100644 --- a/src/main/resources/messages_pt_PT.properties +++ b/src/main/resources/messages_pt_PT.properties @@ -868,7 +868,7 @@ ocr.selectText.10=Modo OCR ocr.selectText.11=Remover imagens após o OCR (remove TODAS as imagens, útil apenas como parte do processo de conversão) ocr.selectText.12=Tipo de renderização (avançado) ocr.help=Por favor, leia a documentação sobre como usar isso para outros idiomas e/ou fora do ambiente Docker -ocr.credit=Este serviço usa OCRmyPDF e Tesseract para OCR. +ocr.credit=Este serviço usa qpdf e Tesseract para OCR. ocr.submit=Processar PDF com OCR @@ -892,7 +892,7 @@ fileToPDF.submit=Converter para PDF #compress compress.title=Comprimir compress.header=Comprimir PDF -compress.credit=Este serviço usa o Ghostscript para compressão/otimização de PDF. +compress.credit=Este serviço usa o qpdf para compressão/otimização de PDF. compress.selectText.1=Modo Manual - De 1 a 4 compress.selectText.2=Nível de Otimização: compress.selectText.3=4 (Pior para imagens de texto) @@ -1112,7 +1112,7 @@ changeMetadata.submit=Mudar #pdfToPDFA pdfToPDFA.title=PDF para PDF/A pdfToPDFA.header=PDF para PDF/A -pdfToPDFA.credit=Este serviço usa ghostscript para Conversão de PDF/A +pdfToPDFA.credit=Este serviço usa qpdf para Conversão de PDF/A pdfToPDFA.submit=Converter pdfToPDFA.tip=Actualmente não funciona para múltiplos inputs de uma só vez pdfToPDFA.outputFormat=Formato de saída diff --git a/src/main/resources/messages_ro_RO.properties b/src/main/resources/messages_ro_RO.properties index b0c6618fe..708f0939d 100644 --- a/src/main/resources/messages_ro_RO.properties +++ b/src/main/resources/messages_ro_RO.properties @@ -868,7 +868,7 @@ ocr.selectText.10=Mod OCR ocr.selectText.11=Elimină imaginile după OCR (Elimină TOATE imaginile, util doar în etapa de conversie) ocr.selectText.12=Tip de redare (Avansat) ocr.help=Citiți documentația pentru a afla cum să utilizați acest serviciu pentru alte limbi și/sau în afara mediului Docker -ocr.credit=Acest serviciu utilizează OCRmyPDF și Tesseract pentru OCR. +ocr.credit=Acest serviciu utilizează qpdf și Tesseract pentru OCR. ocr.submit=Procesează PDF-ul cu OCR @@ -892,7 +892,7 @@ fileToPDF.submit=Convertiți în PDF #compress compress.title=Comprimare compress.header=Comprimare PDF -compress.credit=Acest serviciu utilizează OCRmyPDF pentru comprimarea/optimizarea PDF-urilor. +compress.credit=Acest serviciu utilizează qpdf pentru comprimarea/optimizarea PDF-urilor. compress.selectText.1=Nivel de optimizare: compress.selectText.2=0 (Fără optimizare) compress.selectText.3=1 (Implicit, optimizare fără pierdere) @@ -1112,7 +1112,7 @@ changeMetadata.submit=Schimbă #pdfToPDFA pdfToPDFA.title=PDF către PDF/A pdfToPDFA.header=PDF către PDF/A -pdfToPDFA.credit=Acest serviciu utilizează ghostscript pentru conversia în PDF/A +pdfToPDFA.credit=Acest serviciu utilizează qpdf pentru conversia în PDF/A pdfToPDFA.submit=Convertește pdfToPDFA.tip=În prezent nu funcționează pentru mai multe intrări simultan pdfToPDFA.outputFormat=Format de ieșire diff --git a/src/main/resources/messages_ru_RU.properties b/src/main/resources/messages_ru_RU.properties index 79116da40..ca0427ef1 100644 --- a/src/main/resources/messages_ru_RU.properties +++ b/src/main/resources/messages_ru_RU.properties @@ -868,7 +868,7 @@ ocr.selectText.10=OCR режим ocr.selectText.11=Удалить изображения после OCR (удаляет ВСЕ изображения, полезно только в том случае, если они являются частью шага преобразования) ocr.selectText.12=Тип рендера (расширенный) ocr.help=Прочтите эту документацию о том, как использовать это для других языков и/или использовать не в докере. -ocr.credit=Этот сервис использует OCRmyPDF и Tesseract для OCR. +ocr.credit=Этот сервис использует qpdf и Tesseract для OCR. ocr.submit=Обработка PDF с OCR @@ -892,7 +892,7 @@ fileToPDF.submit=Преобразовать в PDF #compress compress.title=Сжать compress.header=Сжать PDF -compress.credit=Эта служба использует Ghostscript для сжатия/оптимизации PDF. +compress.credit=Эта служба использует qpdf для сжатия/оптимизации PDF. compress.selectText.1=Ручной режим - от 1 до 4 compress.selectText.2=Уровень оптимизации: compress.selectText.3=4 (Ужасно для текстовых изображений) @@ -1112,7 +1112,7 @@ changeMetadata.submit=Изменить #pdfToPDFA pdfToPDFA.title=PDF в PDF/A pdfToPDFA.header=PDF в PDF/A -pdfToPDFA.credit=Этот сервис использует ghostscript для преобразования PDF/A +pdfToPDFA.credit=Этот сервис использует qpdf для преобразования PDF/A pdfToPDFA.submit=Конвертировать pdfToPDFA.tip=В настоящее время не поддерживается при нескольких входных данных одновременно pdfToPDFA.outputFormat=Формат вывода diff --git a/src/main/resources/messages_sk_SK.properties b/src/main/resources/messages_sk_SK.properties index aa356adc5..c7f901915 100644 --- a/src/main/resources/messages_sk_SK.properties +++ b/src/main/resources/messages_sk_SK.properties @@ -868,7 +868,7 @@ ocr.selectText.10=OCR režim ocr.selectText.11=Odstrániť obrázky po OCR (Odstráni VŠETKY obrázky, užitočné iba ak je súčasťou konverzného kroku) ocr.selectText.12=Typ vykreslenia (Pokročilé) ocr.help=Prosím, prečítajte si túto dokumentáciu o tom, ako používať OCR pre iné jazyky a/alebo použitie mimo docker -ocr.credit=Táto služba používa OCRmyPDF a Tesseract pre OCR. +ocr.credit=Táto služba používa qpdf a Tesseract pre OCR. ocr.submit=Spracovať PDF s OCR @@ -892,7 +892,7 @@ fileToPDF.submit=Konvertovať do PDF #compress compress.title=Komprimovať compress.header=Komprimovať PDF -compress.credit=Táto služba používa Ghostscript pre kompresiu/optimalizáciu PDF. +compress.credit=Táto služba používa qpdf pre kompresiu/optimalizáciu PDF. compress.selectText.1=Manuálny režim - Od 1 do 4 compress.selectText.2=Úroveň optimalizácie: compress.selectText.3=4 (Hrozné pre textové obrázky) @@ -1112,7 +1112,7 @@ changeMetadata.submit=Zmeniť #pdfToPDFA pdfToPDFA.title=PDF na PDF/A pdfToPDFA.header=PDF na PDF/A -pdfToPDFA.credit=Táto služba používa ghostscript na konverziu PDF/A +pdfToPDFA.credit=Táto služba používa qpdf na konverziu PDF/A pdfToPDFA.submit=Konvertovať pdfToPDFA.tip=Momentálne nefunguje pre viacero vstupov naraz pdfToPDFA.outputFormat=Výstupný formát diff --git a/src/main/resources/messages_sr_LATN_RS.properties b/src/main/resources/messages_sr_LATN_RS.properties index d1b20952a..394678802 100644 --- a/src/main/resources/messages_sr_LATN_RS.properties +++ b/src/main/resources/messages_sr_LATN_RS.properties @@ -868,7 +868,7 @@ ocr.selectText.10=Režim OCR-a ocr.selectText.11=Ukloni slike nakon OCR-a (Uklanja SVE slike, korisno samo ako je deo koraka konverzije) ocr.selectText.12=Tip rendiranja (Napredno) ocr.help=Molimo vas da pročitate ovu dokumentaciju o tome kako koristiti ovo za druge jezike i/ili korišćenje van docker-a -ocr.credit=Ova usluga koristi OCRmyPDF i Tesseract za OCR. +ocr.credit=Ova usluga koristi qpdf i Tesseract za OCR. ocr.submit=Obradi PDF sa OCR-om @@ -892,7 +892,7 @@ fileToPDF.submit=Konvertuj u PDF #compress compress.title=Kompresija compress.header=Kompresuj PDF -compress.credit=Ova usluga koristi Ghostscript za kompresiju / optimizaciju PDF-a. +compress.credit=Ova usluga koristi qpdf za kompresiju / optimizaciju PDF-a. compress.selectText.1=Ručni režim - Od 1 do 4 compress.selectText.2=Nivo optimizacije: compress.selectText.3=4 (Užasno za tekstualne slike) @@ -1112,7 +1112,7 @@ changeMetadata.submit=Promeni #pdfToPDFA pdfToPDFA.title=PDF u PDF/A pdfToPDFA.header=PDF u PDF/A -pdfToPDFA.credit=Ova usluga koristi ghostscript za konverziju u PDF/A format +pdfToPDFA.credit=Ova usluga koristi qpdf za konverziju u PDF/A format pdfToPDFA.submit=Konvertuj pdfToPDFA.tip=Currently does not work for multiple inputs at once pdfToPDFA.outputFormat=Output format diff --git a/src/main/resources/messages_sv_SE.properties b/src/main/resources/messages_sv_SE.properties index 3a9c88a9a..e0fdf5c96 100644 --- a/src/main/resources/messages_sv_SE.properties +++ b/src/main/resources/messages_sv_SE.properties @@ -868,7 +868,7 @@ ocr.selectText.10=OCR-läge ocr.selectText.11=Ta bort bilder efter OCR (tar bort ALLA bilder, endast användbart som en del av konverteringssteget) ocr.selectText.12=Renderingstyp (avancerat) ocr.help=Vänligen läs denna dokumentation om hur du använder detta för andra språk och/eller använder inte i docker -ocr.credit=Denna tjänst använder OCRmyPDF och Tesseract för OCR. +ocr.credit=Denna tjänst använder qpdf och Tesseract för OCR. ocr.submit=Bearbeta PDF med OCR @@ -892,7 +892,7 @@ fileToPDF.submit=Konvertera till PDF #compress compress.title=Komprimera compress.header=Komprimera PDF -compress.credit=Denna tjänst använder Ghostscript för PDF-komprimering/optimering. +compress.credit=Denna tjänst använder qpdf för PDF-komprimering/optimering. compress.selectText.1=Manuellt läge - Från 1 till 4 compress.selectText.2=Optimeringsnivå: compress.selectText.3=4 (Fruktansvärt för textbilder) @@ -1112,7 +1112,7 @@ changeMetadata.submit=Ändra #pdfToPDFA pdfToPDFA.title=PDF till PDF/A pdfToPDFA.header=PDF till PDF/A -pdfToPDFA.credit=Denna tjänst använder ghostscript för PDF/A-konvertering +pdfToPDFA.credit=Denna tjänst använder qpdf för PDF/A-konvertering pdfToPDFA.submit=Konvertera pdfToPDFA.tip=Fungerar för närvarande inte för flera inmatningar samtidigt pdfToPDFA.outputFormat=Utdataformat diff --git a/src/main/resources/messages_th_TH.properties b/src/main/resources/messages_th_TH.properties index 25f39609d..061a84134 100644 --- a/src/main/resources/messages_th_TH.properties +++ b/src/main/resources/messages_th_TH.properties @@ -868,7 +868,7 @@ ocr.selectText.10=โหมด OCR ocr.selectText.11=ลบภาพหลังจาก OCR (ลบภาพทั้งหมด, มีประโยชน์เฉพาะหากเป็นส่วนหนึ่งของขั้นตอนการแปลง) ocr.selectText.12=ประเภทการเรนเดอร์ (ขั้นสูง) ocr.help=โปรดอ่านเอกสารนี้เพื่อใช้งานภาษาอื่นๆ และ/หรือใช้งานนอก docker -ocr.credit=บริการนี้ใช้ OCRmyPDF และ Tesseract สำหรับ OCR +ocr.credit=บริการนี้ใช้ qpdf และ Tesseract สำหรับ OCR ocr.submit=ประมวลผล PDF ด้วย OCR @@ -892,7 +892,7 @@ fileToPDF.submit=แปลงเป็น PDF #compress compress.title=บีบอัด compress.header=บีบอัด PDF -compress.credit=บริการนี้ใช้ Ghostscript สำหรับการบีบอัด/การเพิ่มประสิทธิภาพ PDF +compress.credit=บริการนี้ใช้ qpdf สำหรับการบีบอัด/การเพิ่มประสิทธิภาพ PDF compress.selectText.1=โหมดแมนนวล - จาก 1 ถึง 4 compress.selectText.2=ระดับการเพิ่มประสิทธิภาพ: compress.selectText.3=4 (ไม่ดีสำหรับภาพข้อความ) @@ -1112,7 +1112,7 @@ changeMetadata.submit=เปลี่ยน #pdfToPDFA pdfToPDFA.title=PDF เป็น PDF/A pdfToPDFA.header=PDF เป็น PDF/A -pdfToPDFA.credit=บริการนี้ใช้ ghostscript สำหรับการแปลง PDF/A +pdfToPDFA.credit=บริการนี้ใช้ qpdf สำหรับการแปลง PDF/A pdfToPDFA.submit=แปลง pdfToPDFA.tip=ปัจจุบันไม่ทำงานสำหรับการป้อนข้อมูลหลายรายการพร้อมกัน pdfToPDFA.outputFormat=รูปแบบผลลัพธ์ diff --git a/src/main/resources/messages_tr_TR.properties b/src/main/resources/messages_tr_TR.properties index 00417d57d..38ffe1a08 100644 --- a/src/main/resources/messages_tr_TR.properties +++ b/src/main/resources/messages_tr_TR.properties @@ -868,7 +868,7 @@ ocr.selectText.10=OCR Modu ocr.selectText.11=OCR'den sonra resimleri kaldır (TÜM resimleri kaldırır, sadece dönüşüm adımının bir parçasıysa yararlıdır) ocr.selectText.12=Render Türü (İleri Seviye) ocr.help=Lütfen bu belgede başka dillerde nasıl kullanılacağı ve/veya docker'da kullanılmaması hakkında bilgi edinin -ocr.credit=Bu hizmet OCR için OCRmyPDF ve Tesseract'ı kullanır. +ocr.credit=Bu hizmet OCR için qpdf ve Tesseract'ı kullanır. ocr.submit=PDF'i OCR(Metin Tanıma) ile İşle @@ -892,7 +892,7 @@ fileToPDF.submit=PDF'e Dönüştür #compress compress.title=Sıkıştır compress.header=PDF'i Sıkıştır -compress.credit=Bu hizmet PDF Sıkıştırma/Optimizasyonu için Ghostscript kullanır. +compress.credit=Bu hizmet PDF Sıkıştırma/Optimizasyonu için qpdf kullanır. compress.selectText.1=Manuel Mod - 1'den 4'e compress.selectText.2=Optimizasyon seviyesi: compress.selectText.3=4 (Metin resimleri için hiç uygun değil) @@ -1112,7 +1112,7 @@ changeMetadata.submit=Değiştir #pdfToPDFA pdfToPDFA.title=PDF'den PDF/A'ya pdfToPDFA.header=PDF'den PDF/A'ya -pdfToPDFA.credit=Bu hizmet PDF/A dönüşümü için ghostscript kullanır +pdfToPDFA.credit=Bu hizmet PDF/A dönüşümü için qpdf kullanır pdfToPDFA.submit=Dönüştür pdfToPDFA.tip=Şu anda aynı anda birden fazla giriş için çalışmıyor pdfToPDFA.outputFormat=Çıkış formatı diff --git a/src/main/resources/messages_uk_UA.properties b/src/main/resources/messages_uk_UA.properties index 7530cb245..53c0c5a7a 100644 --- a/src/main/resources/messages_uk_UA.properties +++ b/src/main/resources/messages_uk_UA.properties @@ -868,7 +868,7 @@ ocr.selectText.10=Режим OCR ocr.selectText.11=Видалити зображення після OCR (видаляє ВСІ зображення, корисно лише в тому випадку, якщо вони є частиною етапу перетворення) ocr.selectText.12=Тип рендеру (розширений) ocr.help=Прочитайте цю документацію про те, як використовувати це для інших мов і/або використовувати не в докері. -ocr.credit=Цей сервіс використовує OCRmyPDF та Tesseract для OCR. +ocr.credit=Цей сервіс використовує qpdf та Tesseract для OCR. ocr.submit=Обробка PDF з OCR @@ -892,7 +892,7 @@ fileToPDF.submit=Перетворити у PDF #compress compress.title=Стиснути compress.header=Стиснути PDF -compress.credit=Ця служба використовує Ghostscript для стиснення/оптимізації PDF. +compress.credit=Ця служба використовує qpdf для стиснення/оптимізації PDF. compress.selectText.1=Ручний режим - від 1 до 4 compress.selectText.2=Рівень оптимізації: compress.selectText.3=4 (Жахливо для текстових зображень) @@ -1112,7 +1112,7 @@ changeMetadata.submit=Змінити #pdfToPDFA pdfToPDFA.title=PDF в PDF/A pdfToPDFA.header=PDF в PDF/A -pdfToPDFA.credit=Цей сервіс використовує ghostscript для перетворення у формат PDF/A +pdfToPDFA.credit=Цей сервіс використовує qpdf для перетворення у формат PDF/A pdfToPDFA.submit=Конвертувати pdfToPDFA.tip=Наразі не працює для кількох вхідних файлів одночасно pdfToPDFA.outputFormat=Вихідний формат diff --git a/src/main/resources/messages_vi_VN.properties b/src/main/resources/messages_vi_VN.properties index 2887c25b1..2f060e910 100644 --- a/src/main/resources/messages_vi_VN.properties +++ b/src/main/resources/messages_vi_VN.properties @@ -868,7 +868,7 @@ ocr.selectText.10=Chế độ OCR ocr.selectText.11=Xóa hình ảnh sau khi OCR (Xóa TẤT CẢ hình ảnh, chỉ hữu ích nếu là một phần của bước chuyển đổi) ocr.selectText.12=Loại hiển thị (Nâng cao) ocr.help=Vui lòng đọc tài liệu này về cách sử dụng cho các ngôn ngữ khác và/hoặc sử dụng không trong docker -ocr.credit=Dịch vụ này sử dụng OCRmyPDF và Tesseract cho OCR. +ocr.credit=Dịch vụ này sử dụng qpdf và Tesseract cho OCR. ocr.submit=Xử lý PDF với OCR @@ -892,7 +892,7 @@ fileToPDF.submit=Chuyển đổi sang PDF #compress compress.title=Nén compress.header=Nén PDF -compress.credit=Dịch vụ này sử dụng Ghostscript để Nén/Tối ưu hóa PDF. +compress.credit=Dịch vụ này sử dụng qpdf để Nén/Tối ưu hóa PDF. compress.selectText.1=Chế độ thủ công - Từ 1 đến 4 compress.selectText.2=Mức độ tối ưu hóa: compress.selectText.3=4 (Tệ cho hình ảnh văn bản) @@ -1112,7 +1112,7 @@ changeMetadata.submit=Thay đổi #pdfToPDFA pdfToPDFA.title=PDF sang PDF/A pdfToPDFA.header=PDF sang PDF/A -pdfToPDFA.credit=Dịch vụ này sử dụng ghostscript để chuyển đổi PDF/A +pdfToPDFA.credit=Dịch vụ này sử dụng qpdf để chuyển đổi PDF/A pdfToPDFA.submit=Chuyển đổi pdfToPDFA.tip=Hiện tại không hoạt động với nhiều đầu vào cùng lúc pdfToPDFA.outputFormat=Định dạng đầu ra diff --git a/src/main/resources/messages_zh_CN.properties b/src/main/resources/messages_zh_CN.properties index 1a50bec15..692874e10 100644 --- a/src/main/resources/messages_zh_CN.properties +++ b/src/main/resources/messages_zh_CN.properties @@ -868,7 +868,7 @@ ocr.selectText.10=OCR模式 ocr.selectText.11=OCR后移除图像(移除所有图像,只有在转换步骤中才有用)。 ocr.selectText.12=渲染类型(高级) ocr.help=请阅读此文档,了解如何将其用于其他语言和/或不在docker中使用。 -ocr.credit=此服务使用OCRmyPDF和Tesseract进行OCR。 +ocr.credit=此服务使用qpdf和Tesseract进行OCR。 ocr.submit=用OCR处理PDF @@ -892,7 +892,7 @@ fileToPDF.submit=转换为 PDF #compress compress.title=压缩 compress.header=压缩PDF -compress.credit=此服务使用Ghostscript进行PDF压缩/优化。 +compress.credit=此服务使用qpdf进行PDF压缩/优化。 compress.selectText.1=手动模式 - 从 1 到 4 compress.selectText.2=优化级别: compress.selectText.3=4(文本图像很糟糕) @@ -1112,7 +1112,7 @@ changeMetadata.submit=更改 #pdfToPDFA pdfToPDFA.title=PDF转PDF/A pdfToPDFA.header=将PDF转换为PDF/A -pdfToPDFA.credit=此服务使用ghostscript进行PDF/A转换 +pdfToPDFA.credit=此服务使用qpdf进行PDF/A转换 pdfToPDFA.submit=转换 pdfToPDFA.tip=目前不支持上传多个 pdfToPDFA.outputFormat=输出格式 diff --git a/src/main/resources/messages_zh_TW.properties b/src/main/resources/messages_zh_TW.properties index 44b398c08..06534d233 100644 --- a/src/main/resources/messages_zh_TW.properties +++ b/src/main/resources/messages_zh_TW.properties @@ -868,7 +868,7 @@ ocr.selectText.10=OCR 模式 ocr.selectText.11=移除 OCR 後的影像(移除所有影像,只有在轉換步驟中才有用) ocr.selectText.12=渲染類型(進階) ocr.help=請閱讀此文件,了解如何使用其他語言和/或在 Docker 中使用 -ocr.credit=此服務使用 OCRmyPDF 和 Tesseract 進行 OCR。 +ocr.credit=此服務使用 qpdf 和 Tesseract 進行 OCR。 ocr.submit=使用 OCR 處理 PDF @@ -892,7 +892,7 @@ fileToPDF.submit=轉換為 PDF #compress compress.title=壓縮 compress.header=壓縮 PDF -compress.credit=此服務使用 Ghostscript 進行 PDF 壓縮/最佳化。 +compress.credit=此服務使用 qpdf 進行 PDF 壓縮/最佳化。 compress.selectText.1=手動模式 - 從 1 到 4 compress.selectText.2=最佳化等級: compress.selectText.3=4(對於含有文字的影像來說結果很糟) @@ -1112,7 +1112,7 @@ changeMetadata.submit=變更 #pdfToPDFA pdfToPDFA.title=PDF 轉 PDF/A pdfToPDFA.header=PDF 轉 PDF/A -pdfToPDFA.credit=此服務使用 ghostscript 進行 PDF/A 轉換 +pdfToPDFA.credit=此服務使用 qpdf 進行 PDF/A 轉換 pdfToPDFA.submit=轉換 pdfToPDFA.tip=目前不支援上傳多個 pdfToPDFA.outputFormat=輸出格式 diff --git a/src/main/resources/settings.yml.template b/src/main/resources/settings.yml.template index d9971d0c8..837acd895 100644 --- a/src/main/resources/settings.yml.template +++ b/src/main/resources/settings.yml.template @@ -107,9 +107,9 @@ processExecutor: sessionLimit: # Process executor instances limits libreOfficeSessionLimit: 1 pdfToHtmlSessionLimit: 1 - ocrMyPdfSessionLimit: 2 + qpdfSessionLimit: 4 + tesseractSessionLimit: 1 pythonOpenCvSessionLimit: 8 - ghostScriptSessionLimit: 16 weasyPrintSessionLimit: 16 installAppSessionLimit: 1 calibreSessionLimit: 1 @@ -117,7 +117,7 @@ processExecutor: libreOfficetimeoutMinutes: 30 pdfToHtmltimeoutMinutes: 20 pythonOpenCvtimeoutMinutes: 30 - ghostScripttimeoutMinutes: 30 weasyPrinttimeoutMinutes: 30 installApptimeoutMinutes: 60 calibretimeoutMinutes: 30 + tesseractTimeoutMinutes: 30 diff --git a/src/main/resources/templates/fragments/footer.html b/src/main/resources/templates/fragments/footer.html index 6cf37fd4a..d18516c3c 100644 --- a/src/main/resources/templates/fragments/footer.html +++ b/src/main/resources/templates/fragments/footer.html @@ -4,6 +4,7 @@
  • Licenses
  • +
  • Releases
  • Survey
  • privacyPolicy
  • termsAndConditions
  • diff --git a/src/main/resources/templates/misc/compress-pdf.html b/src/main/resources/templates/misc/compress-pdf.html index 99dda30db..e22d1ef41 100644 --- a/src/main/resources/templates/misc/compress-pdf.html +++ b/src/main/resources/templates/misc/compress-pdf.html @@ -29,9 +29,14 @@
diff --git a/src/main/resources/templates/misc/ocr-pdf.html b/src/main/resources/templates/misc/ocr-pdf.html index 9b8a87939..d37c1c2a3 100644 --- a/src/main/resources/templates/misc/ocr-pdf.html +++ b/src/main/resources/templates/misc/ocr-pdf.html @@ -62,26 +62,6 @@
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -