Compare commits
14 Commits
v0.34.0
...
pixeebot/g
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5dc0a25b26 | ||
|
|
128cdc90c0 | ||
|
|
5a67b0cfe7 | ||
|
|
d1acda6440 | ||
|
|
5cf3798540 | ||
|
|
b27044016e | ||
|
|
4aec0bd679 | ||
|
|
ab7610f72c | ||
|
|
748392cd29 | ||
|
|
f0810f3952 | ||
|
|
298870ed7d | ||
|
|
6ec2c34c2b | ||
|
|
1db1370420 | ||
|
|
73e64e5898 |
@@ -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.
|
||||||
|
|
||||||
|
|||||||
@@ -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 \
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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 | | | | ✔️ | | | | | | | ✔️ | | |
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
[](https://github.com/Stirling-Tools/Stirling-PDF/)
|
[](https://github.com/Stirling-Tools/Stirling-PDF/)
|
||||||
[](https://github.com/Stirling-Tools/stirling-pdf)
|
[](https://github.com/Stirling-Tools/stirling-pdf)
|
||||||
|
|
||||||
|
<a href="https://www.producthunt.com/posts/stirling-pdf?embed=true&utm_source=badge-featured&utm_medium=badge&utm_souce=badge-stirling-pdf" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=641239&theme=light" alt="Stirling PDF - Open source locally hosted web PDF editor | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a>
|
||||||
[](https://cloud.digitalocean.com/apps/new?repo=https://github.com/Stirling-Tools/Stirling-PDF/tree/digitalOcean&refcode=c3210994b1af)
|
[](https://cloud.digitalocean.com/apps/new?repo=https://github.com/Stirling-Tools/Stirling-PDF/tree/digitalOcean&refcode=c3210994b1af)
|
||||||
|
|
||||||
[Stirling-PDF](https://www.stirlingpdf.com) is a robust, locally hosted web-based PDF manipulation tool using Docker. It enables you to carry out various operations on PDF files, including splitting, merging, converting, reorganizing, adding images, rotating, compressing, and more. This locally hosted web application has evolved to encompass a comprehensive set of features, addressing all your PDF requirements.
|
[Stirling-PDF](https://www.stirlingpdf.com) is a robust, locally hosted web-based PDF manipulation tool using Docker. It enables you to carry out various operations on PDF files, including splitting, merging, converting, reorganizing, adding images, rotating, compressing, and more. This locally hosted web application has evolved to encompass a comprehensive set of features, addressing all your PDF requirements.
|
||||||
@@ -78,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
|
||||||
@@ -101,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)
|
||||||
|
|||||||
@@ -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 |
|
||||||
| ---------------------- | ---------- | ---- |
|
| ---------------------- | ---------- | ---- |
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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");
|
||||||
|
|||||||
@@ -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");
|
||||||
|
|||||||
@@ -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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"));
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|
||||||
|
|||||||
@@ -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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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");
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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"));
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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=تنسيق الإخراج
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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=Изходен формат
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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=Εξόδος αναμορφώσεων
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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=आउटपुट फॉर्मेट
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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=출력 형식
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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=Формат вывода
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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=รูปแบบผลลัพธ์
|
||||||
|
|||||||
@@ -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ı
|
||||||
|
|||||||
@@ -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=Вихідний формат
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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=输出格式
|
||||||
|
|||||||
@@ -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=輸出格式
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
473
src/main/resources/templates/releases.html
Normal file
473
src/main/resources/templates/releases.html
Normal 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>
|
||||||
Reference in New Issue
Block a user