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>
This commit is contained in:
Anthony Stirling 2024-11-26 20:50:35 +00:00 committed by GitHub
parent 654bc94d44
commit 833b3c45c6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
69 changed files with 1106 additions and 665 deletions

View File

@ -11,7 +11,7 @@ Stirling-PDF is built using:
- Spring Boot + Thymeleaf - Spring Boot + Thymeleaf
- PDFBox - PDFBox
- LibreOffice - LibreOffice
- OcrMyPdf - qpdf
- HTML, CSS, JavaScript - HTML, CSS, JavaScript
- Docker - Docker
- PDF.js - PDF.js
@ -243,7 +243,7 @@ To run Stirling-PDF locally:
Important notes: 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!) - 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. - 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.

View File

@ -30,6 +30,7 @@ RUN echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/main" | tee -a /et
tini \ tini \
bash \ bash \
curl \ curl \
qpdf \
shadow \ shadow \
su-exec \ su-exec \
openssl \ openssl \
@ -40,7 +41,6 @@ RUN echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/main" | tee -a /et
# pdftohtml # pdftohtml
poppler-utils \ poppler-utils \
# OCR MY PDF (unpaper for descew and other advanced features) # OCR MY PDF (unpaper for descew and other advanced features)
ocrmypdf \
tesseract-ocr-data-eng \ tesseract-ocr-data-eng \
# CV # CV
py3-opencv \ py3-opencv \

View File

@ -55,7 +55,7 @@ RUN echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/main" | tee -a /et
# pdftohtml # pdftohtml
poppler-utils \ poppler-utils \
# OCR MY PDF (unpaper for descew and other advanced featues) # OCR MY PDF (unpaper for descew and other advanced featues)
ocrmypdf \ qpdf \
tesseract-ocr-data-eng \ tesseract-ocr-data-eng \
font-terminus font-dejavu font-noto font-noto-cjk font-awesome font-noto-extra \ font-terminus font-dejavu font-noto font-noto-cjk font-awesome font-noto-extra \
# CV # CV

View File

@ -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 | ✔️ | | | | | | | | | | ✔️ | | | | adjust-contrast | ✔️ | | | | | | | | | | ✔️ | | |
| auto-split-pdf | ✔️ | | | | | | | | | ✔️ | | | | | auto-split-pdf | ✔️ | | | | | | | | | ✔️ | | | |
@ -16,7 +16,7 @@
| img-to-pdf | | ✔️ | | | | | | | | ✔️ | | | | | img-to-pdf | | ✔️ | | | | | | | | ✔️ | | | |
| pdf-to-html | | ✔️ | | | ✔️ | | | ✔️ | | | | | | | pdf-to-html | | ✔️ | | | ✔️ | | | ✔️ | | | | | |
| pdf-to-img | | ✔️ | | | | ✔️ | | | | ✔️ | | | | | pdf-to-img | | ✔️ | | | | ✔️ | | | | ✔️ | | | |
| pdf-to-pdfa | | ✔️ | | | ✔️ | | | | ✔️ | | | | ✔️ | | pdf-to-pdfa | | ✔️ | | | ✔️ | | | | ✔️ | | | | |
| pdf-to-markdown | | ✔️ | | | | | | | | ✔️ | | | | | pdf-to-markdown | | ✔️ | | | | | | | | ✔️ | | | |
| pdf-to-presentation | | ✔️ | | | ✔️ | | | ✔️ | | | | | | | pdf-to-presentation | | ✔️ | | | ✔️ | | | ✔️ | | | | | |
| pdf-to-text | | ✔️ | | | ✔️ | | | ✔️ | | | | | | | pdf-to-text | | ✔️ | | | ✔️ | | | ✔️ | | | | | |
@ -34,13 +34,13 @@
| auto-rename | | | | ✔️ | | | | | | ✔️ | | | | | auto-rename | | | | ✔️ | | | | | | ✔️ | | | |
| change-metadata | | | | ✔️ | | | | | | ✔️ | | | | | change-metadata | | | | ✔️ | | | | | | ✔️ | | | |
| compare | | | | ✔️ | | | | | | | ✔️ | | | | compare | | | | ✔️ | | | | | | | ✔️ | | |
| compress-pdf | | | | ✔️ | ✔️ | | | | ✔️ | | | | ✔️ | | compress-pdf | | | | ✔️ | ✔️ | | | | ✔️ | | | | |
| extract-image-scans | | | | ✔️ | ✔️ | ✔️ | ✔️ | | | | | | | | extract-image-scans | | | | ✔️ | ✔️ | ✔️ | ✔️ | | | | | | |
| extract-images | | | | ✔️ | | | | | | ✔️ | | | | | extract-images | | | | ✔️ | | | | | | ✔️ | | | |
| flatten | | | | ✔️ | | | | | | | ✔️ | | | | flatten | | | | ✔️ | | | | | | | ✔️ | | |
| get-info-on-pdf | | | | ✔️ | | | | | | ✔️ | | | | | get-info-on-pdf | | | | ✔️ | | | | | | ✔️ | | | |
| ocr-pdf | | | | ✔️ | ✔️ | | | | ✔️ | | | | | | ocr-pdf | | | | ✔️ | ✔️ | | | | | | | | ✔ |
| remove-blanks | | | | ✔️ | ✔️ | ✔️ | ✔️ | | | | | | | | remove-blanks | | | | ✔️ | ✔️ | ✔️ | ✔️ | | | | | | |
| repair | | | | ✔️ | ✔️ | | | ✔️ | | | | | ✔️ | | repair | | | | ✔️ | ✔️ | | | ✔️ | | | | | |
| show-javascript | | | | ✔️ | | | | | | | ✔️ | | | | show-javascript | | | | ✔️ | | | | | | | ✔️ | | |
| sign | | | | ✔️ | | | | | | | ✔️ | | | | sign | | | | ✔️ | | | | | | | ✔️ | | |

View File

@ -8,7 +8,7 @@ The paths have changed for the tessdata locations on new Docker images. Please u
## How does the OCR Work ## 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 ## Language Packs
@ -52,8 +52,6 @@ Add the following to your existing Docker run command:
### Non-Docker Setup ### 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: For Debian-based systems, install languages with this command:
```bash ```bash
@ -83,8 +81,7 @@ rpm -qa | grep tesseract-langpack | sed 's/tesseract-langpack-//g'
For Windows: For Windows:
Ensure ocrmypdf in installed with You must ensure tesseract is installed
``pip install ocrmypdf``
Additional languages must be downloaded manually: Additional languages must be downloaded manually:
Download desired .traineddata files from tessdata or tessdata_fast Download desired .traineddata files from tessdata or tessdata_fast

View File

