Compare commits

...

31 Commits

Author SHA1 Message Date
pixeebot[bot]
fcc78089ad Hardening suggestions for Stirling-PDF / multipleFix (#1743)
Sandboxed URL creation to prevent SSRF attacks

Co-authored-by: pixeebot[bot] <104101892+pixeebot[bot]@users.noreply.github.com>
2024-08-23 09:18:08 +01:00
a
c4efed87b4 fix 2024-08-23 09:08:38 +01:00
a
31777e9fad multiple file logic cleanup 2024-08-23 09:06:33 +01:00
a
2bbbbf8e38 Merge branch 'main' of github.com:Stirling-Tools/Stirling-PDF 2024-08-23 00:02:25 +01:00
github-actions[bot]
dee912f075 📝 Update README: Translation Progress Table (#1734)
📝 Sync README
> Made via sync_files.yml

Co-authored-by: GitHub Action action@github.com <GitHub Action action@github.com>
2024-08-22 21:47:37 +01:00
albanobattistella
faed367cde Update messages_it_IT.properties (#1732) 2024-08-22 11:54:55 +01:00
Anthony Stirling
788744c1be Update pull_request_template.md 2024-08-21 20:43:13 +01:00
github-actions[bot]
686b88d21d 📝 Update README: Translation Progress Table (#1731)
📝 Sync README
> Made via sync_files.yml

Co-authored-by: GitHub Action action@github.com <GitHub Action action@github.com>
2024-08-21 11:37:45 +01:00
Ludy
1a594b27ab Fix: introduces the verification of the python installation (#1730)
* Fix: introduces the verification of the python installation

* Update ExtractImageScansController.java

* Update CheckProgramInstall.java
2024-08-21 11:16:29 +01:00
Anthony Stirling
9f0088c839 Create release.yml 2024-08-20 22:56:15 +01:00
a
c778fa73ef Merge branch 'main' of git@github.com:Stirling-Tools/Stirling-PDF.git into main 2024-08-20 22:33:05 +01:00
github-actions[bot]
5564a6e730 💾 Update Version (#1727)
💾 Sync Versions
> Made via sync_files.yml

Co-authored-by: GitHub Action action@github.com <GitHub Action action@github.com>
2024-08-20 17:49:33 +01:00
Anthony Stirling
8a58647ffd Update build.gradle 2024-08-20 17:49:10 +01:00
Anthony Stirling
37dcae282a ExtractImagesController. null checks 2024-08-20 17:20:18 +01:00
Ludy
58618b3a21 Add: Convert PDF to WebP (#1666)
* Add PDF to WebP

* add swagger param

* back

* creates a custom image for Docker from pymupdf

* Converting with pdf2image and Pillow instead of pymupdf

* webp remove to pdf-to-img

* remove mupdf
2024-08-20 16:17:54 +01:00
github-actions[bot]
4a4c7faf47 📝 Update README: Translation Progress Table (#1725)
📝 Sync README
> Made via sync_files.yml

Co-authored-by: GitHub Action action@github.com <GitHub Action action@github.com>
2024-08-20 10:59:08 +01:00
github-actions[bot]
66324a5bdc Update 3rd Party Licenses (#1724)
Signed-off-by: GitHub Action <action@github.com>
Co-authored-by: GitHub Action <action@github.com>
2024-08-20 10:57:53 +01:00
Guilherme L. Leite Marques
3bd18f7c5e Updated pt_BR ignored translations; Improved pt_BR translation (#1705)
* Updated pt_BR ignored translations

* Fixed blank lines
2024-08-20 10:57:42 +01:00
dependabot[bot]
e693bbb2bd Bump com.bucket4j:bucket4j_jdk17-core from 8.13.1 to 8.14.0 (#1720)
Bumps [com.bucket4j:bucket4j_jdk17-core](https://github.com/bucket4j/bucket4j) from 8.13.1 to 8.14.0.
- [Release notes](https://github.com/bucket4j/bucket4j/releases)
- [Commits](https://github.com/bucket4j/bucket4j/commits/8.14.0)

---
updated-dependencies:
- dependency-name: com.bucket4j:bucket4j_jdk17-core
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-20 10:55:00 +01:00
Црнобог
f11ad92fa5 Typing error in README.md (#1721)
Misstype |  Sebian => Serbian
2024-08-20 10:54:44 +01:00
Anthony Stirling
f443a4e0de reduce google font size (#1723)
* Delete src/main/resources/static/fonts/google-symbol.woff2

* Add files via upload
2024-08-20 09:57:12 +01:00
PingLin8888
fa0152aa2d Fix ConcurrentModificationException by modifying resources outside the iteration. (#1719)
Fix ConcurrentModificationException by collecting XObject names

- Changed  to use a list to collect XObject names before removal.
- Avoids ConcurrentModificationException by modifying resources outside the iteration.

Co-authored-by: Anthony Stirling <77850077+Frooodle@users.noreply.github.com>
2024-08-19 19:42:55 +01:00
Ludy
e1d0f2cd3e Fix: YamlFile - String length limit disable (#1716)
Co-authored-by: Anthony Stirling <77850077+Frooodle@users.noreply.github.com>
2024-08-19 15:03:05 +01:00
Ludy
81e2a77e57 Fix: Failed authentication #1704 (#1708)
* Fix: Failed authentication #1704

* Update account.html
2024-08-19 15:02:40 +01:00
Ludy
6c9a4e8acc Add Option to Specify Installation Method in Bug Report Template (#1710)
* Add Option to Specify Installation Method in Bug Report Template

* Update 1-bug.yml
2024-08-19 15:00:23 +01:00
albanobattistella
8e5b3ea7f1 Update messages_it_IT.properties (#1713) 2024-08-19 15:00:12 +01:00
Ludy
b47f8a2c17 corrects the link to the country selection (#1717) 2024-08-19 15:00:00 +01:00
Ludy
56a07bbf3a increases some versions in the workflows (#1707) 2024-08-18 13:07:14 +01:00
github-actions[bot]
987d793ef4 Update 3rd Party Licenses (#1701)
Signed-off-by: GitHub Action <action@github.com>
Co-authored-by: GitHub Action <action@github.com>
2024-08-16 13:27:18 +01:00
a
89031246cf Merge branch 'main' of git@github.com:Stirling-Tools/Stirling-PDF.git into main 2024-08-16 13:21:39 +01:00
Anthony Stirling
e5cb9a28ac fix 2024-08-16 13:21:35 +01:00
135 changed files with 1084 additions and 588 deletions

View File

@@ -11,6 +11,20 @@ body:
This issue form is for reporting bugs only. Please fill out the following sections to help us understand the issue you are facing.
- type: dropdown
id: installation-method
attributes:
label: Installation Method
description: |
Indicate whether you are using Docker or a local installation.
options:
- Docker
- Docker ultra lite
- Docker fat
- Local Installation
validations:
required: true
- type: textarea
id: problem
validations:

View File

@@ -2,6 +2,7 @@ Translation:
- changed-files:
- any-glob-to-any-file: 'src/main/resources/messages_*_*.properties'
- any-glob-to-any-file: 'scripts/ignore_translation.toml'
- any-glob-to-any-file: 'src/main/resources/templates/fragments/languages.html'
Front End:
- changed-files:

View File

@@ -10,9 +10,3 @@ Closes #(issue_number)
- [ ] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] My changes generate no new warnings
## Contributor License Agreement
By submitting this pull request, I acknowledge and agree that my contributions will be included in Stirling-PDF and that they can be relicensed in the future under the MPL 2.0 (Mozilla Public License Version 2.0) license.
(This does not change the general open-source nature of Stirling-PDF, simply moving from one license to another license)

32
.github/release.yml vendored Normal file
View File

@@ -0,0 +1,32 @@
changelog:
exclude:
labels:
- Documentation
- Test
- Github
categories:
- title: Bug Fixes
labels:
- Bug
- title: Enhancements
labels:
- enhancement
- title: Minor Enhancements
labels:
- Java
- Front End
- title: Docker Updates
labels:
- Docker
- title: Translation Changes
labels:
- Translation
- title: Other Changes
labels:
- "*"

View File

@@ -31,7 +31,7 @@ jobs:
distribution: "temurin"
- name: Set up Gradle
uses: gradle/actions/setup-gradle@v3
uses: gradle/actions/setup-gradle@v4
with:
gradle-version: 8.7
@@ -74,7 +74,7 @@ jobs:
sudo chmod +x /usr/local/bin/docker-compose
- name: Set up Python
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: "3.7"

View File

@@ -25,7 +25,7 @@ jobs:
java-version: "17"
distribution: "adopt"
- uses: gradle/actions/setup-gradle@v3
- uses: gradle/actions/setup-gradle@v4
- name: Run Gradle Command
run: ./gradlew clean generateLicenseReport
@@ -58,6 +58,7 @@ jobs:
title: "Update 3rd Party Licenses"
body: |
Auto-generated by [create-pull-request][1]
[1]: https://github.com/peter-evans/create-pull-request
labels: licenses
draft: false

View File

@@ -22,7 +22,7 @@ jobs:
java-version: "17"
distribution: "temurin"
- uses: gradle/actions/setup-gradle@v3
- uses: gradle/actions/setup-gradle@v4
with:
gradle-version: 8.7
@@ -72,7 +72,7 @@ jobs:
type=raw,value=alpha,enable=${{ github.ref == 'refs/heads/main' }}
- name: Build and push main Dockerfile
uses: docker/build-push-action@v5
uses: docker/build-push-action@v6
with:
builder: ${{ steps.buildx.outputs.name }}
context: .
@@ -98,7 +98,7 @@ jobs:
type=raw,value=latest-ultra-lite,enable=${{ github.ref == 'refs/heads/master' }}
- name: Build and push Dockerfile-ultra-lite
uses: docker/build-push-action@v5
uses: docker/build-push-action@v6
if: github.ref != 'refs/heads/main'
with:
context: .
@@ -111,7 +111,6 @@ jobs:
build-args: VERSION_TAG=${{ steps.versionNumber.outputs.versionNumber }}
platforms: linux/amd64,linux/arm64/v8
- name: Generate tags fat
id: meta3
uses: docker/metadata-action@v5
@@ -125,7 +124,7 @@ jobs:
type=raw,value=latest-fat,enable=${{ github.ref == 'refs/heads/master' }}
- name: Build and push main Dockerfile fat
uses: docker/build-push-action@v5
uses: docker/build-push-action@v6
if: github.ref != 'refs/heads/main'
with:
builder: ${{ steps.buildx.outputs.name }}

View File

@@ -27,7 +27,7 @@ jobs:
java-version: "17"
distribution: "temurin"
- uses: gradle/actions/setup-gradle@v3
- uses: gradle/actions/setup-gradle@v4
with:
gradle-version: 8.7

View File

@@ -18,7 +18,7 @@ jobs:
java-version: "17"
distribution: "temurin"
- uses: gradle/actions/setup-gradle@v3
- uses: gradle/actions/setup-gradle@v4
- name: Generate Swagger documentation
run: ./gradlew generateOpenApiDocs

View File

@@ -39,16 +39,16 @@ RUN echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/main" | tee -a /et
libreoffice \
# pdftohtml
poppler-utils \
# OCR MY PDF (unpaper for descew and other advanced featues)
# OCR MY PDF (unpaper for descew and other advanced features)
ocrmypdf \
tesseract-ocr-data-eng \
# CV
py3-opencv \
# python3/pip
python3 \
py3-pip && \
py3-pip && \
# uno unoconv and HTML
pip install --break-system-packages --no-cache-dir --upgrade unoconv WeasyPrint && \
pip install --break-system-packages --no-cache-dir --upgrade unoconv WeasyPrint pdf2image pillow && \
mv /usr/share/tessdata /usr/share/tessdata-original && \
mkdir -p $HOME /configs /logs /customFiles /pipeline/watchedFolders /pipeline/finishedFolders && \
fc-cache -f -v && \

View File

@@ -64,7 +64,7 @@ RUN echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/main" | tee -a /et
python3 \
py3-pip && \
# uno unoconv and HTML
pip install --break-system-packages --no-cache-dir --upgrade unoconv WeasyPrint && \
pip install --break-system-packages --no-cache-dir --upgrade unoconv WeasyPrint pdf2image pillow && \
mv /usr/share/tessdata /usr/share/tessdata-original && \
mkdir -p $HOME /configs /logs /customFiles /pipeline/watchedFolders /pipeline/finishedFolders && \
fc-cache -f -v && \

View File

@@ -15,7 +15,7 @@
| file-to-pdf | | ✔️ | | | ✔️ | | | ✔️ | | | |
| img-to-pdf | | ✔️ | | | | | | | | ✔️ | |
| pdf-to-html | | ✔️ | | | ✔️ | | | ✔️ | | | |
| pdf-to-img | | ✔️ | | | | | | | | ✔️ | |
| pdf-to-img | | ✔️ | | | | ✔️ | | | | ✔️ | |
| pdf-to-pdfa | | ✔️ | | | ✔️ | | | | ✔️ | | |
| pdf-to-markdown | | ✔️ | | | | | | | | ✔️ | |
| pdf-to-presentation | | ✔️ | | | ✔️ | | | ✔️ | | | |

View File

@@ -169,42 +169,42 @@ Stirling PDF currently supports 38!
| Language | Progress |
| ------------------------------------------- | -------------------------------------- |
| Arabic (العربية) (ar_AR) | ![45%](https://geps.dev/progress/45) |
| Basque (Euskara) (eu_ES) | ![61%](https://geps.dev/progress/61) |
| Bulgarian (Български) (bg_BG) | ![94%](https://geps.dev/progress/94) |
| Catalan (Català) (ca_CA) | ![48%](https://geps.dev/progress/48) |
| Croatian (Hrvatski) (hr_HR) | ![94%](https://geps.dev/progress/94) |
| Czech (Česky) (cs_CZ) | ![89%](https://geps.dev/progress/89) |
| Arabic (العربية) (ar_AR) | ![44%](https://geps.dev/progress/44) |
| Basque (Euskara) (eu_ES) | ![60%](https://geps.dev/progress/60) |
| Bulgarian (Български) (bg_BG) | ![92%](https://geps.dev/progress/92) |
| Catalan (Català) (ca_CA) | ![47%](https://geps.dev/progress/47) |
| Croatian (Hrvatski) (hr_HR) | ![92%](https://geps.dev/progress/92) |
| Czech (Česky) (cs_CZ) | ![88%](https://geps.dev/progress/88) |
| Danish (Dansk) (da_DK) | ![9%](https://geps.dev/progress/9) |
| Dutch (Nederlands) (nl_NL) | ![95%](https://geps.dev/progress/95) |
| Dutch (Nederlands) (nl_NL) | ![94%](https://geps.dev/progress/94) |
| English (English) (en_GB) | ![100%](https://geps.dev/progress/100) |
| English (US) (en_US) | ![100%](https://geps.dev/progress/100) |
| French (Français) (fr_FR) | ![93%](https://geps.dev/progress/93) |
| German (Deutsch) (de_DE) | ![99%](https://geps.dev/progress/99) |
| Greek (Ελληνικά) (el_GR) | ![81%](https://geps.dev/progress/81) |
| Hindi (हिंदी) (hi_IN) | ![76%](https://geps.dev/progress/76) |
| Hungarian (Magyar) (hu_HU) | ![75%](https://geps.dev/progress/75) |
| Indonesia (Bahasa Indonesia) (id_ID) | ![76%](https://geps.dev/progress/76) |
| Irish (Gaeilge) (ga_IE) | ![98%](https://geps.dev/progress/98) |
| French (Français) (fr_FR) | ![91%](https://geps.dev/progress/91) |
| German (Deutsch) (de_DE) | ![98%](https://geps.dev/progress/98) |
| Greek (Ελληνικά) (el_GR) | ![80%](https://geps.dev/progress/80) |
| Hindi (हिंदी) (hi_IN) | ![75%](https://geps.dev/progress/75) |
| Hungarian (Magyar) (hu_HU) | ![74%](https://geps.dev/progress/74) |
| Indonesia (Bahasa Indonesia) (id_ID) | ![74%](https://geps.dev/progress/74) |
| Irish (Gaeilge) (ga_IE) | ![96%](https://geps.dev/progress/96) |
| Italian (Italiano) (it_IT) | ![99%](https://geps.dev/progress/99) |
| Japanese (日本語) (ja_JP) | ![92%](https://geps.dev/progress/92) |
| Korean (한국어) (ko_KR) | ![84%](https://geps.dev/progress/84) |
| Norwegian (Norsk) (no_NB) | ![97%](https://geps.dev/progress/97) |
| Polish (Polski) (pl_PL) | ![92%](https://geps.dev/progress/92) |
| Portuguese (Português) (pt_PT) | ![78%](https://geps.dev/progress/78) |
| Portuguese Brazilian (Português) (pt_BR) | ![85%](https://geps.dev/progress/85) |
| Japanese (日本語) (ja_JP) | ![90%](https://geps.dev/progress/90) |
| Korean (한국어) (ko_KR) | ![82%](https://geps.dev/progress/82) |
| Norwegian (Norsk) (no_NB) | ![96%](https://geps.dev/progress/96) |
| Polish (Polski) (pl_PL) | ![90%](https://geps.dev/progress/90) |
| Portuguese (Português) (pt_PT) | ![76%](https://geps.dev/progress/76) |
| Portuguese Brazilian (Português) (pt_BR) | ![99%](https://geps.dev/progress/99) |
| Romanian (Română) (ro_RO) | ![38%](https://geps.dev/progress/38) |
| Russian (Русский) (ru_RU) | ![83%](https://geps.dev/progress/83) |
| Sebian Latin alphabet (Srpski) (sr_LATN_RS) | ![78%](https://geps.dev/progress/78) |
| Simplified Chinese (简体中文) (zh_CN) | ![98%](https://geps.dev/progress/98) |
| Slovakian (Slovensky) (sk_SK) | ![91%](https://geps.dev/progress/91) |
| Spanish (Español) (es_ES) | ![97%](https://geps.dev/progress/97) |
| Swedish (Svenska) (sv_SE) | ![39%](https://geps.dev/progress/39) |
| Thai (ไทย) (th_TH) | ![99%](https://geps.dev/progress/99) |
| Traditional Chinese (繁體中文) (zh_TW) | ![97%](https://geps.dev/progress/97) |
| Turkish (Türkçe) (tr_TR) | ![98%](https://geps.dev/progress/98) |
| Ukrainian (Українська) (uk_UA) | ![89%](https://geps.dev/progress/89) |
| Vietnamese (Tiếng Việt) (vi_VN) | ![98%](https://geps.dev/progress/98) |
| Russian (Русский) (ru_RU) | ![82%](https://geps.dev/progress/82) |
| Serbian Latin alphabet (Srpski) (sr_LATN_RS) | ![76%](https://geps.dev/progress/76) |
| Simplified Chinese (简体中文) (zh_CN) | ![97%](https://geps.dev/progress/97) |
| Slovakian (Slovensky) (sk_SK) | ![90%](https://geps.dev/progress/90) |
| Spanish (Español) (es_ES) | ![96%](https://geps.dev/progress/96) |
| Swedish (Svenska) (sv_SE) | ![38%](https://geps.dev/progress/38) |
| Thai (ไทย) (th_TH) | ![97%](https://geps.dev/progress/97) |
| Traditional Chinese (繁體中文) (zh_TW) | ![96%](https://geps.dev/progress/96) |
| Turkish (Türkçe) (tr_TR) | ![97%](https://geps.dev/progress/97) |
| Ukrainian (Українська) (uk_UA) | ![88%](https://geps.dev/progress/88) |
| Vietnamese (Tiếng Việt) (vi_VN) | ![97%](https://geps.dev/progress/97) |
## Contributing (creating issues, translations, fixing bugs, etc.)

View File

@@ -21,7 +21,7 @@ ext {
}
group = "stirling.software"
version = "0.28.1"
version = "0.28.2"
java {
// 17 is lowest but we support and recommend 21
@@ -189,7 +189,7 @@ dependencies {
implementation "org.commonmark:commonmark:0.22.0"
implementation "org.commonmark:commonmark-ext-gfm-tables:0.22.0"
// https://mvnrepository.com/artifact/com.bucket4j/bucket4j_jdk17
implementation "com.bucket4j:bucket4j_jdk17-core:8.13.1"
implementation "com.bucket4j:bucket4j_jdk17-core:8.14.0"
implementation "com.fathzer:javaluator:3.0.4"
developmentOnly("org.springframework.boot:spring-boot-devtools:$springBootVersion")

View File

@@ -1,5 +1,5 @@
apiVersion: v2
appVersion: 0.28.1
appVersion: 0.28.2
description: locally hosted web application that allows you to perform various operations
on PDF files
home: https://github.com/Stirling-Tools/Stirling-PDF

View File

@@ -181,7 +181,9 @@ ignore = [
[pt_BR]
ignore = [
'changeMetadata.trapped',
'language.direction',
'pipelineOptions.pipelineHeader',
]
[pt_PT]

174
scripts/png_to_webp.py Normal file
View File

@@ -0,0 +1,174 @@
"""
Author: Ludy87
Description: This script converts a PDF file to WebP images. It includes functionality to resize images if they exceed specified dimensions and handle conversion of PDF pages to WebP format.
Example
-------
To convert a PDF file to WebP images with each page as a separate WebP file:
python script.py input.pdf output_directory
To convert a PDF file to a single WebP image:
python script.py input.pdf output_directory --single
To adjust the DPI resolution for rendering PDF pages:
python script.py input.pdf output_directory --dpi 150
"""
import argparse
import os
from pdf2image import convert_from_path
from PIL import Image
def resize_image(input_image_path, output_image_path, max_size=(16383, 16383)):
"""
Resize the image if its dimensions exceed the maximum allowed size and save it as WebP.
Parameters
----------
input_image_path : str
Path to the input image file.
output_image_path : str
Path where the output WebP image will be saved.
max_size : tuple of int, optional
Maximum allowed dimensions for the image (width, height). Default is (16383, 16383).
Returns
-------
None
"""
try:
# Open the image
image = Image.open(input_image_path)
width, height = image.size
max_width, max_height = max_size
# Check if the image dimensions exceed the maximum allowed dimensions
if width > max_width or height > max_height:
# Calculate the scaling ratio
ratio = min(max_width / width, max_height / height)
new_width = int(width * ratio)
new_height = int(height * ratio)
# Resize the image
resized_image = image.resize((new_width, new_height), Image.LANCZOS)
resized_image.save(output_image_path, format="WEBP", quality=100)
print(
f"The image was successfully resized to ({new_width}, {new_height}) and saved as WebP: {output_image_path}"
)
else:
# If dimensions are within the allowed limits, save the image directly
image.save(output_image_path, format="WEBP", quality=100)
print(f"The image was successfully saved as WebP: {output_image_path}")
except Exception as e:
print(f"An error occurred: {e}")
def convert_image_to_webp(input_image, output_file):
"""
Convert an image to WebP format, resizing it if it exceeds the maximum dimensions.
Parameters
----------
input_image : str
Path to the input image file.
output_file : str
Path where the output WebP image will be saved.
Returns
-------
None
"""
# Resize the image if it exceeds the maximum dimensions
resize_image(input_image, output_file, max_size=(16383, 16383))
def pdf_to_webp(pdf_path, output_dir, dpi=300):
"""
Convert each page of a PDF file to WebP images.
Parameters
----------
pdf_path : str
Path to the input PDF file.
output_dir : str
Directory where the WebP images will be saved.
dpi : int, optional
DPI resolution for rendering PDF pages. Default is 300.
Returns
-------
None
"""
# Convert the PDF to a list of images
images = convert_from_path(pdf_path, dpi=dpi)
for page_number, image in enumerate(images):
# Define temporary PNG path
temp_png_path = os.path.join(output_dir, f"temp_page_{page_number + 1}.png")
image.save(temp_png_path, format="PNG")
# Define the output path for WebP
output_path = os.path.join(output_dir, f"page_{page_number + 1}.webp")
# Convert PNG to WebP
convert_image_to_webp(temp_png_path, output_path)
# Delete the temporary PNG file
os.remove(temp_png_path)
def main(pdf_image_path, output_dir, dpi=300, single_images_flag=False):
"""
Main function to handle conversion from PDF to WebP images.
Parameters
----------
pdf_image_path : str
Path to the input PDF file or image.
output_dir : str
Directory where the WebP images will be saved.
dpi : int, optional
DPI resolution for rendering PDF pages. Default is 300.
single_images_flag : bool, optional
If True, combine all pages into a single WebP image. Default is False.
Returns
-------
None
"""
if single_images_flag:
# Combine all pages into a single WebP image
output_path = os.path.join(output_dir, "combined_image.webp")
convert_image_to_webp(pdf_image_path, output_path)
else:
# Convert each PDF page to a separate WebP image
pdf_to_webp(pdf_image_path, output_dir, dpi)
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Convert a PDF file to WebP images.")
parser.add_argument("pdf_path", help="The path to the input PDF file.")
parser.add_argument(
"output_dir", help="The directory where the WebP images should be saved."
)
parser.add_argument(
"--dpi",
type=int,
default=300,
help="The DPI resolution for rendering the PDF pages (default: 300).",
)
parser.add_argument(
"--single",
action="store_true",
help="Combine all pages into a single WebP image.",
)
args = parser.parse_args()
os.makedirs(args.output_dir, exist_ok=True)
main(
args.pdf_path,
args.output_dir,
dpi=args.dpi,
single_images_flag=args.single,
)

View File

@@ -14,6 +14,8 @@ import java.util.List;
import org.simpleyaml.configuration.comments.CommentType;
import org.simpleyaml.configuration.file.YamlFile;
import org.simpleyaml.configuration.implementation.SimpleYamlImplementation;
import org.simpleyaml.configuration.implementation.snakeyaml.lib.DumperOptions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContextInitializer;
@@ -71,9 +73,17 @@ public class ConfigInitializer
}
final YamlFile settingsTemplateFile = new YamlFile(tempTemplatePath.toFile());
DumperOptions yamlOptionsSettingsTemplateFile =
((SimpleYamlImplementation) settingsTemplateFile.getImplementation())
.getDumperOptions();
yamlOptionsSettingsTemplateFile.setSplitLines(false);
settingsTemplateFile.loadWithComments();
final YamlFile settingsFile = new YamlFile(settingsPath.toFile());
DumperOptions yamlOptionsSettingsFile =
((SimpleYamlImplementation) settingsFile.getImplementation())
.getDumperOptions();
yamlOptionsSettingsFile.setSplitLines(false);
settingsFile.loadWithComments();
// Load headers and comments
@@ -81,6 +91,10 @@ public class ConfigInitializer
// Create a new file for temporary settings
final YamlFile tempSettingFile = new YamlFile(settingsPath.toFile());
DumperOptions yamlOptionsTempSettingFile =
((SimpleYamlImplementation) tempSettingFile.getImplementation())
.getDumperOptions();
yamlOptionsTempSettingFile.setSplitLines(false);
tempSettingFile.createNewFile(true);
tempSettingFile.setHeader(header);

View File

@@ -166,6 +166,7 @@ public class EndpointConfiguration {
addEndpointToGroup("Python", REMOVE_BLANKS);
addEndpointToGroup("Python", "html-to-pdf");
addEndpointToGroup("Python", "url-to-pdf");
addEndpointToGroup("Python", "pdf-to-img");
// openCV
addEndpointToGroup("OpenCV", "extract-image-scans");

View File

@@ -6,6 +6,8 @@ import java.nio.file.Paths;
import java.util.UUID;
import org.simpleyaml.configuration.file.YamlFile;
import org.simpleyaml.configuration.implementation.SimpleYamlImplementation;
import org.simpleyaml.configuration.implementation.snakeyaml.lib.DumperOptions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -92,6 +94,9 @@ public class InitialSecuritySetup {
Path path = Paths.get("configs", "settings.yml"); // Target the configs/settings.yml
final YamlFile settingsYml = new YamlFile(path.toFile());
DumperOptions yamlOptionssettingsYml =
((SimpleYamlImplementation) settingsYml.getImplementation()).getDumperOptions();
yamlOptionssettingsYml.setSplitLines(false);
settingsYml.loadWithComments();

View File

@@ -2,6 +2,8 @@ package stirling.software.SPDF.config.security;
import java.io.IOException;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
@@ -9,6 +11,7 @@ import org.springframework.context.annotation.Lazy;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.session.SessionInformation;
import org.springframework.security.core.userdetails.UserDetails;
@@ -22,6 +25,7 @@ import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import stirling.software.SPDF.config.security.session.SessionPersistentRegistry;
import stirling.software.SPDF.model.ApiKeyAuthenticationToken;
import stirling.software.SPDF.model.User;
@Component
public class UserAuthenticationFilter extends OncePerRequestFilter {
@@ -54,15 +58,20 @@ public class UserAuthenticationFilter extends OncePerRequestFilter {
try {
// Use API key to authenticate. This requires you to have an authentication
// provider for API keys.
UserDetails userDetails = userService.loadUserByApiKey(apiKey);
if (userDetails == null) {
Optional<User> user = userService.loadUserByApiKey(apiKey);
if (!user.isPresent()) {
response.setStatus(HttpStatus.UNAUTHORIZED.value());
response.getWriter().write("Invalid API Key.");
return;
}
authentication =
new ApiKeyAuthenticationToken(
userDetails, apiKey, userDetails.getAuthorities());
List<SimpleGrantedAuthority> authorities =
user.get().getAuthorities().stream()
.map(
authority ->
new SimpleGrantedAuthority(
authority.getAuthority()))
.collect(Collectors.toList());
authentication = new ApiKeyAuthenticationToken(user.get(), apiKey, authorities);
SecurityContextHolder.getContext().setAuthentication(authentication);
} catch (AuthenticationException e) {
// If API key authentication fails, deny the request

View File

@@ -65,8 +65,8 @@ public class UserService implements UserServiceInterface {
}
public Authentication getAuthentication(String apiKey) {
User user = getUserByApiKey(apiKey);
if (user == null) {
Optional<User> user = getUserByApiKey(apiKey);
if (!user.isPresent()) {
throw new UsernameNotFoundException("API key is not valid");
}
@@ -74,7 +74,7 @@ public class UserService implements UserServiceInterface {
return new UsernamePasswordAuthenticationToken(
user, // principal (typically the user)
null, // credentials (we don't expose the password or API key here)
getAuthorities(user) // user's authorities (roles/permissions)
getAuthorities(user.get()) // user's authorities (roles/permissions)
);
}
@@ -89,17 +89,17 @@ public class UserService implements UserServiceInterface {
String apiKey;
do {
apiKey = UUID.randomUUID().toString();
} while (userRepository.findByApiKey(apiKey) != null); // Ensure uniqueness
} while (userRepository.findByApiKey(apiKey).isPresent()); // Ensure uniqueness
return apiKey;
}
public User addApiKeyToUser(String username) {
User user =
findByUsernameIgnoreCase(username)
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
user.setApiKey(generateApiKey());
return userRepository.save(user);
Optional<User> user = findByUsernameIgnoreCase(username);
if (user.isPresent()) {
user.get().setApiKey(generateApiKey());
return userRepository.save(user.get());
}
throw new UsernameNotFoundException("User not found");
}
public User refreshApiKeyForUser(String username) {
@@ -114,21 +114,18 @@ public class UserService implements UserServiceInterface {
}
public boolean isValidApiKey(String apiKey) {
return userRepository.findByApiKey(apiKey) != null;
return userRepository.findByApiKey(apiKey).isPresent();
}
public User getUserByApiKey(String apiKey) {
public Optional<User> getUserByApiKey(String apiKey) {
return userRepository.findByApiKey(apiKey);
}
public UserDetails loadUserByApiKey(String apiKey) {
User user = userRepository.findByApiKey(apiKey);
if (user != null) {
// Convert your User entity to a UserDetails object with authorities
return new org.springframework.security.core.userdetails.User(
user.getUsername(),
user.getPassword(), // you might not need this for API key auth
getAuthorities(user));
public Optional<User> loadUserByApiKey(String apiKey) {
Optional<User> user = userRepository.findByApiKey(apiKey);
if (user.isPresent()) {
return user;
}
return null; // or throw an exception
}

View File

@@ -1,8 +1,18 @@
package stirling.software.SPDF.controller.api.converters;
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URLConnection;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import org.apache.commons.io.FileUtils;
import org.apache.pdfbox.rendering.ImageType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -20,7 +30,10 @@ import io.swagger.v3.oas.annotations.tags.Tag;
import stirling.software.SPDF.model.api.converters.ConvertToImageRequest;
import stirling.software.SPDF.model.api.converters.ConvertToPdfRequest;
import stirling.software.SPDF.utils.CheckProgramInstall;
import stirling.software.SPDF.utils.PdfUtils;
import stirling.software.SPDF.utils.ProcessExecutor;
import stirling.software.SPDF.utils.ProcessExecutor.ProcessExecutorResult;
import stirling.software.SPDF.utils.WebResponseUtils;
@RestController
@@ -60,15 +73,87 @@ public class ConvertImgPDFController {
result =
PdfUtils.convertFromPdf(
pdfBytes,
imageFormat.toUpperCase(),
imageFormat.equalsIgnoreCase("webp") ? "png" : imageFormat.toUpperCase(),
colorTypeResult,
singleImage,
Integer.valueOf(dpi),
filename);
if (result == null || result.length == 0) {
logger.error("resultant bytes for {} is null, error converting ", filename);
}
if (imageFormat.equalsIgnoreCase("webp") && !CheckProgramInstall.isPythonAvailable()) {
throw new IOException("Python is not installed. Required for WebP conversion.");
} else if (imageFormat.equalsIgnoreCase("webp")
&& CheckProgramInstall.isPythonAvailable()) {
// Write the output stream to a temp file
Path tempFile = Files.createTempFile("temp_png", ".png");
try (FileOutputStream fos = new FileOutputStream(tempFile.toFile())) {
fos.write(result);
fos.flush();
}
String pythonVersion = CheckProgramInstall.getAvailablePythonCommand();
List<String> command = new ArrayList<>();
command.add(pythonVersion);
command.add("./scripts/png_to_webp.py"); // Python script to handle the conversion
// Create a temporary directory for the output WebP files
Path tempOutputDir = Files.createTempDirectory("webp_output");
if (singleImage) {
// Run the Python script to convert PNG to WebP
command.add(tempFile.toString());
command.add(tempOutputDir.toString());
command.add("--single");
} else {
// Save the uploaded PDF to a temporary file
Path tempPdfPath = Files.createTempFile("temp_pdf", ".pdf");
file.transferTo(tempPdfPath.toFile());
// Run the Python script to convert PDF to WebP
command.add(tempPdfPath.toString());
command.add(tempOutputDir.toString());
}
command.add("--dpi");
command.add(dpi);
ProcessExecutorResult resultProcess =
ProcessExecutor.getInstance(ProcessExecutor.Processes.PYTHON_OPENCV)
.runCommandWithOutputHandling(command);
// Find all WebP files in the output directory
List<Path> webpFiles =
Files.walk(tempOutputDir)
.filter(path -> path.toString().endsWith(".webp"))
.collect(Collectors.toList());
if (webpFiles.isEmpty()) {
logger.error("No WebP files were created in: {}", tempOutputDir.toString());
throw new IOException("No WebP files were created. " + resultProcess.getMessages());
}
byte[] bodyBytes = new byte[0];
if (webpFiles.size() == 1) {
// Return the single WebP file directly
Path webpFilePath = webpFiles.get(0);
bodyBytes = Files.readAllBytes(webpFilePath);
} else {
// Create a ZIP file containing all WebP images
ByteArrayOutputStream zipOutputStream = new ByteArrayOutputStream();
try (ZipOutputStream zos = new ZipOutputStream(zipOutputStream)) {
for (Path webpFile : webpFiles) {
zos.putNextEntry(new ZipEntry(webpFile.getFileName().toString()));
Files.copy(webpFile, zos);
zos.closeEntry();
}
}
bodyBytes = zipOutputStream.toByteArray();
}
// Clean up the temporary files
Files.deleteIfExists(tempFile);
if (tempOutputDir != null) FileUtils.deleteDirectory(tempOutputDir.toFile());
result = bodyBytes;
}
if (singleImage) {
String docName = filename + "." + imageFormat;
MediaType mediaType = MediaType.parseMediaType(getMediaType(imageFormat));

View File

@@ -32,6 +32,7 @@ import io.swagger.v3.oas.annotations.parameters.RequestBody;
import io.swagger.v3.oas.annotations.tags.Tag;
import stirling.software.SPDF.model.api.misc.ExtractImageScansRequest;
import stirling.software.SPDF.utils.CheckProgramInstall;
import stirling.software.SPDF.utils.ProcessExecutor;
import stirling.software.SPDF.utils.ProcessExecutor.ProcessExecutorResult;
import stirling.software.SPDF.utils.WebResponseUtils;
@@ -76,6 +77,11 @@ public class ExtractImageScansController {
Path tempZipFile = null;
List<Path> tempDirs = new ArrayList<>();
if (!CheckProgramInstall.isPythonAvailable()) {
throw new IOException("Python is not installed.");
}
String pythonVersion = CheckProgramInstall.getAvailablePythonCommand();
try {
// Check if input file is a PDF
if ("pdf".equalsIgnoreCase(extension)) {
@@ -117,7 +123,7 @@ public class ExtractImageScansController {
List<String> command =
new ArrayList<>(
Arrays.asList(
"python3",
pythonVersion,
"./scripts/split_photos.py",
images.get(i),
tempDir.toString(),

View File

@@ -140,6 +140,9 @@ public class ExtractImagesController {
Set<Integer> processedImages,
ZipOutputStream zos)
throws IOException {
if (page.getResources() == null || page.getResources().getXObjectNames() == null) {
return;
}
for (COSName name : page.getResources().getXObjectNames()) {
if (page.getResources().isImageXObject(name)) {
PDImageXObject image = (PDImageXObject) page.getResources().getXObject(name);

View File

@@ -9,6 +9,8 @@ import org.springframework.web.servlet.ModelAndView;
import io.swagger.v3.oas.annotations.Hidden;
import io.swagger.v3.oas.annotations.tags.Tag;
import stirling.software.SPDF.utils.CheckProgramInstall;
@Controller
@Tag(name = "Convert", description = "Convert APIs")
public class ConverterWebController {
@@ -21,14 +23,6 @@ public class ConverterWebController {
return "convert/book-to-pdf";
}
@ConditionalOnExpression("#{bookAndHtmlFormatsInstalled}")
@GetMapping("/pdf-to-book")
@Hidden
public String convertPdfToBookForm(Model model) {
model.addAttribute("currentPage", "pdf-to-book");
return "convert/pdf-to-book";
}
@GetMapping("/img-to-pdf")
@Hidden
public String convertImgToPdfForm(Model model) {
@@ -57,13 +51,6 @@ public class ConverterWebController {
return "convert/url-to-pdf";
}
@GetMapping("/pdf-to-img")
@Hidden
public String pdfToimgForm(Model model) {
model.addAttribute("currentPage", "pdf-to-img");
return "convert/pdf-to-img";
}
@GetMapping("/file-to-pdf")
@Hidden
public String convertToPdfForm(Model model) {
@@ -73,6 +60,23 @@ public class ConverterWebController {
// PDF TO......
@ConditionalOnExpression("#{bookAndHtmlFormatsInstalled}")
@GetMapping("/pdf-to-book")
@Hidden
public String convertPdfToBookForm(Model model) {
model.addAttribute("currentPage", "pdf-to-book");
return "convert/pdf-to-book";
}
@GetMapping("/pdf-to-img")
@Hidden
public String pdfToimgForm(Model model) {
boolean isPython = CheckProgramInstall.isPythonAvailable();
model.addAttribute("isPython", isPython);
model.addAttribute("currentPage", "pdf-to-img");
return "convert/pdf-to-img";
}
@GetMapping("/pdf-to-html")
@Hidden
public ModelAndView pdfToHTML() {

View File

@@ -16,6 +16,7 @@ import io.swagger.v3.oas.annotations.Hidden;
import io.swagger.v3.oas.annotations.tags.Tag;
import stirling.software.SPDF.model.ApplicationProperties;
import stirling.software.SPDF.utils.CheckProgramInstall;
@Controller
@Tag(name = "Misc", description = "Miscellaneous APIs")
@@ -34,6 +35,8 @@ public class OtherWebController {
@Hidden
public ModelAndView extractImageScansForm() {
ModelAndView modelAndView = new ModelAndView("misc/extract-image-scans");
boolean isPython = CheckProgramInstall.isPythonAvailable();
modelAndView.addObject("isPython", isPython);
modelAndView.addObject("currentPage", "extract-image-scans");
return modelAndView;
}

View File

@@ -1,5 +1,7 @@
package stirling.software.SPDF.model;
import java.io.Serializable;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
@@ -11,7 +13,9 @@ import jakarta.persistence.Table;
@Entity
@Table(name = "authorities")
public class Authority {
public class Authority implements Serializable {
private static final long serialVersionUID = 1L;
public Authority() {}

View File

@@ -1,5 +1,6 @@
package stirling.software.SPDF.model;
import java.io.Serializable;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
@@ -23,7 +24,9 @@ import jakarta.persistence.Table;
@Entity
@Table(name = "users")
public class User {
public class User implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)

View File

@@ -12,7 +12,7 @@ public class ConvertToImageRequest extends PDFFile {
@Schema(
description = "The output image format",
allowableValues = {"png", "jpeg", "jpg", "gif"})
allowableValues = {"png", "jpeg", "jpg", "gif", "webp"})
private String imageFormat;
@Schema(

View File

@@ -13,5 +13,5 @@ public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByUsername(String username);
User findByApiKey(String apiKey);
Optional<User> findByApiKey(String apiKey);
}

View File

@@ -1,6 +1,8 @@
package stirling.software.SPDF.service;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.pdmodel.PDDocument;
@@ -16,7 +18,7 @@ public class PdfImageRemovalService {
/**
* Removes all image objects from the provided PDF document.
*
* This method iterates over each page in the document and removes any image XObjects found
* <p>This method iterates over each page in the document and removes any image XObjects found
* in the page's resources.
*
* @param document The PDF document from which images will be removed.
@@ -27,14 +29,22 @@ public class PdfImageRemovalService {
// Iterate over each page in the PDF document
for (PDPage page : document.getPages()) {
PDResources resources = page.getResources();
// Collect the XObject names to remove
List<COSName> namesToRemove = new ArrayList<>();
// Iterate over all XObject names in the page's resources
for (COSName name : resources.getXObjectNames()) {
// Check if the XObject is an image
if (resources.isImageXObject(name)) {
// Remove the image XObject by setting it to null
resources.put(name, (PDXObject) null);
// Collect the name for removal
namesToRemove.add(name);
}
}
// Now, modify the resources by removing the collected names
for (COSName name : namesToRemove) {
resources.put(name, (PDXObject) null);
}
}
return document;
}

View File

@@ -0,0 +1,59 @@
package stirling.software.SPDF.utils;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import stirling.software.SPDF.utils.ProcessExecutor.ProcessExecutorResult;
public class CheckProgramInstall {
private static final List<String> PYTHON_COMMANDS = Arrays.asList("python3", "python");
private static boolean pythonAvailableChecked = false;
private static String availablePythonCommand = null;
/**
* Checks which Python command is available and returns it.
*
* @return The available Python command ("python3" or "python"), or null if neither is
* available.
*/
public static String getAvailablePythonCommand() {
if (!pythonAvailableChecked) {
availablePythonCommand =
PYTHON_COMMANDS.stream()
.filter(CheckProgramInstall::checkPythonVersion)
.findFirst()
.orElse(null);
pythonAvailableChecked = true;
}
return availablePythonCommand;
}
/**
* Checks if the specified command is available by running the command with --version.
*
* @param pythonCommand The Python command to check.
* @return true if the command is available, false otherwise.
*/
private static boolean checkPythonVersion(String pythonCommand) {
try {
ProcessExecutorResult result =
ProcessExecutor.getInstance(ProcessExecutor.Processes.PYTHON_OPENCV)
.runCommandWithOutputHandling(
Arrays.asList(pythonCommand, "--version"));
return true; // Command succeeded, Python is available
} catch (IOException | InterruptedException e) {
return false; // Command failed, Python is not available
}
}
/**
* Checks if any Python command is available.
*
* @return true if any Python command is available, false otherwise.
*/
public static boolean isPythonAvailable() {
return getAvailablePythonCommand() != null;
}
}

View File

@@ -4,7 +4,9 @@ import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -13,8 +15,6 @@ import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.List;
import java.net.URL;
import java.net.HttpURLConnection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -73,12 +73,11 @@ public class GeneralUtils {
} catch (MalformedURLException e) {
return false;
}
}
public static boolean isURLReachable(String urlStr) {
try {
URL url = new URL(urlStr);
URL url = Urls.create(urlStr, Urls.HTTP_PROTOCOLS, HostValidator.DENY_COMMON_INFRASTRUCTURE_TARGETS);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("HEAD");
int responseCode = connection.getResponseCode();
@@ -112,16 +111,19 @@ public class GeneralUtils {
sizeStr = sizeStr.replace(",", ".").replace(" ", "");
try {
if (sizeStr.endsWith("KB")) {
return (long) (Double.parseDouble(sizeStr.substring(0, sizeStr.length() - 2)) * 1024);
return (long)
(Double.parseDouble(sizeStr.substring(0, sizeStr.length() - 2)) * 1024);
} else if (sizeStr.endsWith("MB")) {
return (long) (Double.parseDouble(sizeStr.substring(0, sizeStr.length() - 2))
* 1024
* 1024);
return (long)
(Double.parseDouble(sizeStr.substring(0, sizeStr.length() - 2))
* 1024
* 1024);
} else if (sizeStr.endsWith("GB")) {
return (long) (Double.parseDouble(sizeStr.substring(0, sizeStr.length() - 2))
* 1024
* 1024
* 1024);
return (long)
(Double.parseDouble(sizeStr.substring(0, sizeStr.length() - 2))
* 1024
* 1024
* 1024);
} else if (sizeStr.endsWith("B")) {
return Long.parseLong(sizeStr.substring(0, sizeStr.length() - 1));
} else {
@@ -191,8 +193,7 @@ public class GeneralUtils {
// Check if the result is null or not within bounds
if (result == null || result <= 0 || result.intValue() > maxValue) {
if (n != 0)
break;
if (n != 0) break;
} else {
results.add(result.intValue());
}

View File

@@ -775,6 +775,7 @@ ScannerImageSplit.selectText.7=الحد الأدنى لمنطقة المحيط:
ScannerImageSplit.selectText.8=تعيين الحد الأدنى لمنطقة المحيط للصورة
ScannerImageSplit.selectText.9=حجم الحدود:
ScannerImageSplit.selectText.10=يضبط حجم الحدود المضافة والمزالة لمنع الحدود البيضاء في الإخراج (الافتراضي: 1).
ScannerImageSplit.info=Python is not installed. It is required to run.
#OCR
@@ -925,6 +926,7 @@ pdfToImage.color=اللون
pdfToImage.grey=تدرج الرمادي
pdfToImage.blackwhite=أبيض وأسود (قد يفقد البيانات!)
pdfToImage.submit=تحول
pdfToImage.info=Python is not installed. Required for WebP conversion.
#addPassword

View File

@@ -775,6 +775,7 @@ ScannerImageSplit.selectText.7=Минимална контурна площ:
ScannerImageSplit.selectText.8=Задава минималния праг на контурната площ за изображение
ScannerImageSplit.selectText.9=Размер на рамката:
ScannerImageSplit.selectText.10=Задава размера на добавената и премахната граница, за да предотврати бели граници към изхода (по подразбиране: 1).
ScannerImageSplit.info=Python is not installed. It is required to run.
#OCR
@@ -925,6 +926,7 @@ pdfToImage.color=Цвят
pdfToImage.grey=Скала на сивото
pdfToImage.blackwhite=Черно и бяло (може да загубите данни!)
pdfToImage.submit=Преобразуване
pdfToImage.info=Python is not installed. Required for WebP conversion.
#addPassword

View File

@@ -775,6 +775,7 @@ ScannerImageSplit.selectText.7=Àrea de contorn mínima:
ScannerImageSplit.selectText.8=Estableix el llindar mínim de l'àrea de contorn per a una foto
ScannerImageSplit.selectText.9=Mida Vora:
ScannerImageSplit.selectText.10=Estableix la mida de la vora afegida i eliminada per evitar vores blanques a la sortida (per defecte: 1).
ScannerImageSplit.info=Python is not installed. It is required to run.
#OCR
@@ -925,6 +926,7 @@ pdfToImage.color=Color
pdfToImage.grey=Escala de Grisos
pdfToImage.blackwhite=Blanc i Negre (Pot perdre dades!)
pdfToImage.submit=Converteix
pdfToImage.info=Python is not installed. Required for WebP conversion.
#addPassword

View File

@@ -775,6 +775,7 @@ ScannerImageSplit.selectText.7=Minimální plocha kontury:
ScannerImageSplit.selectText.8=Nastaví minimální plošný práh kontury pro fotografii
ScannerImageSplit.selectText.9=Velikost okraje:
ScannerImageSplit.selectText.10=Nastaví velikost okraje přidaného a odebraného k zabránění bílých ohraničení ve výstupu (výchozí: 1).
ScannerImageSplit.info=Python is not installed. It is required to run.
#OCR
@@ -925,6 +926,7 @@ pdfToImage.color=Barevný
pdfToImage.grey=Stupně šedi
pdfToImage.blackwhite=Černobílý (Může dojít k ztrátě dat!)
pdfToImage.submit=Převést
pdfToImage.info=Python is not installed. Required for WebP conversion.
#addPassword

View File

@@ -775,6 +775,7 @@ ScannerImageSplit.selectText.7=Minimum Contour Area:
ScannerImageSplit.selectText.8=Sets the minimum contour area threshold for a photo
ScannerImageSplit.selectText.9=Border Size:
ScannerImageSplit.selectText.10=Sets the size of the border added and removed to prevent white borders in the output (default: 1).
ScannerImageSplit.info=Python is not installed. It is required to run.
#OCR
@@ -925,6 +926,7 @@ pdfToImage.color=Colour
pdfToImage.grey=Greyscale
pdfToImage.blackwhite=Black and White (May lose data!)
pdfToImage.submit=Convert
pdfToImage.info=Python is not installed. Required for WebP conversion.
#addPassword

View File

@@ -775,6 +775,7 @@ ScannerImageSplit.selectText.7=Minimaler Konturbereich:
ScannerImageSplit.selectText.8=Legt den minimalen Konturbereichsschwellenwert für ein Foto fest
ScannerImageSplit.selectText.9=Randgröße:
ScannerImageSplit.selectText.10=Legt die Größe des hinzugefügten und entfernten Randes fest, um weiße Ränder in der Ausgabe zu verhindern (Standard: 1).
ScannerImageSplit.info=Python is not installed. It is required to run.
#OCR
@@ -925,6 +926,7 @@ pdfToImage.color=Farbe
pdfToImage.grey=Graustufen
pdfToImage.blackwhite=Schwarzweiß (Datenverlust möglich!)
pdfToImage.submit=Umwandeln
pdfToImage.info=Python is not installed. Required for WebP conversion.
#addPassword

View File

@@ -775,6 +775,7 @@ ScannerImageSplit.selectText.7=Ελάχιστη επιφάνεια περιγρ
ScannerImageSplit.selectText.8=Ρυθμίζει το ελάχιστο όριο περιγράμματος για μια φωτογραφία
ScannerImageSplit.selectText.9=Μέγεθος περιγράμματος:
ScannerImageSplit.selectText.10=Ορίζει το μέγεθος του περιγράμματος που προστίθεται και αφαιρείται για να αποτρέπονται λευκά περιγράμματα στην έξοδο (προεπιλογή: 1).
ScannerImageSplit.info=Python is not installed. It is required to run.
#OCR
@@ -925,6 +926,7 @@ pdfToImage.color=Χρώμα
pdfToImage.grey=Κλίμακα του γκρι
pdfToImage.blackwhite=Ασπρόμαυρο (Μπορεί να χαθούν δεδομένα!)
pdfToImage.submit=Μετατροπή
pdfToImage.info=Python is not installed. Required for WebP conversion.
#addPassword

View File

@@ -775,6 +775,7 @@ ScannerImageSplit.selectText.7=Minimum Contour Area:
ScannerImageSplit.selectText.8=Sets the minimum contour area threshold for a photo
ScannerImageSplit.selectText.9=Border Size:
ScannerImageSplit.selectText.10=Sets the size of the border added and removed to prevent white borders in the output (default: 1).
ScannerImageSplit.info=Python is not installed. It is required to run.
#OCR
@@ -925,6 +926,7 @@ pdfToImage.color=Colour
pdfToImage.grey=Greyscale
pdfToImage.blackwhite=Black and White (May lose data!)
pdfToImage.submit=Convert
pdfToImage.info=Python is not installed. Required for WebP conversion.
#addPassword

View File

@@ -775,6 +775,7 @@ ScannerImageSplit.selectText.7=Minimum Contour Area:
ScannerImageSplit.selectText.8=Sets the minimum contour area threshold for a photo
ScannerImageSplit.selectText.9=Border Size:
ScannerImageSplit.selectText.10=Sets the size of the border added and removed to prevent white borders in the output (default: 1).
ScannerImageSplit.info=Python is not installed. It is required to run.
#OCR
@@ -925,6 +926,7 @@ pdfToImage.color=Color
pdfToImage.grey=Grayscale
pdfToImage.blackwhite=Black and White (May lose data!)
pdfToImage.submit=Convert
pdfToImage.info=Python is not installed. Required for WebP conversion.
#addPassword

View File

@@ -775,6 +775,7 @@ ScannerImageSplit.selectText.7=Área mínima de contorno:
ScannerImageSplit.selectText.8=Establecer el umbral mínimo del área de contorno para una foto
ScannerImageSplit.selectText.9=Tamaño del borde:
ScannerImageSplit.selectText.10=Establece el tamaño del borde agregado y eliminado para evitar bordes blancos en la salida (predeterminado: 1).
ScannerImageSplit.info=Python is not installed. It is required to run.
#OCR
@@ -925,6 +926,7 @@ pdfToImage.color=Color
pdfToImage.grey=Escala de grises
pdfToImage.blackwhite=Blanco y Negro (¡Puede perder datos!)
pdfToImage.submit=Convertir
pdfToImage.info=Python is not installed. Required for WebP conversion.
#addPassword

View File

@@ -775,6 +775,7 @@ ScannerImageSplit.selectText.7=Inguruko area gutxienekoa:
ScannerImageSplit.selectText.8=Ezarri inguruko arearen gutxieneko balioa argazki batentzat
ScannerImageSplit.selectText.9=Ertzaren tamaina:
ScannerImageSplit.selectText.10=Ezarri gehitutako eta ezabatutako ertzaren tamaina irteeran ertz zuriak saihesteko (lehenetsia: 1).
ScannerImageSplit.info=Python is not installed. It is required to run.
#OCR
@@ -925,6 +926,7 @@ pdfToImage.color=Kolorea
pdfToImage.grey=Gris-eskala
pdfToImage.blackwhite=Zuria eta Beltza (Datuak galdu ditzake!)
pdfToImage.submit=Bihurtu
pdfToImage.info=Python is not installed. Required for WebP conversion.
#addPassword

View File

@@ -775,6 +775,7 @@ ScannerImageSplit.selectText.7=Surface de contour minimale
ScannerImageSplit.selectText.8=Définit la surface de contour minimale pour une photo (par défaut : 500).
ScannerImageSplit.selectText.9=Taille de la bordure
ScannerImageSplit.selectText.10=Définit la taille de la bordure ajoutée et supprimée pour éviter les bordures blanches dans la sortie (par défaut : 1).
ScannerImageSplit.info=Python is not installed. It is required to run.
#OCR
@@ -925,6 +926,7 @@ pdfToImage.color=Couleur
pdfToImage.grey=Niveaux de gris
pdfToImage.blackwhite=Noir et blanc (peut engendrer une perte de données !)
pdfToImage.submit=Convertir
pdfToImage.info=Python is not installed. Required for WebP conversion.
#addPassword

View File

@@ -775,6 +775,7 @@ ScannerImageSplit.selectText.7=Íos-Limistéar Comhrianta:
ScannerImageSplit.selectText.8=Socraíonn sé an tairseach íosta achar comhrianta le haghaidh grianghraf
ScannerImageSplit.selectText.9=Méid na Teorann:
ScannerImageSplit.selectText.10=Socraíonn sé méid na teorann a chuirtear leis agus a bhaintear chun teorainneacha bán a chosc san aschur (réamhshocraithe: 1).
ScannerImageSplit.info=Python is not installed. It is required to run.
#OCR
@@ -925,6 +926,7 @@ pdfToImage.color=Dath
pdfToImage.grey=Scála Liath
pdfToImage.blackwhite=Dubh agus Bán (Dfhéadfadh sonraí a chailleadh!)
pdfToImage.submit=Tiontaigh
pdfToImage.info=Python is not installed. Required for WebP conversion.
#addPassword

View File

@@ -775,6 +775,7 @@ ScannerImageSplit.selectText.7=न्यूनतम कंटोर क्ष
ScannerImageSplit.selectText.8=फोटो के लिए न्यूनतम कंटोर क्षेत्र थ्रेशोल्ड को सेट करता है।
ScannerImageSplit.selectText.9=बॉर्डर का आकार:
ScannerImageSplit.selectText.10=निकालने और जोड़ने के लिए जोड़ा जाने वाला बॉर्डर का आकार सेट करता है ताकि आउटपुट में सफेद बॉर्डर न आए (डिफ़ॉल्ट: 1)।
ScannerImageSplit.info=Python is not installed. It is required to run.
#OCR
@@ -925,6 +926,7 @@ pdfToImage.color=रंगीन
pdfToImage.grey=ग्रे स्केल
pdfToImage.blackwhite=काला और सफेद (डेटा खो सकता है!)
pdfToImage.submit=परिवर्तित करें
pdfToImage.info=Python is not installed. Required for WebP conversion.
#addPassword

View File

@@ -775,6 +775,7 @@ ScannerImageSplit.selectText.7=Minimalna konturna površina:
ScannerImageSplit.selectText.8=Postavlja minimalni prag površine konture za fotografiju
ScannerImageSplit.selectText.9=Veličina obruba:
ScannerImageSplit.selectText.10=Postavlja veličinu obruba koji se dodaje i uklanja kako bi se spriječili bijeli obrubi u ispisu (zadano: 1).
ScannerImageSplit.info=Python is not installed. It is required to run.
#OCR
@@ -925,6 +926,7 @@ pdfToImage.color=Boja
pdfToImage.grey=Sivi tonovi
pdfToImage.blackwhite=Crno-bijelo (mogu se izgubiti podaci!)
pdfToImage.submit=Pretvori
pdfToImage.info=Python is not installed. Required for WebP conversion.
#addPassword

View File

@@ -775,6 +775,7 @@ ScannerImageSplit.selectText.7=Minimális kontúr terület:
ScannerImageSplit.selectText.8=A fotók minimális kontúrterületének beállítása
ScannerImageSplit.selectText.9=Keret mérete:
ScannerImageSplit.selectText.10=A hozzáadott és eltávolított keret méretének beállítása a fehér keretek elkerülése érdekében a kimeneten (alapértelmezett: 1).
ScannerImageSplit.info=Python is not installed. It is required to run.
#OCR
@@ -925,6 +926,7 @@ pdfToImage.color=színes
pdfToImage.grey=szürkeárnyalatos
pdfToImage.blackwhite=fekete-fehér (adatvesztéssel járhat!)
pdfToImage.submit=Átalakítás
pdfToImage.info=Python is not installed. Required for WebP conversion.
#addPassword

View File

@@ -775,6 +775,7 @@ ScannerImageSplit.selectText.7=Area Kontur Minimum:
ScannerImageSplit.selectText.8=Menetapkan ambang batas area kontur minimum untuk foto
ScannerImageSplit.selectText.9=Ukuran Batas:
ScannerImageSplit.selectText.10=Menetapkan ukuran batas yang ditambahkan dan dihapus untuk mencegah batas putih pada output (default: 1).
ScannerImageSplit.info=Python is not installed. It is required to run.
#OCR
@@ -925,6 +926,7 @@ pdfToImage.color=Warna
pdfToImage.grey=Skala abu-abu
pdfToImage.blackwhite=Black and White (Bisa kehilangan data!)
pdfToImage.submit=Konversi
pdfToImage.info=Python is not installed. Required for WebP conversion.
#addPassword

View File

@@ -55,7 +55,7 @@ userNotFoundMessage=Utente non trovato.
incorrectPasswordMessage=La password attuale non è corretta.
usernameExistsMessage=Il nuovo nome utente esiste già.
invalidUsernameMessage=Nome utente non valido, il nome utente può contenere solo lettere, numeri e i seguenti caratteri speciali @._+- o deve essere un indirizzo email valido.
invalidPasswordMessage=The password must not be empty and must not have spaces at the beginning or end.
invalidPasswordMessage=La password non deve essere vuota e non deve contenere spazi all'inizio o alla fine.
confirmPasswordErrorMessage=La nuova password e la conferma della nuova password devono corrispondere.
deleteCurrentUserMessage=Impossibile eliminare l'utente attualmente connesso.
deleteUsernameExistsMessage=Il nome utente non esiste e non può essere eliminato.
@@ -179,7 +179,7 @@ adminUserSettings.user=Utente
adminUserSettings.addUser=Aggiungi un nuovo Utente
adminUserSettings.deleteUser=Elimina utente
adminUserSettings.confirmDeleteUser=L'utente deve essere eliminato?
adminUserSettings.confirmChangeUserStatus=Should the user be disabled/enabled?
adminUserSettings.confirmChangeUserStatus=L'utente dovrebbe essere disabilitato/abilitato?
adminUserSettings.usernameInfo=Il nome utente può contenere solo lettere, numeri e i seguenti caratteri speciali @._+- oppure deve essere un indirizzo email valido.
adminUserSettings.roles=Ruoli
adminUserSettings.role=Ruolo
@@ -193,13 +193,13 @@ adminUserSettings.forceChange=Forza l'utente a cambiare nome username/password a
adminUserSettings.submit=Salva utente
adminUserSettings.changeUserRole=Cambia il ruolo dell'utente
adminUserSettings.authenticated=Autenticato
adminUserSettings.editOwnProfil=Edit own profile
adminUserSettings.enabledUser=enabled user
adminUserSettings.disabledUser=disabled user
adminUserSettings.activeUsers=Active Users:
adminUserSettings.disabledUsers=Disabled Users:
adminUserSettings.totalUsers=Total Users:
adminUserSettings.lastRequest=Last Request
adminUserSettings.editOwnProfil=Modifica il tuo profilo
adminUserSettings.enabledUser=utente abilitato
adminUserSettings.disabledUser=utente disabilitato
adminUserSettings.activeUsers=Utenti attivi:
adminUserSettings.disabledUsers=Utenti disabili:
adminUserSettings.totalUsers=Utenti totali:
adminUserSettings.lastRequest=Ultima richiesta
database.title=Importazione/Esportazione database
@@ -491,14 +491,14 @@ login.locked=Il tuo account è stato bloccato.
login.signinTitle=Per favore accedi
login.ssoSignIn=Accedi tramite Single Sign-on
login.oauth2AutoCreateDisabled=Creazione automatica utente OAUTH2 DISABILITATA
login.oauth2AdminBlockedUser=Registration or logging in of non-registered users is currently blocked. Please contact the administrator.
login.oauth2AdminBlockedUser=La registrazione o l'accesso degli utenti non registrati è attualmente bloccata. Si prega di contattare l'amministratore.
login.oauth2RequestNotFound=Richiesta di autorizzazione non trovata
login.oauth2InvalidUserInfoResponse=Risposta relativa alle informazioni utente non valida
login.oauth2invalidRequest=Richiesta non valida
login.oauth2AccessDenied=Accesso negato
login.oauth2InvalidTokenResponse=Risposta token non valida
login.oauth2InvalidIdToken=Id Token non valido
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
login.userIsDisabled=L'utente è disattivato, l'accesso è attualmente bloccato con questo nome utente. Si prega di contattare l'amministratore.
#auto-redact
@@ -775,6 +775,7 @@ ScannerImageSplit.selectText.7=Area di contorno minima:
ScannerImageSplit.selectText.8=Imposta l'area minima del contorno di una foto
ScannerImageSplit.selectText.9=Spessore bordo:
ScannerImageSplit.selectText.10=Imposta lo spessore del bordo aggiunto o rimosso per prevenire bordi bianchi nel risultato (predefinito: 1).
ScannerImageSplit.info=Python non è installato. È necessario per l'esecuzione.
#OCR
@@ -925,6 +926,7 @@ pdfToImage.color=A colori
pdfToImage.grey=Scala di grigi
pdfToImage.blackwhite=Bianco e Nero (potresti perdere dettagli!)
pdfToImage.submit=Converti
pdfToImage.info=Python non è installato.È richiesto per la conversione WebP.
#addPassword

View File

@@ -775,6 +775,7 @@ ScannerImageSplit.selectText.7=最小輪郭面積:
ScannerImageSplit.selectText.8=画像の最小の輪郭面積のしきい値を設定。
ScannerImageSplit.selectText.9=境界線サイズ:
ScannerImageSplit.selectText.10=出力に白い縁取りが出ないように追加・削除される境界線の大きさを設定 (初期値:1)。
ScannerImageSplit.info=Python is not installed. It is required to run.
#OCR
@@ -925,6 +926,7 @@ pdfToImage.color=カラー
pdfToImage.grey=グレースケール
pdfToImage.blackwhite=白黒 (データが失われる可能性があります!)
pdfToImage.submit=変換
pdfToImage.info=Python is not installed. Required for WebP conversion.
#addPassword

View File

@@ -775,6 +775,7 @@ ScannerImageSplit.selectText.7=최소 윤곽 영역:
ScannerImageSplit.selectText.8=사진의 최소 윤곽선 영역 임계값을 설정합니다.
ScannerImageSplit.selectText.9=테두리 크기:
ScannerImageSplit.selectText.10=출력에서 흰색 테두리를 방지하기 위해 추가 및 제거되는 테두리의 크기를 설정합니다(기본값: 1).
ScannerImageSplit.info=Python is not installed. It is required to run.
#OCR
@@ -925,6 +926,7 @@ pdfToImage.color=컬러
pdfToImage.grey=그레이스케일
pdfToImage.blackwhite=흑백 (데이터 손실 가능성 있음!)
pdfToImage.submit=변환
pdfToImage.info=Python is not installed. Required for WebP conversion.
#addPassword

View File

@@ -775,6 +775,7 @@ ScannerImageSplit.selectText.7=Minimum contour oppervlakte:
ScannerImageSplit.selectText.8=Stelt de minimale contour oppervlakte drempel in voor een foto
ScannerImageSplit.selectText.9=Randgrootte:
ScannerImageSplit.selectText.10=Stelt de grootte van de toegevoegde en verwijderde rand in om witte randen in de uitvoer te voorkomen (standaard: 1).
ScannerImageSplit.info=Python is not installed. It is required to run.
#OCR
@@ -925,6 +926,7 @@ pdfToImage.color=Kleur
pdfToImage.grey=Grijstinten
pdfToImage.blackwhite=Zwart en wit (kan data verliezen!)
pdfToImage.submit=Omzetten
pdfToImage.info=Python is not installed. Required for WebP conversion.
#addPassword

View File

@@ -775,6 +775,7 @@ ScannerImageSplit.selectText.7=Minimumskonturområde:
ScannerImageSplit.selectText.8=Angir minimumskonturområde terskel for et bilde
ScannerImageSplit.selectText.9=Kantstørrelse:
ScannerImageSplit.selectText.10=Angir størrelsen på kanten som legges til og fjernes for å forhindre hvite kanter i utdataen (standard: 1).
ScannerImageSplit.info=Python is not installed. It is required to run.
#OCR
@@ -925,6 +926,7 @@ pdfToImage.color=Farge
pdfToImage.grey=Gråtone
pdfToImage.blackwhite=Svart-hvitt (kan miste data!)
pdfToImage.submit=Konverter
pdfToImage.info=Python is not installed. Required for WebP conversion.
#addPassword

View File

@@ -775,6 +775,7 @@ ScannerImageSplit.selectText.7=Minimalny obszar konturu:
ScannerImageSplit.selectText.8=Ustawia próg minimalnego obszaru konturu dla zdjęcia
ScannerImageSplit.selectText.9=Rozmiar obramowania:
ScannerImageSplit.selectText.10=Ustawia rozmiar dodawanego i usuwanego obramowania, aby uniknąć białych obramowań na wyjściu (domyślnie: 1).
ScannerImageSplit.info=Python is not installed. It is required to run.
#OCR
@@ -925,6 +926,7 @@ pdfToImage.color=Kolor
pdfToImage.grey=Odcień szarości
pdfToImage.blackwhite=Czarno-biały (może spowodować utratę danych!)
pdfToImage.submit=Konwertuj
pdfToImage.info=Python is not installed. Required for WebP conversion.
#addPassword

File diff suppressed because it is too large Load Diff

View File

@@ -775,6 +775,7 @@ ScannerImageSplit.selectText.7=Área mínima de contorno:
ScannerImageSplit.selectText.8=Define o limite mínimo da área de contorno para uma foto
ScannerImageSplit.selectText.9=Tamanho do contorno:
ScannerImageSplit.selectText.10=Define o tamanho do contorno adicionado e removido para evitar contornos brancos na saída (padrão: 1).
ScannerImageSplit.info=Python is not installed. It is required to run.
#OCR
@@ -925,6 +926,7 @@ pdfToImage.color=Colorida
pdfToImage.grey=Escala de Cinza
pdfToImage.blackwhite=Preto e Branco (pode resultar em perda de dados!)
pdfToImage.submit=Converter
pdfToImage.info=Python is not installed. Required for WebP conversion.
#addPassword

View File

@@ -775,6 +775,7 @@ ScannerImageSplit.selectText.7=Arie minimă a conturului:
ScannerImageSplit.selectText.8=Stabilește pragul minim de arie a conturului pentru o fotografie.
ScannerImageSplit.selectText.9=Mărimea marginii:
ScannerImageSplit.selectText.10=Stabilește mărimea marginii adăugate și eliminate pentru a evita marginile albe în rezultat (implicit: 1).
ScannerImageSplit.info=Python is not installed. It is required to run.
#OCR
@@ -925,6 +926,7 @@ pdfToImage.color=Culoare
pdfToImage.grey=Scală de gri
pdfToImage.blackwhite=Alb și negru (Poate pierde date!)
pdfToImage.submit=Convertă
pdfToImage.info=Python is not installed. Required for WebP conversion.
#addPassword

View File

@@ -775,6 +775,7 @@ ScannerImageSplit.selectText.7=Минимальная площадь конту
ScannerImageSplit.selectText.8=Устанавливает минимальный порог области контура для фотографии
ScannerImageSplit.selectText.9=Размер границы:
ScannerImageSplit.selectText.10=Устанавливает размер добавляемой и удаляемой границы, чтобы предотвратить появление белых границ на выходе (по умолчанию: 1).
ScannerImageSplit.info=Python is not installed. It is required to run.
#OCR
@@ -925,6 +926,7 @@ pdfToImage.color=Цвет
pdfToImage.grey=Оттенки серого
pdfToImage.blackwhite=Черно-белый (может потерять данные!)
pdfToImage.submit=Конвертировать
pdfToImage.info=Python is not installed. Required for WebP conversion.
#addPassword

View File

@@ -775,6 +775,7 @@ ScannerImageSplit.selectText.7=Minimálna plocha obrysu:
ScannerImageSplit.selectText.8=Nastaví minimálnu prahovú hodnotu plochy obrysu pre fotografiu
ScannerImageSplit.selectText.9=Veľkosť okraja:
ScannerImageSplit.selectText.10=Nastaví veľkosť okraja pridaného a odstráneného, aby sa zabránilo bielym okrajom vo výstupe (predvolené: 1).
ScannerImageSplit.info=Python is not installed. It is required to run.
#OCR
@@ -925,6 +926,7 @@ pdfToImage.color=Farba
pdfToImage.grey=Odtiene šedej
pdfToImage.blackwhite=Čierno-biele (Môže stratiť údaje!)
pdfToImage.submit=Konvertovať
pdfToImage.info=Python is not installed. Required for WebP conversion.
#addPassword

View File

@@ -775,6 +775,7 @@ ScannerImageSplit.selectText.7=Minimalna površina konture:
ScannerImageSplit.selectText.8=Postavlja minimalni prag površine konture za fotografiju
ScannerImageSplit.selectText.9=Veličina ivice:
ScannerImageSplit.selectText.10=Postavlja veličinu ivice dodate i uklonjene kako bi se sprečile bele ivice u izlazu (podrazumevano: 1).
ScannerImageSplit.info=Python is not installed. It is required to run.
#OCR
@@ -925,6 +926,7 @@ pdfToImage.color=Boja
pdfToImage.grey=Nijanse sive
pdfToImage.blackwhite=Crno-belo (Može izgubiti podatke!)
pdfToImage.submit=Konvertuj
pdfToImage.info=Python is not installed. Required for WebP conversion.
#addPassword

View File

@@ -775,6 +775,7 @@ ScannerImageSplit.selectText.7=Minsta konturarea:
ScannerImageSplit.selectText.8=Ställer in minsta tröskelvärde för konturarea för ett foto
ScannerImageSplit.selectText.9=Kantstorlek:
ScannerImageSplit.selectText.10=Ställer in storleken på kanten som läggs till och tas bort för att förhindra vita kanter i utdata (standard: 1).
ScannerImageSplit.info=Python is not installed. It is required to run.
#OCR
@@ -925,6 +926,7 @@ pdfToImage.color=Färg
pdfToImage.grey=Gråskala
pdfToImage.blackwhite=Svartvitt (kan förlora data!)
pdfToImage.submit=Konvertera
pdfToImage.info=Python is not installed. Required for WebP conversion.
#addPassword

View File

@@ -775,6 +775,7 @@ ScannerImageSplit.selectText.7=พื้นที่เค้าโครงข
ScannerImageSplit.selectText.8=ตั้งค่าเกณฑ์พื้นที่เค้าโครงขั้นต่ำสำหรับรูปภาพ
ScannerImageSplit.selectText.9=ขนาดขอบ:
ScannerImageSplit.selectText.10=ตั้งค่าขนาดขอบที่เพิ่มและลบเพื่อป้องกันขอบขาวในผลลัพธ์ (ค่าเริ่มต้น: 1)
ScannerImageSplit.info=Python is not installed. It is required to run.
#OCR
@@ -925,6 +926,7 @@ pdfToImage.color=สี
pdfToImage.grey=ระดับสีเทา
pdfToImage.blackwhite=ขาวดำ (อาจสูญเสียข้อมูล!)
pdfToImage.submit=แปลง
pdfToImage.info=Python is not installed. Required for WebP conversion.
#addPassword

View File

@@ -775,6 +775,7 @@ ScannerImageSplit.selectText.7=Minimum Kontur Alanı:
ScannerImageSplit.selectText.8=Bir fotoğraf için minimum kontur alanı eşiğini ayarlar
ScannerImageSplit.selectText.9=Kenar Boyutu:
ScannerImageSplit.selectText.10=Çıktıda beyaz kenarların önlenmesi için eklenen ve kaldırılan kenarın boyutunu ayarlar (varsayılan: 1).
ScannerImageSplit.info=Python is not installed. It is required to run.
#OCR
@@ -925,6 +926,7 @@ pdfToImage.color=Renk
pdfToImage.grey=Gri tonlama
pdfToImage.blackwhite=Siyah ve Beyaz (Veri kaybolabilir!)
pdfToImage.submit=Dönüştür
pdfToImage.info=Python is not installed. Required for WebP conversion.
#addPassword

View File

@@ -775,6 +775,7 @@ ScannerImageSplit.selectText.7=Мінімальна площа контуру:
ScannerImageSplit.selectText.8=Встановлює мінімальний поріг площі контуру для фотографії
ScannerImageSplit.selectText.9=Розмір рамки:
ScannerImageSplit.selectText.10=Встановлює розмір додаваної та видаляної рамки, щоб запобігти появі білих рамок на виході (за замовчуванням: 1).
ScannerImageSplit.info=Python is not installed. It is required to run.
#OCR
@@ -925,6 +926,7 @@ pdfToImage.color=Колір
pdfToImage.grey=Відтінки сірого
pdfToImage.blackwhite=Чорно-білий (може втратити дані!)
pdfToImage.submit=Конвертувати
pdfToImage.info=Python is not installed. Required for WebP conversion.
#addPassword

View File

@@ -775,6 +775,7 @@ ScannerImageSplit.selectText.7=Diện tích đường viền tối thiểu:
ScannerImageSplit.selectText.8=Đặt ngưỡng diện tích đường viền tối thiểu cho một ảnh
ScannerImageSplit.selectText.9=Kích thước viền:
ScannerImageSplit.selectText.10=Đặt kích thước của viền được thêm vào và loại bỏ để ngăn chặn viền trắng trong đầu ra (mặc định: 1).
ScannerImageSplit.info=Python is not installed. It is required to run.
#OCR
@@ -925,6 +926,7 @@ pdfToImage.color=Màu
pdfToImage.grey=Thang độ xám
pdfToImage.blackwhite=Đen trắng (Có thể mất dữ liệu!)
pdfToImage.submit=Chuyển đổi
pdfToImage.info=Python is not installed. Required for WebP conversion.
#addPassword

View File

@@ -775,6 +775,7 @@ ScannerImageSplit.selectText.7=最小轮廓面积:
ScannerImageSplit.selectText.8=设置照片的最小轮廓面积阈值。
ScannerImageSplit.selectText.9=边框尺寸:
ScannerImageSplit.selectText.10=设置添加和删除的边框大小以防止输出中出现白边默认值1
ScannerImageSplit.info=Python is not installed. It is required to run.
#OCR
@@ -925,6 +926,7 @@ pdfToImage.color=颜色
pdfToImage.grey=灰度
pdfToImage.blackwhite=黑白(可能会丢失数据!)。
pdfToImage.submit=转换
pdfToImage.info=Python is not installed. Required for WebP conversion.
#addPassword

View File

@@ -775,6 +775,7 @@ ScannerImageSplit.selectText.7=最小輪廓區域:
ScannerImageSplit.selectText.8=設定照片的最小輪廓區域閾值
ScannerImageSplit.selectText.9=邊框大小:
ScannerImageSplit.selectText.10=設定新增和移除的邊框大小以防止輸出中的白色邊框預設1
ScannerImageSplit.info=Python is not installed. It is required to run.
#OCR
@@ -925,6 +926,7 @@ pdfToImage.color=顏色
pdfToImage.grey=灰度
pdfToImage.blackwhite=黑白(可能會遺失資料!)
pdfToImage.submit=轉換
pdfToImage.info=Python is not installed. Required for WebP conversion.
#addPassword

View File

@@ -17,7 +17,7 @@
{
"moduleName": "com.bucket4j:bucket4j_jdk17-core",
"moduleUrl": "http://github.com/bucket4j/bucket4j/bucket4j_jdk17-core",
"moduleVersion": "8.13.1",
"moduleVersion": "8.14.0",
"moduleLicense": "The Apache Software License, Version 2.0",
"moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0"
},
@@ -1143,7 +1143,7 @@
{
"moduleName": "org.springframework:spring-webmvc",
"moduleUrl": "https://github.com/spring-projects/spring-framework",
"moduleVersion": "6.1.12",
"moduleVersion": "6.1.9",
"moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
},

View File

@@ -37,7 +37,7 @@ $(document).ready(function () {
try {
if (remoteCall === true) {
if (override === "multi" || (!multiple && files.length > 1 && override !== "single")) {
if (override === "multi" || (!multipleInputsForSingleRequest && files.length > 1 && override !== "single")) {
await submitMultiPdfForm(url, files);
} else {
await handleSingleDownload(url, formData);

View File

@@ -24,7 +24,7 @@
<form method="post" enctype="multipart/form-data" th:action="@{'/api/v1/misc/auto-split-pdf'}">
<p th:text="#{autoSplitPDF.formPrompt}"></p>
<div
th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}">
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}">
</div>
<div class="form-check ms-3">
<input type="checkbox" name="duplexMode" id="duplexMode">

View File

@@ -15,7 +15,7 @@
<div class="col-md-6">
<h2 th:text="#{BookToPDF.header}"></h2>
<form method="post" enctype="multipart/form-data" th:action="@{'/api/v1/convert/book/pdf'}">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false)}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false)}"></div>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{BookToPDF.submit}"></button>
</form>
<p class="mt-3" th:text="#{BookToPDF.credit}"></p>

View File

@@ -21,7 +21,7 @@
</div>
<p th:text="#{processTimeWarning}"></p>
<form method="post" enctype="multipart/form-data" th:action="@{'/api/v1/convert/file/pdf'}">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false)}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false)}"></div>
<a class="btn btn-outline-primary" data-bs-toggle="collapse" href="#info" role="button"
aria-expanded="false" aria-controls="info" th:text="#{fileToPDF.supportedFileTypesInfo}"></a>
<div class="collapse" id="info">

View File

@@ -18,7 +18,7 @@
<span class="tool-header-text" th:text="#{HTMLToPDF.header}"></span>
</div>
<form method="post" enctype="multipart/form-data" th:action="@{'/api/v1/convert/html/pdf'}">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='text/html,application/zip' )}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='text/html,application/zip' )}"></div>
<div class="mb-3">
<label for="zoom" th:text="#{HTMLToPDF.zoom}" class="form-label"></label>
<input type="number" step="0.1" class="form-control" id="zoom" name="zoom" value="1">

View File

@@ -18,7 +18,7 @@
<span class="tool-header-text" th:text="#{imageToPDF.header}"></span>
</div>
<form method="post" enctype="multipart/form-data" th:action="@{'/api/v1/convert/img/pdf'}">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='image/*', inputText=#{imgPrompt})}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='image/*', inputText=#{imgPrompt})}"></div>
<div class="mb-3">
<label for="fitOption" th:text="#{imageToPDF.selectLabel}">Fit Options</label>
<select class="form-control" id="fitOption" name="fitOption">

View File

@@ -18,7 +18,7 @@
<span class="tool-header-text" th:text="#{MarkdownToPDF.header}"></span>
</div>
<form method="post" enctype="multipart/form-data" th:action="@{'/api/v1/convert/markdown/pdf'}">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='text/markdown')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='text/markdown')}"></div>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{MarkdownToPDF.submit}"></button>
</form>
<p class="mt-3" th:text="#{MarkdownToPDF.help}"></p>

View File

@@ -15,7 +15,7 @@
<div class="col-md-6">
<h2 th:text="#{PDFToBook.header}"></h2>
<form method="post" enctype="multipart/form-data" th:action="@{'/api/v1/convert/pdf/book'}">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
<div class="mb-3">
<label th:text="#{PDFToBook.selectText.1}"></label>
<select class="form-control" name="outputFormat">

View File

@@ -18,7 +18,7 @@
</div>
<form id="PDFToCSVForm" th:action="@{'/api/v1/convert/pdf/csv'}" method="post" enctype="multipart/form-data">
<input id="pageId" type="hidden" name="pageId">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
<button type="submit" class="btn btn-primary" th:text="#{PDFToCSV.submit}"></button>
</form>
<p id="instruction-text" style="margin: 0; display: none" th:text="#{PDFToCSV.prompt}"></p>

View File

@@ -18,7 +18,7 @@
<span class="tool-header-text" th:text="#{PDFToHTML.header}"></span>
</div>
<form method="post" enctype="multipart/form-data" th:action="@{'/api/v1/convert/pdf/html'}">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
<br>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{PDFToHTML.submit}"></button>
</form>

View File

@@ -19,7 +19,7 @@
</div>
<p th:text="#{processTimeWarning}"></p>
<form method="post" enctype="multipart/form-data" th:action="@{'/api/v1/convert/pdf/img'}">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
<div class="mb-3">
<label th:text="#{pdfToImage.selectText}"></label>
<select class="form-control" name="imageFormat">
@@ -28,6 +28,7 @@
<option value="gif">GIF</option>
<option value="tiff">TIFF</option>
<option value="bmp">BMP</option>
<option value="webp">WEPB</option>
</select>
</div>
<div class="mb-3">

View File

@@ -19,7 +19,7 @@
</div>
<p th:text="#{pdfToPDFA.tip}"></p>
<form method="post" enctype="multipart/form-data" th:action="@{'/api/v1/convert/pdf/pdfa'}">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
<div class="mb-3">
<label for="outputFormat" th:text="#{pdfToPDFA.outputFormat}"></label>
<select class="form-control" name="outputFormat" id="outputFormat">

View File

@@ -18,7 +18,7 @@
<span class="tool-header-text" th:text="#{PDFToPresentation.header}"></span>
</div>
<form method="post" enctype="multipart/form-data" th:action="@{'/api/v1/convert/pdf/presentation'}">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
<div class="mb-3">
<label th:text="#{PDFToPresentation.selectText.1}"></label>
<select class="form-control" name="outputFormat">

View File

@@ -18,7 +18,7 @@
<span class="tool-header-text" th:text="#{PDFToText.header}"></span>
</div>
<form method="post" enctype="multipart/form-data" th:action="@{'/api/v1/convert/pdf/text'}">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
<div class="mb-3">
<label th:text="#{PDFToText.selectText.1}"></label>
<select class="form-control" name="outputFormat">

View File

@@ -18,7 +18,7 @@
<span class="tool-header-text" th:text="#{PDFToWord.header}"></span>
</div>
<form method="post" enctype="multipart/form-data" th:action="@{'/api/v1/convert/pdf/word'}">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
<div class="mb-3">
<label th:text="#{PDFToWord.selectText.1}"></label>
<select class="form-control" name="outputFormat">

View File

@@ -18,7 +18,7 @@
<span class="tool-header-text" th:text="#{PDFToXML.header}"></span>
</div>
<form method="post" enctype="multipart/form-data" th:action="@{'/api/v1/convert/pdf/xml'}">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
<br>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{PDFToXML.submit}"></button>
</form>

View File

@@ -17,7 +17,7 @@
<span class="tool-header-text" th:text="#{crop.header}"></span>
</div>
<form id="cropForm" th:action="@{'/api/v1/general/crop'}" method="post" enctype="multipart/form-data">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
<input id="x" type="hidden" name="x">
<input id="y" type="hidden" name="y">
<input id="width" type="hidden" name="width">

View File

@@ -17,7 +17,7 @@
<span class="tool-header-text" th:text="#{pageExtracter.header}"></span>
</div>
<form th:action="@{'/api/v1/general/rearrange-pages'}" method="post" enctype="multipart/form-data">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
<input type="hidden" id="customMode" name="customMode" value="">
<div class="mb-3">
<label for="pageOrder" th:text="#{pageOrderPrompt}"></label>

View File

@@ -144,17 +144,18 @@
</dialog>
</th:block>
<th:block th:fragment="fileSelector(name, multiple)" th:with="accept=${accept} ?: '*/*', inputText=${inputText} ?: #{pdfPrompt}, remoteCall=${remoteCall} ?: true, notRequired=${notRequired} ?: false">
<th:block th:fragment="fileSelector(name, multipleInputsForSingleRequest)" th:with="accept=${accept} ?: '*/*', inputText=${inputText} ?: #{pdfPrompt}, remoteCall=${remoteCall} ?: true, disableMultipleFiles=${disableMultipleFiles} ?: false, notRequired=${notRequired} ?: false">
<script th:inline="javascript">
const pdfPasswordPrompt = /*[[#{error.pdfPassword}]]*/ '';
const multiple = /*[[${multiple}]]*/ false;
const multipleInputsForSingleRequest = /*[[${multipleInputsForSingleRequest}]]*/ false;
const disableMultipleFiles = /*[[${disableMultipleFiles}]]*/ false;
const remoteCall = /*[[${remoteCall}]]*/ true;
</script>
<script th:src="@{'/js/downloader.js'}"></script>
<div class="custom-file-chooser" th:attr="data-bs-unique-id=${name}, data-bs-element-id=${name+'-input'}, data-bs-files-selected=#{filesSelected}, data-bs-pdf-prompt=#{pdfPrompt}">
<div class="mb-3">
<input type="file" class="form-control" th:name="${name}" th:id="${name}+'-input'" th:accept="${accept}" th:attr="multiple=${multiple}" th:required="${notRequired} ? null : 'required'">
<input type="file" class="form-control" th:name="${name}" th:id="${name}+'-input'" th:accept="${accept}" th:attr="multiple=${!disableMultipleFiles}" th:required="${notRequired} ? null : 'required'">
</div>
<div class="selected-files"></div>
</div>

View File

@@ -12,7 +12,7 @@
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="es_ES"> <img th:src="@{'/images/flags/es.svg'}" alt="icon" width="20" height="15"> Español</a>
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="fr_FR"> <img th:src="@{'/images/flags/fr.svg'}" alt="icon" width="20" height="15"> Français</a>
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="id_ID"> <img th:src="@{'/images/flags/id.svg'}" alt="icon" width="20" height="15"> Indonesia</a>
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="id_ID"> <img th:src="@{'/images/flags/ie.svg'}" alt="icon" width="20" height="15"> Irish</a>
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="ga_IE"> <img th:src="@{'/images/flags/ie.svg'}" alt="icon" width="20" height="15"> Irish</a>
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="it_IT"> <img th:src="@{'/images/flags/it.svg'}" alt="icon" width="20" height="15"> Italiano</a>
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="nl_NL"> <img th:src="@{'/images/flags/nl.svg'}" alt="icon" width="20" height="15"> Nederlands</a>
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="pl_PL"> <img th:src="@{'/images/flags/pl.svg'}" alt="icon" width="20" height="15"> Polski</a>

View File

@@ -20,7 +20,7 @@
<form action="api/v1/general/merge-pdfs" method="post" enctype="multipart/form-data">
<div class="mb-3">
<label th:text="#{multiPdfDropPrompt}" for="fileInput-input"></label>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=true, accept='application/pdf')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=true, accept='application/pdf')}"></div>
</div>
<div class="mb-3">
<input type="checkbox" name="removeCertSign" id="removeCertSign">

View File

@@ -20,7 +20,7 @@
</div>
<!-- pdf selector -->
<div th:replace="~{fragments/common :: fileSelector(name='pdf-upload', multiple=false, accept='application/pdf')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='pdf-upload', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
<script>
let originalFileName = '';
document.querySelector('input[name=pdf-upload]').addEventListener('change', async (event) => {
@@ -46,7 +46,7 @@
<div class="tab-group show-on-file-selected">
<div class="tab-container" th:title="#{addImage.upload}">
<div th:replace="~{fragments/common :: fileSelector(name='image-upload', multiple=true, accept='image/*', inputText=#{imgPrompt})}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='image-upload', multipleInputsForSingleRequest=true, accept='image/*', inputText=#{imgPrompt})}"></div>
<script>
const imageUpload = document.querySelector('input[name=image-upload]');
imageUpload.addEventListener('change', e => {

View File

@@ -61,7 +61,7 @@
<span class="tool-header-text" th:text="#{addPageNumbers.header}"></span>
</div>
<form method="post" enctype="multipart/form-data" th:action="@{'/api/v1/misc/add-page-numbers'}">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
<br>
<div class="mb-3">
<label for="customMargin" th:text="#{addPageNumbers.selectText.2}"></label>

View File

@@ -40,7 +40,7 @@
<span class="tool-header-text" th:text="#{adjustContrast.header}"></span>
</div>
<div class="col-md-8">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf', remoteCall='false')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf', remoteCall='false')}"></div>
</div>
<br>
<canvas id="contrast-pdf-canvas"></canvas>

View File

@@ -18,7 +18,7 @@
<span class="tool-header-text" th:text="#{autoCrop.header}"></span>
</div>
<form method="post" enctype="multipart/form-data" th:action="@{'/api/v1/misc/auto-crop'}">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
<br>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{autoCrop.submit}"></button>
</form>

View File

@@ -18,7 +18,7 @@
<span class="tool-header-text" th:text="#{auto-rename.header}"></span>
</div>
<form method="post" enctype="multipart/form-data" th:action="@{'/api/v1/misc/auto-rename'}">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
<br>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{auto-rename.submit}"></button>
</form>

View File

@@ -17,7 +17,7 @@
<span class="tool-header-text" th:text="#{changeMetadata.header}"></span>
</div>
<form method="post" id="form1" enctype="multipart/form-data" th:action="@{'/api/v1/misc/update-metadata'}">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
<p class="text-muted" th:text="#{changeMetadata.selectText.1}"></p>
<div class="form-check mb-3-inline ms-3">
<input type="checkbox" id="deleteAll" name="deleteAll">

View File

@@ -51,8 +51,8 @@
<span class="material-symbols-rounded tool-header-icon other">compare</span>
<span class="tool-header-text" th:text="#{compare.header}"></span>
</div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf', remoteCall='false')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput2', multiple=false, accept='application/pdf', remoteCall='false')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf', remoteCall='false')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput2', multipleInputsForSingleRequest=false, accept='application/pdf', remoteCall='false')}"></div>
<div class="row">
<div class="flex-container">

Some files were not shown because too many files have changed in this diff Show More