@ -68,7 +68,7 @@ nix-env -iA nixpkgs.jbig2enc
### Step 3: Install Additional Software ### 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: Install the following software:
@ -81,27 +81,27 @@ Install the following software:
- unoconv - unoconv
- pngquant - pngquant
- unpaper - unpaper
- ocrmypdf - qpdf
- opencv-python-headless - opencv-python-headless
For Debian-based systems, you can use the following command: For Debian-based systems, you can use the following command:
```bash ```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 pip3 install uno opencv-python-headless unoconv pngquant WeasyPrint --break-system-packages
``` ```
For Fedora: For Fedora:
```bash ```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 pip3 install uno opencv-python-headless unoconv pngquant WeasyPrint
``` ```
For Nix: For Nix:
```bash ```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 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. 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` 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. **IMPORTANT:** DO NOT REMOVE EXISTING `eng.traineddata`, IT'S REQUIRED.

View File

@ -79,15 +79,15 @@ All files and PDFs exist either exclusively on the client side, reside in server
- Detect and remove blank pages - Detect and remove blank pages
- Compare two PDFs and show differences in text - Compare two PDFs and show differences in text
- Add images to PDFs - Add images to PDFs
- Compress PDFs to decrease their filesize (using OCRMyPDF) - Compress PDFs to decrease their filesize (using qpdf)
- Extract images from PDF - Extract images from PDF
- Remove images from PDF - Remove images from PDF
- Extract images from scans - Extract images from scans
- Remove annotations - Remove annotations
- Add page numbers - Add page numbers
- Auto rename file by detecting PDF header text - Auto rename file by detecting PDF header text
- OCR on PDF (using OCRMyPDF) - OCR on PDF (using tesseract)
- PDF/A conversion (using OCRMyPDF) - PDF/A conversion (using libreoffice)
- Edit metadata - Edit metadata
- Flatten PDFs - Flatten PDFs
- Get all information on a PDF to view or export as JSON - 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 - Spring Boot + Thymeleaf
- [PDFBox](https://github.com/apache/pdfbox/tree/trunk) - [PDFBox](https://github.com/apache/pdfbox/tree/trunk)
- [LibreOffice](https://www.libreoffice.org/discover/libreoffice/) for advanced conversions - [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 - HTML, CSS, JavaScript
- Docker - Docker
- [PDF.js](https://github.com/mozilla/pdf.js) - [PDF.js](https://github.com/mozilla/pdf.js)

View File

@ -8,7 +8,7 @@ The 'Fat' container contains all those found in 'Full' with security jar along w
| Libre | | ✔️ | | Libre | | ✔️ |
| Python | | ✔️ | | Python | | ✔️ |
| OpenCV | | ✔️ | | OpenCV | | ✔️ |
| OCRmyPDF | | ✔️ | | qpdf | | ✔️ |
| Operation | Ultra-Lite | Full | | Operation | Ultra-Lite | Full |
| ---------------------- | ---------- | ---- | | ---------------------- | ---------- | ---- |

View File

@ -24,7 +24,8 @@ ext {
} }
group = "stirling.software" group = "stirling.software"
version = "0.34.0" version = "0.35.0"
java { java {
// 17 is lowest but we support and recommend 21 // 17 is lowest but we support and recommend 21

View File

@ -145,7 +145,7 @@ Feature: API Validation
And the response file should have extension ".pdf" And the response file should have extension ".pdf"
And the response file should have size greater than 100 And the response file should have size greater than 100
@compress @ghostscript @positive @compress @qpdf @positive
Scenario: Compress Scenario: Compress
Given I use an example file at "exampleFiles/ghost3.pdf" as parameter "fileInput" Given I use an example file at "exampleFiles/ghost3.pdf" as parameter "fileInput"
And the request data includes 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 extension ".pdf"
And the response file should have size greater than 100 And the response file should have size greater than 100
@compress @ghostscript @positive @compress @qpdf @positive
Scenario: Compress Scenario: Compress
Given I use an example file at "exampleFiles/ghost2.pdf" as parameter "fileInput" Given I use an example file at "exampleFiles/ghost2.pdf" as parameter "fileInput"
And the request data includes And the request data includes
@ -169,7 +169,7 @@ Feature: API Validation
And the response file should have size greater than 100 And the response file should have size greater than 100
@compress @ghostscript @positive @compress @qpdf @positive
Scenario: Compress Scenario: Compress
Given I use an example file at "exampleFiles/ghost1.pdf" as parameter "fileInput" Given I use an example file at "exampleFiles/ghost1.pdf" as parameter "fileInput"
And the request data includes And the request data includes

View File

@ -1,8 +1,8 @@
#!/bin/bash #!/bin/bash
translation_key="pdfToPDFA.credit" translation_key="pdfToPDFA.credit"
old_value="OCRmyPDF" old_value="qpdf"
new_value="ghostscript" new_value="liibreoffice"
for file in ../src/main/resources/messages_*.properties; do for file in ../src/main/resources/messages_*.properties; do
sed -i "/^$translation_key=/s/$old_value/$new_value/" "$file" sed -i "/^$translation_key=/s/$old_value/$new_value/" "$file"

View File

@ -188,7 +188,7 @@ public class EndpointConfiguration {
addEndpointToGroup("OpenCV", "extract-image-scans"); addEndpointToGroup("OpenCV", "extract-image-scans");
// LibreOffice // LibreOffice
addEndpointToGroup("LibreOffice", "repair"); addEndpointToGroup("qpdf", "repair");
addEndpointToGroup("LibreOffice", "file-to-pdf"); addEndpointToGroup("LibreOffice", "file-to-pdf");
addEndpointToGroup("LibreOffice", "pdf-to-word"); addEndpointToGroup("LibreOffice", "pdf-to-word");
addEndpointToGroup("LibreOffice", "pdf-to-presentation"); addEndpointToGroup("LibreOffice", "pdf-to-presentation");
@ -199,10 +199,11 @@ public class EndpointConfiguration {
// Unoconv // Unoconv
addEndpointToGroup("Unoconv", "file-to-pdf"); addEndpointToGroup("Unoconv", "file-to-pdf");
// OCRmyPDF // qpdf
addEndpointToGroup("OCRmyPDF", "compress-pdf"); addEndpointToGroup("qpdf", "compress-pdf");
addEndpointToGroup("OCRmyPDF", "pdf-to-pdfa"); addEndpointToGroup("qpdf", "pdf-to-pdfa");
addEndpointToGroup("OCRmyPDF", "ocr-pdf");
addEndpointToGroup("tesseract", "ocr-pdf");
// Java // Java
addEndpointToGroup("Java", "merge-pdfs"); addEndpointToGroup("Java", "merge-pdfs");
@ -248,10 +249,10 @@ public class EndpointConfiguration {
addEndpointToGroup("Javascript", "compare"); addEndpointToGroup("Javascript", "compare");
addEndpointToGroup("Javascript", "adjust-contrast"); addEndpointToGroup("Javascript", "adjust-contrast");
// Ghostscript dependent endpoints // qpdf dependent endpoints
addEndpointToGroup("Ghostscript", "compress-pdf"); addEndpointToGroup("qpdf", "compress-pdf");
addEndpointToGroup("Ghostscript", "pdf-to-pdfa"); addEndpointToGroup("qpdf", "pdf-to-pdfa");
addEndpointToGroup("Ghostscript", "repair"); addEndpointToGroup("qpdf", "repair");
// Weasyprint dependent endpoints // Weasyprint dependent endpoints
addEndpointToGroup("Weasyprint", "html-to-pdf"); addEndpointToGroup("Weasyprint", "html-to-pdf");

View File

@ -37,12 +37,13 @@ public class ExternalAppDepConfig {
private final Map<String, List<String>> commandToGroupMapping = private final Map<String, List<String>> commandToGroupMapping =
new HashMap<>() { new HashMap<>() {
{ {
put("gs", List.of("Ghostscript"));
put("soffice", List.of("LibreOffice")); put("soffice", List.of("LibreOffice"));
put("ocrmypdf", List.of("OCRmyPDF"));
put("weasyprint", List.of("Weasyprint")); put("weasyprint", List.of("Weasyprint"));
put("pdftohtml", List.of("Pdftohtml")); put("pdftohtml", List.of("Pdftohtml"));
put("unoconv", List.of("Unoconv")); 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() { public void checkDependencies() {
// Check core dependencies // Check core dependencies
checkDependencyAndDisableGroup("gs"); checkDependencyAndDisableGroup("tesseract");
checkDependencyAndDisableGroup("soffice"); checkDependencyAndDisableGroup("soffice");
checkDependencyAndDisableGroup("ocrmypdf"); checkDependencyAndDisableGroup("qpdf");
checkDependencyAndDisableGroup("weasyprint"); checkDependencyAndDisableGroup("weasyprint");
checkDependencyAndDisableGroup("pdftohtml"); checkDependencyAndDisableGroup("pdftohtml");
checkDependencyAndDisableGroup("unoconv"); checkDependencyAndDisableGroup("unoconv");

View File

@ -1,12 +1,13 @@
package stirling.software.SPDF.controller.api.converters; package stirling.software.SPDF.controller.api.converters;
import java.io.FileOutputStream; import java.io.File;
import java.io.OutputStream;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import java.util.List;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
@ -37,59 +38,90 @@ public class ConvertPDFToPDFA {
@Operation( @Operation(
summary = "Convert a PDF to a PDF/A", summary = "Convert a PDF to a PDF/A",
description = 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<byte[]> pdfToPdfA(@ModelAttribute PdfToPdfARequest request) public ResponseEntity<byte[]> pdfToPdfA(@ModelAttribute PdfToPdfARequest request)
throws Exception { throws Exception {
MultipartFile inputFile = request.getFileInput(); MultipartFile inputFile = request.getFileInput();
String outputFormat = request.getOutputFormat(); String outputFormat = request.getOutputFormat();
// Convert MultipartFile to byte[] // Validate input file type
byte[] pdfBytes = inputFile.getBytes(); if (!"application/pdf".equals(inputFile.getContentType())) {
logger.error("Invalid input file type: {}", inputFile.getContentType());
// Save the uploaded file to a temporary location throw new IllegalArgumentException("Input file must be a PDF");
Path tempInputFile = Files.createTempFile("input_", ".pdf");
try (OutputStream outputStream = new FileOutputStream(tempInputFile.toFile())) {
outputStream.write(pdfBytes);
} }
// Prepare the output file path // Get the original filename without extension
Path tempOutputFile = Files.createTempFile("output_", ".pdf"); String originalFileName = Filenames.toSimpleFileName(inputFile.getOriginalFilename());
if (originalFileName == null || originalFileName.trim().isEmpty()) {
// Prepare the ghostscript command originalFileName = "output.pdf";
List<String> 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());
} }
String baseFileName =
originalFileName.contains(".")
? originalFileName.substring(0, originalFileName.lastIndexOf('.'))
: originalFileName;
Path tempInputFile = null;
Path tempOutputDir = null;
byte[] fileBytes;
try { try {
byte[] pdfBytesOutput = Files.readAllBytes(tempOutputFile); // Save uploaded file to temp location
// Return the optimized PDF as a response tempInputFile = Files.createTempFile("input_", ".pdf");
String outputFilename = inputFile.transferTo(tempInputFile);
Filenames.toSimpleFileName(inputFile.getOriginalFilename())
.replaceFirst("[.][^.]+$", "") // Create temp output directory
+ "_PDFA.pdf"; 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<String> 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( return WebResponseUtils.bytesToWebResponse(
pdfBytesOutput, outputFilename, MediaType.APPLICATION_PDF); fileBytes, outputFilename, MediaType.APPLICATION_PDF);
} finally { } finally {
// Clean up the temporary files // Clean up temporary files
Files.deleteIfExists(tempInputFile); if (tempInputFile != null) {
Files.deleteIfExists(tempOutputFile); Files.deleteIfExists(tempInputFile);
}
if (tempOutputDir != null) {
FileUtils.deleteDirectory(tempOutputDir.toFile());
}
} }
} }
} }

View File

@ -20,7 +20,7 @@ import org.springframework.web.bind.annotation.RestController;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag; 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.model.api.extract.PDFFilePage;
import stirling.software.SPDF.pdf.FlexibleCSVWriter; import stirling.software.SPDF.pdf.FlexibleCSVWriter;
import technology.tabula.ObjectExtractor; import technology.tabula.ObjectExtractor;
@ -37,11 +37,15 @@ public class ExtractCSVController {
private static final Logger logger = LoggerFactory.getLogger(ExtractCSVController.class); private static final Logger logger = LoggerFactory.getLogger(ExtractCSVController.class);
@PostMapping(value = "/pdf/csv", consumes = "multipart/form-data") @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<String> PdfToCsv(@ModelAttribute PDFFilePage form) throws Exception { public ResponseEntity<String> PdfToCsv(@ModelAttribute PDFFilePage form) throws Exception {
StringWriter writer = new StringWriter(); StringWriter writer = new StringWriter();
try (PDDocument document = Loader.loadPDF(form.getFileInput().getBytes())) { 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); Writer csvWriter = new FlexibleCSVWriter(format);
SpreadsheetExtractionAlgorithm sea = new SpreadsheetExtractionAlgorithm(); SpreadsheetExtractionAlgorithm sea = new SpreadsheetExtractionAlgorithm();
try (ObjectExtractor extractor = new ObjectExtractor(document)) { try (ObjectExtractor extractor = new ObjectExtractor(document)) {
@ -56,8 +60,8 @@ public class ExtractCSVController {
ContentDisposition.builder("attachment") ContentDisposition.builder("attachment")
.filename( .filename(
form.getFileInput() form.getFileInput()
.getOriginalFilename() .getOriginalFilename()
.replaceFirst("[.][^.]+$", "") .replaceFirst("[.][^.]+$", "")
+ "_extracted.csv") + "_extracted.csv")
.build()); .build());
headers.setContentType(MediaType.parseMediaType("text/csv")); headers.setContentType(MediaType.parseMediaType("text/csv"));

View File

@ -10,7 +10,6 @@ import java.util.List;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
import org.apache.commons.io.FileUtils;
import org.apache.pdfbox.Loader; import org.apache.pdfbox.Loader;
import org.apache.pdfbox.cos.COSName; import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocument;
@ -53,6 +52,54 @@ public class CompressController {
this.pdfDocumentFactory = pdfDocumentFactory; 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") @PostMapping(consumes = "multipart/form-data", value = "/compress-pdf")
@Operation( @Operation(
summary = "Optimize PDF file", summary = "Optimize PDF file",
@ -75,209 +122,92 @@ public class CompressController {
autoMode = true; autoMode = true;
} }
// Save the uploaded file to a temporary location
Path tempInputFile = Files.createTempFile("input_", ".pdf"); Path tempInputFile = Files.createTempFile("input_", ".pdf");
inputFile.transferTo(tempInputFile.toFile()); inputFile.transferTo(tempInputFile.toFile());
long inputFileSize = Files.size(tempInputFile); long inputFileSize = Files.size(tempInputFile);
// Prepare the output file path
Path tempOutputFile = null; Path tempOutputFile = null;
byte[] pdfBytes; byte[] pdfBytes;
try { try {
tempOutputFile = Files.createTempFile("output_", ".pdf"); tempOutputFile = Files.createTempFile("output_", ".pdf");
// Determine initial optimization level based on expected size reduction, only if in
// autoMode
if (autoMode) { if (autoMode) {
double sizeReductionRatio = expectedOutputSize / (double) inputFileSize; double sizeReductionRatio = expectedOutputSize / (double) inputFileSize;
if (sizeReductionRatio > 0.7) { optimizeLevel = determineOptimizeLevel(sizeReductionRatio);
optimizeLevel = 1;
} else if (sizeReductionRatio > 0.5) {
optimizeLevel = 2;
} else if (sizeReductionRatio > 0.35) {
optimizeLevel = 3;
} else {
optimizeLevel = 3;
}
} }
boolean sizeMet = false; boolean sizeMet = false;
while (!sizeMet && optimizeLevel <= 4) { while (!sizeMet && optimizeLevel <= 9) {
// Prepare the Ghostscript command
List<String> command = new ArrayList<>();
command.add("gs");
command.add("-sDEVICE=pdfwrite");
command.add("-dCompatibilityLevel=1.5");
switch (optimizeLevel) { // Apply additional image compression for levels 6-9
case 1: if (optimizeLevel >= 6) {
command.add("-dPDFSETTINGS=/prepress"); // Calculate scale factor based on optimization level
break; double scaleFactor =
case 2: switch (optimizeLevel) {
command.add("-dPDFSETTINGS=/printer"); case 6 -> 0.9; // 90% of original size
break; case 7 -> 0.8; // 80% of original size
case 3: case 8 -> 0.65; // 70% of original size
command.add("-dPDFSETTINGS=/ebook"); case 9 -> 0.5; // 60% of original size
break; default -> 1.0;
case 4: };
command.add("-dPDFSETTINGS=/screen"); compressImagesInPDF(tempInputFile, scaleFactor);
break;
default:
command.add("-dPDFSETTINGS=/default");
} }
command.add("-dNOPAUSE"); // Run QPDF optimization
command.add("-dQUIET"); List<String> command = new ArrayList<>();
command.add("-dBATCH"); command.add("qpdf");
command.add("-sOutputFile=" + tempOutputFile.toString()); 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(tempInputFile.toString());
command.add(tempOutputFile.toString());
ProcessExecutorResult returnCode = ProcessExecutorResult returnCode = null;
ProcessExecutor.getInstance(ProcessExecutor.Processes.GHOSTSCRIPT) try {
.runCommandWithOutputHandling(command); 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); long outputFileSize = Files.size(tempOutputFile);
if (outputFileSize <= expectedOutputSize || !autoMode) { if (outputFileSize <= expectedOutputSize || !autoMode) {
sizeMet = true; sizeMet = true;
} else { } else {
// Increase optimization level for next iteration optimizeLevel =
optimizeLevel++; incrementOptimizeLevel(
if (autoMode && optimizeLevel > 4) { optimizeLevel, outputFileSize, expectedOutputSize);
logger.info("Skipping level 5 due to bad results in auto mode"); if (autoMode && optimizeLevel > 9) {
logger.info("Maximum compression level reached in auto mode");
sizeMet = true; 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 // Read the optimized PDF file
pdfBytes = Files.readAllBytes(tempOutputFile); pdfBytes = Files.readAllBytes(tempOutputFile);
Path finalFile = tempOutputFile; Path finalFile = tempOutputFile;
// Check if optimized file is larger than the original // Check if optimized file is larger than the original
if (pdfBytes.length > inputFileSize) { if (pdfBytes.length > inputFileSize) {
// Log the occurrence
logger.warn( logger.warn(
"Optimized file is larger than the original. Returning the original file instead."); "Optimized file is larger than the original. Returning the original file instead.");
// Read the original file again
finalFile = tempInputFile; finalFile = tempInputFile;
} }
// Return the optimized PDF as a response
String outputFilename = String outputFilename =
Filenames.toSimpleFileName(inputFile.getOriginalFilename()) Filenames.toSimpleFileName(inputFile.getOriginalFilename())
.replaceFirst("[.][^.]+$", "") .replaceFirst("[.][^.]+$", "")
@ -286,10 +216,31 @@ public class CompressController {
pdfDocumentFactory.load(finalFile.toFile()), outputFilename); pdfDocumentFactory.load(finalFile.toFile()), outputFilename);
} finally { } finally {
// Clean up the temporary files
// deleted by multipart file handler deu to transferTo?
// Files.deleteIfExists(tempInputFile);
Files.deleteIfExists(tempOutputFile); 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);
}
} }

View File

@ -58,7 +58,7 @@ public class FakeScanControllerWIP {
@Operation( @Operation(
summary = "Repair a PDF file", summary = "Repair a PDF file",
description = 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<byte[]> fakeScan(@ModelAttribute PDFFile request) throws IOException { public ResponseEntity<byte[]> fakeScan(@ModelAttribute PDFFile request) throws IOException {
MultipartFile inputFile = request.getFileInput(); MultipartFile inputFile = request.getFileInput();

View File

@ -1,19 +1,31 @@
package stirling.software.SPDF.controller.api.misc; 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.File;
import java.io.FileOutputStream; import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream; 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.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; 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.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile; 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 io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.model.ApplicationProperties; import stirling.software.SPDF.model.ApplicationProperties;
import stirling.software.SPDF.model.api.misc.ProcessPdfWithOcrRequest; import stirling.software.SPDF.model.api.misc.ProcessPdfWithOcrRequest;
import stirling.software.SPDF.service.CustomPDDocumentFactory; 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 @RestController
@RequestMapping("/api/v1/misc") @RequestMapping("/api/v1/misc")
@Tag(name = "Misc", description = "Miscellaneous APIs") @Tag(name = "Misc", description = "Miscellaneous APIs")
@Slf4j
public class OCRController { 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<String> getAvailableTesseractLanguages() { public List<String> getAvailableTesseractLanguages() {
String tessdataDir = applicationProperties.getSystem().getTessdataDir(); String tessdataDir = applicationProperties.getSystem().getTessdataDir();
File[] files = new File(tessdataDir).listFiles(); File[] files = new File(tessdataDir).listFiles();
@ -54,196 +71,161 @@ public class OCRController {
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
private final CustomPDDocumentFactory pdfDocumentFactory;
@Autowired
public OCRController(CustomPDDocumentFactory pdfDocumentFactory) {
this.pdfDocumentFactory = pdfDocumentFactory;
}
@PostMapping(consumes = "multipart/form-data", value = "/ocr-pdf") @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<byte[]> processPdfWithOCR( public ResponseEntity<byte[]> processPdfWithOCR(
@ModelAttribute ProcessPdfWithOcrRequest request) @ModelAttribute ProcessPdfWithOcrRequest request)
throws IOException, InterruptedException { throws IOException, InterruptedException {
MultipartFile inputFile = request.getFileInput(); MultipartFile inputFile = request.getFileInput();
List<String> selectedLanguages = request.getLanguages(); List<String> languages = request.getLanguages();
Boolean sidecar = request.isSidecar();
Boolean deskew = request.isDeskew();
Boolean clean = request.isClean();
Boolean cleanFinal = request.isCleanFinal();
String ocrType = request.getOcrType(); 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)) { Path tempDir = Files.createTempDirectory("ocr_process");
throw new IOException("ocrRenderType wrong"); 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 Files.createDirectories(tempOutputDir);
List<String> availableLanguages = getAvailableTesseractLanguages(); Files.createDirectories(tempImagesDir);
// 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;
try { try {
// Save input file
inputFile.transferTo(tempInputFile.toFile()); inputFile.transferTo(tempInputFile.toFile());
PDFMergerUtility merger = new PDFMergerUtility();
merger.setDestinationFileName(finalOutputFile.toString());
// Run OCR Command try (PDDocument document = pdfDocumentFactory.load(tempInputFile.toFile())) {
String languageOption = String.join("+", selectedLanguages); PDFRenderer pdfRenderer = new PDFRenderer(document);
int pageCount = document.getNumberOfPages();
List<String> command = for (int pageNum = 0; pageNum < pageCount; pageNum++) {
new ArrayList<>( PDPage page = document.getPage(pageNum);
Arrays.asList( boolean hasText = false;
"ocrmypdf",
"--verbose",
"2",
"--output-type",
"pdf",
"--pdf-renderer",
ocrRenderType));
if (sidecar != null && sidecar) { // Check for existing text
sidecarTextPath = Files.createTempFile("sidecar", ".txt"); try (PDDocument tempDoc = new PDDocument()) {
command.add("--sidecar"); tempDoc.addPage(page);
command.add(sidecarTextPath.toString()); PDFTextStripper stripper = new PDFTextStripper();
} hasText = !stripper.getText(tempDoc).trim().isEmpty();
}
if (deskew != null && deskew) { boolean shouldOcr =
command.add("--deskew"); switch (ocrType) {
} case "skip-text" -> !hasText;
if (clean != null && clean) { case "force-ocr" -> true;
command.add("--clean"); default -> true;
} };
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)) {
} Path pageOutputPath =
} tempOutputDir.resolve(String.format("page_%d.pdf", pageNum));
command.addAll( if (shouldOcr) {
Arrays.asList( // Convert page to image
"--language", BufferedImage image = pdfRenderer.renderImageWithDPI(pageNum, 300);
languageOption, Path imagePath =
tempInputFile.toString(), tempImagesDir.resolve(String.format("page_%d.png", pageNum));
tempOutputFile.toString())); ImageIO.write(image, "png", imagePath.toFile());
// Run CLI command // Build OCR command
ProcessExecutorResult result = List<String> command = new ArrayList<>();
ProcessExecutor.getInstance(ProcessExecutor.Processes.OCR_MY_PDF) command.add("tesseract");
.runCommandWithOutputHandling(command); command.add(imagePath.toString());
if (result.getRc() != 0 command.add(
&& result.getMessages().contains("multiprocessing/synchronize.py") tempOutputDir
&& result.getMessages() .resolve(String.format("page_%d", pageNum))
.contains("OSError: [Errno 38] Function not implemented")) { .toString());
command.add("--jobs"); command.add("-l");
command.add("1"); command.add(String.join("+", languages));
result = command.add("pdf"); // Always output PDF
ProcessExecutor.getInstance(ProcessExecutor.Processes.OCR_MY_PDF)
.runCommandWithOutputHandling(command);
}
// Remove images from the OCR processed PDF if the flag is set to true ProcessBuilder pb = new ProcessBuilder(command);
if (removeImagesAfter != null && removeImagesAfter) { Process process = pb.start();
Path tempPdfWithoutImages = Files.createTempFile("output_", "_no_images.pdf");
List<String> gsCommand = // Capture any error output
Arrays.asList( try (BufferedReader reader =
"gs", new BufferedReader(
"-sDEVICE=pdfwrite", new InputStreamReader(process.getErrorStream()))) {
"-dFILTERIMAGE", String line;
"-o", while ((line = BoundedLineReader.readLine(reader, 5_000_000)) != null) {
tempPdfWithoutImages.toString(), log.debug("Tesseract: {}", line);
tempOutputFile.toString()); }
}
ProcessExecutor.getInstance(ProcessExecutor.Processes.GHOSTSCRIPT) int exitCode = process.waitFor();
.runCommandWithOutputHandling(gsCommand); if (exitCode != 0) {
tempOutputFile = tempPdfWithoutImages; throw new RuntimeException(
} "Tesseract failed with exit code: " + exitCode);
// Read the OCR processed PDF file }
byte[] pdfBytes = pdfDocumentFactory.loadToBytes(tempOutputFile.toFile());
// Return the OCR processed PDF as a response // Add OCR'd PDF to merger
String outputFilename = merger.addSource(pageOutputPath.toFile());
Filenames.toSimpleFileName(inputFile.getOriginalFilename()) } else {
.replaceFirst("[.][^.]+$", "") // Save original page without OCR
+ "_OCR.pdf"; try (PDDocument pageDoc = new PDDocument()) {
pageDoc.addPage(page);
if (sidecar != null && sidecar) { pageDoc.save(pageOutputPath.toFile());
// Create a zip file containing both the PDF and the text file merger.addSource(pageOutputPath.toFile());
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);
} }
} }
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 { } finally {
// Clean up the temporary files // Clean up temporary files
Files.deleteIfExists(tempOutputFile); deleteDirectory(tempDir);
// Comment out as transferTo makes multipart handle cleanup }
// Files.deleteIfExists(tempInputFile); }
if (sidecarTextPath != null) {
Files.deleteIfExists(sidecarTextPath); 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());
} }
} }
} }

View File

@ -44,7 +44,7 @@ public class RepairController {
@Operation( @Operation(
summary = "Repair a PDF file", summary = "Repair a PDF file",
description = 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<byte[]> repairPdf(@ModelAttribute PDFFile request) public ResponseEntity<byte[]> repairPdf(@ModelAttribute PDFFile request)
throws IOException, InterruptedException { throws IOException, InterruptedException {
MultipartFile inputFile = request.getFileInput(); MultipartFile inputFile = request.getFileInput();
@ -56,14 +56,15 @@ public class RepairController {
try { try {
List<String> command = new ArrayList<>(); List<String> command = new ArrayList<>();
command.add("gs"); command.add("qpdf");
command.add("-o"); command.add("--replace-input"); // Automatically fixes problems it can
command.add(tempOutputFile.toString()); command.add("--qdf"); // Linearizes and normalizes PDF structure
command.add("-sDEVICE=pdfwrite"); command.add("--object-streams=disable"); // Can help with some corruptions
command.add(tempInputFile.toString()); command.add(tempInputFile.toString());
command.add(tempOutputFile.toString());
ProcessExecutorResult returnCode = ProcessExecutorResult returnCode =
ProcessExecutor.getInstance(ProcessExecutor.Processes.GHOSTSCRIPT) ProcessExecutor.getInstance(ProcessExecutor.Processes.QPDF)
.runCommandWithOutputHandling(command); .runCommandWithOutputHandling(command);
// Read the optimized PDF file // Read the optimized PDF file

View File

@ -98,10 +98,10 @@ public class CertSignController {
public CreateSignature(KeyStore keystore, char[] pin) public CreateSignature(KeyStore keystore, char[] pin)
throws KeyStoreException, throws KeyStoreException,
UnrecoverableKeyException, UnrecoverableKeyException,
NoSuchAlgorithmException, NoSuchAlgorithmException,
IOException, IOException,
CertificateException { CertificateException {
super(keystore, pin); super(keystore, pin);
ClassPathResource resource = new ClassPathResource("static/images/signature.png"); ClassPathResource resource = new ClassPathResource("static/images/signature.png");
try (InputStream is = resource.getInputStream()) { try (InputStream is = resource.getInputStream()) {
@ -160,7 +160,8 @@ public class CertSignController {
extState.setNonStrokingAlphaConstant(0.5f); extState.setNonStrokingAlphaConstant(0.5f);
cs.setGraphicsStateParameters(extState); cs.setGraphicsStateParameters(extState);
cs.transform(Matrix.getScaleInstance(0.08f, 0.08f)); 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.drawImage(img, 100, 0);
cs.restoreGraphicsState(); cs.restoreGraphicsState();
} }
@ -208,7 +209,10 @@ public class CertSignController {
} }
@PostMapping(consumes = "multipart/form-data", value = "/cert-sign") @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<byte[]> signPDFWithCert(@ModelAttribute SignPDFWithCertRequest request) public ResponseEntity<byte[]> signPDFWithCert(@ModelAttribute SignPDFWithCertRequest request)
throws Exception { throws Exception {
MultipartFile pdf = request.getFileInput(); MultipartFile pdf = request.getFileInput();
@ -238,7 +242,7 @@ public class CertSignController {
PrivateKey privateKey = getPrivateKeyFromPEM(privateKeyFile.getBytes(), password); PrivateKey privateKey = getPrivateKeyFromPEM(privateKeyFile.getBytes(), password);
Certificate cert = (Certificate) getCertificateFromPEM(certFile.getBytes()); Certificate cert = (Certificate) getCertificateFromPEM(certFile.getBytes());
ks.setKeyEntry( ks.setKeyEntry(
"alias", privateKey, password.toCharArray(), new Certificate[] { cert }); "alias", privateKey, password.toCharArray(), new Certificate[] {cert});
break; break;
case "PKCS12": case "PKCS12":
ks = KeyStore.getInstance("PKCS12"); ks = KeyStore.getInstance("PKCS12");
@ -310,19 +314,22 @@ public class CertSignController {
private PrivateKey getPrivateKeyFromPEM(byte[] pemBytes, String password) private PrivateKey getPrivateKeyFromPEM(byte[] pemBytes, String password)
throws IOException, OperatorCreationException, PKCSException { 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(); Object pemObject = pemParser.readObject();
JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC"); JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC");
PrivateKeyInfo pkInfo; PrivateKeyInfo pkInfo;
if (pemObject instanceof PKCS8EncryptedPrivateKeyInfo) { if (pemObject instanceof PKCS8EncryptedPrivateKeyInfo) {
InputDecryptorProvider decProv = new JceOpenSSLPKCS8DecryptorProviderBuilder() InputDecryptorProvider decProv =
.build(password.toCharArray()); new JceOpenSSLPKCS8DecryptorProviderBuilder().build(password.toCharArray());
pkInfo = ((PKCS8EncryptedPrivateKeyInfo) pemObject).decryptPrivateKeyInfo(decProv); pkInfo = ((PKCS8EncryptedPrivateKeyInfo) pemObject).decryptPrivateKeyInfo(decProv);
} else if (pemObject instanceof PEMEncryptedKeyPair) { } else if (pemObject instanceof PEMEncryptedKeyPair) {
PEMDecryptorProvider decProv = new JcePEMDecryptorProviderBuilder().build(password.toCharArray()); PEMDecryptorProvider decProv =
pkInfo = ((PEMEncryptedKeyPair) pemObject) new JcePEMDecryptorProviderBuilder().build(password.toCharArray());
.decryptKeyPair(decProv) pkInfo =
.getPrivateKeyInfo(); ((PEMEncryptedKeyPair) pemObject)
.decryptKeyPair(decProv)
.getPrivateKeyInfo();
} else { } else {
pkInfo = ((PEMKeyPair) pemObject).getPrivateKeyInfo(); pkInfo = ((PEMKeyPair) pemObject).getPrivateKeyInfo();
} }

View File

@ -55,6 +55,11 @@ public class HomeWebController {
return "licenses"; return "licenses";
} }
@GetMapping("/releases")
public String getReleaseNotes(Model model) {
return "releases";
}
@GetMapping("/") @GetMapping("/")
public String home(Model model) { public String home(Model model) {
model.addAttribute("currentPage", "home"); model.addAttribute("currentPage", "home");

View File

@ -320,12 +320,20 @@ public class ApplicationProperties {
public static class SessionLimit { public static class SessionLimit {
private int libreOfficeSessionLimit; private int libreOfficeSessionLimit;
private int pdfToHtmlSessionLimit; private int pdfToHtmlSessionLimit;
private int ocrMyPdfSessionLimit;
private int pythonOpenCvSessionLimit; private int pythonOpenCvSessionLimit;
private int ghostScriptSessionLimit;
private int weasyPrintSessionLimit; private int weasyPrintSessionLimit;
private int installAppSessionLimit; private int installAppSessionLimit;
private int calibreSessionLimit; 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() { public int getLibreOfficeSessionLimit() {
return libreOfficeSessionLimit > 0 ? libreOfficeSessionLimit : 1; return libreOfficeSessionLimit > 0 ? libreOfficeSessionLimit : 1;
@ -335,18 +343,10 @@ public class ApplicationProperties {
return pdfToHtmlSessionLimit > 0 ? pdfToHtmlSessionLimit : 1; return pdfToHtmlSessionLimit > 0 ? pdfToHtmlSessionLimit : 1;
} }
public int getOcrMyPdfSessionLimit() {
return ocrMyPdfSessionLimit > 0 ? ocrMyPdfSessionLimit : 2;
}
public int getPythonOpenCvSessionLimit() { public int getPythonOpenCvSessionLimit() {
return pythonOpenCvSessionLimit > 0 ? pythonOpenCvSessionLimit : 8; return pythonOpenCvSessionLimit > 0 ? pythonOpenCvSessionLimit : 8;
} }
public int getGhostScriptSessionLimit() {
return ghostScriptSessionLimit > 0 ? ghostScriptSessionLimit : 16;
}
public int getWeasyPrintSessionLimit() { public int getWeasyPrintSessionLimit() {
return weasyPrintSessionLimit > 0 ? weasyPrintSessionLimit : 16; return weasyPrintSessionLimit > 0 ? weasyPrintSessionLimit : 16;
} }
@ -364,12 +364,20 @@ public class ApplicationProperties {
public static class TimeoutMinutes { public static class TimeoutMinutes {
private long libreOfficeTimeoutMinutes; private long libreOfficeTimeoutMinutes;
private long pdfToHtmlTimeoutMinutes; private long pdfToHtmlTimeoutMinutes;
private long ocrMyPdfTimeoutMinutes;
private long pythonOpenCvTimeoutMinutes; private long pythonOpenCvTimeoutMinutes;
private long ghostScriptTimeoutMinutes;
private long weasyPrintTimeoutMinutes; private long weasyPrintTimeoutMinutes;
private long installAppTimeoutMinutes; private long installAppTimeoutMinutes;
private long calibreTimeoutMinutes; 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() { public long getLibreOfficeTimeoutMinutes() {
return libreOfficeTimeoutMinutes > 0 ? libreOfficeTimeoutMinutes : 30; return libreOfficeTimeoutMinutes > 0 ? libreOfficeTimeoutMinutes : 30;
@ -379,18 +387,10 @@ public class ApplicationProperties {
return pdfToHtmlTimeoutMinutes > 0 ? pdfToHtmlTimeoutMinutes : 20; return pdfToHtmlTimeoutMinutes > 0 ? pdfToHtmlTimeoutMinutes : 20;
} }
public long getOcrMyPdfTimeoutMinutes() {
return ocrMyPdfTimeoutMinutes > 0 ? ocrMyPdfTimeoutMinutes : 30;
}
public long getPythonOpenCvTimeoutMinutes() { public long getPythonOpenCvTimeoutMinutes() {
return pythonOpenCvTimeoutMinutes > 0 ? pythonOpenCvTimeoutMinutes : 30; return pythonOpenCvTimeoutMinutes > 0 ? pythonOpenCvTimeoutMinutes : 30;
} }
public long getGhostScriptTimeoutMinutes() {
return ghostScriptTimeoutMinutes > 0 ? ghostScriptTimeoutMinutes : 30;
}
public long getWeasyPrintTimeoutMinutes() { public long getWeasyPrintTimeoutMinutes() {
return weasyPrintTimeoutMinutes > 0 ? weasyPrintTimeoutMinutes : 30; return weasyPrintTimeoutMinutes > 0 ? weasyPrintTimeoutMinutes : 30;
} }

View File

@ -18,4 +18,15 @@ public class OptimizePdfRequest extends PDFFile {
@Schema(description = "The expected output size, e.g. '100MB', '25KB', etc.") @Schema(description = "The expected output size, e.g. '100MB', '25KB', etc.")
private String expectedOutputSize; 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;
} }

View File

@ -15,18 +15,6 @@ public class ProcessPdfWithOcrRequest extends PDFFile {
@Schema(description = "List of languages to use in OCR processing") @Schema(description = "List of languages to use in OCR processing")
private List<String> languages; private List<String> 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( @Schema(
description = "Specify the OCR type, e.g., 'skip-text', 'force-ocr', or 'Normal'", description = "Specify the OCR type, e.g., 'skip-text', 'force-ocr', or 'Normal'",
allowableValues = {"skip-text", "force-ocr", "Normal"}) allowableValues = {"skip-text", "force-ocr", "Normal"})
@ -37,7 +25,4 @@ public class ProcessPdfWithOcrRequest extends PDFFile {
allowableValues = {"hocr", "sandwich"}, allowableValues = {"hocr", "sandwich"},
defaultValue = "hocr") defaultValue = "hocr")
private String ocrRenderType = "hocr"; private String ocrRenderType = "hocr";
@Schema(description = "Remove images from the output PDF if set to true")
private boolean removeImagesAfter;
} }

View File

@ -34,17 +34,15 @@ public class MetricsAggregatorService {
counter -> { counter -> {
String method = counter.getId().getTag("method"); String method = counter.getId().getTag("method");
String uri = counter.getId().getTag("uri"); String uri = counter.getId().getTag("uri");
// Skip if either method or uri is null // Skip if either method or uri is null
if (method == null || uri == null) { if (method == null || uri == null) {
return; return;
} }
String key = String.format( String key =
"http_requests_%s_%s", String.format(
method, "http_requests_%s_%s", method, uri.replace("/", "_"));
uri.replace("/", "_")
);
double currentCount = counter.count(); double currentCount = counter.count();
double lastCount = lastSentMetrics.getOrDefault(key, 0.0); double lastCount = lastSentMetrics.getOrDefault(key, 0.0);

View File

@ -31,7 +31,7 @@ public class PostHogService {
private final ApplicationProperties applicationProperties; private final ApplicationProperties applicationProperties;
private final UserServiceInterface userService; private final UserServiceInterface userService;
private final Environment env; private final Environment env;
@Autowired @Autowired
public PostHogService( public PostHogService(
PostHog postHog, PostHog postHog,
@ -71,16 +71,16 @@ public class PostHogService {
Map<String, Object> metrics = new HashMap<>(); Map<String, Object> metrics = new HashMap<>();
try { try {
//Application version // Application version
metrics.put("app_version", appVersion); metrics.put("app_version", appVersion);
String deploymentType = "JAR"; // default String deploymentType = "JAR"; // default
if ("true".equalsIgnoreCase(env.getProperty("BROWSER_OPEN"))) { if ("true".equalsIgnoreCase(env.getProperty("BROWSER_OPEN"))) {
deploymentType = "EXE"; deploymentType = "EXE";
} else if (isRunningInDocker()) { } else if (isRunningInDocker()) {
deploymentType = "DOCKER"; deploymentType = "DOCKER";
} }
metrics.put("deployment_type", deploymentType); metrics.put("deployment_type", deploymentType);
// System info // System info
metrics.put("os_name", System.getProperty("os.name")); metrics.put("os_name", System.getProperty("os.name"));
metrics.put("os_version", System.getProperty("os.version")); metrics.put("os_version", System.getProperty("os.version"));

View File

@ -29,12 +29,12 @@ public class ProcessExecutor {
public enum Processes { public enum Processes {
LIBRE_OFFICE, LIBRE_OFFICE,
PDFTOHTML, PDFTOHTML,
OCR_MY_PDF,
PYTHON_OPENCV, PYTHON_OPENCV,
GHOSTSCRIPT,
WEASYPRINT, WEASYPRINT,
INSTALL_APP, INSTALL_APP,
CALIBRE CALIBRE,
TESSERACT,
QPDF
} }
private static final Map<Processes, ProcessExecutor> instances = new ConcurrentHashMap<>(); private static final Map<Processes, ProcessExecutor> instances = new ConcurrentHashMap<>();
@ -59,21 +59,11 @@ public class ProcessExecutor {
.getProcessExecutor() .getProcessExecutor()
.getSessionLimit() .getSessionLimit()
.getPdfToHtmlSessionLimit(); .getPdfToHtmlSessionLimit();
case OCR_MY_PDF ->
applicationProperties
.getProcessExecutor()
.getSessionLimit()
.getOcrMyPdfSessionLimit();
case PYTHON_OPENCV -> case PYTHON_OPENCV ->
applicationProperties applicationProperties
.getProcessExecutor() .getProcessExecutor()
.getSessionLimit() .getSessionLimit()
.getPythonOpenCvSessionLimit(); .getPythonOpenCvSessionLimit();
case GHOSTSCRIPT ->
applicationProperties
.getProcessExecutor()
.getSessionLimit()
.getGhostScriptSessionLimit();
case WEASYPRINT -> case WEASYPRINT ->
applicationProperties applicationProperties
.getProcessExecutor() .getProcessExecutor()
@ -84,6 +74,16 @@ public class ProcessExecutor {
.getProcessExecutor() .getProcessExecutor()
.getSessionLimit() .getSessionLimit()
.getInstallAppSessionLimit(); .getInstallAppSessionLimit();
case TESSERACT ->
applicationProperties
.getProcessExecutor()
.getSessionLimit()
.getTesseractSessionLimit();
case QPDF ->
applicationProperties
.getProcessExecutor()
.getSessionLimit()
.getQpdfSessionLimit();
case CALIBRE -> case CALIBRE ->
applicationProperties applicationProperties
.getProcessExecutor() .getProcessExecutor()
@ -103,21 +103,11 @@ public class ProcessExecutor {
.getProcessExecutor() .getProcessExecutor()
.getTimeoutMinutes() .getTimeoutMinutes()
.getPdfToHtmlTimeoutMinutes(); .getPdfToHtmlTimeoutMinutes();
case OCR_MY_PDF ->
applicationProperties
.getProcessExecutor()
.getTimeoutMinutes()
.getOcrMyPdfTimeoutMinutes();
case PYTHON_OPENCV -> case PYTHON_OPENCV ->
applicationProperties applicationProperties
.getProcessExecutor() .getProcessExecutor()
.getTimeoutMinutes() .getTimeoutMinutes()
.getPythonOpenCvTimeoutMinutes(); .getPythonOpenCvTimeoutMinutes();
case GHOSTSCRIPT ->
applicationProperties
.getProcessExecutor()
.getTimeoutMinutes()
.getGhostScriptTimeoutMinutes();
case WEASYPRINT -> case WEASYPRINT ->
applicationProperties applicationProperties
.getProcessExecutor() .getProcessExecutor()
@ -128,6 +118,16 @@ public class ProcessExecutor {
.getProcessExecutor() .getProcessExecutor()
.getTimeoutMinutes() .getTimeoutMinutes()
.getInstallAppTimeoutMinutes(); .getInstallAppTimeoutMinutes();
case TESSERACT ->
applicationProperties
.getProcessExecutor()
.getTimeoutMinutes()
.getTesseractTimeoutMinutes();
case QPDF ->
applicationProperties
.getProcessExecutor()
.getTimeoutMinutes()
.getQpdfTimeoutMinutes();
case CALIBRE -> case CALIBRE ->
applicationProperties applicationProperties
.getProcessExecutor() .getProcessExecutor()

View File

@ -868,7 +868,7 @@ ocr.selectText.10=وضع التعرف الضوئي على الحروف
ocr.selectText.11=إزالة الصور بعد التعرف الضوئي على الحروف (يزيل كل الصور، يكون مفيدًا فقط إذا كان جزءًا من خطوة التحويل) ocr.selectText.11=إزالة الصور بعد التعرف الضوئي على الحروف (يزيل كل الصور، يكون مفيدًا فقط إذا كان جزءًا من خطوة التحويل)
ocr.selectText.12=نوع العرض (متقدم) ocr.selectText.12=نوع العرض (متقدم)
ocr.help=يرجى قراءة هذه الوثائق حول كيفية استخدام هذا للغات أخرى و/أو الاستخدام ليس في Docker ocr.help=يرجى قراءة هذه الوثائق حول كيفية استخدام هذا للغات أخرى و/أو الاستخدام ليس في Docker
ocr.credit=تستخدم هذه الخدمة OCRmyPDF و Tesseract للتعرف الضوئي على الحروف. ocr.credit=تستخدم هذه الخدمة qpdf و Tesseract للتعرف الضوئي على الحروف.
ocr.submit=معالجة PDF باستخدام OCR ocr.submit=معالجة PDF باستخدام OCR
@ -892,7 +892,7 @@ fileToPDF.submit=تحويل إلى PDF
#compress #compress
compress.title=ضغط compress.title=ضغط
compress.header=ضغط ملف PDF compress.header=ضغط ملف PDF
compress.credit=تستخدم هذه الخدمة OCRmyPDF لضغط / تحسين PDF. compress.credit=تستخدم هذه الخدمة qpdf لضغط / تحسين PDF.
compress.selectText.1=الوضع اليدوي - من 1 إلى 4 compress.selectText.1=الوضع اليدوي - من 1 إلى 4
compress.selectText.2=مستوى التحسين: compress.selectText.2=مستوى التحسين:
compress.selectText.3=4 (رهيب للصور النصية) compress.selectText.3=4 (رهيب للصور النصية)
@ -1112,7 +1112,7 @@ changeMetadata.submit=تغيير
#pdfToPDFA #pdfToPDFA
pdfToPDFA.title=PDF إلى PDF/A pdfToPDFA.title=PDF إلى PDF/A
pdfToPDFA.header=PDF إلى PDF/A pdfToPDFA.header=PDF إلى PDF/A
pdfToPDFA.credit=تستخدم هذه الخدمة ghostscript لتحويل PDF/A. pdfToPDFA.credit=تستخدم هذه الخدمة qpdf لتحويل PDF/A.
pdfToPDFA.submit=تحويل pdfToPDFA.submit=تحويل
pdfToPDFA.tip=لا يعمل حاليًا لمدخلات متعددة في وقت واحد pdfToPDFA.tip=لا يعمل حاليًا لمدخلات متعددة في وقت واحد
pdfToPDFA.outputFormat=تنسيق الإخراج pdfToPDFA.outputFormat=تنسيق الإخراج

View File

@ -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.11=Remove images after OCR (Removes ALL images, only useful if part of conversion step)
ocr.selectText.12=Render Type (Advanced) 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.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 ocr.submit=Process PDF with OCR
@ -892,7 +892,7 @@ fileToPDF.submit=PDF-ə Çevir
#compress #compress
compress.title=Compress compress.title=Compress
compress.header=Compress PDF 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.1=Manual Mode - From 1 to 4
compress.selectText.2=Optimization level: compress.selectText.2=Optimization level:
compress.selectText.3=4 (Terrible for text images) compress.selectText.3=4 (Terrible for text images)
@ -1112,7 +1112,7 @@ changeMetadata.submit=Change
#pdfToPDFA #pdfToPDFA
pdfToPDFA.title=PDF To PDF/A pdfToPDFA.title=PDF To PDF/A
pdfToPDFA.header=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.submit=Convert
pdfToPDFA.tip=Currently does not work for multiple inputs at once pdfToPDFA.tip=Currently does not work for multiple inputs at once
pdfToPDFA.outputFormat=Output format pdfToPDFA.outputFormat=Output format

View File

@ -868,7 +868,7 @@ ocr.selectText.10=OCR режим
ocr.selectText.11=Премахване на изображения след OCR (Премахва ВСИЧКИ изображения, полезно само ако е част от стъпката на преобразуване) ocr.selectText.11=Премахване на изображения след OCR (Премахва ВСИЧКИ изображения, полезно само ако е част от стъпката на преобразуване)
ocr.selectText.12=Тип изобразяване (Разширен) ocr.selectText.12=Тип изобразяване (Разширен)
ocr.help=Моля, прочетете тази документация за това как да използвате това за други езици и/или да не използвате в docker ocr.help=Моля, прочетете тази документация за това как да използвате това за други езици и/или да не използвате в docker
ocr.credit=Тази услуга използва OCRmyPDF и Tesseract за OCR. ocr.credit=Тази услуга използва qpdf и Tesseract за OCR.
ocr.submit=Обработка на PDF чрез OCR ocr.submit=Обработка на PDF чрез OCR
@ -892,7 +892,7 @@ fileToPDF.submit=Преобразуване към PDF
#compress #compress
compress.title=Компресиране compress.title=Компресиране
compress.header=Компресиране на PDF compress.header=Компресиране на PDF
compress.credit=Тази услуга използва Ghostscript за PDF компресиране/оптимизиране. compress.credit=Тази услуга използва qpdf за PDF компресиране/оптимизиране.
compress.selectText.1=Ръчен режим - от 1 до 4 compress.selectText.1=Ръчен режим - от 1 до 4
compress.selectText.2=Ниво на оптимизация: compress.selectText.2=Ниво на оптимизация:
compress.selectText.3=4 (Ужасно за текстови изображения) compress.selectText.3=4 (Ужасно за текстови изображения)
@ -1112,7 +1112,7 @@ changeMetadata.submit=Промени
#pdfToPDFA #pdfToPDFA
pdfToPDFA.title=PDF към PDF/A pdfToPDFA.title=PDF към PDF/A
pdfToPDFA.header=PDF към PDF/A pdfToPDFA.header=PDF към PDF/A
pdfToPDFA.credit=Тази услуга използва ghostscript за PDF/A преобразуване. pdfToPDFA.credit=Тази услуга използва qpdf за PDF/A преобразуване.
pdfToPDFA.submit=Преобразуване pdfToPDFA.submit=Преобразуване
pdfToPDFA.tip=В момента не работи за няколко входа наведнъж pdfToPDFA.tip=В момента не работи за няколко входа наведнъж
pdfToPDFA.outputFormat=Изходен формат pdfToPDFA.outputFormat=Изходен формат

View File

@ -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.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.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.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 ocr.submit=Processa PDF amb OCR
@ -892,7 +892,7 @@ fileToPDF.submit=Converteix a PDF
#compress #compress
compress.title=Comprimir compress.title=Comprimir
compress.header=Comprimir PDF 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.1=Mode manual: de l'1 al 4
compress.selectText.2=Nivell d'optimització: compress.selectText.2=Nivell d'optimització:
compress.selectText.3=4 (terrible per a imatges de text) compress.selectText.3=4 (terrible per a imatges de text)
@ -1112,7 +1112,7 @@ changeMetadata.submit=Canvia
#pdfToPDFA #pdfToPDFA
pdfToPDFA.title=PDF a PDF/A pdfToPDFA.title=PDF a PDF/A
pdfToPDFA.header=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.submit=Converteix
pdfToPDFA.tip=Actualment no funciona per a múltiples entrades al mateix temps pdfToPDFA.tip=Actualment no funciona per a múltiples entrades al mateix temps
pdfToPDFA.outputFormat=Format de sortida pdfToPDFA.outputFormat=Format de sortida

View File

@ -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.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.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.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 ocr.submit=Zpracovat PDF s OCR
@ -892,7 +892,7 @@ fileToPDF.submit=Převést na PDF
#compress #compress
compress.title=Komprese compress.title=Komprese
compress.header=Komprimovat PDF 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.1=Ruční režim - Od 1 do 4
compress.selectText.2=Úroveň optimalizace: compress.selectText.2=Úroveň optimalizace:
compress.selectText.3=4 (Hrozné pro textové obrázky) compress.selectText.3=4 (Hrozné pro textové obrázky)
@ -1112,7 +1112,7 @@ changeMetadata.submit=Změnit
#pdfToPDFA #pdfToPDFA
pdfToPDFA.title=PDF na PDF/A pdfToPDFA.title=PDF na PDF/A
pdfToPDFA.header=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.submit=Převést
pdfToPDFA.tip=V současné době nepracuje pro více vstupů najednou pdfToPDFA.tip=V současné době nepracuje pro více vstupů najednou
pdfToPDFA.outputFormat=Výstupní formát pdfToPDFA.outputFormat=Výstupní formát

View File

@ -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.11=Fjern billeder efter OCR (Fjerner ALLE billeder, kun nyttigt hvis det er en del af konverteringstrinnet)
ocr.selectText.12=Renderingstype (Avanceret) 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.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 ocr.submit=Behandl PDF med OCR
@ -892,7 +892,7 @@ fileToPDF.submit=Konvertér til PDF
#compress #compress
compress.title=Komprimer compress.title=Komprimer
compress.header=Komprimer PDF 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.1=Manuel Tilstand - Fra 1 til 4
compress.selectText.2=Optimeringsniveau: compress.selectText.2=Optimeringsniveau:
compress.selectText.3=4 (Forfærdelig for tekstbilleder) compress.selectText.3=4 (Forfærdelig for tekstbilleder)
@ -1112,7 +1112,7 @@ changeMetadata.submit=Ændre
#pdfToPDFA #pdfToPDFA
pdfToPDFA.title=PDF Til PDF/A pdfToPDFA.title=PDF Til PDF/A
pdfToPDFA.header=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.submit=Konvertér
pdfToPDFA.tip=Fungerer i øjeblikket ikke for flere input på én gang pdfToPDFA.tip=Fungerer i øjeblikket ikke for flere input på én gang
pdfToPDFA.outputFormat=Outputformat pdfToPDFA.outputFormat=Outputformat

View File

@ -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.11=Bilder nach OCR entfernen (Entfernt ALLE Bilder, nur sinnvoll, wenn Teil des Konvertierungsschritts)
ocr.selectText.12=Rendertyp (Erweitert) 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.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 ocr.submit=PDF mit OCR verarbeiten
@ -892,7 +892,7 @@ fileToPDF.submit=In PDF konvertieren
#compress #compress
compress.title=Komprimieren compress.title=Komprimieren
compress.header=PDF 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.1=Manueller Modus Von 1 bis 4
compress.selectText.2=Optimierungsstufe: compress.selectText.2=Optimierungsstufe:
compress.selectText.3=4 (Schrecklich für Textbilder) compress.selectText.3=4 (Schrecklich für Textbilder)
@ -1112,7 +1112,7 @@ changeMetadata.submit=Ändern
#pdfToPDFA #pdfToPDFA
pdfToPDFA.title=PDF zu PDF/A pdfToPDFA.title=PDF zu PDF/A
pdfToPDFA.header=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.submit=Konvertieren
pdfToPDFA.tip=Dieser Dienst kann nur einzelne Eingangsdateien verarbeiten. pdfToPDFA.tip=Dieser Dienst kann nur einzelne Eingangsdateien verarbeiten.
pdfToPDFA.outputFormat=Ausgabeformat pdfToPDFA.outputFormat=Ausgabeformat

View File

@ -868,7 +868,7 @@ ocr.selectText.10=Λειτουργία OCR
ocr.selectText.11=Κατάργηση εικόνων μετά το OCR (Καταργεί ΟΛΕΣ τις εικόνες, είναι χρήσιμο μόνο αν αποτελεί μέρος του βήματος μετατροπής) ocr.selectText.11=Κατάργηση εικόνων μετά το OCR (Καταργεί ΟΛΕΣ τις εικόνες, είναι χρήσιμο μόνο αν αποτελεί μέρος του βήματος μετατροπής)
ocr.selectText.12=Τύπος απόδοσης (Για προχωρημένους) ocr.selectText.12=Τύπος απόδοσης (Για προχωρημένους)
ocr.help=Διαβάστε αυτήν την τεκμηρίωση σχετικά με τον τρόπο χρήσης αυτής για άλλες γλώσσες ή/και μη χρήσης σε docker ocr.help=Διαβάστε αυτήν την τεκμηρίωση σχετικά με τον τρόπο χρήσης αυτής για άλλες γλώσσες ή/και μη χρήσης σε docker
ocr.credit=Αυτή η υπηρεσία χρησιμοποιεί OCRmyPDF και Tesseract για OCR. ocr.credit=Αυτή η υπηρεσία χρησιμοποιεί qpdf και Tesseract για OCR.
ocr.submit=Επεξεργασία PDF με OCR ocr.submit=Επεξεργασία PDF με OCR
@ -892,7 +892,7 @@ fileToPDF.submit=Μετατροπή σε PDF
#compress #compress
compress.title=Συμπίεση compress.title=Συμπίεση
compress.header=Συμπίεση PDF compress.header=Συμπίεση PDF
compress.credit=Αυτή η υπηρεσία χρησιμοποιεί Ghostscript για PDF Συμπίεση/Βελτιστοποίηση. compress.credit=Αυτή η υπηρεσία χρησιμοποιεί qpdf για PDF Συμπίεση/Βελτιστοποίηση.
compress.selectText.1=Χειροκίνητη Λειτουργία - Από 1 έως 4 compress.selectText.1=Χειροκίνητη Λειτουργία - Από 1 έως 4
compress.selectText.2=Επίπεδο Βελτιστοποίησης: compress.selectText.2=Επίπεδο Βελτιστοποίησης:
compress.selectText.3=4 (Πολύ κακό για εικόνες κειμένου) compress.selectText.3=4 (Πολύ κακό για εικόνες κειμένου)
@ -1112,7 +1112,7 @@ changeMetadata.submit=Αλλαγή
#pdfToPDFA #pdfToPDFA
pdfToPDFA.title=PDF σε PDF/A pdfToPDFA.title=PDF σε PDF/A
pdfToPDFA.header=PDF σε PDF/A pdfToPDFA.header=PDF σε PDF/A
pdfToPDFA.credit=Αυτή η υπηρεσία χρησιμοποιεί ghostscript για PDF/A μετατροπή pdfToPDFA.credit=Αυτή η υπηρεσία χρησιμοποιεί qpdf για PDF/A μετατροπή
pdfToPDFA.submit=Μετατροπή pdfToPDFA.submit=Μετατροπή
pdfToPDFA.tip=Προς το παρόν δεν λειτουργεί για πολλαπλές εισόδους ταυτόχρονα pdfToPDFA.tip=Προς το παρόν δεν λειτουργεί για πολλαπλές εισόδους ταυτόχρονα
pdfToPDFA.outputFormat=Εξόδος αναμορφώσεων pdfToPDFA.outputFormat=Εξόδος αναμορφώσεων

View File

@ -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.11=Remove images after OCR (Removes ALL images, only useful if part of conversion step)
ocr.selectText.12=Render Type (Advanced) 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.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 ocr.submit=Process PDF with OCR
@ -892,7 +892,7 @@ fileToPDF.submit=Convert to PDF
#compress #compress
compress.title=Compress compress.title=Compress
compress.header=Compress PDF 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.1=Manual Mode - From 1 to 4
compress.selectText.2=Optimization level: compress.selectText.2=Optimization level:
compress.selectText.3=4 (Terrible for text images) compress.selectText.3=4 (Terrible for text images)
@ -1112,7 +1112,7 @@ changeMetadata.submit=Change
#pdfToPDFA #pdfToPDFA
pdfToPDFA.title=PDF To PDF/A pdfToPDFA.title=PDF To PDF/A
pdfToPDFA.header=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.submit=Convert
pdfToPDFA.tip=Currently does not work for multiple inputs at once pdfToPDFA.tip=Currently does not work for multiple inputs at once
pdfToPDFA.outputFormat=Output format 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.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.desc.4=Allow Duplicates: If checked, allows multiple bookmarks on the same page to create separate PDFs.
splitByChapters.submit=Split PDF 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

View File

@ -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.11=Remove images after OCR (Removes ALL images, only useful if part of conversion step)
ocr.selectText.12=Render Type (Advanced) 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.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 ocr.submit=Process PDF with OCR
@ -892,7 +892,7 @@ fileToPDF.submit=Convert to PDF
#compress #compress
compress.title=Compress compress.title=Compress
compress.header=Compress PDF 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.1=Manual Mode - From 1 to 4
compress.selectText.2=Optimization level: compress.selectText.2=Optimization level:
compress.selectText.3=4 (Terrible for text images) compress.selectText.3=4 (Terrible for text images)
@ -1112,7 +1112,7 @@ changeMetadata.submit=Change
#pdfToPDFA #pdfToPDFA
pdfToPDFA.title=PDF To PDF/A pdfToPDFA.title=PDF To PDF/A
pdfToPDFA.header=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.submit=Convert
pdfToPDFA.tip=Currently does not work for multiple inputs at once pdfToPDFA.tip=Currently does not work for multiple inputs at once
pdfToPDFA.outputFormat=Output format pdfToPDFA.outputFormat=Output format

View File

@ -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.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.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.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 ocr.submit=Procesar PDF con OCR
@ -892,7 +892,7 @@ fileToPDF.submit=Convertir a PDF
#compress #compress
compress.title=Comprimir compress.title=Comprimir
compress.header=Comprimir PDF 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.1=Modo manual - De 1 a 4
compress.selectText.2=Nivel de optimización: compress.selectText.2=Nivel de optimización:
compress.selectText.3=4 (Terrible para imágenes de texto) compress.selectText.3=4 (Terrible para imágenes de texto)
@ -1112,7 +1112,7 @@ changeMetadata.submit=Cambiar
#pdfToPDFA #pdfToPDFA
pdfToPDFA.title=PDF a PDF/A pdfToPDFA.title=PDF a PDF/A
pdfToPDFA.header=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.submit=Convertir
pdfToPDFA.tip=Actualmente no funciona para múltiples entrada a la vez pdfToPDFA.tip=Actualmente no funciona para múltiples entrada a la vez
pdfToPDFA.outputFormat=Formato de salida pdfToPDFA.outputFormat=Formato de salida

View File

@ -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.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.selectText.12=Prozesaketa-mota (aurreratua)
ocr.help=Irakurri honen erabilerari buruzko dokumentazioa beste hizkuntza batzuetarako eta/edo ez erabili Docker-en 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 ocr.submit=PDF prozesatu OCR-rekin
@ -892,7 +892,7 @@ fileToPDF.submit=PDF bihurtu
#compress #compress
compress.title=Konprimatu compress.title=Konprimatu
compress.header=PDFa 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.1=Eskuz 1etik 4ra
compress.selectText.2=Optimizazio maila: compress.selectText.2=Optimizazio maila:
compress.selectText.3=4 (Izugarria testu-irudietarako) compress.selectText.3=4 (Izugarria testu-irudietarako)
@ -1112,7 +1112,7 @@ changeMetadata.submit=Aldatu
#pdfToPDFA #pdfToPDFA
pdfToPDFA.title=PDFa PDF/A bihurtu pdfToPDFA.title=PDFa PDF/A bihurtu
pdfToPDFA.header=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.submit=Bihurtu
pdfToPDFA.tip=Currently does not work for multiple inputs at once pdfToPDFA.tip=Currently does not work for multiple inputs at once
pdfToPDFA.outputFormat=Output format pdfToPDFA.outputFormat=Output format

View File

@ -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.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.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.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 ocr.submit=Traiter
@ -892,7 +892,7 @@ fileToPDF.submit=Convertir
#compress #compress
compress.title=Compresser un PDF compress.title=Compresser un PDF
compress.header=Compresser un PDF (lorsque c'est possible!) 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.1=Mode manuel de 1 à 4
compress.selectText.2=Niveau d'optimisation compress.selectText.2=Niveau d'optimisation
compress.selectText.3=4 (terrible pour les images textuelles) compress.selectText.3=4 (terrible pour les images textuelles)
@ -1112,7 +1112,7 @@ changeMetadata.submit=Modifier
#pdfToPDFA #pdfToPDFA
pdfToPDFA.title=PDF en PDF/A pdfToPDFA.title=PDF en PDF/A
pdfToPDFA.header=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.submit=Convertir
pdfToPDFA.tip=Ne fonctionne actuellement pas pour plusieurs entrées à la fois pdfToPDFA.tip=Ne fonctionne actuellement pas pour plusieurs entrées à la fois
pdfToPDFA.outputFormat=Format de sortie pdfToPDFA.outputFormat=Format de sortie

View File

@ -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.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.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.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 ocr.submit=Próiseáil PDF le OCR
@ -892,7 +892,7 @@ fileToPDF.submit=Tiontaigh go PDF
#compress #compress
compress.title=Comhbhrúigh compress.title=Comhbhrúigh
compress.header=Comhbhrúigh PDF 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.1=Mód Láimhe - Ó 1 go 4
compress.selectText.2=Leibhéal optamaithe: compress.selectText.2=Leibhéal optamaithe:
compress.selectText.3=4 (Uafásach le haghaidh íomhánna téacs) compress.selectText.3=4 (Uafásach le haghaidh íomhánna téacs)
@ -1112,7 +1112,7 @@ changeMetadata.submit=Athrú
#pdfToPDFA #pdfToPDFA
pdfToPDFA.title=PDF Go PDF/A pdfToPDFA.title=PDF Go PDF/A
pdfToPDFA.header=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.submit=Tiontaigh
pdfToPDFA.tip=Faoi láthair ní oibríonn sé le haghaidh ionchuir iolracha ag an am céanna pdfToPDFA.tip=Faoi láthair ní oibríonn sé le haghaidh ionchuir iolracha ag an am céanna
pdfToPDFA.outputFormat=Formáid aschuir pdfToPDFA.outputFormat=Formáid aschuir

View File

@ -868,7 +868,7 @@ ocr.selectText.10=OCR मोड
ocr.selectText.11=OCR के बाद छवियां हटाएँ (सभी छवियां हटाएँ, केवल परिवर्तन चरण का हिस्सा होता है) ocr.selectText.11=OCR के बाद छवियां हटाएँ (सभी छवियां हटाएँ, केवल परिवर्तन चरण का हिस्सा होता है)
ocr.selectText.12=रेंडर टाइप (उन्नत) ocr.selectText.12=रेंडर टाइप (उन्नत)
ocr.help=कृपया इस डॉक्यूमेंटेशन को पढ़ें कि इसे अन्य भाषाओं के लिए कैसे उपयोग किया जाता है और/या डॉकर में नहीं हैं ocr.help=कृपया इस डॉक्यूमेंटेशन को पढ़ें कि इसे अन्य भाषाओं के लिए कैसे उपयोग किया जाता है और/या डॉकर में नहीं हैं
ocr.credit=इस सेवा में OCRmyPDF और टेसरेक्ट का उपयोग होता है। ocr.credit=इस सेवा में qpdf और टेसरेक्ट का उपयोग होता है।
ocr.submit=OCR के साथ PDF प्रोसेस करें ocr.submit=OCR के साथ PDF प्रोसेस करें
@ -892,7 +892,7 @@ fileToPDF.submit=पीडीएफ़ में बदलें
#compress #compress
compress.title=संकुचित करें compress.title=संकुचित करें
compress.header=PDF को संकुचित करें compress.header=PDF को संकुचित करें
compress.credit=यह सेवा PDF संकुचन/अनुकूलन के लिए Ghostscript का उपयोग करती है। compress.credit=यह सेवा PDF संकुचन/अनुकूलन के लिए qpdf का उपयोग करती है।
compress.selectText.1=मैनुअल मोड - 1 से 4 तक compress.selectText.1=मैनुअल मोड - 1 से 4 तक
compress.selectText.2=अनुकूलन स्तर: compress.selectText.2=अनुकूलन स्तर:
compress.selectText.3=4 (पाठ छवियों के लिए अत्यधिक) compress.selectText.3=4 (पाठ छवियों के लिए अत्यधिक)
@ -1112,7 +1112,7 @@ changeMetadata.submit=बदलें
#pdfToPDFA #pdfToPDFA
pdfToPDFA.title=PDF से PDF/A में pdfToPDFA.title=PDF से PDF/A में
pdfToPDFA.header=PDF से PDF/A में pdfToPDFA.header=PDF से PDF/A में
pdfToPDFA.credit=इस सेवा में PDF/A परिवर्तन के लिए ghostscript का उपयोग किया जाता है। pdfToPDFA.credit=इस सेवा में PDF/A परिवर्तन के लिए qpdf का उपयोग किया जाता है।
pdfToPDFA.submit=परिवर्तित करें pdfToPDFA.submit=परिवर्तित करें
pdfToPDFA.tip=यह सैकड़ों प्रविष्टियाँ एक ही समय में काम करते हैं pdfToPDFA.tip=यह सैकड़ों प्रविष्टियाँ एक ही समय में काम करते हैं
pdfToPDFA.outputFormat=आउटपुट फॉर्मेट pdfToPDFA.outputFormat=आउटपुट फॉर्मेट

View File

@ -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.11=Ukloni slike nakon OCR-a (Uklanja SVE slike, korisno samo ako je dio koraka konverzije)
ocr.selectText.12=Vrsta iscrtavanja (napredno) 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.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 ocr.submit=Obradi PDF sa OCR-om
@ -892,7 +892,7 @@ fileToPDF.submit=Pretvori u PDF
#compress #compress
compress.title=Komprimirajte compress.title=Komprimirajte
compress.header=Komprimirajte PDF 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.1=Ručni režim - Od 1 do 4
compress.selectText.2=Nivo optimizacije: compress.selectText.2=Nivo optimizacije:
compress.selectText.3=4 (Užasno za tekstualne slike) compress.selectText.3=4 (Užasno za tekstualne slike)
@ -1112,7 +1112,7 @@ changeMetadata.submit=Promijeniti
#pdfToPDFA #pdfToPDFA
pdfToPDFA.title=PDF u PDF/A pdfToPDFA.title=PDF u PDF/A
pdfToPDFA.header=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.submit=Pretvoriti
pdfToPDFA.tip=Trenutno ne radi za više unosa odjednom pdfToPDFA.tip=Trenutno ne radi za više unosa odjednom
pdfToPDFA.outputFormat=Izlazni format pdfToPDFA.outputFormat=Izlazni format

View File

@ -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.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.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.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 ocr.submit=PDF feldolgozása OCR-rel
@ -892,7 +892,7 @@ fileToPDF.submit=Konvertálás PDF dokumentummá
#compress #compress
compress.title=Tömörítés compress.title=Tömörítés
compress.header=PDF tömörítése 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.1=Kézi mód - 1-től 4-ig
compress.selectText.2=Optimalizálási szint: compress.selectText.2=Optimalizálási szint:
compress.selectText.3=4 (nem ajánlott a szöveges képekhez) compress.selectText.3=4 (nem ajánlott a szöveges képekhez)
@ -1112,7 +1112,7 @@ changeMetadata.submit=Módosítás
#pdfToPDFA #pdfToPDFA
pdfToPDFA.title=PDF >> PDF/A pdfToPDFA.title=PDF >> PDF/A
pdfToPDFA.header=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.submit=Konvertálás
pdfToPDFA.tip=Jelenleg egyszerre több fájl nem működik ezzel a funkcióval pdfToPDFA.tip=Jelenleg egyszerre több fájl nem működik ezzel a funkcióval
pdfToPDFA.outputFormat=Kimeneti formátum pdfToPDFA.outputFormat=Kimeneti formátum

View File

@ -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.11=Hapus gambar setelah OCR (Menghapus Semua gambar, hanya berguna jika merupakan bagian dari langkah konversi)
ocr.selectText.12=Jenis Render (Lanjutan) 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.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 ocr.submit=Memproses PDF dengan OCR
@ -892,7 +892,7 @@ fileToPDF.submit=Konversi ke PDF
#compress #compress
compress.title=Kompres compress.title=Kompres
compress.header=Kompres PDF 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.1=Mode Manual - Dari 1 hingga 4
compress.selectText.2=Tingkat Optimalisasi: compress.selectText.2=Tingkat Optimalisasi:
compress.selectText.3=4 (Buruk untuk gambar teks) compress.selectText.3=4 (Buruk untuk gambar teks)
@ -1112,7 +1112,7 @@ changeMetadata.submit=Ganti
#pdfToPDFA #pdfToPDFA
pdfToPDFA.title=PDF Ke PDF/A pdfToPDFA.title=PDF Ke PDF/A
pdfToPDFA.header=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.submit=Konversi
pdfToPDFA.tip=Saat ini tidak dapat digunakan untuk beberapa input sekaligus pdfToPDFA.tip=Saat ini tidak dapat digunakan untuk beberapa input sekaligus
pdfToPDFA.outputFormat=Format keluaran pdfToPDFA.outputFormat=Format keluaran

View File

@ -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.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.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.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 ocr.submit=Scansiona testo nel PDF con OCR
@ -892,7 +892,7 @@ fileToPDF.submit=Converti in PDF
#compress #compress
compress.title=Comprimi compress.title=Comprimi
compress.header=Comprimi PDF 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.1=Modalità manuale - Da 1 a 4
compress.selectText.2=Livello di ottimizzazione: compress.selectText.2=Livello di ottimizzazione:
compress.selectText.3=4 (Terribile per le immagini di testo) compress.selectText.3=4 (Terribile per le immagini di testo)
@ -1112,7 +1112,7 @@ changeMetadata.submit=Cambia proprietà
#pdfToPDFA #pdfToPDFA
pdfToPDFA.title=Da PDF a PDF/A pdfToPDFA.title=Da PDF a PDF/A
pdfToPDFA.header=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.submit=Converti
pdfToPDFA.tip=Attualmente non funziona per più input contemporaneamente pdfToPDFA.tip=Attualmente non funziona per più input contemporaneamente
pdfToPDFA.outputFormat=Formato di output pdfToPDFA.outputFormat=Formato di output

View File

@ -868,7 +868,7 @@ ocr.selectText.10=OCRモード
ocr.selectText.11=OCR後に画像を削除する (すべての画像を削除します。変換ステップの一部である場合にのみ有効です)。 ocr.selectText.11=OCR後に画像を削除する (すべての画像を削除します。変換ステップの一部である場合にのみ有効です)。
ocr.selectText.12=レンダリングタイプ (高度) ocr.selectText.12=レンダリングタイプ (高度)
ocr.help=他の言語でこれを使用する方法やDocker以外で使用する方法についてはこのドキュメントをお読みください。 ocr.help=他の言語でこれを使用する方法やDocker以外で使用する方法についてはこのドキュメントをお読みください。
ocr.credit=本サービスにはOCRにOCRmyPDFとTesseractを使用しています。 ocr.credit=本サービスにはOCRにqpdfとTesseractを使用しています。
ocr.submit=OCRでPDFを処理する ocr.submit=OCRでPDFを処理する
@ -892,7 +892,7 @@ fileToPDF.submit=PDFを変換
#compress #compress
compress.title=圧縮 compress.title=圧縮
compress.header=PDFを圧縮 compress.header=PDFを圧縮
compress.credit=本サービスはPDFの圧縮/最適化にGhostscriptを使用しています。 compress.credit=本サービスはPDFの圧縮/最適化にqpdfを使用しています。
compress.selectText.1=手動モード - 1 から 4 compress.selectText.1=手動モード - 1 から 4
compress.selectText.2=品質レベル: compress.selectText.2=品質レベル:
compress.selectText.3=4 (テキスト画像は最悪) compress.selectText.3=4 (テキスト画像は最悪)
@ -1112,7 +1112,7 @@ changeMetadata.submit=変更
#pdfToPDFA #pdfToPDFA
pdfToPDFA.title=PDFをPDF/Aに変換 pdfToPDFA.title=PDFをPDF/Aに変換
pdfToPDFA.header=PDFをPDF/Aに変換 pdfToPDFA.header=PDFをPDF/Aに変換
pdfToPDFA.credit=本サービスはPDF/Aの変換にghostscriptを使用しています。 pdfToPDFA.credit=本サービスはPDF/Aの変換にqpdfを使用しています。
pdfToPDFA.submit=変換 pdfToPDFA.submit=変換
pdfToPDFA.tip=現在、一度に複数の入力に対して機能しません pdfToPDFA.tip=現在、一度に複数の入力に対して機能しません
pdfToPDFA.outputFormat=Output format pdfToPDFA.outputFormat=Output format

View File

@ -868,7 +868,7 @@ ocr.selectText.10=OCR 모드
ocr.selectText.11=OCR 후 이미지 제거(모든 이미지 제거, 변환 단계의 일부인 경우에만 유용) ocr.selectText.11=OCR 후 이미지 제거(모든 이미지 제거, 변환 단계의 일부인 경우에만 유용)
ocr.selectText.12=렌더 유형(고급) ocr.selectText.12=렌더 유형(고급)
ocr.help=다른 언어 또는 Docker에 포함되지 않은 언어에 대해 사용하는 방법에 대해서는 이 문서를 참조합니다. ocr.help=다른 언어 또는 Docker에 포함되지 않은 언어에 대해 사용하는 방법에 대해서는 이 문서를 참조합니다.
ocr.credit=이 서비스는 OCR에 OCRmyPDF와 Tesseract를 사용합니다. ocr.credit=이 서비스는 OCR에 qpdf와 Tesseract를 사용합니다.
ocr.submit=인식 ocr.submit=인식
@ -892,7 +892,7 @@ fileToPDF.submit=PDF로 변환
#compress #compress
compress.title=압축 compress.title=압축
compress.header=PDF 압축 compress.header=PDF 압축
compress.credit=이 서비스는 PDF 압축 및 최적화를 위해 Ghostscript를 사용합니다. compress.credit=이 서비스는 PDF 압축 및 최적화를 위해 qpdf를 사용합니다.
compress.selectText.1=수동 모드 - 1에서 4 compress.selectText.1=수동 모드 - 1에서 4
compress.selectText.2=최적화 수준: compress.selectText.2=최적화 수준:
compress.selectText.3=4 (텍스트 이미지에 적합하지 않음) compress.selectText.3=4 (텍스트 이미지에 적합하지 않음)
@ -1112,7 +1112,7 @@ changeMetadata.submit=변경
#pdfToPDFA #pdfToPDFA
pdfToPDFA.title=PDF를 PDF/A로 pdfToPDFA.title=PDF를 PDF/A로
pdfToPDFA.header=PDF 문서를 PDF/A로 변환 pdfToPDFA.header=PDF 문서를 PDF/A로 변환
pdfToPDFA.credit=이 서비스는 PDF/A 변환을 위해 ghostscript 문서를 사용합니다. pdfToPDFA.credit=이 서비스는 PDF/A 변환을 위해 qpdf 문서를 사용합니다.
pdfToPDFA.submit=변환 pdfToPDFA.submit=변환
pdfToPDFA.tip=현재 한 번에 여러 입력에 대해 작동하지 않습니다. pdfToPDFA.tip=현재 한 번에 여러 입력에 대해 작동하지 않습니다.
pdfToPDFA.outputFormat=출력 형식 pdfToPDFA.outputFormat=출력 형식

View File

@ -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.11=Verwijder afbeeldingen na OCR (Verwijdert ALLE afbeeldingen, alleen nuttig als onderdeel van conversiestap)
ocr.selectText.12=Weergave Type (Geavanceerd) 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.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 ocr.submit=Verwerk PDF met OCR
@ -892,7 +892,7 @@ fileToPDF.submit=Omzetten naar PDF
#compress #compress
compress.title=Comprimeren compress.title=Comprimeren
compress.header=PDF 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.1=Handmatige modus - Van 1 tot 4
compress.selectText.2=Optimalisatieniveau: compress.selectText.2=Optimalisatieniveau:
compress.selectText.3=4 (Verschrikkelijk voor tekstafbeeldingen) compress.selectText.3=4 (Verschrikkelijk voor tekstafbeeldingen)
@ -1112,7 +1112,7 @@ changeMetadata.submit=Wijzigen
#pdfToPDFA #pdfToPDFA
pdfToPDFA.title=PDF naar PDF/A pdfToPDFA.title=PDF naar PDF/A
pdfToPDFA.header=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.submit=Converteren
pdfToPDFA.tip=Werkt momenteel niet voor meerdere inputs tegelijkertijd. pdfToPDFA.tip=Werkt momenteel niet voor meerdere inputs tegelijkertijd.
pdfToPDFA.outputFormat=Uitvoerindeling pdfToPDFA.outputFormat=Uitvoerindeling

View File

@ -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.11=Fjern bilder etter OCR (Fjerner ALLE bilder, kun nyttig hvis det er en del av konverteringsprosessen)
ocr.selectText.12=Renderingstype (Avansert) 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.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 ocr.submit=Behandle PDF med OCR
@ -892,7 +892,7 @@ fileToPDF.submit=Konverter til PDF
#compress #compress
compress.title=Komprimer compress.title=Komprimer
compress.header=Komprimer PDF 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.1=Manuell modus - Fra 1 til 4
compress.selectText.2=Optimeringsnivå: compress.selectText.2=Optimeringsnivå:
compress.selectText.3=4 (Dårlig for tekstbilder) compress.selectText.3=4 (Dårlig for tekstbilder)
@ -1112,7 +1112,7 @@ changeMetadata.submit=Endre
#pdfToPDFA #pdfToPDFA
pdfToPDFA.title=PDF til PDF/A pdfToPDFA.title=PDF til PDF/A
pdfToPDFA.header=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.submit=Konverter
pdfToPDFA.tip=Fungere for øyeblikket ikke for flere innganger samtidig pdfToPDFA.tip=Fungere for øyeblikket ikke for flere innganger samtidig
pdfToPDFA.outputFormat=Utdataformat pdfToPDFA.outputFormat=Utdataformat

View File

@ -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.11=Usuń obrazy po OCR (usuwa wszystkie obrazy, przydatne tylko, jeśli jest częścią etapu konwersji)
ocr.selectText.12=Typ renderowania (zaawansowany) 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.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 ocr.submit=Przetwarzaj PDF za pomocą OCR
@ -892,7 +892,7 @@ fileToPDF.submit=Konwertuj na PDF
#compress #compress
compress.title=Kompresuj compress.title=Kompresuj
compress.header=Kompresuj PDF 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.1=Tryb ręczny - Od 1 do 4
compress.selectText.2=Poziom optymalizacji: compress.selectText.2=Poziom optymalizacji:
compress.selectText.3=4 (Duże dla obrazów tekstowych) compress.selectText.3=4 (Duże dla obrazów tekstowych)
@ -1112,7 +1112,7 @@ changeMetadata.submit=Zmień
#pdfToPDFA #pdfToPDFA
pdfToPDFA.title=PDF na PDF/A pdfToPDFA.title=PDF na PDF/A
pdfToPDFA.header=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.submit=Konwertuj
pdfToPDFA.tip=Tylko jeden plik na raz pdfToPDFA.tip=Tylko jeden plik na raz
pdfToPDFA.outputFormat=Format wyjściowy: pdfToPDFA.outputFormat=Format wyjściowy:

View File

@ -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.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.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.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 ocr.submit=Processar PDF com OCR
@ -892,7 +892,7 @@ fileToPDF.submit=Converter para PDF
#compress #compress
compress.title=Comprimir compress.title=Comprimir
compress.header=Comprimir PDF 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.1=Modo Manual - De 1 a 4
compress.selectText.2=Nível de Otimização: compress.selectText.2=Nível de Otimização:
compress.selectText.3=4 (Pior para imagens de texto) compress.selectText.3=4 (Pior para imagens de texto)
@ -1112,7 +1112,7 @@ changeMetadata.submit=Alterar
#pdfToPDFA #pdfToPDFA
pdfToPDFA.title=PDF para PDF/A pdfToPDFA.title=PDF para PDF/A
pdfToPDFA.header=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.submit=Converter
pdfToPDFA.tip=Atualmente não funciona para múltiplas entradas ao mesmo tempo pdfToPDFA.tip=Atualmente não funciona para múltiplas entradas ao mesmo tempo
pdfToPDFA.outputFormat=Formato de saída pdfToPDFA.outputFormat=Formato de saída

View File

@ -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.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.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.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 ocr.submit=Processar PDF com OCR
@ -892,7 +892,7 @@ fileToPDF.submit=Converter para PDF
#compress #compress
compress.title=Comprimir compress.title=Comprimir
compress.header=Comprimir PDF 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.1=Modo Manual - De 1 a 4
compress.selectText.2=Nível de Otimização: compress.selectText.2=Nível de Otimização:
compress.selectText.3=4 (Pior para imagens de texto) compress.selectText.3=4 (Pior para imagens de texto)
@ -1112,7 +1112,7 @@ changeMetadata.submit=Mudar
#pdfToPDFA #pdfToPDFA
pdfToPDFA.title=PDF para PDF/A pdfToPDFA.title=PDF para PDF/A
pdfToPDFA.header=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.submit=Converter
pdfToPDFA.tip=Actualmente não funciona para múltiplos inputs de uma só vez pdfToPDFA.tip=Actualmente não funciona para múltiplos inputs de uma só vez
pdfToPDFA.outputFormat=Formato de saída pdfToPDFA.outputFormat=Formato de saída

View File

@ -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.11=Elimină imaginile după OCR (Elimină TOATE imaginile, util doar în etapa de conversie)
ocr.selectText.12=Tip de redare (Avansat) 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.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 ocr.submit=Procesează PDF-ul cu OCR
@ -892,7 +892,7 @@ fileToPDF.submit=Convertiți în PDF
#compress #compress
compress.title=Comprimare compress.title=Comprimare
compress.header=Comprimare PDF 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.1=Nivel de optimizare:
compress.selectText.2=0 (Fără optimizare) compress.selectText.2=0 (Fără optimizare)
compress.selectText.3=1 (Implicit, optimizare fără pierdere) compress.selectText.3=1 (Implicit, optimizare fără pierdere)
@ -1112,7 +1112,7 @@ changeMetadata.submit=Schimbă
#pdfToPDFA #pdfToPDFA
pdfToPDFA.title=PDF către PDF/A pdfToPDFA.title=PDF către PDF/A
pdfToPDFA.header=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.submit=Convertește
pdfToPDFA.tip=În prezent nu funcționează pentru mai multe intrări simultan pdfToPDFA.tip=În prezent nu funcționează pentru mai multe intrări simultan
pdfToPDFA.outputFormat=Format de ieșire pdfToPDFA.outputFormat=Format de ieșire

View File

@ -868,7 +868,7 @@ ocr.selectText.10=OCR режим
ocr.selectText.11=Удалить изображения после OCR (удаляет ВСЕ изображения, полезно только в том случае, если они являются частью шага преобразования) ocr.selectText.11=Удалить изображения после OCR (удаляет ВСЕ изображения, полезно только в том случае, если они являются частью шага преобразования)
ocr.selectText.12=Тип рендера (расширенный) ocr.selectText.12=Тип рендера (расширенный)
ocr.help=Прочтите эту документацию о том, как использовать это для других языков и/или использовать не в докере. ocr.help=Прочтите эту документацию о том, как использовать это для других языков и/или использовать не в докере.
ocr.credit=Этот сервис использует OCRmyPDF и Tesseract для OCR. ocr.credit=Этот сервис использует qpdf и Tesseract для OCR.
ocr.submit=Обработка PDF с OCR ocr.submit=Обработка PDF с OCR
@ -892,7 +892,7 @@ fileToPDF.submit=Преобразовать в PDF
#compress #compress
compress.title=Сжать compress.title=Сжать
compress.header=Сжать PDF compress.header=Сжать PDF
compress.credit=Эта служба использует Ghostscript для сжатия/оптимизации PDF. compress.credit=Эта служба использует qpdf для сжатия/оптимизации PDF.
compress.selectText.1=Ручной режим - от 1 до 4 compress.selectText.1=Ручной режим - от 1 до 4
compress.selectText.2=Уровень оптимизации: compress.selectText.2=Уровень оптимизации:
compress.selectText.3=4 (Ужасно для текстовых изображений) compress.selectText.3=4 (Ужасно для текстовых изображений)
@ -1112,7 +1112,7 @@ changeMetadata.submit=Изменить
#pdfToPDFA #pdfToPDFA
pdfToPDFA.title=PDF в PDF/A pdfToPDFA.title=PDF в PDF/A
pdfToPDFA.header=PDF в PDF/A pdfToPDFA.header=PDF в PDF/A
pdfToPDFA.credit=Этот сервис использует ghostscript для преобразования PDF/A pdfToPDFA.credit=Этот сервис использует qpdf для преобразования PDF/A
pdfToPDFA.submit=Конвертировать pdfToPDFA.submit=Конвертировать
pdfToPDFA.tip=В настоящее время не поддерживается при нескольких входных данных одновременно pdfToPDFA.tip=В настоящее время не поддерживается при нескольких входных данных одновременно
pdfToPDFA.outputFormat=Формат вывода pdfToPDFA.outputFormat=Формат вывода

View File

@ -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.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.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.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 ocr.submit=Spracovať PDF s OCR
@ -892,7 +892,7 @@ fileToPDF.submit=Konvertovať do PDF
#compress #compress
compress.title=Komprimovať compress.title=Komprimovať
compress.header=Komprimovať PDF 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.1=Manuálny režim - Od 1 do 4
compress.selectText.2=Úroveň optimalizácie: compress.selectText.2=Úroveň optimalizácie:
compress.selectText.3=4 (Hrozné pre textové obrázky) compress.selectText.3=4 (Hrozné pre textové obrázky)
@ -1112,7 +1112,7 @@ changeMetadata.submit=Zmeniť
#pdfToPDFA #pdfToPDFA
pdfToPDFA.title=PDF na PDF/A pdfToPDFA.title=PDF na PDF/A
pdfToPDFA.header=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.submit=Konvertovať
pdfToPDFA.tip=Momentálne nefunguje pre viacero vstupov naraz pdfToPDFA.tip=Momentálne nefunguje pre viacero vstupov naraz
pdfToPDFA.outputFormat=Výstupný formát pdfToPDFA.outputFormat=Výstupný formát

View File

@ -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.11=Ukloni slike nakon OCR-a (Uklanja SVE slike, korisno samo ako je deo koraka konverzije)
ocr.selectText.12=Tip rendiranja (Napredno) 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.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 ocr.submit=Obradi PDF sa OCR-om
@ -892,7 +892,7 @@ fileToPDF.submit=Konvertuj u PDF
#compress #compress
compress.title=Kompresija compress.title=Kompresija
compress.header=Kompresuj PDF 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.1=Ručni režim - Od 1 do 4
compress.selectText.2=Nivo optimizacije: compress.selectText.2=Nivo optimizacije:
compress.selectText.3=4 (Užasno za tekstualne slike) compress.selectText.3=4 (Užasno za tekstualne slike)
@ -1112,7 +1112,7 @@ changeMetadata.submit=Promeni
#pdfToPDFA #pdfToPDFA
pdfToPDFA.title=PDF u PDF/A pdfToPDFA.title=PDF u PDF/A
pdfToPDFA.header=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.submit=Konvertuj
pdfToPDFA.tip=Currently does not work for multiple inputs at once pdfToPDFA.tip=Currently does not work for multiple inputs at once
pdfToPDFA.outputFormat=Output format pdfToPDFA.outputFormat=Output format

View File

@ -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.11=Ta bort bilder efter OCR (tar bort ALLA bilder, endast användbart som en del av konverteringssteget)
ocr.selectText.12=Renderingstyp (avancerat) 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.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 ocr.submit=Bearbeta PDF med OCR
@ -892,7 +892,7 @@ fileToPDF.submit=Konvertera till PDF
#compress #compress
compress.title=Komprimera compress.title=Komprimera
compress.header=Komprimera PDF 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.1=Manuellt läge - Från 1 till 4
compress.selectText.2=Optimeringsnivå: compress.selectText.2=Optimeringsnivå:
compress.selectText.3=4 (Fruktansvärt för textbilder) compress.selectText.3=4 (Fruktansvärt för textbilder)
@ -1112,7 +1112,7 @@ changeMetadata.submit=Ändra
#pdfToPDFA #pdfToPDFA
pdfToPDFA.title=PDF till PDF/A pdfToPDFA.title=PDF till PDF/A
pdfToPDFA.header=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.submit=Konvertera
pdfToPDFA.tip=Fungerar för närvarande inte för flera inmatningar samtidigt pdfToPDFA.tip=Fungerar för närvarande inte för flera inmatningar samtidigt
pdfToPDFA.outputFormat=Utdataformat pdfToPDFA.outputFormat=Utdataformat

View File

@ -868,7 +868,7 @@ ocr.selectText.10=โหมด OCR
ocr.selectText.11=ลบภาพหลังจาก OCR (ลบภาพทั้งหมด, มีประโยชน์เฉพาะหากเป็นส่วนหนึ่งของขั้นตอนการแปลง) ocr.selectText.11=ลบภาพหลังจาก OCR (ลบภาพทั้งหมด, มีประโยชน์เฉพาะหากเป็นส่วนหนึ่งของขั้นตอนการแปลง)
ocr.selectText.12=ประเภทการเรนเดอร์ (ขั้นสูง) ocr.selectText.12=ประเภทการเรนเดอร์ (ขั้นสูง)
ocr.help=โปรดอ่านเอกสารนี้เพื่อใช้งานภาษาอื่นๆ และ/หรือใช้งานนอก docker ocr.help=โปรดอ่านเอกสารนี้เพื่อใช้งานภาษาอื่นๆ และ/หรือใช้งานนอก docker
ocr.credit=บริการนี้ใช้ OCRmyPDF และ Tesseract สำหรับ OCR ocr.credit=บริการนี้ใช้ qpdf และ Tesseract สำหรับ OCR
ocr.submit=ประมวลผล PDF ด้วย OCR ocr.submit=ประมวลผล PDF ด้วย OCR
@ -892,7 +892,7 @@ fileToPDF.submit=แปลงเป็น PDF
#compress #compress
compress.title=บีบอัด compress.title=บีบอัด
compress.header=บีบอัด PDF compress.header=บีบอัด PDF
compress.credit=บริการนี้ใช้ Ghostscript สำหรับการบีบอัด/การเพิ่มประสิทธิภาพ PDF compress.credit=บริการนี้ใช้ qpdf สำหรับการบีบอัด/การเพิ่มประสิทธิภาพ PDF
compress.selectText.1=โหมดแมนนวล - จาก 1 ถึง 4 compress.selectText.1=โหมดแมนนวล - จาก 1 ถึง 4
compress.selectText.2=ระดับการเพิ่มประสิทธิภาพ: compress.selectText.2=ระดับการเพิ่มประสิทธิภาพ:
compress.selectText.3=4 (ไม่ดีสำหรับภาพข้อความ) compress.selectText.3=4 (ไม่ดีสำหรับภาพข้อความ)
@ -1112,7 +1112,7 @@ changeMetadata.submit=เปลี่ยน
#pdfToPDFA #pdfToPDFA
pdfToPDFA.title=PDF เป็น PDF/A pdfToPDFA.title=PDF เป็น PDF/A
pdfToPDFA.header=PDF เป็น PDF/A pdfToPDFA.header=PDF เป็น PDF/A
pdfToPDFA.credit=บริการนี้ใช้ ghostscript สำหรับการแปลง PDF/A pdfToPDFA.credit=บริการนี้ใช้ qpdf สำหรับการแปลง PDF/A
pdfToPDFA.submit=แปลง pdfToPDFA.submit=แปลง
pdfToPDFA.tip=ปัจจุบันไม่ทำงานสำหรับการป้อนข้อมูลหลายรายการพร้อมกัน pdfToPDFA.tip=ปัจจุบันไม่ทำงานสำหรับการป้อนข้อมูลหลายรายการพร้อมกัน
pdfToPDFA.outputFormat=รูปแบบผลลัพธ์ pdfToPDFA.outputFormat=รูปแบบผลลัพธ์

View File

@ -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.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.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.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 ocr.submit=PDF'i OCR(Metin Tanıma) ile İşle
@ -892,7 +892,7 @@ fileToPDF.submit=PDF'e Dönüştür
#compress #compress
compress.title=Sıkıştır compress.title=Sıkıştır
compress.header=PDF'i 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.1=Manuel Mod - 1'den 4'e
compress.selectText.2=Optimizasyon seviyesi: compress.selectText.2=Optimizasyon seviyesi:
compress.selectText.3=4 (Metin resimleri için hiç uygun değil) compress.selectText.3=4 (Metin resimleri için hiç uygun değil)
@ -1112,7 +1112,7 @@ changeMetadata.submit=Değiştir
#pdfToPDFA #pdfToPDFA
pdfToPDFA.title=PDF'den PDF/A'ya pdfToPDFA.title=PDF'den PDF/A'ya
pdfToPDFA.header=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.submit=Dönüştür
pdfToPDFA.tip=Şu anda aynı anda birden fazla giriş için çalışmıyor pdfToPDFA.tip=Şu anda aynı anda birden fazla giriş için çalışmıyor
pdfToPDFA.outputFormat=Çıkış formatı pdfToPDFA.outputFormat=Çıkış formatı

View File

@ -868,7 +868,7 @@ ocr.selectText.10=Режим OCR
ocr.selectText.11=Видалити зображення після OCR (видаляє ВСІ зображення, корисно лише в тому випадку, якщо вони є частиною етапу перетворення) ocr.selectText.11=Видалити зображення після OCR (видаляє ВСІ зображення, корисно лише в тому випадку, якщо вони є частиною етапу перетворення)
ocr.selectText.12=Тип рендеру (розширений) ocr.selectText.12=Тип рендеру (розширений)
ocr.help=Прочитайте цю документацію про те, як використовувати це для інших мов і/або використовувати не в докері. ocr.help=Прочитайте цю документацію про те, як використовувати це для інших мов і/або використовувати не в докері.
ocr.credit=Цей сервіс використовує OCRmyPDF та Tesseract для OCR. ocr.credit=Цей сервіс використовує qpdf та Tesseract для OCR.
ocr.submit=Обробка PDF з OCR ocr.submit=Обробка PDF з OCR
@ -892,7 +892,7 @@ fileToPDF.submit=Перетворити у PDF
#compress #compress
compress.title=Стиснути compress.title=Стиснути
compress.header=Стиснути PDF compress.header=Стиснути PDF
compress.credit=Ця служба використовує Ghostscript для стиснення/оптимізації PDF. compress.credit=Ця служба використовує qpdf для стиснення/оптимізації PDF.
compress.selectText.1=Ручний режим - від 1 до 4 compress.selectText.1=Ручний режим - від 1 до 4
compress.selectText.2=Рівень оптимізації: compress.selectText.2=Рівень оптимізації:
compress.selectText.3=4 (Жахливо для текстових зображень) compress.selectText.3=4 (Жахливо для текстових зображень)
@ -1112,7 +1112,7 @@ changeMetadata.submit=Змінити
#pdfToPDFA #pdfToPDFA
pdfToPDFA.title=PDF в PDF/A pdfToPDFA.title=PDF в PDF/A
pdfToPDFA.header=PDF в PDF/A pdfToPDFA.header=PDF в PDF/A
pdfToPDFA.credit=Цей сервіс використовує ghostscript для перетворення у формат PDF/A pdfToPDFA.credit=Цей сервіс використовує qpdf для перетворення у формат PDF/A
pdfToPDFA.submit=Конвертувати pdfToPDFA.submit=Конвертувати
pdfToPDFA.tip=Наразі не працює для кількох вхідних файлів одночасно pdfToPDFA.tip=Наразі не працює для кількох вхідних файлів одночасно
pdfToPDFA.outputFormat=Вихідний формат pdfToPDFA.outputFormat=Вихідний формат

View File

@ -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.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.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.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 ocr.submit=Xử lý PDF với OCR
@ -892,7 +892,7 @@ fileToPDF.submit=Chuyển đổi sang PDF
#compress #compress
compress.title=Nén compress.title=Nén
compress.header=Nén PDF 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.1=Chế độ thủ công - Từ 1 đến 4
compress.selectText.2=Mức độ tối ưu hóa: compress.selectText.2=Mức độ tối ưu hóa:
compress.selectText.3=4 (Tệ cho hình ảnh văn bản) compress.selectText.3=4 (Tệ cho hình ảnh văn bản)
@ -1112,7 +1112,7 @@ changeMetadata.submit=Thay đổi
#pdfToPDFA #pdfToPDFA
pdfToPDFA.title=PDF sang PDF/A pdfToPDFA.title=PDF sang PDF/A
pdfToPDFA.header=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.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.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 pdfToPDFA.outputFormat=Định dạng đầu ra

View File

@ -868,7 +868,7 @@ ocr.selectText.10=OCR模式
ocr.selectText.11=OCR后移除图像移除所有图像只有在转换步骤中才有用 ocr.selectText.11=OCR后移除图像移除所有图像只有在转换步骤中才有用
ocr.selectText.12=渲染类型(高级) ocr.selectText.12=渲染类型(高级)
ocr.help=请阅读此文档,了解如何将其用于其他语言和/或不在docker中使用。 ocr.help=请阅读此文档,了解如何将其用于其他语言和/或不在docker中使用。
ocr.credit=此服务使用OCRmyPDF和Tesseract进行OCR。 ocr.credit=此服务使用qpdf和Tesseract进行OCR。
ocr.submit=用OCR处理PDF ocr.submit=用OCR处理PDF
@ -892,7 +892,7 @@ fileToPDF.submit=转换为 PDF
#compress #compress
compress.title=压缩 compress.title=压缩
compress.header=压缩PDF compress.header=压缩PDF
compress.credit=此服务使用Ghostscript进行PDF压缩/优化。 compress.credit=此服务使用qpdf进行PDF压缩/优化。
compress.selectText.1=手动模式 - 从 1 到 4 compress.selectText.1=手动模式 - 从 1 到 4
compress.selectText.2=优化级别: compress.selectText.2=优化级别:
compress.selectText.3=4文本图像很糟糕 compress.selectText.3=4文本图像很糟糕
@ -1112,7 +1112,7 @@ changeMetadata.submit=更改
#pdfToPDFA #pdfToPDFA
pdfToPDFA.title=PDF转PDF/A pdfToPDFA.title=PDF转PDF/A
pdfToPDFA.header=将PDF转换为PDF/A pdfToPDFA.header=将PDF转换为PDF/A
pdfToPDFA.credit=此服务使用ghostscript进行PDF/A转换 pdfToPDFA.credit=此服务使用qpdf进行PDF/A转换
pdfToPDFA.submit=转换 pdfToPDFA.submit=转换
pdfToPDFA.tip=目前不支持上传多个 pdfToPDFA.tip=目前不支持上传多个
pdfToPDFA.outputFormat=输出格式 pdfToPDFA.outputFormat=输出格式

View File

@ -868,7 +868,7 @@ ocr.selectText.10=OCR 模式
ocr.selectText.11=移除 OCR 後的影像(移除所有影像,只有在轉換步驟中才有用) ocr.selectText.11=移除 OCR 後的影像(移除所有影像,只有在轉換步驟中才有用)
ocr.selectText.12=渲染類型(進階) ocr.selectText.12=渲染類型(進階)
ocr.help=請閱讀此文件,了解如何使用其他語言和/或在 Docker 中使用 ocr.help=請閱讀此文件,了解如何使用其他語言和/或在 Docker 中使用
ocr.credit=此服務使用 OCRmyPDF 和 Tesseract 進行 OCR。 ocr.credit=此服務使用 qpdf 和 Tesseract 進行 OCR。
ocr.submit=使用 OCR 處理 PDF ocr.submit=使用 OCR 處理 PDF
@ -892,7 +892,7 @@ fileToPDF.submit=轉換為 PDF
#compress #compress
compress.title=壓縮 compress.title=壓縮
compress.header=壓縮 PDF compress.header=壓縮 PDF
compress.credit=此服務使用 Ghostscript 進行 PDF 壓縮/最佳化。 compress.credit=此服務使用 qpdf 進行 PDF 壓縮/最佳化。
compress.selectText.1=手動模式 - 從 1 到 4 compress.selectText.1=手動模式 - 從 1 到 4
compress.selectText.2=最佳化等級: compress.selectText.2=最佳化等級:
compress.selectText.3=4對於含有文字的影像來說結果很糟 compress.selectText.3=4對於含有文字的影像來說結果很糟
@ -1112,7 +1112,7 @@ changeMetadata.submit=變更
#pdfToPDFA #pdfToPDFA
pdfToPDFA.title=PDF 轉 PDF/A pdfToPDFA.title=PDF 轉 PDF/A
pdfToPDFA.header=PDF 轉 PDF/A pdfToPDFA.header=PDF 轉 PDF/A
pdfToPDFA.credit=此服務使用 ghostscript 進行 PDF/A 轉換 pdfToPDFA.credit=此服務使用 qpdf 進行 PDF/A 轉換
pdfToPDFA.submit=轉換 pdfToPDFA.submit=轉換
pdfToPDFA.tip=目前不支援上傳多個 pdfToPDFA.tip=目前不支援上傳多個
pdfToPDFA.outputFormat=輸出格式 pdfToPDFA.outputFormat=輸出格式

View File

@ -107,9 +107,9 @@ processExecutor:
sessionLimit: # Process executor instances limits sessionLimit: # Process executor instances limits
libreOfficeSessionLimit: 1 libreOfficeSessionLimit: 1
pdfToHtmlSessionLimit: 1 pdfToHtmlSessionLimit: 1
ocrMyPdfSessionLimit: 2 qpdfSessionLimit: 4
tesseractSessionLimit: 1
pythonOpenCvSessionLimit: 8 pythonOpenCvSessionLimit: 8
ghostScriptSessionLimit: 16
weasyPrintSessionLimit: 16 weasyPrintSessionLimit: 16
installAppSessionLimit: 1 installAppSessionLimit: 1
calibreSessionLimit: 1 calibreSessionLimit: 1
@ -117,7 +117,7 @@ processExecutor:
libreOfficetimeoutMinutes: 30 libreOfficetimeoutMinutes: 30
pdfToHtmltimeoutMinutes: 20 pdfToHtmltimeoutMinutes: 20
pythonOpenCvtimeoutMinutes: 30 pythonOpenCvtimeoutMinutes: 30
ghostScripttimeoutMinutes: 30
weasyPrinttimeoutMinutes: 30 weasyPrinttimeoutMinutes: 30
installApptimeoutMinutes: 60 installApptimeoutMinutes: 60
calibretimeoutMinutes: 30 calibretimeoutMinutes: 30
tesseractTimeoutMinutes: 30

View File

@ -4,6 +4,7 @@
<div class="d-flex justify-content-center"> <div class="d-flex justify-content-center">
<ul class="list-unstyled d-flex"> <ul class="list-unstyled d-flex">
<li><a class="footer-link px-2" id="licenses" target="_blank" th:href="@{'/licenses'}" th:text="#{licenses.nav}">Licenses</a></li> <li><a class="footer-link px-2" id="licenses" target="_blank" th:href="@{'/licenses'}" th:text="#{licenses.nav}">Licenses</a></li>
<li><a class="footer-link px-2" id="licenses" target="_blank" th:href="@{'/releases'}" th:text="#{releases.footer}">Releases</a></li>
<li><a class="footer-link px-2" id="survey" target="_blank" href="https://stirlingpdf.info/s/cm28y3niq000o56dv7liv8wsu" th:text="#{survey.nav}">Survey</a></li> <li><a class="footer-link px-2" id="survey" target="_blank" href="https://stirlingpdf.info/s/cm28y3niq000o56dv7liv8wsu" th:text="#{survey.nav}">Survey</a></li>
<li th:if="${@privacyPolicy != ''}"><a class="footer-link px-2" target="_blank" th:href="${@privacyPolicy}" th:text="#{legal.privacy}">privacyPolicy</a></li> <li th:if="${@privacyPolicy != ''}"><a class="footer-link px-2" target="_blank" th:href="${@privacyPolicy}" th:text="#{legal.privacy}">privacyPolicy</a></li>
<li th:if="${@termsAndConditions != ''}"><a class="footer-link px-2" target="_blank" th:href="${@termsAndConditions}" th:text="#{legal.terms}">termsAndConditions</a></li> <li th:if="${@termsAndConditions != ''}"><a class="footer-link px-2" target="_blank" th:href="${@termsAndConditions}" th:text="#{legal.terms}">termsAndConditions</a></li>

View File

@ -29,9 +29,14 @@
<label for="optimizeLevel" th:text="#{compress.selectText.2}"></label> <label for="optimizeLevel" th:text="#{compress.selectText.2}"></label>
<select name="optimizeLevel" id="optimizeLevel" class="form-control"> <select name="optimizeLevel" id="optimizeLevel" class="form-control">
<option value="1">1</option> <option value="1">1</option>
<option value="2" selected>2</option> <option value="2">2</option>
<option value="3">3</option> <option value="3">3</option>
<option value="4" th:text="#{compress.selectText.3}"></option> <option value="4">4</option>
<option value="5" selected>5</option>
<option value="6">6</option>
<option value="7">7</option>
<option value="8">8</option>
<option value="9">9</option>
</select> </select>
</div> </div>
</div> </div>

View File

@ -62,26 +62,6 @@
</div> </div>
<br> <br>
<label for="languages" class="form-label" th:text="#{ocr.selectText.9}"></label> <label for="languages" class="form-label" th:text="#{ocr.selectText.9}"></label>
<div class="form-check ms-3">
<input type="checkbox" name="sidecar" id="sidecar" />
<label for="sidecar" th:text="#{ocr.selectText.2}"></label>
</div>
<div class="form-check ms-3">
<input type="checkbox" name="deskew" id="deskew" />
<label for="deskew" th:text="#{ocr.selectText.3}"></label>
</div>
<div class="form-check ms-3">
<input type="checkbox" name="clean" id="clean" />
<label for="clean" th:text="#{ocr.selectText.4}"></label>
</div>
<div class="form-check ms-3">
<input type="checkbox" name="clean-final" id="clean-final" />
<label for="clean-final" th:text="#{ocr.selectText.5}"></label>
</div>
<div class="form-check ms-3">
<input type="checkbox" name="removeImagesAfter" id="removeImagesAfter" />
<label for="removeImagesAfter" th:text="#{ocr.selectText.11}"></label>
</div>
<div class="mb-3"> <div class="mb-3">
<label th:text="#{ocr.selectText.12}"></label> <label th:text="#{ocr.selectText.12}"></label>
<select class="form-control" name="ocrRenderType"> <select class="form-control" name="ocrRenderType">
@ -239,7 +219,6 @@
}); });
}); });
</script> </script>
<p th:text="#{ocr.credit}"></p>
<p th:text="#{ocr.help}"></p> <p th:text="#{ocr.help}"></p>
<a href="https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToUseOCR.md">https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToUseOCR.md</a> <a href="https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToUseOCR.md">https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToUseOCR.md</a>
</div> </div>

View File

@ -0,0 +1,473 @@
<!DOCTYPE html>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}" xmlns:th="https://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{releases.title}, header=#{releases.title})}"></th:block>
</head>
<body>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8 bg-card">
<div class="tool-header">
<span class="material-symbols-rounded tool-header-icon history">update</span>
<span class="tool-header-text" th:text="#{releases.header}">Release Notes</span>
</div>
<div class="alert alert-info" role="alert">
<strong th:text="#{releases.current.version}">Current Installed Version</strong>:
<span id="currentVersion" th:text="${@appVersion}"></span>
</div>
<div class="alert alert-warning" role="alert">
<span th:text="#{releases.note}">All release notes are only available in English</span>
</div>
<div id="loading" class="text-center my-4">
<div class="spinner-border text-primary" role="status">
<span class="visually-hidden">Loading...</span>
</div>
</div>
<div id="error-message" class="alert alert-danger d-none" role="alert">
Failed to load release notes. Please try again later.
</div>
<!-- Release Notes Container -->
<div id="release-notes-container" class="release-notes-container">
<!-- Release notes will be dynamically inserted here -->
</div>
</div>
</div>
</div>
</div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
<style>
.release-notes-container {
margin-top: 2rem;
}
.release-card {
border: 1px solid #dee2e6;
border-radius: 0.25rem;
margin-bottom: 1.5rem;
padding: 1rem;
}
.release-card.current-version {
border-color: #28a745;
background-color: rgba(40, 167, 69, 0.05);
}
.release-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1rem;
}
.release-header h3 {
margin: 0;
display: flex;
gap: 1rem;
align-items: center;
}
.version {
font-weight: bold;
}
.release-date {
color: #6c757d;
font-size: 0.9em;
}
.release-body {
font-size: 0.9rem;
white-space: pre-wrap;
word-break: break-word;
}
.release-link {
color: #0d6efd;
text-decoration: none;
}
.release-link:hover {
text-decoration: underline;
}
</style>
<script th:inline="javascript">
/*<![CDATA[*/
// Get the current version from the appVersion bean
const appVersion = [[${@appVersion}]];
// GitHub API configuration
const REPO_OWNER = 'Stirling-Tools';
const REPO_NAME = 'Stirling-PDF';
const GITHUB_API = 'https://api.github.com/repos/' + REPO_OWNER + '/' + REPO_NAME;
const GITHUB_URL = 'https://github.com/' + REPO_OWNER + '/' + REPO_NAME;
const MAX_RELEASES = 8;
// Secure element creation helper
function createElement(tag, attributes = {}, children = []) {
const element = document.createElement(tag);
Object.entries(attributes).forEach(([key, value]) => {
if (typeof value === 'string') {
element.setAttribute(key, value);
}
});
children.forEach(child => {
if (typeof child === 'string') {
element.appendChild(document.createTextNode(child));
} else if (child instanceof Node) {
element.appendChild(child);
}
});
return element;
}
const ALLOWED_TAGS = {
'a': ['href', 'target', 'rel', 'class'],
'img': ['src', 'alt', 'width', 'height', 'style'],
'br': [],
'p': [],
'div': [],
'span': []
};
// Function to safely create HTML elements from string
function createSafeElement(htmlString) {
try {
const parser = new DOMParser();
const doc = parser.parseFromString(htmlString, 'text/html');
function sanitizeNode(node) {
// Safety check for null/undefined
if (!node) return null;
// Handle text nodes
if (node.nodeType === Node.TEXT_NODE) {
return node.cloneNode(true);
}
// Handle element nodes
if (node.nodeType === Node.ELEMENT_NODE) {
const tagName = node.tagName.toLowerCase();
// Check if tag is allowed
if (!ALLOWED_TAGS[tagName]) {
return document.createTextNode(node.textContent);
}
// Create new element
const cleanElement = document.createElement(tagName);
// Copy allowed attributes
const allowedAttributes = ALLOWED_TAGS[tagName];
Array.from(node.attributes).forEach(attr => {
if (allowedAttributes.includes(attr.name)) {
let value = attr.value;
if (attr.name === 'href' || attr.name === 'src') {
try {
value = encodeURI(value);
} catch {
return;
}
}
cleanElement.setAttribute(attr.name, value);
}
});
// Add security attributes for links
if (tagName === 'a') {
cleanElement.setAttribute('rel', 'noopener noreferrer');
}
// Process children
Array.from(node.childNodes).forEach(child => {
const cleanChild = sanitizeNode(child);
if (cleanChild) {
cleanElement.appendChild(cleanChild);
}
});
return cleanElement;
}
// If not text or element node, return null
return null;
}
// Get the actual content from the body
const content = doc.body.children;
if (!content || content.length === 0) {
return null;
}
// If it's a single element, process it directly
if (content.length === 1) {
return sanitizeNode(content[0]);
}
// If multiple elements, wrap them in a div
const wrapper = document.createElement('div');
Array.from(content).forEach(node => {
const cleanNode = sanitizeNode(node);
if (cleanNode) {
wrapper.appendChild(cleanNode);
}
});
return wrapper;
} catch (error) {
console.error('Error parsing HTML:', error);
return null;
}
}
function processGitHubReferences(text) {
if (!text || typeof text !== 'string') {
return text;
}
let currentText = text;
let match;
let lastIndex = 0;
const result = document.createElement('span');
const urlRegex = new RegExp('https://github\\.com/' + REPO_OWNER + '/' + REPO_NAME + '/(?:issues|pull)/(\\d+)', 'g');
while ((match = urlRegex.exec(text)) !== null) {
// Add text before the match
if (match.index > lastIndex) {
result.appendChild(document.createTextNode(text.substring(lastIndex, match.index)));
}
// Create link element
const link = document.createElement('a');
link.href = encodeURI(match[0]);
link.textContent = `#${match[1]}`; // Use issue/PR number
link.className = 'release-link';
link.target = '_blank';
link.rel = 'noopener noreferrer';
result.appendChild(link);
lastIndex = match.index + match[0].length;
}
// Add remaining text after last match
if (lastIndex < text.length) {
result.appendChild(document.createTextNode(text.substring(lastIndex)));
}
return result;
}
// Update formatText function to handle processGitHubReferences properly
function formatText(text) {
const container = document.createElement('div');
// Split the text into lines
const lines = text.split('\n');
let currentList = null;
lines.forEach(line => {
const trimmedLine = line.trim();
// Skip empty lines but add spacing
if (!trimmedLine) {
if (currentList) {
container.appendChild(currentList);
currentList = null;
}
container.appendChild(document.createElement('br'));
return;
}
// Check if the line is HTML
if (trimmedLine.startsWith('<') && trimmedLine.endsWith('>')) {
if (currentList) {
container.appendChild(currentList);
currentList = null;
}
const safeElement = createSafeElement(trimmedLine);
if (safeElement) {
container.appendChild(safeElement);
} else {
// If HTML parsing fails, treat as plain text
container.appendChild(document.createTextNode(trimmedLine));
}
return;
}
// Check for headers
const headerMatch = trimmedLine.match(/^(#{1,3})\s+(.+)$/);
if (headerMatch) {
if (currentList) {
container.appendChild(currentList);
currentList = null;
}
const headerLevel = headerMatch[1].length;
const headerContent = headerMatch[2];
// Process GitHub references in headers
const processedContent = processGitHubReferences(headerContent);
const header = createElement(`h${headerLevel}`);
header.appendChild(processedContent);
container.appendChild(header);
return;
}
// Check for bullet points
const bulletMatch = trimmedLine.match(/^[-*]\s+(.+)$/);
if (bulletMatch) {
if (!currentList) {
currentList = document.createElement('ul');
}
const listContent = bulletMatch[1];
const listItem = document.createElement('li');
// Process GitHub references in list items
listItem.appendChild(processGitHubReferences(listContent));
currentList.appendChild(listItem);
return;
}
// If we reach here and have a list, append it
if (currentList) {
container.appendChild(currentList);
currentList = null;
}
// Handle regular paragraph
const paragraph = document.createElement('p');
paragraph.appendChild(processGitHubReferences(trimmedLine));
container.appendChild(paragraph);
});
// Append any remaining list
if (currentList) {
container.appendChild(currentList);
}
return container;
}
const MAX_PREVIOUS_RELEASES = 5;
function compareVersions(v1, v2) {
const normalize = v => v.replace(/^v/, '');
const v1Parts = normalize(v1).split('.').map(Number);
const v2Parts = normalize(v2).split('.').map(Number);
for (let i = 0; i < Math.max(v1Parts.length, v2Parts.length); i++) {
const v1Part = v1Parts[i] || 0;
const v2Part = v2Parts[i] || 0;
if (v1Part > v2Part) return 1;
if (v1Part < v2Part) return -1;
}
return 0;
}
async function loadReleases() {
const container = document.getElementById('release-notes-container');
const loading = document.getElementById('loading');
const errorMessage = document.getElementById('error-message');
try {
loading.classList.remove('d-none');
errorMessage.classList.add('d-none');
// Clear container safely
while (container.firstChild) {
container.removeChild(container.firstChild);
}
const response = await fetch(GITHUB_API + '/releases');
if (!response.ok) throw new Error('Failed to fetch releases');
const releases = await response.json();
// Sort releases by version number (descending)
releases.sort((a, b) => compareVersions(b.tag_name, a.tag_name));
// Find index of current version
const currentVersionIndex = releases.findIndex(release =>
compareVersions(release.tag_name, 'v' + appVersion) === 0 ||
compareVersions(release.tag_name, appVersion) === 0
);
if (currentVersionIndex === -1) {
container.appendChild(createElement('div', {
class: 'alert alert-warning'
}, ['Current version not found in releases.']));
return;
}
// Get current version and 8 previous releases
const endIndex = Math.min(currentVersionIndex + MAX_PREVIOUS_RELEASES + 1, releases.length);
const relevantReleases = releases.slice(currentVersionIndex, endIndex);
if (relevantReleases.length === 0) {
container.appendChild(createElement('div', {
class: 'alert alert-warning'
}, ['No releases found.']));
return;
}
relevantReleases.forEach((release, index) => {
const isCurrentVersion = index === 0; // First release in the array is current version
const releaseCard = createElement('div', {
class: `release-card ${isCurrentVersion ? 'current-version' : ''}`
});
const header = createElement('div', { class: 'release-header' });
const h3 = createElement('h3', {}, [
createElement('span', { class: 'version' }, [release.tag_name]),
createElement('span', { class: 'release-date' }, [
new Date(release.created_at).toLocaleDateString()
])
]);
header.appendChild(h3);
if (isCurrentVersion) {
header.appendChild(createElement('span', {
class: 'badge bg-success'
}, ['Installed']));
}
releaseCard.appendChild(header);
const body = createElement('div', { class: 'release-body' });
body.appendChild(formatText(release.body || 'No release notes available.'));
releaseCard.appendChild(body);
container.appendChild(releaseCard);
});
} catch (error) {
console.error('Error loading releases:', error);
errorMessage.classList.remove('d-none');
} finally {
loading.classList.add('d-none');
}
}
// Load releases when the page loads
document.addEventListener('DOMContentLoaded', loadReleases);
/*]]>*/
</script>
</body>
</html>