Compare commits

...

111 Commits

Author SHA1 Message Date
Anthony Stirling
f2a65dc360 Update build.gradle 2023-06-10 18:15:23 +01:00
Anthony Stirling
7b4a889ea7 Remove debugs 2023-06-10 18:14:34 +01:00
Anthony Stirling
f627d251c3 Update releaseArtifacts.yml 2023-06-10 18:06:59 +01:00
Anthony Stirling
d5b7125415 Rename app to Stirling-PDF not S-pdf 2023-06-10 18:04:06 +01:00
Anthony Stirling
67dd3cf0e3 Update releaseArtifacts.yml 2023-06-10 17:54:50 +01:00
Anthony Stirling
b8b62bb5af Update releaseArtifacts.yml 2023-06-10 17:44:02 +01:00
Anthony Stirling
b4a9d1ac18 Update releaseArtifacts.yml 2023-06-10 17:39:36 +01:00
Anthony Stirling
8aae651c2c Update releaseArtifacts.yml 2023-06-10 15:22:35 +01:00
Anthony Stirling
fc9465b324 Update releaseArtifacts.yml 2023-06-10 15:21:54 +01:00
Anthony Stirling
579a50be2c Update releaseArtifacts.yml 2023-06-10 15:15:51 +01:00
Anthony Stirling
9c5b967e4c fix 2023-06-10 15:10:37 +01:00
Anthony Stirling
d41deb729b fix for action 2023-06-10 15:07:20 +01:00
Anthony Stirling
a93a89f3f0 github action for release upload 2023-06-10 15:06:01 +01:00
Anthony Stirling
11d642a25f Upload jar and .exe to release 2023-06-10 15:05:38 +01:00
Anthony Stirling
67448498ea documentation stuff and downloer fix for image to pdf 2023-06-10 00:46:44 +01:00
Anthony Stirling
489b8da713 fix for actions 2023-06-08 22:28:13 +01:00
Anthony Stirling
728d4d0fa8 latest 2023-06-08 22:02:09 +01:00
Anthony Stirling
e70f4ff3a6 version bump 2023-06-08 20:45:35 +01:00
Anthony Stirling
04032c0dfe sign changes min size, lite docker, merge fix #172 2023-06-08 20:25:55 +01:00
Anthony Stirling
ecba6461df Merge branch 'main' of git@github.com:Frooodle/Stirling-PDF.git into
main
2023-06-07 14:47:06 +01:00
Anthony Stirling
433ba6c250 groups update 2023-06-07 14:13:18 +01:00
Anthony Stirling
ff6c55d1d0 fix naming issues in split and made it allign with others 2023-06-07 14:01:37 +01:00
Anthony Stirling
d9f8facf2e Merge pull request #232 from NeilJared/patch-1
Update messages_es_ES.properties
2023-06-06 00:26:20 +01:00
NeilJared
6bd3e5cc8f Update messages_es_ES.properties
Updated Spanish translation
2023-06-06 01:12:03 +02:00
Anthony Stirling
936fb5ae45 Update README.md 2023-06-05 23:08:55 +01:00
Anthony Stirling
8e661e1260 fix spanish remove blanks 2023-06-05 22:30:18 +01:00
Anthony Stirling
987d9b0502 Merge pull request #227 from jordyjordy/fix-imgtopdf-typo
fix typo in img conversion filename
2023-06-05 19:42:48 +01:00
Anthony Stirling
f6a9169446 Merge branch 'main' into fix-imgtopdf-typo 2023-06-05 19:41:24 +01:00
Anthony Stirling
d5c1c43eb1 Merge pull request #228 from jordyjordy/fix-multitool-filemixing
await pdf creation in each for loop.
2023-06-05 19:41:15 +01:00
Anthony Stirling
20c74dac60 Merge branch 'main' into fix-multitool-filemixing 2023-06-05 19:40:00 +01:00
Anthony Stirling
30161275a3 Merge branch 'main' into fix-imgtopdf-typo 2023-06-05 19:39:14 +01:00
jordy
7e9479806e await pdf creation in each for loop. 2023-06-05 19:38:25 +02:00
jordy
39b9ea9f1d fix typo 2023-06-05 19:25:36 +02:00
Anthony Stirling
5a9165d7c6 missing lang 2023-06-05 18:23:15 +01:00
Anthony Stirling
4f851156b7 dummy lang changes 2023-06-04 22:40:14 +01:00
Anthony Stirling
b8fa278173 finalise auto swagger docs 2023-06-03 23:32:47 +01:00
Anthony Stirling
2cfb344e13 swagger 2023-06-03 23:24:40 +01:00
Anthony Stirling
1d2bf92abe Merge branch 'main' of git@github.com:Frooodle/Stirling-PDF.git into
main
2023-06-03 23:21:47 +01:00
Anthony Stirling
cefcda9f40 test 2023-06-03 23:21:12 +01:00
Anthony Stirling
a4bc67ff8e Update swagger.yml 2023-06-03 23:08:10 +01:00
Anthony Stirling
48b3dea256 remove push 2023-06-03 23:05:20 +01:00
Anthony Stirling
1f5231d905 @./SwaggerDoc.json 2023-06-03 23:01:14 +01:00
Anthony Stirling
c526e18992 Split pages support n function and other stuff 2023-06-03 22:56:15 +01:00
Anthony Stirling
4594765cbd rearrange support n numbers, downloader.js fixes 2023-06-03 17:21:59 +01:00
Anthony Stirling
e2a787e519 Hide banner if mobile 2023-06-02 23:43:22 +01:00
Anthony Stirling
45b3e0aa6a JS and css cleanup 2023-06-02 20:15:10 +01:00
Anthony Stirling
7bdb2615d4 css cleanup 2023-06-01 23:12:55 +01:00
systo
019a502490 Merge branch 'main' of git@github.com:Frooodle/Stirling-PDF.git into main 2023-06-01 21:37:40 +01:00
Anthony Stirling
6793fd5bc7 thirs party js folder 2023-06-01 21:37:33 +01:00
systo
8ac5cf759e Merge branch 'main' of git@github.com:Frooodle/Stirling-PDF.git into main 2023-06-01 11:22:56 +01:00
Anthony Stirling
19880567ba Donation buttons 2023-06-01 11:22:44 +01:00
Anthony Stirling
5c5a3fefc1 Merge pull request #205 from Frooodle/dependabot/gradle/org.springframework.boot-spring-boot-starter-test-3.1.0
Bump org.springframework.boot:spring-boot-starter-test from 3.0.6 to 3.1.0
2023-06-01 10:53:13 +01:00
Anthony Stirling
90057828a5 Merge branch 'main' into dependabot/gradle/org.springframework.boot-spring-boot-starter-test-3.1.0 2023-06-01 10:52:24 +01:00
Anthony Stirling
7cfc3a09a2 Merge pull request #206 from Frooodle/dependabot/gradle/org.springframework.boot-3.1.0
Bump org.springframework.boot from 3.0.6 to 3.1.0
2023-06-01 10:52:08 +01:00
dependabot[bot]
b8bbee27f8 Bump org.springframework.boot:spring-boot-starter-test
Bumps [org.springframework.boot:spring-boot-starter-test](https://github.com/spring-projects/spring-boot) from 3.0.6 to 3.1.0.
- [Release notes](https://github.com/spring-projects/spring-boot/releases)
- [Commits](https://github.com/spring-projects/spring-boot/compare/v3.0.6...v3.1.0)

---
updated-dependencies:
- dependency-name: org.springframework.boot:spring-boot-starter-test
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-01 09:51:11 +00:00
Anthony Stirling
8e5c665e49 Merge branch 'main' into dependabot/gradle/org.springframework.boot-3.1.0 2023-06-01 10:50:49 +01:00
Anthony Stirling
7b1e6fb953 Merge pull request #204 from Frooodle/dependabot/gradle/org.springframework.boot-spring-boot-starter-thymeleaf-3.1.0
Bump org.springframework.boot:spring-boot-starter-thymeleaf from 3.0.6 to 3.1.0
2023-06-01 10:50:22 +01:00
dependabot[bot]
79936e69c5 Bump org.springframework.boot from 3.0.6 to 3.1.0
Bumps [org.springframework.boot](https://github.com/spring-projects/spring-boot) from 3.0.6 to 3.1.0.
- [Release notes](https://github.com/spring-projects/spring-boot/releases)
- [Commits](https://github.com/spring-projects/spring-boot/compare/v3.0.6...v3.1.0)

---
updated-dependencies:
- dependency-name: org.springframework.boot
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-01 09:49:33 +00:00
dependabot[bot]
a19cd327f3 Bump org.springframework.boot:spring-boot-starter-thymeleaf
Bumps [org.springframework.boot:spring-boot-starter-thymeleaf](https://github.com/spring-projects/spring-boot) from 3.0.6 to 3.1.0.
- [Release notes](https://github.com/spring-projects/spring-boot/releases)
- [Commits](https://github.com/spring-projects/spring-boot/compare/v3.0.6...v3.1.0)

---
updated-dependencies:
- dependency-name: org.springframework.boot:spring-boot-starter-thymeleaf
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-01 09:49:26 +00:00
Anthony Stirling
5c831c156b Merge pull request #203 from Frooodle/dependabot/gradle/org.springframework.boot-spring-boot-starter-web-3.1.0
Bump org.springframework.boot:spring-boot-starter-web from 3.0.6 to 3.1.0
2023-06-01 10:48:55 +01:00
Anthony Stirling
602df08df5 Merge branch 'main' into dependabot/gradle/org.springframework.boot-spring-boot-starter-web-3.1.0 2023-06-01 10:48:04 +01:00
Anthony Stirling
3b8d06a9e3 Merge pull request #222 from MarcO-79/patch-14
Update messages_pl_PL.properties
2023-06-01 07:40:45 +01:00
Marcin Bielicki
5fd43b50e0 Update messages_pl_PL.properties
Adjusting the translation to the new version
2023-06-01 08:00:39 +02:00
Anthony Stirling
15d39413f3 scaled changes 2023-05-31 21:33:45 +01:00
systo
5e01946981 Merge branch 'main' of git@github.com:Frooodle/Stirling-PDF.git into main 2023-05-31 21:28:15 +01:00
Anthony Stirling
576d11f09a Download cleanup 2023-05-31 21:28:05 +01:00
Anthony Stirling
a43c296eef Merge pull request #221 from LaserKaspar/main
Scale Pages
2023-05-31 21:27:32 +01:00
Felix Kaspar
6015591e34 Language 2023-05-31 21:44:59 +02:00
Felix Kaspar
6f5f031b24 Scale Pages 2023-05-31 21:39:40 +02:00
Anthony Stirling
1499e78795 GeneralUtils 2023-05-31 20:37:13 +01:00
Anthony Stirling
1b45ab7222 util move around 2023-05-31 20:15:48 +01:00
Anthony Stirling
005b158ad3 naming fix 2023-05-31 18:44:32 +01:00
Anthony Stirling
26d003e840 css fix 2023-05-31 00:17:48 +01:00
Anthony Stirling
192fb39302 new error display logic init 2023-05-31 00:06:35 +01:00
dependabot[bot]
1650dfcc29 Bump org.springframework.boot:spring-boot-starter-web
Bumps [org.springframework.boot:spring-boot-starter-web](https://github.com/spring-projects/spring-boot) from 3.0.6 to 3.1.0.
- [Release notes](https://github.com/spring-projects/spring-boot/releases)
- [Commits](https://github.com/spring-projects/spring-boot/compare/v3.0.6...v3.1.0)

---
updated-dependencies:
- dependency-name: org.springframework.boot:spring-boot-starter-web
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-05-30 19:55:06 +00:00
Anthony Stirling
fda83c4c1d password fix init 2023-05-30 20:54:19 +01:00
systo
1a6ebbb8e5 Merge branch 'main' of git@github.com:Frooodle/Stirling-PDF.git into main 2023-05-30 19:46:09 +01:00
Anthony Stirling
ea1f8912b8 cert change remove requirement for para,s. pdf-to-img dpi fix 2023-05-30 19:45:51 +01:00
Anthony Stirling
a5885d2628 Merge pull request #218 from blairw/patch-1
Update LocalRunGuide.md
2023-05-29 23:27:57 +01:00
Blair Wang
716d4c6f28 Update LocalRunGuide.md
Correct small typo (`/build/libs/` does not exist, `./build/libs/` does)
2023-05-30 08:15:26 +10:00
Anthony Stirling
b3a36c82bb Merge pull request #215 from MarcO-79/patch-13
Update messages_pl_PL.properties
2023-05-29 14:02:43 +01:00
Marcin Bielicki
75fff1d52f Update messages_pl_PL.properties
Adjusting the translation to the new version
2023-05-29 13:34:20 +02:00
Anthony Stirling
2d88987cb3 Multi page layout init for #212 2023-05-27 14:23:25 +01:00
Anthony Stirling
8bbbdbd359 duplex 2023-05-26 23:53:11 +01:00
systo
8a2aa44de8 Merge branch 'main' of git@github.com:Frooodle/Stirling-PDF.git into main 2023-05-26 19:30:06 +01:00
Anthony Stirling
3715c555d3 fix for #213 2023-05-26 15:25:18 +01:00
Anthony Stirling
5e4de6cc5f some filename fixes for nicer extraction 2023-05-26 14:39:08 +01:00
Anthony Stirling
4cadfc64f6 Merge pull request #211 from itsfks/main
Adição tradução PT_BR
2023-05-26 10:15:35 +01:00
felipe
a3f0d47cad Adição tradução PT_BR 2023-05-25 20:49:04 -03:00
Anthony Stirling
bc55b5fdda Merge pull request #208 from MarcO-79/patch-12
Update messages_pl_PL.properties
2023-05-23 11:29:20 +01:00
Marcin Bielicki
43474712eb Update messages_pl_PL.properties
Corrections to the translation
2023-05-23 12:02:37 +02:00
Anthony Stirling
bf48cbb89c Update README.md 2023-05-22 20:43:24 +01:00
Anthony Stirling
c50d8e216a Merge pull request #202 from Frooodle/compress
Fixes for split photo scans
2023-05-22 20:37:55 +01:00
Anthony Stirling
8c160bcddf version bump 2023-05-22 20:33:10 +01:00
Anthony Stirling
6fd6aa2733 fixes to split photos 2023-05-22 20:32:50 +01:00
Anthony Stirling
d7431c459d Merge pull request #200 from ButaNicolae/Romanian-language-and-fixes
Romanian language and fixes
2023-05-22 20:25:51 +01:00
Anthony Stirling
464a11cd69 Merge branch 'main' into Romanian-language-and-fixes 2023-05-22 20:22:37 +01:00
Anthony Stirling
154535a7b1 Merge pull request #201 from Alanimdeo/ko_KR
Add Korean translation
2023-05-22 20:14:38 +01:00
Alanimdeo
f379a759bb Add Korean translation 2023-05-23 00:57:48 +09:00
Nicolae Buta
8cbd7eafb8 required select one language on ocr-pdf 2023-05-22 18:28:16 +03:00
Nicolae Buta
6fb304d010 Detect/Split Scanned photos fixes 2023-05-22 18:27:46 +03:00
Nicolae Buta
089b2290bd blank page path problem fixed 2023-05-22 18:26:29 +03:00
Nicolae Buta
c4e5bfdabb added language Romanian and dosplay u/notpdates button ? 2023-05-22 18:25:58 +03:00
Nicolae Buta
9d5cb104c3 fixed zip on ocr-pdf 2023-05-22 18:25:23 +03:00
Nicolae Buta
d577b8b34e added language Romanian and dosplay u/notpdates button ? 2023-05-22 18:24:55 +03:00
Anthony Stirling
6219cd1d86 Update cert-sign.html 2023-05-22 15:44:45 +01:00
Anthony Stirling
dfd1ac7e99 Merge pull request #197 from wrongecho/patch-1
Update README.md - typo
2023-05-22 09:21:53 +01:00
Anthony Stirling
e3838b291f Merge branch 'main' into patch-1 2023-05-22 08:46:30 +01:00
Anthony Stirling
04a073c7cf Merge pull request #198 from MarcO-79/patch-11
Update messages_pl_PL.properties
2023-05-22 08:46:21 +01:00
Marcin Bielicki
04d5c60957 Update messages_pl_PL.properties
Adjusting the translation to the new version
2023-05-22 08:22:42 +02:00
wrongecho
2be6cad7e1 Update README.md
Correct typo in readme
2023-05-22 06:50:37 +01:00
115 changed files with 4840 additions and 1469 deletions

View File

@@ -8,7 +8,6 @@ on:
- main
jobs:
push:
runs-on: ubuntu-latest
steps:
@@ -46,13 +45,17 @@ jobs:
username: ${{ github.actor }}
password: ${{ github.token }}
- name: Convert repository owner to lowercase
id: repoowner
run: echo "::set-output name=lowercase::$(echo ${{ github.repository_owner }} | awk '{print tolower($0)}')"
- name: Generate tags
id: meta
uses: docker/metadata-action@v4.4.0
with:
images: |
${{ secrets.DOCKER_HUB_USERNAME }}/s-pdf
ghcr.io/${{ github.repository_owner }}/s-pdf
ghcr.io/${{ steps.repoowner.outputs.lowercase }}/s-pdf
tags: |
type=raw,value=${{ steps.versionNumber.outputs.versionNumber }},enable=${{ github.ref == 'refs/heads/master' }}
type=raw,value=latest,enable=${{ github.ref == 'refs/heads/master' }}
@@ -76,27 +79,25 @@ jobs:
labels: ${{ steps.meta.outputs.labels }}
platforms: linux/amd64,linux/arm64/v8
- name: Generate tags
- name: Generate tags ultra-lite
id: meta2
uses: docker/metadata-action@v4.4.0
with:
images: |
${{ secrets.DOCKER_HUB_USERNAME }}/s-pdf
ghcr.io/${{ github.repository_owner }}/s-pdf
ghcr.io/${{ steps.repoowner.outputs.lowercase }}/s-pdf
tags: |
type=raw,value=${{ steps.versionNumber.outputs.versionNumber }}-ultra-light,enable=${{ github.ref == 'refs/heads/master' }}
type=raw,value=latest-ultra-light,enable=${{ github.ref == 'refs/heads/master' }}
type=raw,value=alpha-ultra-light,enable=${{ github.ref == 'refs/heads/main' }}
type=raw,value=${{ steps.versionNumber.outputs.versionNumber }}-ultra-lite,enable=${{ github.ref == 'refs/heads/master' }}
type=raw,value=latest-ultra-lite,enable=${{ github.ref == 'refs/heads/master' }}
- name: Convert repository owner to lowercase
id: repoowner
run: echo "::set-output name=lowercase::$(echo ${{ github.repository_owner }} | awk '{print tolower($0)}')"
- name: Build and push Dockerfile-ultralite
- name: Build and push Dockerfile-ultra-lite
uses: docker/build-push-action@v4.0.0
with:
context: .
file: ./Dockerfile-ultralite
file: ./Dockerfile-ultra-lite
push: true
cache-from: type=gha
cache-to: type=gha,mode=max
@@ -105,3 +106,27 @@ jobs:
platforms: linux/amd64,linux/arm64/v8
- name: Generate tags lite
id: meta3
uses: docker/metadata-action@v4.4.0
with:
images: |
${{ secrets.DOCKER_HUB_USERNAME }}/s-pdf
ghcr.io/${{ steps.repoowner.outputs.lowercase }}/s-pdf
tags: |
type=raw,value=${{ steps.versionNumber.outputs.versionNumber }}-lite,enable=${{ github.ref == 'refs/heads/master' }}
type=raw,value=latest-lite,enable=${{ github.ref == 'refs/heads/master' }}
- name: Build and push Dockerfile-lite
uses: docker/build-push-action@v4.0.0
with:
context: .
file: ./Dockerfile-lite
push: true
cache-from: type=gha
cache-to: type=gha,mode=max
tags: ${{ steps.meta3.outputs.tags }}
labels: ${{ steps.meta3.outputs.labels }}
platforms: linux/amd64,linux/arm64/v8

45
.github/workflows/releaseArtifacts.yml vendored Normal file
View File

@@ -0,0 +1,45 @@
name: Release Artifacts
on:
release:
types: [created]
jobs:
push:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3.5.2
- name: Set up JDK 17
uses: actions/setup-java@v3.11.0
with:
java-version: '17'
distribution: 'temurin'
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Generate jar
run: ./gradlew clean createExe
- name: Upload binaries to release
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: ./build/launch4j/Stirling-PDF.exe
asset_name: Stirling-PDF.exe
tag: ${{ github.ref }}
overwrite: true
- name: Get version number
id: versionNumber
run: echo "::set-output name=versionNumber::$(./gradlew printVersion --quiet | tail -1)"
- name: Upload binaries to release
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: ./build/libs/Stirling-PDF-${{ steps.versionNumber.outputs.versionNumber }}.jar
asset_name: S-PDF.jar
tag: ${{ github.ref }}
overwrite: true

37
.github/workflows/swagger.yml vendored Normal file
View File

@@ -0,0 +1,37 @@
name: Update Swagger
on:
workflow_dispatch:
push:
branches:
- master
jobs:
push:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3.5.2
- name: Set up JDK 17
uses: actions/setup-java@v3.11.0
with:
java-version: '17'
distribution: 'temurin'
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Generate Swagger documentation
run: ./gradlew generateOpenApiDocs
- name: Upload Swagger Documentation to SwaggerHub
run: ./gradlew swaggerhubUpload
env:
SWAGGERHUB_API_KEY: ${{ secrets.SWAGGERHUB_API_KEY }}
- name: Set API version as published and default on SwaggerHub
run: |
curl -X PUT -H "Authorization: ${SWAGGERHUB_API_KEY}" "https://api.swaggerhub.com/apis/Frooodle/Stirling-PDF/${{ steps.versionNumber.outputs.versionNumber }}/settings/lifecycle" -H "accept: application/json" -H "Content-Type: application/json" -d "{\"published\":true,\"default\":true}"
env:
SWAGGERHUB_API_KEY: ${{ secrets.SWAGGERHUB_API_KEY }}

1
.gitignore vendored
View File

@@ -14,6 +14,7 @@ local.properties
.recommenders
.classpath
.project
version.properties
# Gradle
.gradle

View File

@@ -1,5 +1,5 @@
# Build jbig2enc in a separate stage
FROM frooodle/stirling-pdf-base:beta3
FROM frooodle/stirling-pdf-base:latest
# Create scripts folder and copy local scripts
RUN mkdir /scripts

23
Dockerfile-lite Normal file
View File

@@ -0,0 +1,23 @@
# Build jbig2enc in a separate stage
FROM bellsoft/liberica-openjdk-debian:17
RUN apt-get update && \
apt-get install -y --no-install-recommends \
libreoffice-core-nogui \
libreoffice-common \
libreoffice-writer-nogui \
libreoffice-calc-nogui \
libreoffice-impress-nogui \
unoconv && \
rm -rf /var/lib/apt/lists/*
# Copy the application JAR file
COPY build/libs/*.jar app.jar
# Expose the application port
EXPOSE 8080
# Set environment variables
ENV GROUPS_TO_REMOVE=Python,OpenCV,OCRmyPDF
# Run the application
CMD ["java", "-jar", "/app.jar"]

View File

@@ -1,5 +1,5 @@
# Build jbig2enc in a separate stage
FROM openjdk:17-jdk-slim
FROM bellsoft/liberica-openjdk-alpine:17
# Copy the application JAR file
COPY build/libs/*.jar app.jar
@@ -8,7 +8,7 @@ COPY build/libs/*.jar app.jar
EXPOSE 8080
# Set environment variables
ENV GROUPS_TO_REMOVE=LibreOffice,CLI
ENV GROUPS_TO_REMOVE=CLI
# Run the application
CMD ["java", "-jar", "/app.jar"]

View File

@@ -1,5 +1,5 @@
# Main stage
FROM openjdk:17-jdk-slim AS base
FROM bellsoft/liberica-openjdk-debian:17 AS base
RUN apt-get update && \
apt-get install -y --no-install-recommends \
libreoffice-core-nogui \

35
Endpoint-groups.md Normal file
View File

@@ -0,0 +1,35 @@
| Operation | PageOps | Convert | Security | Other | CLI | Python | OpenCV | LibreOffice | OCRmyPDF | Java | Javascript |
|---------------------|---------|---------|----------|-------|------|--------|--------|-------------|----------|----------|------------|
| merge-pdfs | ✔️ | | | | | | | | | ✔️ | |
| multi-page-layout | ✔️ | | | | | | | | | ✔️ | |
| pdf-organizer | ✔️ | | | | | | | | | ✔️ | ✔️ |
| remove-pages | ✔️ | | | | | | | | | ✔️ | |
| rotate-pdf | ✔️ | | | | | | | | | ✔️ | |
| scale-pages | ✔️ | | | | | | | | | ✔️ | |
| split-pdfs | ✔️ | | | | | | | | | ✔️ | |
| file-to-pdf | | ✔️ | | | ✔️ | | | ✔️ | | | |
| img-to-pdf | | ✔️ | | | | | | | | ✔️ | |
| pdf-to-html | | ✔️ | | | ✔️ | | | ✔️ | | | |
| pdf-to-img | | ✔️ | | | | | | | | ✔️ | |
| pdf-to-pdfa | | ✔️ | | | ✔️ | | | | ✔️ | | |
| pdf-to-presentation | | ✔️ | | | ✔️ | | | ✔️ | | | |
| pdf-to-text | | ✔️ | | | ✔️ | | | ✔️ | | | |
| pdf-to-word | | ✔️ | | | ✔️ | | | ✔️ | | | |
| pdf-to-xml | | ✔️ | | | ✔️ | | | ✔️ | | | |
| xlsx-to-pdf | | ✔️ | | | ✔️ | | | ✔️ | | | |
| add-password | | | ✔️ | | | | | | | ✔️ | |
| add-watermark | | | ✔️ | | | | | | | ✔️ | |
| cert-sign | | | ✔️ | | | | | | | ✔️ | |
| change-permissions | | | ✔️ | | | | | | | ✔️ | |
| remove-password | | | ✔️ | | | | | | | ✔️ | |
| add-image | | | | ✔️ | | | | | | ✔️ | |
| change-metadata | | | | ✔️ | | | | | | ✔️ | |
| compare | | | | ✔️ | | | | | | | ✔️ |
| compress-pdf | | | | ✔️ | ✔️ | | | | ✔️ | | |
| extract-image-scans | | | | ✔️ | ✔️ | ✔️ | ✔️ | | | | |
| extract-images | | | | ✔️ | | | | | | ✔️ | |
| flatten | | | | ✔️ | | | | | | | |
| ocr-pdf | | | | ✔️ | ✔️ | | | | ✔️ | | |
| remove-blanks | | | | ✔️ | ✔️ | ✔️ | ✔️ | | | | |
| repair | | | | ✔️ | ✔️ | | | ✔️ | | | |
| sign | | | | ✔️ | | | | | | | ✔️ |

View File

@@ -123,7 +123,7 @@ This folder is required for the python scripts using OpenCV
```bash
sudo mkdir /opt/Stirling-PDF &&\
sudo mv /build/libs/S-PDF-*.jar /opt/Stirling-PDF/ &&\
sudo mv ./build/libs/Stirling-PDF-*.jar /opt/Stirling-PDF/ &&\
sudo mv scripts /opt/Stirling-PDF/ &&\
echo "Scripts installed."
```

View File

@@ -10,7 +10,7 @@
This is a powerful locally hosted web based PDF manipulation tool using docker that allows you to perform various operations on PDF files, such as splitting merging, converting, reorganizing, adding images, rotating, compressing, and more. This locally hosted web application started as a 100% ChatGPT-made application and has evolved to include a wide range of features to handle all your PDF needs.
Stirling PDF makes no outbount calls for any record keeping or tracking.
Stirling PDF makes no outbound calls for any record keeping or tracking.
All files and PDFs are either purely client side, in server memory only during the execution of the task or within a temporay file only for execution of the task.
Any file which has been downloaded by the user will have already been deleted from the server by that time.
@@ -21,7 +21,7 @@ Feel free to request any features or bug fixes either in github issues or our [D
![stirling-home](images/stirling-home.png)
## Features
- Full intractable GUI for merging/splitting/rotating/moving PDFs and their pages.
- Full interactive GUI for merging/splitting/rotating/moving PDFs and their pages.
- Split PDFs into multiple files at specified page numbers or extract all pages as individual files.
- Merge multiple PDFs together into a single resultant file
- Convert PDFs to and from images
@@ -53,6 +53,7 @@ Hosted instance/demo of the app can be seen [here](https://pdf.adminforge.de/) h
## Technologies used
- Spring Boot + Thymeleaf
- PDFBox
- IText7
- [LibreOffice](https://www.libreoffice.org/discover/libreoffice/) for advanced conversions
- [OcrMyPdf](https://github.com/ocrmypdf/OCRmyPDF)
- HTML, CSS, JavaScript
@@ -68,13 +69,20 @@ Please view https://github.com/Frooodle/Stirling-PDF/blob/main/LocalRunGuide.md
### Docker
https://hub.docker.com/r/frooodle/s-pdf
Stirling PDF has 3 different versions, a Full version, Lite and ultra-Lite. Depending on the types of features you use you may want a smaller image to save on space.
To see what the different versions offer please look at our [version mapping](https://github.com/Frooodle/Stirling-PDF/blob/main/Version-groups.md)
For people that dont mind about space optimisation just use latest tag.
![Docker Image Size (tag)](https://img.shields.io/docker/image-size/frooodle/s-pdf/latest?label=Stirling-PDF%20Full)
![Docker Image Size (tag)](https://img.shields.io/docker/image-size/frooodle/s-pdf/latest-lite?label=Stirling-PDF%20Lite)
![Docker Image Size (tag)](https://img.shields.io/docker/image-size/frooodle/s-pdf/latest-ultra-lite?label=Stirling-PDF%20Ultra-Lite)
Docker Run
```
docker run -d \
-p 8080:8080 \
-v /location/of/trainingData:/usr/share/tesseract-ocr/4.00/tessdata \
--name stirling-pdf \
frooodle/s-pdf
frooodle/s-pdf:latest
Can also add these for customisation but are not required
@@ -90,7 +98,7 @@ Docker Compose
version: '3.3'
services:
stirling-pdf:
image: frooodle/s-pdf
image: frooodle/s-pdf:latest
ports:
- '8080:8080'
volumes:
@@ -122,6 +130,8 @@ Stirling PDF currently supports
- Italian (Italiano) (it_IT)
- Swedish (Svenska) (sv_SE)
- Polish (Polski) (pl_PL)
- Romanian (Română) (ro_RO)
- Korean (한국어) (ko_KR)
If you want to add your own language to Stirling-PDF please refer
https://github.com/Frooodle/Stirling-PDF/blob/main/HowToAddNewLanguage.md

48
Version-groups.md Normal file
View File

@@ -0,0 +1,48 @@
|Technology | Ultra-Lite | Lite | Full |
|----------------|:----------:|:----:|:----:|
| Java | ✔️ | ✔️ | ✔️ |
| JavaScript | ✔️ | ✔️ | ✔️ |
| Libre | | ✔️ | ✔️ |
| Python | | | ✔️ |
| OpenCV | | | ✔️ |
| OCRmyPDF | | | ✔️ |
Operation | Ultra-Lite | Lite | Full
--------------------|------------|------|-----
add-password | ✔️ | ✔️ | ✔️
add-watermark | ✔️ | ✔️ | ✔️
cert-sign | ✔️ | ✔️ | ✔️
change-metadata | ✔️ | ✔️ | ✔️
change-permissions | ✔️ | ✔️ | ✔️
compare | ✔️ | ✔️ | ✔️
extract-images | ✔️ | ✔️ | ✔️
flatten | ✔️ | ✔️ | ✔️
img-to-pdf | ✔️ | ✔️ | ✔️
merge-pdfs | ✔️ | ✔️ | ✔️
multi-page-layout | ✔️ | ✔️ | ✔️
pdf-organizer | ✔️ | ✔️ | ✔️
pdf-to-img | ✔️ | ✔️ | ✔️
remove-pages | ✔️ | ✔️ | ✔️
remove-password | ✔️ | ✔️ | ✔️
rotate-pdf | ✔️ | ✔️ | ✔️
scale-pages | ✔️ | ✔️ | ✔️
sign | ✔️ | ✔️ | ✔️
split-pdfs | ✔️ | ✔️ | ✔️
add-image | ✔️ | ✔️ | ✔️
file-to-pdf | | ✔️ | ✔️
pdf-to-html | | ✔️ | ✔️
pdf-to-presentation | | ✔️ | ✔️
pdf-to-text | | ✔️ | ✔️
pdf-to-word | | ✔️ | ✔️
pdf-to-xml | | ✔️ | ✔️
repair | | ✔️ | ✔️
xlsx-to-pdf | | ✔️ | ✔️
compress-pdf | | | ✔️
extract-image-scans | | | ✔️
ocr-pdf | | | ✔️
pdf-to-pdfa | | | ✔️
remove-blanks | | | ✔️

View File

@@ -1,21 +1,53 @@
plugins {
id 'java'
id 'org.springframework.boot' version '3.0.6'
id 'org.springframework.boot' version '3.1.0'
id 'io.spring.dependency-management' version '1.1.0'
id 'org.springdoc.openapi-gradle-plugin' version '1.6.0'
id "io.swagger.swaggerhub" version "1.1.0"
id 'edu.sc.seis.launch4j' version '3.0.1'
}
group = 'stirling.software'
version = '0.9.0'
version = '0.10.2'
sourceCompatibility = '17'
repositories {
mavenCentral()
}
openApi {
apiDocsUrl = "http://localhost:8080/v3/api-docs"
outputDir = file("$projectDir")
outputFileName = "SwaggerDoc.json"
}
launch4j {
icon = "${projectDir}/src/main/resources/static/favicon.ico"
outfile="Stirling-PDF.exe"
headerType="console"
jarTask = tasks.bootJar
errTitle="Encountered error, Do you have Java 17?"
downloadUrl="https://download.oracle.com/java/17/latest/jdk-17_windows-x64_bin.exe"
variables=["BROWSER_OPEN=true"]
jreMinVersion="17"
mutexName="Stirling-PDF"
windowTitle="Stirling-PDF"
messagesStartupError="An error occurred while starting Stirling-PDF"
//messagesJreNotFoundError="This application requires a Java Runtime Environment, Please download Java 17."
messagesJreVersionError="You are running the wrong version of Java, Please download Java 17."
messagesLauncherError="Java is corrupted. Please uninstall and then install Java 17."
messagesInstanceAlreadyExists="Stirling-PDF is already running."
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web:3.0.6'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf:3.0.6'
testImplementation 'org.springframework.boot:spring-boot-starter-test:3.0.6'
implementation 'org.springframework.boot:spring-boot-starter-web:3.1.0'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf:3.1.0'
testImplementation 'org.springframework.boot:spring-boot-starter-test:3.1.0'
// https://mvnrepository.com/artifact/org.apache.pdfbox/jbig2-imageio
implementation group: 'org.apache.pdfbox', name: 'jbig2-imageio', version: '3.0.4'
implementation 'commons-io:commons-io:2.11.0'
@@ -34,6 +66,25 @@ dependencies {
}
task writeVersion {
def propsFile = file('src/main/resources/version.properties')
def props = new Properties()
props.setProperty('version', version)
props.store(propsFile.newWriter(), null)
}
swaggerhubUpload {
//dependsOn generateOpenApiDocs // Depends on your task generating Swagger docs
api 'Stirling-PDF' // The name of your API on SwaggerHub
owner 'Frooodle' // Your SwaggerHub username (or organization name)
version project.version // The version of your API
inputFile './SwaggerDoc.json' // The path to your Swagger docs
token "${System.getenv('SWAGGERHUB_API_KEY')}" // Your SwaggerHub API key, passed as an environment variable
oas '3.0.0' // The version of the OpenAPI Specification you're using
}
jar {
enabled = false
manifest {

View File

@@ -1,32 +0,0 @@
Operation | PageOps | Convert | Security | Other | CLI | Python | OpenCV | LibreOffice | OCRmyPDF | Java | Javascript
--------------------|---------|---------|----------|-------|------|--------|--------|-------------|--------- |-------- |-----------
remove-pages | X | | | | | | | | | X |
merge-pdfs | X | | | | | | | | | X |
split-pdfs | X | | | | | | | | | X |
pdf-organizer | X | | | | | | | | | X | X
rotate-pdf | X | | | | | | | | | X |
pdf-to-img | | X | | | | | | | | X |
img-to-pdf | | X | | | | | | | | X |
pdf-to-pdfa | | X | | | X | | | | X | |
file-to-pdf | | X | | | X | | | X | | |
xlsx-to-pdf | | X | | | X | | | X | | |
pdf-to-word | | X | | | X | | | X | | |
pdf-to-presentation | | X | | | X | | | X | | |
pdf-to-text | | X | | | X | | | X | | |
pdf-to-html | | X | | | X | | | X | | |
pdf-to-xml | | X | | | X | | | X | | |
add-password | | | X | | | | | | | X |
remove-password | | | X | | | | | | | X |
change-permissions | | | X | | | | | | | X |
add-watermark | | | X | | | | | | | X |
ocr-pdf | | | | X | X | | | | X | |
add-image | | | | X | | | | | | X |
compress-pdf | | | | X | X | | | | X
extract-images | | | | X | | | | | | X |
change-metadata | | | | X | | | | | | X |
extract-image-scans | | | | X | X | X | X | | | |
sign | | | | X | | | | | | | X
flatten | | | | X | | | | | | |
repair | | | | X | X | | | X | | |
remove-blanks | | | | X | X | X | X | | | |
compare | | | | X | | | | | | | X

36
lauch4jConfig.xml Normal file
View File

@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<launch4jConfig>
<dontWrapJar>false</dontWrapJar>
<headerType>console</headerType>
<jar>.\build\libs\S-PDF-0.10.1.jar</jar>
<outfile>.\Stirling-PDF.exe</outfile>
<errTitle>Please download Java17</errTitle>
<cmdLine></cmdLine>
<chdir>.</chdir>
<priority>normal</priority>
<downloadUrl>https://download.oracle.com/java/17/latest/jdk-17_windows-x64_bin.exe</downloadUrl>
<supportUrl></supportUrl>
<stayAlive>false</stayAlive>
<restartOnCrash>false</restartOnCrash>
<manifest></manifest>
<icon>./src/main/resources/static/favicon.ico</icon>
<var>BROWSER_OPEN=true</var>
<singleInstance>
<mutexName>Stirling-PDF</mutexName>
<windowTitle>Stirling-PDF</windowTitle>
</singleInstance>
<jre>
<path>%JAVA_HOME%;%PATH%</path>
<requiresJdk>false</requiresJdk>
<requires64Bit>false</requires64Bit>
<minVersion>17</minVersion>
<maxVersion></maxVersion>
</jre>
<messages>
<startupErr>An error occurred while starting Stirling-PDF</startupErr>
<jreNotFoundErr>This application requires a Java Runtime Environment, Please download Java 17.</jreNotFoundErr>
<jreVersionErr>You are running the wrong version of Java, Please download Java 17.</jreVersionErr>
<launcherErr>Java is corrupted. Please uninstall and then install Java 17.</launcherErr>
<instanceAlreadyExistsMsg>Stirling-PDF is already running.</instanceAlreadyExistsMsg>
</messages>
</launch4jConfig>

View File

@@ -14,13 +14,21 @@ def is_blank_image(image_path, threshold=10, white_percent=99, white_value=255,
blurred_image = cv2.GaussianBlur(image, (blur_size, blur_size), 0)
_, thresholded_image = cv2.threshold(blurred_image, white_value - threshold, white_value, cv2.THRESH_BINARY)
# Calculate the percentage of white pixels in the thresholded image
white_pixels = np.sum(thresholded_image == white_value)
white_pixels = 0
total_pixels = thresholded_image.size
white_pixel_percentage = (white_pixels / total_pixels) * 100
for i in range(0, thresholded_image.shape[0], 2):
for j in range(0, thresholded_image.shape[1], 2):
if thresholded_image[i, j] == white_value:
white_pixels += 1
white_pixel_percentage = (white_pixels / (i * thresholded_image.shape[1] + j + 1)) * 100
if white_pixel_percentage < white_percent:
return False
print(f"Page has white pixel percent of {white_pixel_percentage}")
return white_pixel_percentage > white_percent
return True
if __name__ == "__main__":

View File

@@ -1,3 +1,4 @@
import argparse
import sys
import cv2
import numpy as np
@@ -36,33 +37,21 @@ def estimate_background_color(image, sample_points=5):
return np.median(colors, axis=0)
def auto_rotate(image, angle_threshold=10):
def auto_rotate(image, angle_threshold=1):
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
ret, binary = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
contours, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
edges = cv2.Canny(gray, 50, 150, apertureSize=3)
lines = cv2.HoughLines(edges, 1, np.pi / 180, 200)
if len(contours) == 0:
if lines is None:
return image
largest_contour = max(contours, key=cv2.contourArea)
mu = cv2.moments(largest_contour)
# compute the median angle of the lines
angles = []
for rho, theta in lines[:, 0]:
angles.append((theta * 180) / np.pi - 90)
if mu["m00"] == 0:
return image
angle = np.median(angles)
x_centroid = int(mu["m10"] / mu["m00"])
y_centroid = int(mu["m01"] / mu["m00"])
coords = np.column_stack(np.where(binary > 0))
u, _, vt = np.linalg.svd(coords - np.array([[y_centroid, x_centroid]]), full_matrices=False)
angle = np.arctan2(u[1, 0], u[0, 0]) * 180 / np.pi
if angle < -45:
angle = -(90 + angle)
else:
angle = -angle
if abs(angle) < angle_threshold:
return image
@@ -73,6 +62,7 @@ def auto_rotate(image, angle_threshold=10):
def crop_borders(image, border_color, tolerance=30):
mask = cv2.inRange(image, border_color - tolerance, border_color + tolerance)
@@ -112,23 +102,15 @@ def split_photos(input_file, output_directory, tolerance=30, min_area=10000, min
print(f"Saved {output_path}")
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Usage: python3 split_photos.py <input_file> <output_directory> [tolerance] [min_area] [min_contour_area] [angle_threshold] [border_size]")
print("\nParameters:")
print(" <input_file> - The input scanned image containing multiple photos.")
print(" <output_directory> - The directory where the result images should be placed.")
print(" [tolerance] - Optional. Determines the range of color variation around the estimated background color (default: 30).")
print(" [min_area] - Optional. Sets the minimum area threshold for a photo (default: 10000).")
print(" [min_contour_area] - Optional. Sets the minimum contour area threshold for a photo (default: 500).")
print(" [angle_threshold] - Optional. Sets the minimum absolute angle required for the image to be rotated (default: 10).")
print(" [border_size] - Optional. Sets the size of the border added and removed to prevent white borders in the output (default: 0).")
sys.exit(1)
parser = argparse.ArgumentParser(description="Split photos in an image")
parser.add_argument("input_file", help="The input scanned image containing multiple photos.")
parser.add_argument("output_directory", help="The directory where the result images should be placed.")
parser.add_argument("--tolerance", type=int, default=30, help="Determines the range of color variation around the estimated background color (default: 30).")
parser.add_argument("--min_area", type=int, default=10000, help="Sets the minimum area threshold for a photo (default: 10000).")
parser.add_argument("--min_contour_area", type=int, default=500, help="Sets the minimum contour area threshold for a photo (default: 500).")
parser.add_argument("--angle_threshold", type=int, default=10, help="Sets the minimum absolute angle required for the image to be rotated (default: 10).")
parser.add_argument("--border_size", type=int, default=0, help="Sets the size of the border added and removed to prevent white borders in the output (default: 0).")
args = parser.parse_args()
input_file = sys.argv[1]
output_directory = sys.argv[2]
tolerance = int(sys.argv[3]) if len(sys.argv) > 3 else 20
min_area = int(sys.argv[4]) if len(sys.argv) > 4 else 8000
min_contour_area = int(sys.argv[5]) if len(sys.argv) > 5 else 500
angle_threshold = int(sys.argv[6]) if len(sys.argv) > 6 else 60
border_size = int(sys.argv[7]) if len(sys.argv) > 7 else 0
split_photos(input_file, output_directory, tolerance=tolerance, min_area=min_area, min_contour_area=min_contour_area, angle_threshold=angle_threshold, border_size=border_size)
split_photos(args.input_file, args.output_directory, tolerance=args.tolerance, min_area=args.min_area, min_contour_area=args.min_contour_area, angle_threshold=args.angle_threshold, border_size=args.border_size)

View File

@@ -1 +1 @@
rootProject.name = 'S-PDF'
rootProject.name = 'Stirling-PDF'

View File

@@ -2,10 +2,63 @@ package stirling.software.SPDF;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import java.awt.*;
import java.net.URI;
import jakarta.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
@SpringBootApplication
public class SPdfApplication {
@Autowired
private Environment env;
@PostConstruct
public void init() {
// Check if the BROWSER_OPEN environment variable is set to true
String browserOpenEnv = env.getProperty("BROWSER_OPEN");
boolean browserOpen = browserOpenEnv != null && browserOpenEnv.equalsIgnoreCase("true");
if (browserOpen) {
try {
String port = env.getProperty("local.server.port");
if(port == null || port.length() == 0) {
port="8080";
}
String url = "http://localhost:" + port;
String os = System.getProperty("os.name").toLowerCase();
Runtime rt = Runtime.getRuntime();
if (os.contains("win")) {
// For Windows
rt.exec("rundll32 url.dll,FileProtocolHandler " + url);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
SpringApplication.run(SPdfApplication.class, args);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("Stirling-PDF Started.");
String port = System.getProperty("local.server.port");
if(port == null || port.length() == 0) {
port="8080";
}
String url = "http://localhost:" + port;
System.out.println("Navigate to " + url);
}
}

View File

@@ -16,7 +16,7 @@ public class AppConfig {
@Bean(name = "appVersion")
public String appVersion() {
String version = getClass().getPackage().getImplementationVersion();
return (version != null) ? version : "0.3.3";
return (version != null) ? version : "0.0.0";
}
@Bean(name = "homeText")

View File

@@ -20,12 +20,14 @@ public class EndpointConfiguration {
}
public void enableEndpoint(String endpoint) {
endpointStatuses.put(endpoint, true);
endpointStatuses.put(endpoint, true);
}
public void disableEndpoint(String endpoint) {
logger.info("Disabling {}", endpoint);
endpointStatuses.put(endpoint, false);
if(!endpointStatuses.containsKey(endpoint) || endpointStatuses.get(endpoint) != false) {
logger.info("Disabling {}", endpoint);
endpointStatuses.put(endpoint, false);
}
}
public boolean isEndpointEnabled(String endpoint) {
@@ -64,7 +66,9 @@ public class EndpointConfiguration {
addEndpointToGroup("PageOps", "split-pdfs");
addEndpointToGroup("PageOps", "pdf-organizer");
addEndpointToGroup("PageOps", "rotate-pdf");
addEndpointToGroup("PageOps", "multi-page-layout");
addEndpointToGroup("PageOps", "scale-pages");
// Adding endpoints to "Convert" group
addEndpointToGroup("Convert", "pdf-to-img");
addEndpointToGroup("Convert", "img-to-pdf");
@@ -82,6 +86,9 @@ public class EndpointConfiguration {
addEndpointToGroup("Security", "remove-password");
addEndpointToGroup("Security", "change-permissions");
addEndpointToGroup("Security", "add-watermark");
addEndpointToGroup("Security", "cert-sign");
// Adding endpoints to "Other" group
addEndpointToGroup("Other", "ocr-pdf");
@@ -158,6 +165,9 @@ public class EndpointConfiguration {
addEndpointToGroup("Java", "add-image");
addEndpointToGroup("Java", "extract-images");
addEndpointToGroup("Java", "change-metadata");
addEndpointToGroup("Java", "cert-sign");
addEndpointToGroup("Java", "multi-page-layout");
addEndpointToGroup("Java", "scale-pages");
//Javascript

View File

@@ -1,5 +1,9 @@
package stirling.software.SPDF.config;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -10,13 +14,23 @@ import io.swagger.v3.oas.models.info.Info;
@Configuration
public class OpenApiConfig {
@Bean
public OpenAPI customOpenAPI() {
String version = getClass().getPackage().getImplementationVersion();
version = (version != null) ? version : "1.0.0";
return new OpenAPI().components(new Components()).info(
new Info().title("Stirling PDF API").version(version).description("API documentation for all Server-Side processing.\nPlease note some functionality might be UI only and missing from here."));
}
@Bean
public OpenAPI customOpenAPI() {
String version = getClass().getPackage().getImplementationVersion();
if (version == null) {
Properties props = new Properties();
try (InputStream input = getClass().getClassLoader().getResourceAsStream("version.properties")) {
props.load(input);
version = props.getProperty("version");
} catch (IOException ex) {
ex.printStackTrace();
version = "1.0.0"; // default version if all else fails
}
}
return new OpenAPI().components(new Components()).info(
new Info().title("Stirling PDF API").version(version).description("API documentation for all Server-Side processing.\nPlease note some functionality might be UI only and missing from here."));
}
}

View File

@@ -17,7 +17,7 @@ import org.springframework.web.multipart.MultipartFile;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import stirling.software.SPDF.utils.PdfUtils;
import stirling.software.SPDF.utils.WebResponseUtils;
@RestController
public class MergeController {
@@ -65,7 +65,7 @@ public class MergeController {
// Return the merged PDF as a response
ResponseEntity<byte[]> response = PdfUtils.pdfDocToWebResponse(mergedDoc, files[0].getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_merged.pdf");
ResponseEntity<byte[]> response = WebResponseUtils.pdfDocToWebResponse(mergedDoc, files[0].getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_merged.pdf");
for (PDDocument doc : documents) {
// Close the document after processing

View File

@@ -0,0 +1,99 @@
package stirling.software.SPDF.controller.api;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import com.itextpdf.kernel.geom.PageSize;
import com.itextpdf.kernel.geom.Rectangle;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfPage;
import com.itextpdf.kernel.pdf.PdfReader;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.kernel.pdf.canvas.PdfCanvas;
import com.itextpdf.kernel.pdf.xobject.PdfFormXObject;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Schema;
import stirling.software.SPDF.utils.WebResponseUtils;
@RestController
public class MultiPageLayoutController {
private static final Logger logger = LoggerFactory.getLogger(MultiPageLayoutController.class);
@PostMapping(value = "/multi-page-layout", consumes = "multipart/form-data")
@Operation(summary = "Merge multiple pages of a PDF document into a single page", description = "This operation takes an input PDF file and the number of pages to merge into a single sheet in the output PDF file.")
public ResponseEntity<byte[]> mergeMultiplePagesIntoOne(
@Parameter(description = "The input PDF file", required = true) @RequestParam("fileInput") MultipartFile file,
@Parameter(description = "The number of pages to fit onto a single sheet in the output PDF. Acceptable values are 2, 3, 4, 9, 16.", required = true, schema = @Schema(type = "integer", allowableValues = {
"2", "3", "4", "9", "16" })) @RequestParam("pagesPerSheet") int pagesPerSheet)
throws IOException {
if (pagesPerSheet != 2 && pagesPerSheet != 3
&& pagesPerSheet != (int) Math.sqrt(pagesPerSheet) * Math.sqrt(pagesPerSheet)) {
throw new IllegalArgumentException("pagesPerSheet must be 2, 3 or a perfect square");
}
int cols = pagesPerSheet == 2 || pagesPerSheet == 3 ? pagesPerSheet : (int) Math.sqrt(pagesPerSheet);
int rows = pagesPerSheet == 2 || pagesPerSheet == 3 ? 1 : (int) Math.sqrt(pagesPerSheet);
byte[] bytes = file.getBytes();
PdfReader reader = new PdfReader(new ByteArrayInputStream(bytes));
PdfDocument pdfDoc = new PdfDocument(reader);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PdfWriter writer = new PdfWriter(baos);
PdfDocument outputPdf = new PdfDocument(writer);
PageSize pageSize = new PageSize(PageSize.A4.rotate());
int totalPages = pdfDoc.getNumberOfPages();
float cellWidth = pageSize.getWidth() / cols;
float cellHeight = pageSize.getHeight() / rows;
for (int i = 1; i <= totalPages; i += pagesPerSheet) {
PdfPage page = outputPdf.addNewPage(pageSize);
PdfCanvas pdfCanvas = new PdfCanvas(page);
for (int row = 0; row < rows; row++) {
for (int col = 0; col < cols; col++) {
int index = i + row * cols + col;
if (index <= totalPages) {
// Get the page and calculate scaling factors
Rectangle rect = pdfDoc.getPage(index).getPageSize();
float scaleWidth = cellWidth / rect.getWidth();
float scaleHeight = cellHeight / rect.getHeight();
float scale = Math.min(scaleWidth, scaleHeight);
PdfFormXObject formXObject = pdfDoc.getPage(index).copyAsFormXObject(outputPdf);
float x = col * cellWidth + (cellWidth - rect.getWidth() * scale) / 2;
float y = (rows - 1 - row) * cellHeight + (cellHeight - rect.getHeight() * scale) / 2;
// Save the graphics state, apply the transformations, add the object, and then
// restore the graphics state
pdfCanvas.saveState();
pdfCanvas.concatMatrix(scale, 0, 0, scale, x, y);
pdfCanvas.addXObject(formXObject, 0, 0);
pdfCanvas.restoreState();
}
}
}
}
outputPdf.close();
byte[] pdfContent = baos.toByteArray();
pdfDoc.close();
return WebResponseUtils.bytesToWebResponse(pdfContent, file.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_layoutChanged.pdf");
}
}

View File

@@ -17,111 +17,191 @@ import org.springframework.web.multipart.MultipartFile;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import stirling.software.SPDF.utils.PdfUtils;
import io.swagger.v3.oas.annotations.media.Schema;
import stirling.software.SPDF.utils.GeneralUtils;
import stirling.software.SPDF.utils.WebResponseUtils;
@RestController
public class RearrangePagesPDFController {
private static final Logger logger = LoggerFactory.getLogger(RearrangePagesPDFController.class);
private static final Logger logger = LoggerFactory.getLogger(RearrangePagesPDFController.class);
@PostMapping(consumes = "multipart/form-data", value = "/remove-pages")
@Operation(summary = "Remove pages from a PDF file", description = "This endpoint removes specified pages from a given PDF file. Users can provide a comma-separated list of page numbers or ranges to delete.")
public ResponseEntity<byte[]> deletePages(
@RequestPart(required = true, value = "fileInput") @Parameter(description = "The input PDF file from which pages will be removed") MultipartFile pdfFile,
@RequestParam("pagesToDelete") @Parameter(description = "Comma-separated list of pages or page ranges to delete, e.g., '1,3,5-8'") String pagesToDelete)
throws IOException {
@PostMapping(consumes = "multipart/form-data", value = "/remove-pages")
@Operation(summary = "Remove pages from a PDF file",
description = "This endpoint removes specified pages from a given PDF file. Users can provide a comma-separated list of page numbers or ranges to delete.")
public ResponseEntity<byte[]> deletePages(
@RequestPart(required = true, value = "fileInput")
@Parameter(description = "The input PDF file from which pages will be removed")
MultipartFile pdfFile,
@RequestParam("pagesToDelete")
@Parameter(description = "Comma-separated list of pages or page ranges to delete, e.g., '1,3,5-8'")
String pagesToDelete) throws IOException {
PDDocument document = PDDocument.load(pdfFile.getBytes());
PDDocument document = PDDocument.load(pdfFile.getBytes());
// Split the page order string into an array of page numbers or range of numbers
String[] pageOrderArr = pagesToDelete.split(",");
// Split the page order string into an array of page numbers or range of numbers
String[] pageOrderArr = pagesToDelete.split(",");
List<Integer> pagesToRemove = GeneralUtils.parsePageList(pageOrderArr, document.getNumberOfPages());
List<Integer> pagesToRemove = pageOrderToString(pageOrderArr, document.getNumberOfPages());
for (int i = pagesToRemove.size() - 1; i >= 0; i--) {
int pageIndex = pagesToRemove.get(i);
document.removePage(pageIndex);
}
return WebResponseUtils.pdfDocToWebResponse(document,
pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_removed_pages.pdf");
for (int i = pagesToRemove.size() - 1; i >= 0; i--) {
int pageIndex = pagesToRemove.get(i);
document.removePage(pageIndex);
}
return PdfUtils.pdfDocToWebResponse(document, pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_removed_pages.pdf");
}
}
private enum CustomMode {
REVERSE_ORDER, DUPLEX_SORT, BOOKLET_SORT, ODD_EVEN_SPLIT, REMOVE_FIRST, REMOVE_LAST, REMOVE_FIRST_AND_LAST,
}
private List<Integer> pageOrderToString(String[] pageOrderArr, int totalPages) {
List<Integer> newPageOrder = new ArrayList<>();
// loop through the page order array
for (String element : pageOrderArr) {
// check if the element contains a range of pages
if (element.contains("-")) {
// split the range into start and end page
String[] range = element.split("-");
int start = Integer.parseInt(range[0]);
int end = Integer.parseInt(range[1]);
// check if the end page is greater than total pages
if (end > totalPages) {
end = totalPages;
}
// loop through the range of pages
for (int j = start; j <= end; j++) {
// print the current index
newPageOrder.add(j - 1);
}
} else {
// if the element is a single page
newPageOrder.add(Integer.parseInt(element) - 1);
}
}
private List<Integer> removeFirst(int totalPages) {
if (totalPages <= 1)
return new ArrayList<>();
List<Integer> newPageOrder = new ArrayList<>();
for (int i = 2; i <= totalPages; i++) {
newPageOrder.add(i - 1);
}
return newPageOrder;
}
return newPageOrder;
}
private List<Integer> removeLast(int totalPages) {
if (totalPages <= 1)
return new ArrayList<>();
List<Integer> newPageOrder = new ArrayList<>();
for (int i = 1; i < totalPages; i++) {
newPageOrder.add(i - 1);
}
return newPageOrder;
}
@PostMapping(consumes = "multipart/form-data", value = "/rearrange-pages")
@Operation(summary = "Rearrange pages in a PDF file",
description = "This endpoint rearranges pages in a given PDF file based on the specified page order. Users can provide a page order as a comma-separated list of page numbers or page ranges.")
public ResponseEntity<byte[]> rearrangePages(
@RequestPart(required = true, value = "fileInput")
@Parameter(description = "The input PDF file to rearrange pages")
MultipartFile pdfFile,
@RequestParam("pageOrder")
@Parameter(description = "The new page order as a comma-separated list of page numbers or page ranges (e.g., '1,3,5-7')")
String pageOrder) {
try {
// Load the input PDF
PDDocument document = PDDocument.load(pdfFile.getInputStream());
private List<Integer> removeFirstAndLast(int totalPages) {
if (totalPages <= 2)
return new ArrayList<>();
List<Integer> newPageOrder = new ArrayList<>();
for (int i = 2; i < totalPages; i++) {
newPageOrder.add(i - 1);
}
return newPageOrder;
}
// Split the page order string into an array of page numbers or range of numbers
String[] pageOrderArr = pageOrder.split(",");
// int[] newPageOrder = new int[pageOrderArr.length];
int totalPages = document.getNumberOfPages();
private List<Integer> reverseOrder(int totalPages) {
List<Integer> newPageOrder = new ArrayList<>();
for (int i = totalPages; i >= 1; i--) {
newPageOrder.add(i - 1);
}
return newPageOrder;
}
List<Integer> newPageOrder = pageOrderToString(pageOrderArr, totalPages);
private List<Integer> duplexSort(int totalPages) {
List<Integer> newPageOrder = new ArrayList<>();
int half = (totalPages + 1) / 2; // This ensures proper behavior with odd numbers of pages
for (int i = 1; i <= half; i++) {
newPageOrder.add(i - 1);
if (i <= totalPages - half) { // Avoid going out of bounds
newPageOrder.add(totalPages - i);
}
}
return newPageOrder;
}
// Create a new list to hold the pages in the new order
List<PDPage> newPages = new ArrayList<>();
for (int i = 0; i < newPageOrder.size(); i++) {
newPages.add(document.getPage(newPageOrder.get(i)));
}
private List<Integer> bookletSort(int totalPages) {
List<Integer> newPageOrder = new ArrayList<>();
for (int i = 0; i < totalPages / 2; i++) {
newPageOrder.add(i);
newPageOrder.add(totalPages - i - 1);
}
return newPageOrder;
}
// Remove all the pages from the original document
for (int i = document.getNumberOfPages() - 1; i >= 0; i--) {
document.removePage(i);
}
private List<Integer> oddEvenSplit(int totalPages) {
List<Integer> newPageOrder = new ArrayList<>();
for (int i = 1; i <= totalPages; i += 2) {
newPageOrder.add(i - 1);
}
for (int i = 2; i <= totalPages; i += 2) {
newPageOrder.add(i - 1);
}
return newPageOrder;
}
// Add the pages in the new order
for (PDPage page : newPages) {
document.addPage(page);
}
private List<Integer> processCustomMode(String customMode, int totalPages) {
try {
CustomMode mode = CustomMode.valueOf(customMode.toUpperCase());
switch (mode) {
case REVERSE_ORDER:
return reverseOrder(totalPages);
case DUPLEX_SORT:
return duplexSort(totalPages);
case BOOKLET_SORT:
return bookletSort(totalPages);
case ODD_EVEN_SPLIT:
return oddEvenSplit(totalPages);
case REMOVE_FIRST:
return removeFirst(totalPages);
case REMOVE_LAST:
return removeLast(totalPages);
case REMOVE_FIRST_AND_LAST:
return removeFirstAndLast(totalPages);
default:
throw new IllegalArgumentException("Unsupported custom mode");
}
} catch (IllegalArgumentException e) {
logger.error("Unsupported custom mode", e);
return null;
}
}
return PdfUtils.pdfDocToWebResponse(document, pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_rearranged.pdf");
} catch (IOException e) {
@PostMapping(consumes = "multipart/form-data", value = "/rearrange-pages")
@Operation(summary = "Rearrange pages in a PDF file", description = "This endpoint rearranges pages in a given PDF file based on the specified page order or custom mode. Users can provide a page order as a comma-separated list of page numbers or page ranges, or a custom mode.")
public ResponseEntity<byte[]> rearrangePages(
@RequestPart(required = true, value = "fileInput") @Parameter(description = "The input PDF file to rearrange pages") MultipartFile pdfFile,
@RequestParam(required = false, value = "pageOrder") @Parameter(description = "The new page order as a comma-separated list of page numbers, page ranges (e.g., '1,3,5-7'), or functions in the format 'an+b' where 'a' is the multiplier of the page number 'n', and 'b' is a constant (e.g., '2n+1', '3n', '6n-5')") String pageOrder,
@RequestParam(required = false, value = "customMode") @Parameter(schema = @Schema(implementation = CustomMode.class, description = "The custom mode for page rearrangement. "
+ "Valid values are:\n" + "REVERSE_ORDER: Reverses the order of all pages.\n"
+ "DUPLEX_SORT: Sorts pages as if all fronts were scanned then all backs in reverse (1, n, 2, n-1, ...). "
+ "BOOKLET_SORT: Arranges pages for booklet printing (last, first, second, second last, ...).\n"
+ "ODD_EVEN_SPLIT: Splits and arranges pages into odd and even numbered pages.\n"
+ "REMOVE_FIRST: Removes the first page.\n" + "REMOVE_LAST: Removes the last page.\n"
+ "REMOVE_FIRST_AND_LAST: Removes both the first and the last pages.\n")) String customMode) {
try {
// Load the input PDF
PDDocument document = PDDocument.load(pdfFile.getInputStream());
logger.error("Failed rearranging documents", e);
return null;
}
}
// Split the page order string into an array of page numbers or range of numbers
String[] pageOrderArr = pageOrder != null ? pageOrder.split(",") : new String[0];
int totalPages = document.getNumberOfPages();
System.out.println("pageOrder=" + pageOrder);
System.out.println("customMode length =" + customMode.length());
List<Integer> newPageOrder;
if (customMode != null && customMode.length() > 0) {
newPageOrder = processCustomMode(customMode, totalPages);
} else {
newPageOrder = GeneralUtils.parsePageList(pageOrderArr, totalPages);
}
// Create a new list to hold the pages in the new order
List<PDPage> newPages = new ArrayList<>();
for (int i = 0; i < newPageOrder.size(); i++) {
newPages.add(document.getPage(newPageOrder.get(i)));
}
// Remove all the pages from the original document
for (int i = document.getNumberOfPages() - 1; i >= 0; i--) {
document.removePage(i);
}
// Add the pages in the new order
for (PDPage page : newPages) {
document.addPage(page);
}
return WebResponseUtils.pdfDocToWebResponse(document,
pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_rearranged.pdf");
} catch (IOException e) {
logger.error("Failed rearranging documents", e);
return null;
}
}
}

View File

@@ -16,7 +16,7 @@ import org.springframework.web.multipart.MultipartFile;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import stirling.software.SPDF.utils.PdfUtils;
import stirling.software.SPDF.utils.WebResponseUtils;
@RestController
public class RotationController {
@@ -46,7 +46,7 @@ public class RotationController {
page.setRotation(page.getRotation() + angle);
}
return PdfUtils.pdfDocToWebResponse(document, pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_rotated.pdf");
return WebResponseUtils.pdfDocToWebResponse(document, pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_rotated.pdf");
}

View File

@@ -0,0 +1,241 @@
package stirling.software.SPDF.controller.api;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import com.itextpdf.kernel.geom.PageSize;
import com.itextpdf.kernel.geom.Rectangle;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfPage;
import com.itextpdf.kernel.pdf.PdfReader;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.kernel.pdf.canvas.PdfCanvas;
import com.itextpdf.kernel.pdf.canvas.parser.EventType;
import com.itextpdf.kernel.pdf.canvas.parser.PdfCanvasProcessor;
import com.itextpdf.kernel.pdf.canvas.parser.data.IEventData;
import com.itextpdf.kernel.pdf.canvas.parser.data.TextRenderInfo;
import com.itextpdf.kernel.pdf.canvas.parser.listener.IEventListener;
import com.itextpdf.kernel.pdf.xobject.PdfFormXObject;
import io.swagger.v3.oas.annotations.Hidden;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Schema;
import stirling.software.SPDF.utils.WebResponseUtils;
@RestController
public class ScalePagesController {
private static final Logger logger = LoggerFactory.getLogger(ScalePagesController.class);
@PostMapping(value = "/scale-pages", consumes = "multipart/form-data")
@Operation(summary = "Change the size of a PDF page/document", description = "This operation takes an input PDF file and the size to scale the pages to in the output PDF file.")
public ResponseEntity<byte[]> scalePages(
@Parameter(description = "The input PDF file", required = true) @RequestParam("fileInput") MultipartFile file,
@Parameter(description = "The scale of pages in the output PDF. Acceptable values are A0-A10, B0-B9, LETTER, TABLOID, LEDGER, LEGAL, EXECUTIVE.", required = true, schema = @Schema(type = "String", allowableValues = {
"A0", "A1", "A2", "A3", "A4", "A5", "A6", "A7", "A8", "A9", "A10", "B0", "B1", "B2", "B3", "B4",
"B5", "B6", "B7", "B8", "B9", "LETTER", "TABLOID", "LEDGER", "LEGAL",
"EXECUTIVE" })) @RequestParam("pageSize") String targetPageSize,
@Parameter(description = "The scale of the content on the pages of the output PDF. Acceptable values are floats.", required = true, schema = @Schema(type = "float")) @RequestParam("scaleFactor") float scaleFactor)
throws IOException {
Map<String, PageSize> sizeMap = new HashMap<>();
// Add A0 - A10
sizeMap.put("A0", PageSize.A0);
sizeMap.put("A1", PageSize.A1);
sizeMap.put("A2", PageSize.A2);
sizeMap.put("A3", PageSize.A3);
sizeMap.put("A4", PageSize.A4);
sizeMap.put("A5", PageSize.A5);
sizeMap.put("A6", PageSize.A6);
sizeMap.put("A7", PageSize.A7);
sizeMap.put("A8", PageSize.A8);
sizeMap.put("A9", PageSize.A9);
sizeMap.put("A10", PageSize.A10);
// Add B0 - B9
sizeMap.put("B0", PageSize.B0);
sizeMap.put("B1", PageSize.B1);
sizeMap.put("B2", PageSize.B2);
sizeMap.put("B3", PageSize.B3);
sizeMap.put("B4", PageSize.B4);
sizeMap.put("B5", PageSize.B5);
sizeMap.put("B6", PageSize.B6);
sizeMap.put("B7", PageSize.B7);
sizeMap.put("B8", PageSize.B8);
sizeMap.put("B9", PageSize.B9);
// Add other sizes
sizeMap.put("LETTER", PageSize.LETTER);
sizeMap.put("TABLOID", PageSize.TABLOID);
sizeMap.put("LEDGER", PageSize.LEDGER);
sizeMap.put("LEGAL", PageSize.LEGAL);
sizeMap.put("EXECUTIVE", PageSize.EXECUTIVE);
if (!sizeMap.containsKey(targetPageSize)) {
throw new IllegalArgumentException(
"Invalid pageSize. It must be one of the following: A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10");
}
PageSize pageSize = sizeMap.get(targetPageSize);
byte[] bytes = file.getBytes();
PdfReader reader = new PdfReader(new ByteArrayInputStream(bytes));
PdfDocument pdfDoc = new PdfDocument(reader);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PdfWriter writer = new PdfWriter(baos);
PdfDocument outputPdf = new PdfDocument(writer);
int totalPages = pdfDoc.getNumberOfPages();
for (int i = 1; i <= totalPages; i++) {
PdfPage page = outputPdf.addNewPage(pageSize);
PdfCanvas pdfCanvas = new PdfCanvas(page);
// Get the page and calculate scaling factors
Rectangle rect = pdfDoc.getPage(i).getPageSize();
float scaleWidth = pageSize.getWidth() / rect.getWidth();
float scaleHeight = pageSize.getHeight() / rect.getHeight();
float scale = Math.min(scaleWidth, scaleHeight) * scaleFactor;
System.out.println("Scale: " + scale);
PdfFormXObject formXObject = pdfDoc.getPage(i).copyAsFormXObject(outputPdf);
float x = (pageSize.getWidth() - rect.getWidth() * scale) / 2; // Center Page
float y = (pageSize.getHeight() - rect.getHeight() * scale) / 2;
// Save the graphics state, apply the transformations, add the object, and then
// restore the graphics state
pdfCanvas.saveState();
pdfCanvas.concatMatrix(scale, 0, 0, scale, x, y);
pdfCanvas.addXObject(formXObject, 0, 0);
pdfCanvas.restoreState();
}
outputPdf.close();
byte[] pdfContent = baos.toByteArray();
pdfDoc.close();
return WebResponseUtils.bytesToWebResponse(pdfContent,
file.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_scaled.pdf");
}
//TODO
@Hidden
@PostMapping(value = "/auto-crop", consumes = "multipart/form-data")
public ResponseEntity<byte[]> cropPdf(@RequestParam("fileInput") MultipartFile file) throws IOException {
byte[] bytes = file.getBytes();
PdfReader reader = new PdfReader(new ByteArrayInputStream(bytes));
PdfDocument pdfDoc = new PdfDocument(reader);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PdfWriter writer = new PdfWriter(baos);
PdfDocument outputPdf = new PdfDocument(writer);
int totalPages = pdfDoc.getNumberOfPages();
for (int i = 1; i <= totalPages; i++) {
PdfPage page = pdfDoc.getPage(i);
Rectangle originalMediaBox = page.getMediaBox();
Rectangle contentBox = determineContentBox(page);
// Make sure we don't go outside the original media box.
Rectangle intersection = originalMediaBox.getIntersection(contentBox);
page.setCropBox(intersection);
// Copy page to the new document
outputPdf.addPage(page.copyTo(outputPdf));
}
outputPdf.close();
byte[] pdfContent = baos.toByteArray();
pdfDoc.close();
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\""
+ file.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_cropped.pdf\"")
.contentType(MediaType.APPLICATION_PDF).body(pdfContent);
}
private Rectangle determineContentBox(PdfPage page) {
// Extract the text from the page and find the bounding box.
TextBoundingRectangleFinder finder = new TextBoundingRectangleFinder();
PdfCanvasProcessor processor = new PdfCanvasProcessor(finder);
processor.processPageContent(page);
return finder.getBoundingBox();
}
private static class TextBoundingRectangleFinder implements IEventListener {
private List<Rectangle> allTextBoxes = new ArrayList<>();
public Rectangle getBoundingBox() {
// Sort the text boxes based on their vertical position
allTextBoxes.sort(Comparator.comparingDouble(Rectangle::getTop));
// Consider a box an outlier if its top is more than 1.5 times the IQR above the
// third quartile.
int q1Index = allTextBoxes.size() / 4;
int q3Index = 3 * allTextBoxes.size() / 4;
double iqr = allTextBoxes.get(q3Index).getTop() - allTextBoxes.get(q1Index).getTop();
double threshold = allTextBoxes.get(q3Index).getTop() + 1.5 * iqr;
// Initialize boundingBox to the first non-outlier box
int i = 0;
while (i < allTextBoxes.size() && allTextBoxes.get(i).getTop() > threshold) {
i++;
}
if (i == allTextBoxes.size()) {
// If all boxes are outliers, just return the first one
return allTextBoxes.get(0);
}
Rectangle boundingBox = allTextBoxes.get(i);
// Extend the bounding box to include all non-outlier boxes
for (; i < allTextBoxes.size(); i++) {
Rectangle textBoundingBox = allTextBoxes.get(i);
if (textBoundingBox.getTop() > threshold) {
// This box is an outlier, skip it
continue;
}
float left = Math.min(boundingBox.getLeft(), textBoundingBox.getLeft());
float bottom = Math.min(boundingBox.getBottom(), textBoundingBox.getBottom());
float right = Math.max(boundingBox.getRight(), textBoundingBox.getRight());
float top = Math.max(boundingBox.getTop(), textBoundingBox.getTop());
// Add a small padding around the bounding box
float padding = 10;
boundingBox = new Rectangle(left - padding, bottom - padding, right - left + 2 * padding,
top - bottom + 2 * padding);
}
return boundingBox;
}
@Override
public void eventOccurred(IEventData data, EventType type) {
if (type == EventType.RENDER_TEXT) {
TextRenderInfo renderInfo = (TextRenderInfo) data;
allTextBoxes.add(renderInfo.getBaseline().getBoundingRectangle());
}
}
@Override
public Set<EventType> getSupportedEvents() {
return Collections.singleton(EventType.RENDER_TEXT);
}
}
}

View File

@@ -6,7 +6,6 @@ import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
@@ -29,6 +28,8 @@ import org.springframework.web.multipart.MultipartFile;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import stirling.software.SPDF.utils.GeneralUtils;
import stirling.software.SPDF.utils.WebResponseUtils;
@RestController
public class SplitPDFController {
@@ -38,7 +39,7 @@ public class SplitPDFController {
@PostMapping(consumes = "multipart/form-data", value = "/split-pages")
@Operation(summary = "Split a PDF file into separate documents",
description = "This endpoint splits a given PDF file into separate documents based on the specified page numbers or ranges. Users can specify pages using individual numbers, ranges, or 'all' for every page.")
public ResponseEntity<Resource> splitPdf(
public ResponseEntity<byte[]> splitPdf(
@RequestPart(required = true, value = "fileInput")
@Parameter(description = "The input PDF file to be split")
MultipartFile file,
@@ -58,39 +59,28 @@ public class SplitPDFController {
pageNumbers.add(i);
}
} else {
List<String> pageNumbersStr = new ArrayList<>(Arrays.asList(pages.split(",")));
if (!pageNumbersStr.contains(String.valueOf(document.getNumberOfPages()))) {
String lastpage = String.valueOf(document.getNumberOfPages());
pageNumbersStr.add(lastpage);
}
for (String page : pageNumbersStr) {
if (page.contains("-")) {
String[] range = page.split("-");
int start = Integer.parseInt(range[0]);
int end = Integer.parseInt(range[1]);
for (int i = start; i <= end; i++) {
pageNumbers.add(i);
}
} else {
pageNumbers.add(Integer.parseInt(page));
}
String[] splitPoints = pages.split(",");
for (String splitPoint : splitPoints) {
List<Integer> orderedPages = GeneralUtils.parsePageList(new String[] {splitPoint}, document.getNumberOfPages());
pageNumbers.addAll(orderedPages);
}
// Add the last page as a split point
pageNumbers.add(document.getNumberOfPages() - 1);
}
logger.info("Splitting PDF into pages: {}", pageNumbers.stream().map(String::valueOf).collect(Collectors.joining(",")));
// split the document
List<ByteArrayOutputStream> splitDocumentsBoas = new ArrayList<>();
int currentPage = 0;
for (int pageNumber : pageNumbers) {
int previousPageNumber = 0;
for (int splitPoint : pageNumbers) {
try (PDDocument splitDocument = new PDDocument()) {
for (int i = currentPage; i < pageNumber; i++) {
for (int i = previousPageNumber; i <= splitPoint; i++) {
PDPage page = document.getPage(i);
splitDocument.addPage(page);
logger.debug("Adding page {} to split document", i);
}
currentPage = pageNumber;
logger.debug("Setting current page to {}", currentPage);
previousPageNumber = splitPoint + 1;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
splitDocument.save(baos);
@@ -102,15 +92,17 @@ public class SplitPDFController {
}
}
// closing the original document
document.close();
Path zipFile = Files.createTempFile("split_documents", ".zip");
String filename = file.getOriginalFilename().replaceFirst("[.][^.]+$", "");
try (ZipOutputStream zipOut = new ZipOutputStream(Files.newOutputStream(zipFile))) {
// loop through the split documents and write them to the zip file
for (int i = 0; i < splitDocumentsBoas.size(); i++) {
String fileName = "split_document_" + (i + 1) + ".pdf";
String fileName = filename + "_" + (i + 1) + ".pdf";
ByteArrayOutputStream baos = splitDocumentsBoas.get(i);
byte[] pdf = baos.toByteArray();
@@ -129,12 +121,11 @@ public class SplitPDFController {
logger.info("Successfully created zip file with split documents: {}", zipFile.toString());
byte[] data = Files.readAllBytes(zipFile);
ByteArrayResource resource = new ByteArrayResource(data);
Files.delete(zipFile);
// return the Resource in the response
return ResponseEntity.ok().header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + file.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_split.zip")
.contentType(MediaType.APPLICATION_OCTET_STREAM).contentLength(resource.contentLength()).body(resource);
return WebResponseUtils.bytesToWebResponse(data, filename + ".zip", MediaType.APPLICATION_OCTET_STREAM);
}
}

View File

@@ -21,6 +21,7 @@ import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Schema;
import stirling.software.SPDF.utils.PdfUtils;
import stirling.software.SPDF.utils.WebResponseUtils;
@RestController
public class ConvertImgPDFController {
@@ -56,8 +57,9 @@ public class ConvertImgPDFController {
// returns bytes for image
boolean singleImage = singleOrMultiple.equals("single");
byte[] result = null;
String filename = file.getOriginalFilename().replaceFirst("[.][^.]+$", "");
try {
result = PdfUtils.convertFromPdf(pdfBytes, imageFormat.toUpperCase(), colorTypeResult, singleImage, Integer.valueOf(dpi));
result = PdfUtils.convertFromPdf(pdfBytes, imageFormat.toUpperCase(), colorTypeResult, singleImage, Integer.valueOf(dpi), filename);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
@@ -74,7 +76,7 @@ public class ConvertImgPDFController {
ByteArrayResource resource = new ByteArrayResource(result);
// return the Resource in the response
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + file.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_convertedToImages.zip")
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + filename + "_convertedToImages.zip")
.contentType(MediaType.APPLICATION_OCTET_STREAM).contentLength(resource.contentLength()).body(resource);
}
}
@@ -97,7 +99,7 @@ public class ConvertImgPDFController {
boolean autoRotate) throws IOException {
// Convert the file to PDF and get the resulting bytes
byte[] bytes = PdfUtils.imageToPdf(file, stretchToFit, autoRotate, colorType);
return PdfUtils.bytesToWebResponse(bytes, file[0].getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_coverted.pdf");
return WebResponseUtils.bytesToWebResponse(bytes, file[0].getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_converted.pdf");
}
private String getMediaType(String imageFormat) {

View File

@@ -17,8 +17,8 @@ import org.springframework.web.multipart.MultipartFile;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import stirling.software.SPDF.utils.PdfUtils;
import stirling.software.SPDF.utils.ProcessExecutor;
import stirling.software.SPDF.utils.WebResponseUtils;
@RestController
public class ConvertOfficeController {
@@ -72,7 +72,7 @@ public class ConvertOfficeController {
// LibreOfficeListener.getInstance().start();
byte[] pdfByteArray = convertToPdf(inputFile);
return PdfUtils.bytesToWebResponse(pdfByteArray, inputFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_convertedToPDF.pdf");
return WebResponseUtils.bytesToWebResponse(pdfByteArray, inputFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_convertedToPDF.pdf");
}
}

View File

@@ -14,8 +14,8 @@ import org.springframework.web.multipart.MultipartFile;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import stirling.software.SPDF.utils.PdfUtils;
import stirling.software.SPDF.utils.ProcessExecutor;
import stirling.software.SPDF.utils.WebResponseUtils;
@RestController
public class ConvertPDFToPDFA {
@@ -58,7 +58,7 @@ public class ConvertPDFToPDFA {
// Return the optimized PDF as a response
String outputFilename = inputFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_PDFA.pdf";
return PdfUtils.bytesToWebResponse(pdfBytes, outputFilename);
return WebResponseUtils.bytesToWebResponse(pdfBytes, outputFilename);
}
}

View File

@@ -28,9 +28,9 @@ import org.springframework.web.multipart.MultipartFile;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import stirling.software.SPDF.utils.ImageFinder;
import stirling.software.SPDF.utils.PdfUtils;
import stirling.software.SPDF.pdf.ImageFinder;
import stirling.software.SPDF.utils.ProcessExecutor;
import stirling.software.SPDF.utils.WebResponseUtils;
@RestController
public class BlankPageController {
@@ -81,7 +81,7 @@ public class BlankPageController {
BufferedImage image = pdfRenderer.renderImageWithDPI(pageIndex, 300);
ImageIO.write(image, "png", tempFile.toFile());
List<String> command = new ArrayList<>(Arrays.asList("python3", System.getProperty("user.dir") + "scripts/detect-blank-pages.py", tempFile.toString() ,"--threshold", String.valueOf(threshold), "--white_percent", String.valueOf(whitePercent)));
List<String> command = new ArrayList<>(Arrays.asList("python3", System.getProperty("user.dir") + "/scripts/detect-blank-pages.py", tempFile.toString() ,"--threshold", String.valueOf(threshold), "--white_percent", String.valueOf(whitePercent)));
// Run CLI command
int returnCode = ProcessExecutor.getInstance(ProcessExecutor.Processes.PYTHON_OPENCV).runCommandWithOutputHandling(command);
@@ -109,7 +109,7 @@ public class BlankPageController {
}
}
return PdfUtils.pdfDocToWebResponse(document, inputFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_blanksRemoved.pdf");
return WebResponseUtils.pdfDocToWebResponse(document, inputFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_blanksRemoved.pdf");
} catch (IOException e) {
e.printStackTrace();
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);

View File

@@ -31,8 +31,9 @@ import org.springframework.web.multipart.MultipartFile;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Schema;
import stirling.software.SPDF.utils.PdfUtils;
import stirling.software.SPDF.utils.GeneralUtils;
import stirling.software.SPDF.utils.ProcessExecutor;
import stirling.software.SPDF.utils.WebResponseUtils;
@RestController
public class CompressController {
@@ -55,7 +56,7 @@ public class CompressController {
Long expectedOutputSize = 0L;
boolean autoMode = false;
if (expectedOutputSizeString != null && expectedOutputSizeString.length() > 1 ) {
expectedOutputSize = PdfUtils.convertSizeToBytes(expectedOutputSizeString);
expectedOutputSize = GeneralUtils.convertSizeToBytes(expectedOutputSizeString);
autoMode = true;
}
@@ -224,7 +225,7 @@ public class CompressController {
// Return the optimized PDF as a response
String outputFilename = inputFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_Optimized.pdf";
return PdfUtils.bytesToWebResponse(pdfBytes, outputFilename);
return WebResponseUtils.bytesToWebResponse(pdfBytes, outputFilename);
}
}

View File

@@ -31,8 +31,8 @@ import org.springframework.web.multipart.MultipartFile;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import stirling.software.SPDF.utils.PdfUtils;
import stirling.software.SPDF.utils.ProcessExecutor;
import stirling.software.SPDF.utils.WebResponseUtils;
@RestController
public class ExtractImageScansController {
@@ -147,11 +147,11 @@ public class ExtractImageScansController {
// Clean up the temporary zip file
Files.delete(tempZipFile);
return PdfUtils.bytesToWebResponse(zipBytes, outputZipFilename, MediaType.APPLICATION_OCTET_STREAM);
return WebResponseUtils.bytesToWebResponse(zipBytes, outputZipFilename, MediaType.APPLICATION_OCTET_STREAM);
} else {
// Return the processed image as a response
byte[] imageBytes = processedImageBytes.get(0);
return PdfUtils.bytesToWebResponse(imageBytes, fileName.replaceFirst("[.][^.]+$", "") + ".png", MediaType.IMAGE_PNG);
return WebResponseUtils.bytesToWebResponse(imageBytes, fileName.replaceFirst("[.][^.]+$", "") + ".png", MediaType.IMAGE_PNG);
}
}

View File

@@ -29,7 +29,7 @@ import org.springframework.web.multipart.MultipartFile;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Schema;
import stirling.software.SPDF.utils.PdfUtils;
import stirling.software.SPDF.utils.WebResponseUtils;
@RestController
public class ExtractImagesController {
@@ -59,7 +59,7 @@ public class ExtractImagesController {
zos.setLevel(Deflater.BEST_COMPRESSION);
int imageIndex = 1;
String filename = file.getOriginalFilename().replaceFirst("[.][^.]+$", "");
int pageNum = 1;
// Iterate over each page
for (PDPage page : document.getPages()) {
@@ -81,7 +81,7 @@ public class ExtractImagesController {
}
// Write image to zip file
String imageName = "Image " + imageIndex + " (Page " + pageNum + ")." + format;
String imageName = filename + "_" + imageIndex + " (Page " + pageNum + ")." + format;
ZipEntry zipEntry = new ZipEntry(imageName);
zos.putNextEntry(zipEntry);
@@ -106,7 +106,7 @@ public class ExtractImagesController {
// Create ByteArrayResource from byte array
byte[] zipContents = baos.toByteArray();
return PdfUtils.boasToWebResponse(baos, file.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_extracted-images.zip", MediaType.APPLICATION_OCTET_STREAM);
return WebResponseUtils.boasToWebResponse(baos, filename + "_extracted-images.zip", MediaType.APPLICATION_OCTET_STREAM);
}
}

View File

@@ -0,0 +1,161 @@
package stirling.software.SPDF.controller.api.other;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import com.itextpdf.io.source.ByteArrayOutputStream;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import stirling.software.SPDF.utils.ProcessExecutor;
import stirling.software.SPDF.utils.WebResponseUtils;
//Required for PDF manipulation
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
import org.apache.pdfbox.pdmodel.graphics.image.LosslessFactory;
import org.apache.pdfbox.rendering.ImageType;
import org.apache.pdfbox.rendering.PDFRenderer;
//Required for image manipulation
import java.awt.image.BufferedImage;
import java.awt.image.BufferedImageOp;
import java.awt.image.RescaleOp;
import java.awt.image.AffineTransformOp;
import java.awt.image.ConvolveOp;
import java.awt.image.Kernel;
import java.awt.Color;
import java.awt.geom.AffineTransform;
//Required for image input/output
import javax.imageio.ImageIO;
//Required for file input/output
import java.io.File;
//Other required classes
import java.util.Random;
import io.swagger.v3.oas.annotations.Hidden;
@RestController
public class FakeScanController {
private static final Logger logger = LoggerFactory.getLogger(FakeScanController.class);
//TODO
@Hidden
@PostMapping(consumes = "multipart/form-data", value = "/fakeScan")
@Operation(
summary = "Repair a PDF file",
description = "This endpoint repairs a given PDF file by running Ghostscript command. The PDF is first saved to a temporary location, repaired, read back, and then returned as a response."
)
public ResponseEntity<byte[]> repairPdf(
@RequestPart(required = true, value = "fileInput")
@Parameter(description = "The input PDF file to be repaired", required = true)
MultipartFile inputFile) throws IOException, InterruptedException {
PDDocument document = PDDocument.load(inputFile.getBytes());
PDFRenderer pdfRenderer = new PDFRenderer(document);
for (int page = 0; page < document.getNumberOfPages(); ++page)
{
BufferedImage image = pdfRenderer.renderImageWithDPI(page, 300, ImageType.RGB);
ImageIO.write(image, "png", new File("scanned-" + (page+1) + ".png"));
}
document.close();
// Constants
int scannedness = 90; // Value between 0 and 100
int dirtiness = 0; // Value between 0 and 100
// Load the source image
BufferedImage sourceImage = ImageIO.read(new File("scanned-1.png"));
// Create the destination image
BufferedImage destinationImage = new BufferedImage(sourceImage.getWidth(), sourceImage.getHeight(), sourceImage.getType());
// Apply a brightness and contrast effect based on the "scanned-ness"
float scaleFactor = 1.0f + (scannedness / 100.0f) * 0.5f; // Between 1.0 and 1.5
float offset = scannedness * 1.5f; // Between 0 and 150
BufferedImageOp op = new RescaleOp(scaleFactor, offset, null);
op.filter(sourceImage, destinationImage);
// Apply a rotation effect
double rotationRequired = Math.toRadians((new Random().nextInt(3 - 1) + 1)); // Random angle between 1 and 3 degrees
double locationX = destinationImage.getWidth() / 2;
double locationY = destinationImage.getHeight() / 2;
AffineTransform tx = AffineTransform.getRotateInstance(rotationRequired, locationX, locationY);
AffineTransformOp rotateOp = new AffineTransformOp(tx, AffineTransformOp.TYPE_BILINEAR);
destinationImage = rotateOp.filter(destinationImage, null);
// Apply a blur effect based on the "scanned-ness"
float blurIntensity = scannedness / 100.0f * 0.2f; // Between 0.0 and 0.2
float[] matrix = {
blurIntensity, blurIntensity, blurIntensity,
blurIntensity, blurIntensity, blurIntensity,
blurIntensity, blurIntensity, blurIntensity
};
BufferedImageOp blurOp = new ConvolveOp(new Kernel(3, 3, matrix), ConvolveOp.EDGE_NO_OP, null);
destinationImage = blurOp.filter(destinationImage, null);
// Add noise to the image based on the "dirtiness"
Random random = new Random();
for (int y = 0; y < destinationImage.getHeight(); y++) {
for (int x = 0; x < destinationImage.getWidth(); x++) {
if (random.nextInt(100) < dirtiness) {
// Change the pixel color to black randomly based on the "dirtiness"
destinationImage.setRGB(x, y, Color.BLACK.getRGB());
}
}
}
// Save the image
ImageIO.write(destinationImage, "PNG", new File("scanned-1.png"));
PDDocument documentOut = new PDDocument();
for (int page = 1; page <= document.getNumberOfPages(); ++page)
{
BufferedImage bim = ImageIO.read(new File("scanned-" + page + ".png"));
// Adjust the dimensions of the page
PDPage pdPage = new PDPage(new PDRectangle(bim.getWidth() - 1, bim.getHeight() - 1));
documentOut.addPage(pdPage);
PDImageXObject pdImage = LosslessFactory.createFromImage(documentOut, bim);
PDPageContentStream contentStream = new PDPageContentStream(documentOut, pdPage);
// Draw the image with a slight offset and enlarged dimensions
contentStream.drawImage(pdImage, -1, -1, bim.getWidth() + 2, bim.getHeight() + 2);
contentStream.close();
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
documentOut.save(baos);
documentOut.close();
// Return the optimized PDF as a response
String outputFilename = inputFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_scanned.pdf";
return WebResponseUtils.boasToWebResponse(baos, outputFilename);
}
}

View File

@@ -19,7 +19,7 @@ import org.springframework.web.multipart.MultipartFile;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import stirling.software.SPDF.utils.PdfUtils;
import stirling.software.SPDF.utils.WebResponseUtils;
@RestController
public class MetadataController {
@@ -159,7 +159,7 @@ public class MetadataController {
info.setTrapped(trapped);
document.setDocumentInformation(info);
return PdfUtils.pdfDocToWebResponse(document, pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_metadata.pdf");
return WebResponseUtils.pdfDocToWebResponse(document, pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_metadata.pdf");
}
}

View File

@@ -27,8 +27,8 @@ import org.springframework.web.multipart.MultipartFile;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Schema;
import stirling.software.SPDF.utils.PdfUtils;
import stirling.software.SPDF.utils.ProcessExecutor;
import stirling.software.SPDF.utils.WebResponseUtils;
@RestController
public class OCRController {
@@ -189,11 +189,11 @@ public class OCRController {
Files.delete(sidecarTextPath);
// Return the zip file containing both the PDF and the text file
return PdfUtils.bytesToWebResponse(pdfBytes, outputZipFilename, MediaType.APPLICATION_OCTET_STREAM);
return WebResponseUtils.bytesToWebResponse(zipBytes, outputZipFilename, MediaType.APPLICATION_OCTET_STREAM);
} else {
// Return the OCR processed PDF as a response
Files.delete(tempOutputFile);
return PdfUtils.bytesToWebResponse(pdfBytes, outputFilename);
return WebResponseUtils.bytesToWebResponse(pdfBytes, outputFilename);
}
}

View File

@@ -15,6 +15,7 @@ import org.springframework.web.multipart.MultipartFile;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import stirling.software.SPDF.utils.PdfUtils;
import stirling.software.SPDF.utils.WebResponseUtils;
@RestController
public class OverlayImageController {
@@ -47,7 +48,7 @@ public class OverlayImageController {
byte[] imageBytes = imageFile.getBytes();
byte[] result = PdfUtils.overlayImage(pdfBytes, imageBytes, x, y, everyPage);
return PdfUtils.bytesToWebResponse(result, pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_overlayed.pdf");
return WebResponseUtils.bytesToWebResponse(result, pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_overlayed.pdf");
} catch (IOException e) {
logger.error("Failed to add image to PDF", e);
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);

View File

@@ -16,8 +16,8 @@ import org.springframework.web.multipart.MultipartFile;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import stirling.software.SPDF.utils.PdfUtils;
import stirling.software.SPDF.utils.ProcessExecutor;
import stirling.software.SPDF.utils.WebResponseUtils;
@RestController
public class RepairController {
@@ -60,7 +60,7 @@ public class RepairController {
// Return the optimized PDF as a response
String outputFilename = inputFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_repaired.pdf";
return PdfUtils.bytesToWebResponse(pdfBytes, outputFilename);
return WebResponseUtils.bytesToWebResponse(pdfBytes, outputFilename);
}
}

View File

@@ -1,7 +1,6 @@
package stirling.software.SPDF.controller.api.security;
import java.io.ByteArrayInputStream;
import io.swagger.v3.oas.annotations.media.Schema;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
@@ -51,7 +50,8 @@ import com.itextpdf.signatures.SignatureUtil;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import stirling.software.SPDF.utils.PdfUtils;
import io.swagger.v3.oas.annotations.media.Schema;
import stirling.software.SPDF.utils.WebResponseUtils;
@RestController
public class CertSignController {
@@ -177,8 +177,17 @@ public class CertSignController {
String signingDate = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss z").format(new Date());
// Prepare the text for the digital signature
String layer2Text = String.format("Digitally signed by: %s\nDate: %s\nReason: %s\nLocation: %s", name, signingDate, reason, location);
StringBuilder layer2TextBuilder = new StringBuilder(String.format("Digitally signed by: %s\nDate: %s",
name != null ? name : "Unknown", signingDate));
if (reason != null && !reason.isEmpty()) {
layer2TextBuilder.append("\nReason: ").append(reason);
}
if (location != null && !location.isEmpty()) {
layer2TextBuilder.append("\nLocation: ").append(location);
}
String layer2Text = layer2TextBuilder.toString();
// Get the PDF font and measure the width and height of the text block
PdfFont font = PdfFontFactory.createFont(StandardFonts.HELVETICA_BOLD);
float textWidth = Arrays.stream(layer2Text.split("\n"))
@@ -206,12 +215,12 @@ public class CertSignController {
// Configure the appearance of the digital signature
appearance.setPageRect(rect)
.setContact(name)
.setPageNumber(pageNumber)
.setReason(reason)
.setLocation(location)
.setReuseAppearance(false)
.setLayer2Text(layer2Text);
.setContact(name != null ? name : "")
.setPageNumber(pageNumber)
.setReason(reason != null ? reason : "")
.setLocation(location != null ? location : "")
.setReuseAppearance(false)
.setLayer2Text(layer2Text.toString());
signer.setFieldName("sig");
} else {
@@ -230,7 +239,7 @@ public class CertSignController {
System.out.println("Signed PDF size: " + signedPdf.size());
System.out.println("PDF signed = " + isPdfSigned(signedPdf.toByteArray()));
return PdfUtils.bytesToWebResponse(signedPdf.toByteArray(), "example.pdf");
return WebResponseUtils.bytesToWebResponse(signedPdf.toByteArray(), "example.pdf");
}
public boolean isPdfSigned(byte[] pdfData) throws IOException {

View File

@@ -17,7 +17,7 @@ import org.springframework.web.multipart.MultipartFile;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Schema;
import stirling.software.SPDF.utils.PdfUtils;
import stirling.software.SPDF.utils.WebResponseUtils;
@RestController
public class PasswordController {
@@ -38,7 +38,7 @@ public class PasswordController {
String password) throws IOException {
PDDocument document = PDDocument.load(fileInput.getBytes(), password);
document.setAllSecurityToBeRemoved(true);
return PdfUtils.pdfDocToWebResponse(document, fileInput.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_password_removed.pdf");
return WebResponseUtils.pdfDocToWebResponse(document, fileInput.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_password_removed.pdf");
}
@PostMapping(consumes = "multipart/form-data", value = "/add-password")
@@ -50,8 +50,11 @@ public class PasswordController {
@RequestPart(required = true, value = "fileInput")
@Parameter(description = "The input PDF file to which the password should be added", required = true)
MultipartFile fileInput,
@RequestParam(defaultValue = "", name = "ownerPassword")
@Parameter(description = "The owner password to be added to the PDF file (Restricts what can be done with the document once it is opened)")
String ownerPassword,
@RequestParam(defaultValue = "", name = "password")
@Parameter(description = "The password to be added to the PDF file")
@Parameter(description = "The password to be added to the PDF file (Restricts the opening of the document itself.)")
String password,
@RequestParam(defaultValue = "128", name = "keyLength")
@Parameter(description = "The length of the encryption key", schema = @Schema(allowableValues = {"40", "128", "256"}))
@@ -84,7 +87,6 @@ public class PasswordController {
PDDocument document = PDDocument.load(fileInput.getBytes());
AccessPermission ap = new AccessPermission();
ap.setCanAssembleDocument(!canAssembleDocument);
ap.setCanExtractContent(!canExtractContent);
ap.setCanExtractForAccessibility(!canExtractForAccessibility);
@@ -93,14 +95,17 @@ public class PasswordController {
ap.setCanModifyAnnotations(!canModifyAnnotations);
ap.setCanPrint(!canPrint);
ap.setCanPrintFaithful(!canPrintFaithful);
StandardProtectionPolicy spp = new StandardProtectionPolicy(password, password, ap);
StandardProtectionPolicy spp = new StandardProtectionPolicy(ownerPassword, password, ap);
spp.setEncryptionKeyLength(keyLength);
spp.setPermissions(ap);
document.protect(spp);
return PdfUtils.pdfDocToWebResponse(document, fileInput.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_passworded.pdf");
return WebResponseUtils.pdfDocToWebResponse(document, fileInput.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_passworded.pdf");
}

View File

@@ -19,7 +19,7 @@ import org.springframework.web.multipart.MultipartFile;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import stirling.software.SPDF.utils.PdfUtils;
import stirling.software.SPDF.utils.WebResponseUtils;
@RestController
public class WatermarkController {
@@ -91,7 +91,7 @@ public class WatermarkController {
// Close the content stream
contentStream.close();
}
return PdfUtils.pdfDocToWebResponse(document, pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_watermarked.pdf");
return WebResponseUtils.pdfDocToWebResponse(document, pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_watermarked.pdf");
}
}

View File

@@ -74,7 +74,9 @@ public class OtherWebController {
@Hidden
public ModelAndView ocrPdfPage() {
ModelAndView modelAndView = new ModelAndView("other/ocr-pdf");
modelAndView.addObject("languages", getAvailableTesseractLanguages());
List<String> languages = getAvailableTesseractLanguages();
Collections.sort(languages);
modelAndView.addObject("languages", languages);
modelAndView.addObject("currentPage", "ocr-pdf");
return modelAndView;
}
@@ -108,4 +110,25 @@ public class OtherWebController {
return "other/remove-blanks";
}
@GetMapping("/multi-page-layout")
@Hidden
public String multiPageLayoutForm(Model model) {
model.addAttribute("currentPage", "multi-page-layout");
return "other/multi-page-layout";
}
@GetMapping("/scale-pages")
@Hidden
public String scalePagesFrom(Model model) {
model.addAttribute("currentPage", "scale-pages");
return "other/scale-pages";
}
@GetMapping("/auto-crop")
@Hidden
public String autoCropForm(Model model) {
model.addAttribute("currentPage", "auto-crop");
return "other/auto-crop";
}
}

View File

@@ -1,4 +1,4 @@
package stirling.software.SPDF.utils;
package stirling.software.SPDF.pdf;
import java.awt.geom.Point2D;
import java.io.IOException;

View File

@@ -0,0 +1,91 @@
package stirling.software.SPDF.utils;
import java.util.ArrayList;
import java.util.List;
public class GeneralUtils {
public static Long convertSizeToBytes(String sizeStr) {
if (sizeStr == null) {
return null;
}
sizeStr = sizeStr.trim().toUpperCase();
try {
if (sizeStr.endsWith("KB")) {
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);
} else if (sizeStr.endsWith("GB")) {
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 {
// Input string does not have a valid format, handle this case
}
} catch (NumberFormatException e) {
// The numeric part of the input string cannot be parsed, handle this case
}
return null;
}
public static List<Integer> parsePageList(String[] pageOrderArr, int totalPages) {
List<Integer> newPageOrder = new ArrayList<>();
// loop through the page order array
for (String element : pageOrderArr) {
// check if the element contains a range of pages
if (element.matches("\\d*n\\+?-?\\d*|\\d*\\+?n")) {
// Handle page order as a function
int coefficient = 0;
int constant = 0;
boolean coefficientExists = false;
boolean constantExists = false;
if (element.contains("n")) {
String[] parts = element.split("n");
if (!parts[0].equals("") && parts[0] != null) {
coefficient = Integer.parseInt(parts[0]);
coefficientExists = true;
}
if (parts.length > 1 && !parts[1].equals("") && parts[1] != null) {
constant = Integer.parseInt(parts[1]);
constantExists = true;
}
} else if (element.contains("+")) {
constant = Integer.parseInt(element.replace("+", ""));
constantExists = true;
}
for (int i = 1; i <= totalPages; i++) {
int pageNum = coefficientExists ? coefficient * i : i;
pageNum += constantExists ? constant : 0;
if (pageNum <= totalPages && pageNum > 0) {
newPageOrder.add(pageNum - 1);
}
}
} else if (element.contains("-")) {
// split the range into start and end page
String[] range = element.split("-");
int start = Integer.parseInt(range[0]);
int end = Integer.parseInt(range[1]);
// check if the end page is greater than total pages
if (end > totalPages) {
end = totalPages;
}
// loop through the range of pages
for (int j = start; j <= end; j++) {
// print the current index
newPageOrder.add(j - 1);
}
} else {
// if the element is a single page
newPageOrder.add(Integer.parseInt(element) - 1);
}
}
return newPageOrder;
}
}

View File

@@ -0,0 +1,25 @@
package stirling.software.SPDF.utils;
import java.awt.image.BufferedImage;
public class ImageProcessingUtils {
static BufferedImage convertColorType(BufferedImage sourceImage, String colorType) {
BufferedImage convertedImage;
switch (colorType) {
case "greyscale":
convertedImage = new BufferedImage(sourceImage.getWidth(), sourceImage.getHeight(), BufferedImage.TYPE_BYTE_GRAY);
convertedImage.getGraphics().drawImage(sourceImage, 0, 0, null);
break;
case "blackwhite":
convertedImage = new BufferedImage(sourceImage.getWidth(), sourceImage.getHeight(), BufferedImage.TYPE_BYTE_BINARY);
convertedImage.getGraphics().drawImage(sourceImage, 0, 0, null);
break;
default: // full color
convertedImage = sourceImage;
break;
}
return convertedImage;
}
}

View File

@@ -0,0 +1,5 @@
package stirling.software.SPDF.utils;
public class PDFManipulationUtils {
}

View File

@@ -92,6 +92,6 @@ public class PDFToFile {
if (tempOutputDir != null)
FileUtils.deleteDirectory(tempOutputDir.toFile());
}
return PdfUtils.bytesToWebResponse(fileBytes, fileName, MediaType.APPLICATION_OCTET_STREAM);
return WebResponseUtils.bytesToWebResponse(fileBytes, fileName, MediaType.APPLICATION_OCTET_STREAM);
}
}

View File

@@ -8,15 +8,7 @@ import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.security.KeyPair;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -30,46 +22,20 @@ import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.graphics.image.JPEGFactory;
import org.apache.pdfbox.pdmodel.graphics.image.LosslessFactory;
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
import org.apache.pdfbox.rendering.ImageType;
import org.apache.pdfbox.rendering.PDFRenderer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.multipart.MultipartFile;
public class PdfUtils {
private static final Logger logger = LoggerFactory.getLogger(PdfUtils.class);
public static ResponseEntity<byte[]> boasToWebResponse(ByteArrayOutputStream baos, String docName) throws IOException {
return PdfUtils.bytesToWebResponse(baos.toByteArray(), docName);
}
public static ResponseEntity<byte[]> boasToWebResponse(ByteArrayOutputStream baos, String docName, MediaType mediaType) throws IOException {
return PdfUtils.bytesToWebResponse(baos.toByteArray(), docName, mediaType);
}
public static ResponseEntity<byte[]> bytesToWebResponse(byte[] bytes, String docName, MediaType mediaType) throws IOException {
// Return the PDF as a response
HttpHeaders headers = new HttpHeaders();
headers.setContentType(mediaType);
headers.setContentLength(bytes.length);
String encodedDocName = URLEncoder.encode(docName, StandardCharsets.UTF_8.toString()).replaceAll("\\+", "%20");
headers.setContentDispositionFormData("attachment", encodedDocName);
return new ResponseEntity<>(bytes, headers, HttpStatus.OK);
}
public static ResponseEntity<byte[]> bytesToWebResponse(byte[] bytes, String docName) throws IOException {
return bytesToWebResponse(bytes, docName, MediaType.APPLICATION_PDF);
}
public static byte[] convertFromPdf(byte[] inputStream, String imageType, ImageType colorType, boolean singleImage, int DPI) throws IOException, Exception {
public static byte[] convertFromPdf(byte[] inputStream, String imageType, ImageType colorType, boolean singleImage, int DPI, String filename) throws IOException, Exception {
try (PDDocument document = PDDocument.load(new ByteArrayInputStream(inputStream))) {
PDFRenderer pdfRenderer = new PDFRenderer(document);
int pageCount = document.getNumberOfPages();
@@ -107,7 +73,7 @@ public class PdfUtils {
ImageIO.write(image, imageType, baosImage);
// Add the image to the zip file
zos.putNextEntry(new ZipEntry(String.format("page_%d.%s", i + 1, imageType.toLowerCase())));
zos.putNextEntry(new ZipEntry(String.format(filename + "_%d.%s", i + 1, imageType.toLowerCase())));
zos.write(baosImage.toByteArray());
}
}
@@ -125,6 +91,7 @@ public class PdfUtils {
public static byte[] imageToPdf(MultipartFile[] files, boolean stretchToFit, boolean autoRotate, String colorType) throws IOException {
try (PDDocument doc = new PDDocument()) {
for (MultipartFile file : files) {
String contentType = file.getContentType();
String originalFilename = file.getOriginalFilename();
if (originalFilename != null && (originalFilename.toLowerCase().endsWith(".tiff") || originalFilename.toLowerCase().endsWith(".tif")) ) {
ImageReader reader = ImageIO.getImageReadersByFormatName("tiff").next();
@@ -132,7 +99,7 @@ public class PdfUtils {
int numPages = reader.getNumImages(true);
for (int i = 0; i < numPages; i++) {
BufferedImage pageImage = reader.read(i);
BufferedImage convertedImage = convertColorType(pageImage, colorType);
BufferedImage convertedImage = ImageProcessingUtils.convertColorType(pageImage, colorType);
PDImageXObject pdImage = LosslessFactory.createFromImage(doc, convertedImage);
addImageToDocument(doc, pdImage, stretchToFit, autoRotate);
}
@@ -145,8 +112,13 @@ public class PdfUtils {
fos.write(buffer, 0, len);
}
BufferedImage image = ImageIO.read(imageFile);
BufferedImage convertedImage = convertColorType(image, colorType);
PDImageXObject pdImage = LosslessFactory.createFromImage(doc, convertedImage);
BufferedImage convertedImage = ImageProcessingUtils.convertColorType(image, colorType);
PDImageXObject pdImage;
if (contentType != null && (contentType.equals("image/jpeg"))) {
pdImage = JPEGFactory.createFromImage(doc, convertedImage);
} else {
pdImage = LosslessFactory.createFromImage(doc, convertedImage);
}
addImageToDocument(doc, pdImage, stretchToFit, autoRotate);
} catch (IOException e) {
logger.error("Error writing image to file: {}", imageFile.getAbsolutePath(), e);
@@ -163,24 +135,6 @@ public class PdfUtils {
}
}
private static BufferedImage convertColorType(BufferedImage sourceImage, String colorType) {
BufferedImage convertedImage;
switch (colorType) {
case "greyscale":
convertedImage = new BufferedImage(sourceImage.getWidth(), sourceImage.getHeight(), BufferedImage.TYPE_BYTE_GRAY);
convertedImage.getGraphics().drawImage(sourceImage, 0, 0, null);
break;
case "blackwhite":
convertedImage = new BufferedImage(sourceImage.getWidth(), sourceImage.getHeight(), BufferedImage.TYPE_BYTE_BINARY);
convertedImage.getGraphics().drawImage(sourceImage, 0, 0, null);
break;
default: // full color
convertedImage = sourceImage;
break;
}
return convertedImage;
}
private static void addImageToDocument(PDDocument doc, PDImageXObject image, boolean stretchToFit, boolean autoRotate) throws IOException {
boolean imageIsLandscape = image.getWidth() > image.getHeight();
PDRectangle pageSize = PDRectangle.A4;
@@ -217,33 +171,6 @@ public class PdfUtils {
}
}
public static X509Certificate[] loadCertificateChainFromKeystore(InputStream keystoreInputStream, String keystorePassword) throws Exception {
KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
keystore.load(keystoreInputStream, keystorePassword.toCharArray());
String alias = keystore.aliases().nextElement();
Certificate[] certChain = keystore.getCertificateChain(alias);
X509Certificate[] x509CertChain = new X509Certificate[certChain.length];
for (int i = 0; i < certChain.length; i++) {
x509CertChain[i] = (X509Certificate) certChain[i];
}
return x509CertChain;
}
public static KeyPair loadKeyPairFromKeystore(InputStream keystoreInputStream, String keystorePassword) throws Exception {
KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
keystore.load(keystoreInputStream, keystorePassword.toCharArray());
String alias = keystore.aliases().nextElement();
PrivateKey privateKey = (PrivateKey) keystore.getKey(alias, keystorePassword.toCharArray());
Certificate cert = keystore.getCertificate(alias);
PublicKey publicKey = cert.getPublicKey();
return new KeyPair(publicKey, privateKey);
}
public static byte[] overlayImage(byte[] pdfBytes, byte[] imageBytes, float x, float y, boolean everyPage) throws IOException {
PDDocument document = PDDocument.load(new ByteArrayInputStream(pdfBytes));
@@ -275,41 +202,7 @@ public class PdfUtils {
return baos.toByteArray();
}
public static ResponseEntity<byte[]> pdfDocToWebResponse(PDDocument document, String docName) throws IOException {
// Open Byte Array and save document to it
ByteArrayOutputStream baos = new ByteArrayOutputStream();
document.save(baos);
// Close the document
document.close();
return PdfUtils.boasToWebResponse(baos, docName);
}
public static Long convertSizeToBytes(String sizeStr) {
if (sizeStr == null) {
return null;
}
sizeStr = sizeStr.trim().toUpperCase();
try {
if (sizeStr.endsWith("KB")) {
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);
} else if (sizeStr.endsWith("GB")) {
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 {
// Input string does not have a valid format, handle this case
}
} catch (NumberFormatException e) {
// The numeric part of the input string cannot be parsed, handle this case
}
return null;
}
}

View File

@@ -0,0 +1,50 @@
package stirling.software.SPDF.utils;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
public class WebResponseUtils {
public static ResponseEntity<byte[]> boasToWebResponse(ByteArrayOutputStream baos, String docName) throws IOException {
return WebResponseUtils.bytesToWebResponse(baos.toByteArray(), docName);
}
public static ResponseEntity<byte[]> boasToWebResponse(ByteArrayOutputStream baos, String docName, MediaType mediaType) throws IOException {
return WebResponseUtils.bytesToWebResponse(baos.toByteArray(), docName, mediaType);
}
public static ResponseEntity<byte[]> bytesToWebResponse(byte[] bytes, String docName, MediaType mediaType) throws IOException {
// Return the PDF as a response
HttpHeaders headers = new HttpHeaders();
headers.setContentType(mediaType);
headers.setContentLength(bytes.length);
String encodedDocName = URLEncoder.encode(docName, StandardCharsets.UTF_8.toString()).replaceAll("\\+", "%20");
headers.setContentDispositionFormData("attachment", encodedDocName);
return new ResponseEntity<>(bytes, headers, HttpStatus.OK);
}
public static ResponseEntity<byte[]> bytesToWebResponse(byte[] bytes, String docName) throws IOException {
return bytesToWebResponse(bytes, docName, MediaType.APPLICATION_PDF);
}
public static ResponseEntity<byte[]> pdfDocToWebResponse(PDDocument document, String docName) throws IOException {
// Open Byte Array and save document to it
ByteArrayOutputStream baos = new ByteArrayOutputStream();
document.save(baos);
// Close the document
document.close();
return boasToWebResponse(baos, docName);
}
}

View File

@@ -129,15 +129,37 @@ home.repair.desc = يحاول إصلاح ملف PDF تالف / معطل
home.removeBlanks.title = إزالة الصفحات الفارغة
home.removeBlanks.desc = يكتشف ويزيل الصفحات الفارغة من المستند
home.certSign.title=Sign with Certificate
home.certSign.desc=Signs a PDF with a Certificate/Key (PEM/P12)
home.compare.title = قارن
home.compare.desc = يقارن ويظهر الاختلافات بين 2 من مستندات PDF
home.pageLayout.title=Multi-Page Layout
home.pageLayout.desc=Merge multiple pages of a PDF document into a single page
home.scalePages.title=Adjust page size/scale
home.scalePages.desc=Change the size/scale of page and/or its contents.
error.pdfPassword=The PDF Document is passworded and either the password was not provided or was incorrect
downloadPdf = تنزيل PDF
text=نص
font=الخط
selectFillter = - حدد -
pageNum = رقم الصفحة
pageLayout.title=Multi Page Layout
pageLayout.header=Multi Page Layout
pageLayout.pagesPerSheet=Pages per sheet:
pageLayout.submit=Submit
scalePages.title=Adjust page-scale
scalePages.header=Adjust page-scale
scalePages.pageSize=Size of a page of the document.
scalePages.scaleFactor=Zoom level (crop) of a page.
scalePages.submit=Submit
certSign.title = توقيع الشهادة
certSign.header = قم بتوقيع ملف PDF بشهادتك (العمل قيد التقدم)
certSign.selectPDF = حدد ملف PDF للتوقيع:
@@ -339,6 +361,9 @@ addPassword.selectText.10=منع التعديل
addPassword.selectText.11=منع تعديل التعليقات التوضيحية
addPassword.selectText.12=منع الطباعة
addPassword.selectText.13=منع طباعة تنسيقات مختلفة
addPassword.selectText.14=Owner Password
addPassword.selectText.15=Restricts what can be done with the document once it is opened (Not supported by all readers)
addPassword.selectText.16=Restricts the opening of the document itself
addPassword.submit=تشفير
#watermark

View File

@@ -122,15 +122,37 @@ home.repair.desc=Intenta reparar un PDF danyat o trencat
home.removeBlanks.title=Elimina les pàgines en blanc
home.removeBlanks.desc=Detecta i elimina les pàgines en blanc d'un document
home.certSign.title=Sign with Certificate
home.certSign.desc=Signs a PDF with a Certificate/Key (PEM/P12)
home.compare.title=Compara
home.compare.desc=Compara i mostra les diferències entre 2 documents PDF
home.pageLayout.title=Multi-Page Layout
home.pageLayout.desc=Merge multiple pages of a PDF document into a single page
home.scalePages.title=Adjust page size/scale
home.scalePages.desc=Change the size/scale of page and/or its contents.
error.pdfPassword=The PDF Document is passworded and either the password was not provided or was incorrect
downloadPdf=Descarregueu PDF
text=Text
font=Tipus de lletra
selectFillter=-- Selecciona --
pageNum=Número de pàgina
pageLayout.title=Multi Page Layout
pageLayout.header=Multi Page Layout
pageLayout.pagesPerSheet=Pages per sheet:
pageLayout.submit=Submit
scalePages.title=Adjust page-scale
scalePages.header=Adjust page-scale
scalePages.pageSize=Size of a page of the document.
scalePages.scaleFactor=Zoom level (crop) of a page.
scalePages.submit=Submit
certSign.title=Significació del certificat
certSign.header=Firmar un PDF amb el vostre certificat (Treball en curs)
certSign.selectPDF=Seleccioneu un fitxer PDF per signar:
@@ -337,6 +359,9 @@ addPassword.selectText.10=Evita modificacions
addPassword.selectText.11=Evita modificacions d'annotacions
addPassword.selectText.12=Evita impressió
addPassword.selectText.13=Evita impressió de diferents formats
addPassword.selectText.14=Owner Password
addPassword.selectText.15=Restricts what can be done with the document once it is opened (Not supported by all readers)
addPassword.selectText.16=Restricts the opening of the document itself
addPassword.submit=Encripta
#watermark

View File

@@ -121,15 +121,37 @@ home.repair.desc=Versucht, ein beschädigtes/kaputtes PDF zu reparieren
home.removeBlanks.title=Leere Seiten entfernen
home.removeBlanks.desc=Erkennt und entfernt leere Seiten aus einem Dokument
home.certSign.title=Sign with Certificate
home.certSign.desc=Signs a PDF with a Certificate/Key (PEM/P12)
home.compare.title=Vergleichen
home.compare.desc=Vergleicht und zeigt die Unterschiede zwischen zwei PDF-Dokumenten an
home.pageLayout.title=Multi-Page Layout
home.pageLayout.desc=Merge multiple pages of a PDF document into a single page
home.scalePages.title=Adjust page size/scale
home.scalePages.desc=Change the size/scale of page and/or its contents.
error.pdfPassword=The PDF Document is passworded and either the password was not provided or was incorrect
downloadPdf=PDF herunterladen
text=Text
font=Schriftart
selectFillter=-- Auswählen --
pageNum=Seitenzahl
pageLayout.title=Multi Page Layout
pageLayout.header=Multi Page Layout
pageLayout.pagesPerSheet=Pages per sheet:
pageLayout.submit=Submit
scalePages.title=Adjust page-scale
scalePages.header=Adjust page-scale
scalePages.pageSize=Size of a page of the document.
scalePages.scaleFactor=Zoom level (crop) of a page.
scalePages.submit=Submit
certSign.title=Zertifikatsignierung
certSign.header=Signieren Sie ein PDF mit Ihrem Zertifikat (in Arbeit)
certSign.selectPDF=Wählen Sie eine PDF-Datei zum Signieren aus:
@@ -334,6 +356,9 @@ addPassword.selectText.10=Modifizierung verhindern
addPassword.selectText.11=Ändern von Kommentaren verhindern
addPassword.selectText.12=Drucken verhindern
addPassword.selectText.13=Drucken verschiedener Formate verhindern
addPassword.selectText.14=Owner Password
addPassword.selectText.15=Restricts what can be done with the document once it is opened (Not supported by all readers)
addPassword.selectText.16=Restricts the opening of the document itself
addPassword.submit=Verschlüsseln
#watermark

View File

@@ -10,7 +10,7 @@ multiPdfDropPrompt=Select (or drag & drop) all PDFs you require
imgPrompt=Select Image(s)
genericSubmit=Submit
processTimeWarning=Warning: This process can take up to a minute depending on file-size
pageOrderPrompt=Page Order (Enter a comma-separated list of page numbers) :
pageOrderPrompt=Custom Page Order (Enter a comma-separated list of page numbers or Functions like 2n+1) :
goToPage=Go
true=True
false=False
@@ -54,14 +54,11 @@ home.pdfOrganiser.title=Organise
home.pdfOrganiser.desc=Remove/Rearrange pages in any order
home.addImage.title=Add image
home.addImage.desc=Adds a image onto a set location on the PDF (Work in progress)
home.addImage.desc=Adds a image onto a set location on the PDF
home.watermark.title=Add Watermark
home.watermark.desc=Add a custom watermark to your PDF document.
home.remove-watermark.title=Remove Watermark
home.remove-watermark.desc=Remove watermarks from your PDF document.
home.permissions.title=Change Permissions
home.permissions.desc=Change the permissions of your PDF document
@@ -128,6 +125,13 @@ home.compare.desc=Compares and shows the differences between 2 PDF Documents
home.certSign.title=Sign with Certificate
home.certSign.desc=Signs a PDF with a Certificate/Key (PEM/P12)
home.pageLayout.title=Multi-Page Layout
home.pageLayout.desc=Merge multiple pages of a PDF document into a single page
home.scalePages.title=Adjust page size/scale
home.scalePages.desc=Change the size/scale of a page and/or its contents.
error.pdfPassword=The PDF Document is passworded and either the password was not provided or was incorrect
downloadPdf=Download PDF
text=Text
@@ -135,6 +139,17 @@ font=Font
selectFillter=-- Select --
pageNum=Page Number
pageLayout.title=Multi Page Layout
pageLayout.header=Multi Page Layout
pageLayout.pagesPerSheet=Pages per sheet:
pageLayout.submit=Submit
scalePages.title=Adjust page-scale
scalePages.header=Adjust page-scale
scalePages.pageSize=Size of a page of the document.
scalePages.scaleFactor=Zoom level (crop) of a page.
scalePages.submit=Submit
certSign.title=Certificate Signing
certSign.header=Sign a PDF with your certificate (Work in progress)
certSign.selectPDF=Select a PDF File for Signing:
@@ -333,10 +348,10 @@ pdfToImage.submit=Convert
addPassword.title=Add Password
addPassword.header=Add password (Encrypt)
addPassword.selectText.1=Select PDF to encrypt
addPassword.selectText.2=Password
addPassword.selectText.2=User Password
addPassword.selectText.3=Encryption Key Length
addPassword.selectText.4=Higher values are stronger, but lower values have better compatibility.
addPassword.selectText.5=Permissions to set
addPassword.selectText.5=Permissions to set (Recommended to be used along with Owner password)
addPassword.selectText.6=Prevent assembly of document
addPassword.selectText.7=Prevent content extraction
addPassword.selectText.8=Prevent extraction for accessibility
@@ -345,6 +360,9 @@ addPassword.selectText.10=Prevent modification
addPassword.selectText.11=Prevent annotation modification
addPassword.selectText.12=Prevent printing
addPassword.selectText.13=Prevent printing different formats
addPassword.selectText.14=Owner Password
addPassword.selectText.15=Restricts what can be done with the document once it is opened (Not supported by all readers)
addPassword.selectText.16=Restricts the opening of the document itself
addPassword.submit=Encrypt
#watermark

View File

@@ -11,7 +11,7 @@ imgPrompt=Seleccionar Imagen(es)
genericSubmit=Enviar
processTimeWarning=Advertencia: este proceso puede tardar hasta un minuto dependiendo del tamaño del archivo
pageOrderPrompt=Orden de páginas (Introduzca una lista de números de página separados por coma):
goToPage=Ir
goToPage=Ir a
true=Verdadero
false=Falso
unknown=Desconocido
@@ -23,7 +23,7 @@ bored=¿Aburrido de esperar?
#############
# HOME-PAGE #
#############
home.desc=Tu autohospedada ventanilla única para todas tus necesidades PDF.
home.desc=Tu ventanilla única autohospedada para todas tus necesidades PDF
navbar.convert=Convertir
navbar.security=Seguridad
@@ -35,19 +35,19 @@ home.multiTool.title=Multi-herramienta PDF
home.multiTool.desc=Combinar, rotar, reorganizar y eliminar páginas
home.merge.title=Unir
home.merge.desc=Unir fácilmente múltiples PDFs en uno.
home.merge.desc=Unir fácilmente múltiples PDFs en uno
home.split.title=Dividir
home.split.desc=Dividir PDFs en múltiples documentos
home.rotate.title=Rotar
home.rotate.desc=Rotar fácilmente tus PDFs.
home.rotate.desc=Rotar fácilmente tus PDFs
home.imageToPdf.title=Imagen a PDF
home.imageToPdf.desc=Convertir una imagen (PNG, JPEG, GIF) a PDF.
home.imageToPdf.desc=Convertir una imagen (PNG, JPEG, GIF) a PDF
home.pdfToImage.title=PDF a Imagen
home.pdfToImage.desc=Convertir un PDF a una imagen. (PNG, JPEG, GIF)
home.pdfToImage.desc=Convertir un PDF a una imagen (PNG, JPEG, GIF)
home.pdfOrganiser.title=Organizador
home.pdfOrganiser.desc=Eliminar/Reorganizar páginas en cualquier orden
@@ -56,37 +56,37 @@ home.addImage.title=Agregar imagen al PDF
home.addImage.desc=Agregar una imagen en una ubicación establecida en el PDF (trabajo en progreso)
home.watermark.title=Añadir marca de agua
home.watermark.desc=Añadir una marca de agua predefinida a tu documento PDF.
home.watermark.desc=Añadir una marca de agua predefinida al documento PDF
home.remove-watermark.title=Eliminar marca de agua
home.remove-watermark.desc=Eliminar marcas de agua de tu documento PDF.
home.remove-watermark.desc=Eliminar marca de agua de tu documento PDF
home.permissions.title=Cambiar Permisos
home.permissions.desc=Cambiar los permisos de tu documento PDF
home.permissions.title=Cambiar permisos
home.permissions.desc=Cambiar los permisos del documento PDF
home.removePages.title=Eliminar
home.removePages.desc=Eliminar páginas no deseadas de tu documento PDF.
home.removePages.desc=Eliminar páginas no deseadas del documento PDF
home.addPassword.title=Añadir Contraseña
home.addPassword.desc=Encriptar el documento PDF con una contraseña.
home.addPassword.title=Añadir contraseña
home.addPassword.desc=Encriptar el documento PDF con una contraseña
home.removePassword.title=Eliminar Contraseña
home.removePassword.desc=Eliminar la contraseña del documento PDF.
home.removePassword.title=Eliminar contraseña
home.removePassword.desc=Eliminar la contraseña del documento PDF
home.compressPdfs.title=Comprimir
home.compressPdfs.desc=Comprimir PDFs para reducir el tamaño del fichero.
home.compressPdfs.desc=Comprimir PDFs para reducir el tamaño del fichero
home.changeMetadata.title=Cambiar Metadatos
home.changeMetadata.desc=Cambiar/Eliminar/Añadir metadatos al documento PDF.
home.changeMetadata.title=Cambiar metadatos
home.changeMetadata.desc=Cambiar/Eliminar/Añadir metadatos al documento PDF
home.fileToPDF.title=Convertir fichero a PDF
home.fileToPDF.desc=Convertir casi cualquier archivo a PDF (DOCX, PNG, XLS, PPT, TXT y más)
home.ocr.title=Ejecutar OCR en PDF y/o escaneos de limpieza
home.ocr.desc=Escaneos de limpieza y detecta texto de imágenes dentro de un PDF y lo vuelve a agregar como texto.
home.ocr.desc=Escaneos de limpieza y detectar texto de imágenes dentro de un PDF y volver a agregarlo como texto
home.extractImages.title=Extraer imágenes
home.extractImages.desc=Extraer todas las imágenes de un PDF y guardarlas en zip
home.extractImages.desc=Extraer todas las imágenes de un PDF y guardarlas en ZIP
home.pdfToPDFA.title=Convertir PDF a PDF/A
home.pdfToPDFA.desc=Convertir PDF a PDF/A para almacenamiento a largo plazo
@@ -110,19 +110,30 @@ home.ScannerImageSplit.title=Detectar/Dividir fotos escaneadas
home.ScannerImageSplit.desc=Dividir varias fotos dentro de una foto/PDF
home.sign.title=Firmar
home.sign.desc=Añade firma a PDF mediante dibujo, texto o imagen
home.sign.desc=Añadir firma a PDF mediante dibujo, texto o imagen
home.flatten.title=Aplanar
home.flatten.desc=Eliminar todos los elementos y formularios interactivos de un PDF
home.repair.title=Reparar
home.repair.desc=Intenta reparar un PDF corrupto/roto
home.repair.desc=Intentar reparar un PDF corrupto/roto
home.removeBlanks.title=Eliminar páginas en blanco
home.removeBlanks.descdetecta y elimina páginas en blanco de un documento
home.removeBlanks.desc=Detectar y eliminar páginas en blanco de un documento
home.certSign.title=Firmar con certificado
home.certSign.desc=Firmar un PDF con un Certificado/Clave (PEM/P12)
home.compare.title=Comparar
home.compare.desc=Compara y muestra las diferencias entre 2 documentos PDF
home.compare.desc=Comparar y mostrar las diferencias entre 2 documentos PDF
home.pageLayout.title=Diseño de varias páginas
home.pageLayout.desc=Unir varias páginas de un documento PDF en una sola página
home.scalePages.title=Escalar/ajustar tamaño de página
home.scalePages.desc=Escalar/cambiar el tamaño de una pagina y/o su contenido
error.pdfPassword=El documento PDF está protegido con contraseña y no se ha proporcionado o es incorrecta
downloadPdf=Descargar PDF
text=Texto
@@ -130,8 +141,19 @@ font=Fuente
selectFilter=-- Seleccionar --
pageNum=Número de página
pageLayout.title=Diseño de varias páginas
pageLayout.header=Diseño de varias páginas
pageLayout.pagesPerSheet=Páginas por hoja:
pageLayout.submit=Entregar
scalePages.title=Ajustar escala de la página
scalePages.header=Adjustar escala de la página
scalePages.pageSize=Tamaño de la página del documento
scalePages.scaleFactor=Nivel de zoom (recorte) de la página
scalePages.submit=Entregar
certSign.title=Firma de certificado
certSign.header=Firme un PDF con su certificado (Trabajo en progreso)
certSign.header=Firmar un PDF con su certificado (Trabajo en progreso)
certSign.selectPDF=Seleccione un archivo PDF para firmar:
certSign.selectKey=Seleccione su archivo de clave privada (formato PKCS#8, podría ser .pem o .der):
certSign.selectCert=Seleccione su archivo de certificado (formato X.509, podría ser .pem o .der):
@@ -175,13 +197,13 @@ flatten.header=Acoplar archivos PDF
flatten.submit=Aplanar
ScannerImageSplit.selectText.1=Umbral de ángulo:
ScannerImageSplit.selectText.2=Establece el ángulo absoluto mínimo requerido para rotar la imagen (predeterminado: 10).
ScannerImageSplit.selectText.2=Establecer el ángulo absoluto mínimo requerido para rotar la imagen (predeterminado: 10).
ScannerImageSplit.selectText.3=Tolerancia:
ScannerImageSplit.selectText.4=Determina el rango de variación de color alrededor del color de fondo estimado (predeterminado: 30).
ScannerImageSplit.selectText.4=Determinar el rango de variación de color alrededor del color de fondo estimado (predeterminado: 30).
ScannerImageSplit.selectText.5=Área mínima:
ScannerImageSplit.selectText.6=Establece el umbral mínimo de área para una foto (predeterminado: 10000).
ScannerImageSplit.selectText.6=Establecer el umbral mínimo de área para una foto (predeterminado: 10000).
ScannerImageSplit.selectText.7=Área de contorno mínima:
ScannerImageSplit.selectText.8=Establece el umbral mínimo del área de contorno para una foto
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).
@@ -189,10 +211,10 @@ navbar.settings=Ajustes
settings.title=Ajustes
settings.update=Actualización disponible
settings.appVersion=Versión de la aplicación:
settings.downloadOption.title=Elige la opción de descarga (para descargas de un solo archivo sin ZIP):
settings.downloadOption.1=Abre en la misma ventana
settings.downloadOption.2=Abre en una nueva ventana
settings.downloadOption.3=Descarga el fichero
settings.downloadOption.title=Elegir la opción de descarga (para descargas de un solo archivo sin ZIP):
settings.downloadOption.1=Abrir en la misma ventana
settings.downloadOption.2=Abrir en una nueva ventana
settings.downloadOption.3=Descargar el fichero
settings.zipThreshold=Ficheros ZIP cuando excede el número de ficheros descargados
@@ -201,53 +223,53 @@ settings.zipThreshold=Ficheros ZIP cuando excede el número de ficheros descarga
#OCR
ocr.title=OCR / Escaneo de limpieza
ocr.header=Escaneos de limpieza / OCR (Reconocimiento óptico de caracteres)
ocr.selectText.1=Selecciona los idiomas que se detectarán en el PDF (Los enumerados son los detectados actualmente):
ocr.selectText.2=Produzca un archivo de texto que contenga texto OCR junto con el PDF editado con OCR
ocr.selectText.3=Corrija las páginas que se escanearon en un ángulo torcido girándolas nuevamente a su lugar
ocr.selectText.4=Limpie la página para que sea menos probable que el OCR encuentre texto en el ruido de fondo. (Sin cambio de salida)
ocr.selectText.5=Limpie la página para que sea menos probable que el OCR encuentre texto en el ruido de fondo, mantiene la limpieza en la salida.
ocr.selectText.6=Ignora las páginas que tienen texto interactivo, solo las páginas OCR que son imágenes
ocr.selectText.7=Fuerza OCR, OCR eliminará en cada página todo el texto original
ocr.selectText.8=Normal (Se producirá un error si el PDF contiene texto)
ocr.selectText.9=Ajustes Adicionales
ocr.selectText.1=Seleccionar los idiomas que se detectarán en el PDF (Los enumerados son los detectados actualmente):
ocr.selectText.2=Producir un archivo de texto que contenga texto OCR junto con el PDF editado con OCR
ocr.selectText.3=Corregir las páginas que se escanearon en un ángulo torcido girándolas nuevamente a su lugar
ocr.selectText.4=Limpiar la página para que sea menos probable que el OCR encuentre texto en el ruido de fondo (Sin cambio de salida)
ocr.selectText.5=Limpiar la página para que sea menos probable que el OCR encuentre texto en el ruido de fondo, mantiene la limpieza en la salida.
ocr.selectText.6=Ignorar las páginas que tienen texto interactivo, solo las páginas OCR que son imágenes
ocr.selectText.7=Forzar OCR, OCR eliminará en cada página todo el texto original
ocr.selectText.8=Normal (se producirá un error si el PDF contiene texto)
ocr.selectText.9=Ajustes adicionales
ocr.selectText.10=Modo OCR
ocr.selectText.11=Eliminar imágenes después de OCR (Elimina TODAS las imágenes, solo es útil si es parte del paso de conversión)
ocr.selectText.12=Tipo de procesamiento (avanzado)
ocr.help=Lea esta documentación sobre cómo usar esto para otros idiomas y/o no usarlo en docker
ocr.credit=Este servicio utiliza OCRmyPDF y Tesseract para OCR.
ocr.submit=Procesa PDF con OCR
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.submit=Procesar PDF con OCR
extractImages.title=Extraer imágenes
extractImages.header=Extraer imágenes
extractImages.selectText=Selecciona el formato de imagen para convertir las imágenes extraídas
extractImages.selectText=Seleccionar el formato de imagen para convertir las imágenes extraídas
extractImages.submit=Extraer
#File to PDF
fileToPDF.title=Fichero a PDF
fileToPDF.header=Convierte cualquier fichero a PDF
fileToPDF.credit=Este servicio usa LibreOffice y Unoconv para la conversión de ficheros.
fileToPDF.supportedFileTypes=Los tipos de ficheros soportados deben incluir los de abajo; sin embargo para una completa y acutualizada lista de formatos soportados, por favor consulte la documentación de LibreOffice
fileToPDF.title=Archivo a PDF
fileToPDF.header=Convertir cualquier archivo a PDF
fileToPDF.credit=Este servicio usa LibreOffice y Unoconv para la conversión de ficheros
fileToPDF.supportedFileTypes=Los tipos de ficheros soportados deben incluir los de abajo; sin embargo, para una completa y acutualizada lista de formatos soportados, por favor consulte la documentación de LibreOffice
fileToPDF.submit=Convertir a PDF
#compress
compress.title=Comprimir
compress.header=Comprimir PDF
compress.credit=Este servicio utiliza Ghostscript para compresión/optimización de PDF.
compress.credit=Este servicio utiliza Ghostscript para compresión/optimización de PDF
compress.selectText.1=Modo manual - De 1 a 4
compress.selectText.2=Nivel de optimización:
compress.selectText.3=4 (Terrible para imágenes de texto)
compress.selectText.4=Modo automático: ajusta automáticamente la calidad para que el PDF tenga el tamaño exacto
compress.selectText.5=Tamaño de PDF esperado (por ejemplo, 25 MB, 10,8 MB, 25 KB)
compress.selectText.5=Tamaño esperado del PDF (por ejemplo, 25 MB, 10.8 MB, 25 KB)
compress.submit=Comprimir
#Add image
addImage.title=Añadir Imagen
addImage.header=Añadir image de PDF
addImage.title=Añadir imagen
addImage.header=Añadir imagen de PDF
addImage.everyPage=¿Todas las páginas?
addImage.submit=Añadir imagen
@@ -269,13 +291,13 @@ multiTool.header=Multi-herramienta PDF
#pageRemover
pageRemover.title=Eliminador de páginas
pageRemover.header=Eliminador de páginas PDF
pageRemover.pagesToDelete=Páginas a eliminar (Introduzca una lista de números de página separados por coma):
pageRemover.pagesToDelete=Páginas a eliminar (introducir una lista de números de página separados por coma):
pageRemover.submit=Eliminar Páginas
#rotate
rotate.title=Rotar PDF
rotate.header=Rotar PDF
rotate.SeleccionaAngle=Seleccionar ángulo de rotación (múltiple de 90 grados):
rotate.SeleccionaAngle=Seleccionar ángulo de rotación (múltiplo de 90 grados):
rotate.submit=Rotar
@@ -284,7 +306,7 @@ rotate.submit=Rotar
#merge
split.title=Dividir PDF
split.header=Dividir PDF
split.desc.1=Los números que selecciones son el número de página en el que desea hacer una división
split.desc.1=Los números que seleccione son el número de página en el que desea hacer una división
split.desc.2=Como tal, seleccionar 1,3,7-8 dividiría un documento de 10 páginas en 6 archivos PDF separados con:
split.desc.3=Documento #1: Página 1
split.desc.4=Documento #2: Páginas 2 y 3
@@ -292,7 +314,7 @@ split.desc.5=Documento #3: Páginas 4, 5 y 6
split.desc.6=Documento #4: Página 7
split.desc.7=Documento #5: Página 8
split.desc.8=Documento #6: Páginas 9 y 10
split.splitPages=Introduce las páginas para dividir:
split.splitPages=Introducir las páginas para dividir:
split.submit=Dividir
@@ -302,7 +324,7 @@ imageToPDF.header=Imagen a PDF
imageToPDF.submit=Convertir
imageToPDF.selectText.1=Estirar para ajustar
imageToPDF.selectText.2=Rotación automática del PDF
imageToPDF.selectText.3=Lógica de archivos múltiples (Únicamente activado si funciona con multiples imágenes)
imageToPDF.selectText.3=Lógica de archivos múltiples (únicamente activado si funciona con multiples imágenes)
imageToPDF.selectText.4=Unir en un único archivo PDF
imageToPDF.selectText.5=Convertir a PDFs separados
@@ -311,21 +333,21 @@ pdfToImage.title=PDF a Imagen
pdfToImage.header=PDF a Imagen
pdfToImage.selectText=Formato de Imagen
pdfToImage.singleOrMultiple=Tipo resultante de imagen
pdfToImage.single=Una Imagen Grande Única
pdfToImage.multi=Múltiples Imágenes
pdfToImage.single=Una única imagen grande
pdfToImage.multi=Múltiples imágenes
pdfToImage.colorType=Tipo de color
pdfToImage.color=Color
pdfToImage.grey=Escala de Grises
pdfToImage.blackwhite=Blanco y Negro (¡Puedes perder datos!)
pdfToImage.grey=Escala de grises
pdfToImage.blackwhite=Blanco y Negro (¡Puede perder datos!)
pdfToImage.submit=Convertir
#addPassword
addPassword.title=Añadir Contraseña
addPassword.header=Añadir contraseña (Encriptar)
addPassword.title=Añadir contraseña
addPassword.header=Añadir contraseña (encriptar)
addPassword.selectText.1=Seleccionar PDF para encriptar
addPassword.selectText.2=Contraseña
addPassword.selectText.3=Longitud de la clave de cifrado
addPassword.selectText.4=Valores altos son más fuertes, pero valores bajos tienen mejor compatibilidad.
addPassword.selectText.4=Valores altos son más fuertes, pero valores bajos tienen mejor compatibilidad
addPassword.selectText.5=Permisos para establecer
addPassword.selectText.6=Impedir el ensamblaje del documento
addPassword.selectText.7=Impedir la extracción de contenido
@@ -335,6 +357,9 @@ addPassword.selectText.10=Impedir modificación
addPassword.selectText.11=Impedir modificación de anotaciones
addPassword.selectText.12=Impedir imprimir
addPassword.selectText.13=Impedir imprimir diferentes formatos
addPassword.selectText.14=Contraseña
addPassword.selectText.15=Restringe qué se puede hacer con el documento una vez abierto (no soportado por todos los lectores)
addPassword.selectText.16=Restringe la apertura del propio documento
addPassword.submit=Encriptar
#watermark
@@ -357,8 +382,8 @@ remove-watermark.selectText.2=Texto de la marca de agua:
remove-watermark.submit=Eliminar marca de agua
#Change permissions
permissions.title=Cambiar Permisos
permissions.header=Cambiar Permisos
permissions.title=Cambiar permisos
permissions.header=Cambiar permisos
permissions.warning=Advertencia: para que estos permisos no se puedan cambiar, se recomienda configurarlos con una contraseña a través de la página de cambio de contraseña
permissions.selectText.1=Seleccionar PDF para cambiar los permisos
permissions.selectText.2=Permisos a establecer
@@ -374,21 +399,21 @@ permissions.submit=Cambiar
#remove password
removePassword.title=Eliminar contraseña
removePassword.header=Eliminar contraseña (Desencriptar)
removePassword.selectText.1=Seleccionar PDF para Desencriptar
removePassword.header=Eliminar contraseña (desencriptar)
removePassword.selectText.1=Seleccionar PDF para desencriptar
removePassword.selectText.2=Contraseña
removePassword.submit=Eliminar
changeMetadata.title=Cambiar Metadatos
changeMetadata.header=Cambiar Metadatos
changeMetadata.title=Cambiar metadatos
changeMetadata.header=Cambiar metadatos
changeMetadata.selectText.1=Editar las variables que desea cambiar
changeMetadata.selectText.2=Eliminar todos los metadatos
changeMetadata.selectText.3=Mostrar metadatos personalizados:
changeMetadata.author=Autor:
changeMetadata.creationDate=Fecha de Creación (aaaa/MM/dd HH:mm:ss):
changeMetadata.creationDate=Fecha de creación (aaaa/MM/dd HH:mm:ss):
changeMetadata.creator=Creador:
changeMetadata.keywords=Palabras clave:
changeMetadata.modDate=Fecha de Modificación (aaaa/MM/dd HH:mm:ss):
changeMetadata.modDate=Fecha de modificación (aaaa/MM/dd HH:mm:ss):
changeMetadata.producer=Productor:
changeMetadata.subject=Asunto:
changeMetadata.title=Título:
@@ -415,29 +440,29 @@ pdfToPDFA.submit=Convertir
PDFToWord.title=PDF a Word
PDFToWord.header=PDF a Word
PDFToWord.selectText.1=Formato de archivo de salida
PDFToWord.credit=Este servicio utiliza LibreOffice para la conversión de archivos.
PDFToWord.credit=Este servicio utiliza LibreOffice para la conversión de archivos
PDFToWord.submit=Convertir
PDFToPresentation.title=PDF a presentación
PDFToPresentation.header=PDF a presentación
PDFToPresentation.selectText.1=Formato de archivo de salida
PDFToPresentation.credit=Este servicio utiliza LibreOffice para la conversión de archivos.
PDFToPresentation.credit=Este servicio utiliza LibreOffice para la conversión de archivos
PDFToPresentation.submit=Convertir
PDFToText.title=PDF a TXT/RTF
PDFToText.header=PDF a TXT/RTF
PDFToText.selectText.1=Formato de archivo de salida
PDFToText.credit=Este servicio utiliza LibreOffice para la conversión de archivos.
PDFToText.credit=Este servicio utiliza LibreOffice para la conversión de archivos
PDFToText.submit=Convertir
PDFToHTML.title=PDF a HTML
PDFToHTML.header=PDF a HTML
PDFToHTML.credit=Este servicio utiliza LibreOffice para la conversión de archivos.
PDFToHTML.credit=Este servicio utiliza LibreOffice para la conversión de archivos
PDFToHTML.submit=Convertir
PDFToXML.title=PDF a XML
PDFToXML.header=PDF a XML
PDFToXML.credit=Este servicio utiliza LibreOffice para la conversión de archivos.
PDFToXML.credit=Este servicio utiliza LibreOffice para la conversión de archivos
PDFToXML.submit=Convertir

View File

@@ -127,15 +127,37 @@ home.repair.desc=Essaye de réparer un PDF corrompu/cassé
home.removeBlanks.title=Supprimer les pages vierges
home.removeBlanks.desc=Détecte et supprime les pages vierges d'un document
home.certSign.title=Sign with Certificate
home.certSign.desc=Signs a PDF with a Certificate/Key (PEM/P12)
home.compare.title=Comparer
home.compare.desc=Compare et affiche les différences entre 2 documents PDF
home.pageLayout.title=Multi-Page Layout
home.pageLayout.desc=Merge multiple pages of a PDF document into a single page
home.scalePages.title=Adjust page size/scale
home.scalePages.desc=Change the size/scale of page and/or its contents.
error.pdfPassword=The PDF Document is passworded and either the password was not provided or was incorrect
downloadPdf=Télécharger le PDF
text=Texte
font=Police
selectFilter=-- Sélectionner --
pageNum=numéro de page
pageLayout.title=Multi Page Layout
pageLayout.header=Multi Page Layout
pageLayout.pagesPerSheet=Pages per sheet:
pageLayout.submit=Submit
scalePages.title=Adjust page-scale
scalePages.header=Adjust page-scale
scalePages.pageSize=Size of a page of the document.
scalePages.scaleFactor=Zoom level (crop) of a page.
scalePages.submit=Submit
certSign.title=Signature du certificat
certSign.header=Signer un PDF avec votre certificat (Travail en cours)
certSign.selectPDF=Sélectionnez un fichier PDF à signer :
@@ -334,6 +356,9 @@ addPassword.selectText.10=Empêcher la modification
addPassword.selectText.11=Empêcher la modification des annotations
addPassword.selectText.12=Empêcher l'impression
addPassword.selectText.13=Empêcher l'impression de différents formats
addPassword.selectText.14=Owner Password
addPassword.selectText.15=Restricts what can be done with the document once it is opened (Not supported by all readers)
addPassword.selectText.16=Restricts the opening of the document itself
addPassword.submit=Crypter
#watermark

View File

@@ -122,15 +122,37 @@ home.repair.desc=Prova a riparare un PDF corrotto.
home.removeBlanks.title=Rimuovi pagine vuote
home.removeBlanks.desc=Trova e rimuovi pagine vuote da un PDF.
home.certSign.title=Sign with Certificate
home.certSign.desc=Signs a PDF with a Certificate/Key (PEM/P12)
home.compare.title=Compara
home.compare.desc=Vedi e compara le differenze tra due PDF.
home.pageLayout.title=Multi-Page Layout
home.pageLayout.desc=Merge multiple pages of a PDF document into a single page
home.scalePages.title=Adjust page size/scale
home.scalePages.desc=Change the size/scale of page and/or its contents.
error.pdfPassword=The PDF Document is passworded and either the password was not provided or was incorrect
downloadPdf=Scarica PDF
text=Testo
font=Font
selectFillter=-- Seleziona --
pageNum=Numero pagina
pageLayout.title=Multi Page Layout
pageLayout.header=Multi Page Layout
pageLayout.pagesPerSheet=Pages per sheet:
pageLayout.submit=Submit
scalePages.title=Adjust page-scale
scalePages.header=Adjust page-scale
scalePages.pageSize=Size of a page of the document.
scalePages.scaleFactor=Zoom level (crop) of a page.
scalePages.submit=Submit
certSign.title=Firma del certificato
certSign.header=Firma un PDF con il tuo certificato (Lavoro in corso)
certSign.selectPDF=Seleziona un file PDF per la firma:
@@ -337,6 +359,9 @@ addPassword.selectText.10=Previeni modifiche
addPassword.selectText.11=Previeni annotazioni
addPassword.selectText.12=Previeni stampa
addPassword.selectText.13=Previeni stampa in diversi formati
addPassword.selectText.14=Owner Password
addPassword.selectText.15=Restricts what can be done with the document once it is opened (Not supported by all readers)
addPassword.selectText.16=Restricts the opening of the document itself
addPassword.submit=Crittografa
#watermark

View File

@@ -0,0 +1,510 @@
###########
# Generic #
###########
# the direction that the language is written (ltr = left to right, rtl = right to left)
language.direction=ltr
pdfPrompt=PDF 선택
multiPdfPrompt=PDF 선택(2개 이상)
multiPdfDropPrompt=사용할 모든 PDF를 선택(또는 드래그 앤 드롭)하세요
imgPrompt=이미지 선택
genericSubmit=제출
processTimeWarning=경고: 파일 크기에 따라 1분 정도 소요될 수 있습니다
pageOrderPrompt=페이지 순서(쉼표로 구분된 페이지 번호 목록 입력):
goToPage=이동
true=
false=거짓
unknown=알 수 없음
save=저장
close=닫기
filesSelected=개 파일 선택됨
noFavourites=즐겨찾기 없음
bored=기다리는 게 지루하신가요?
#############
# HOME-PAGE #
#############
home.desc=당신의 PDF에 필요한 모든 것이 있는 로컬 호스팅된 원스톱 숍입니다.
navbar.convert=변환
navbar.security=보안
navbar.other=기타
navbar.darkmode=다크 모드
navbar.pageOps=Page Operations
home.multiTool.title=PDF 멀티 툴
home.multiTool.desc=페이지를 병합, 회전, 재배열, 제거하세요.
home.merge.title=병합
home.merge.desc=여러 개의 PDF를 쉽게 하나로 합치세요.
home.split.title=분할
home.split.desc=PDF를 여러 개의 문서로 분할하세요.
home.rotate.title=회전
home.rotate.desc=PDF를 쉽게 회전하세요.
home.imageToPdf.title=Image to PDF
home.imageToPdf.desc=이미지(PNG, JPEG, GIF)를 PDF로 변환하세요.
home.pdfToImage.title=PDF to Image
home.pdfToImage.desc=PDF를 이미지(PNG, JPEG, GIF)로 변환하세요.
home.pdfOrganiser.title=정렬
home.pdfOrganiser.desc=페이지를 원하는 순서대로 제거/재배열하세요.
home.addImage.title=사진 추가
home.addImage.desc=PDF의 설정된 위치에 이미지를 추가하세요.(개발 중)
home.watermark.title=워터마크 추가
home.watermark.desc=PDF 문서에 사용자 지정 워터마크를 추가하세요.
home.remove-watermark.title=워터마크 제거
home.remove-watermark.desc=PDF 문서에서 워터마크를 제거하세요.
home.permissions.title=권한 변경
home.permissions.desc=PDF 문서의 권한을 변경하세요.
home.removePages.title=제거
home.removePages.desc=PDF 문서에서 원치 않는 페이지를 제거하세요.
home.addPassword.title=비밀번호 추가
home.addPassword.desc=PDF 문서를 비밀번호로 암호화하세요.
home.removePassword.title=비밀번호 제거
home.removePassword.desc=PDF 문서에서 비밀번호를 제거하세요.
home.compressPdfs.title=압축
home.compressPdfs.desc=파일 크기를 줄이기 위해 PDF 문서를 압축하세요.
home.changeMetadata.title=메타데이터 변경
home.changeMetadata.desc=PDF 문서의 메타데이터를 수정/제거/추가하세요.
home.fileToPDF.title=파일을 PDF로 변환
home.fileToPDF.desc=거의 모든 파일을 PDF로 변환하세요(DOCX, PNG, XLS, PPT, TXT 등)
home.ocr.title=OCR / 깔끔하게 스캔
home.ocr.desc=깔끔하게 스캔하고 PDF 내의 이미지에서 텍스트를 감지하여 텍스트로 다시 추가합니다.
home.extractImages.title=이미지 추출
home.extractImages.desc=PDF에서 모든 이미지를 추출하여 zip으로 저장합니다.
home.pdfToPDFA.title=PDF to PDF/A
home.pdfToPDFA.desc=장기 보관을 위해 PDF를 PDF/A 문서로 변환하세요.
home.PDFToWord.title=PDF to Word
home.PDFToWord.desc=PDF를 Word 형식으로 변환하세요. (DOC, DOCX, ODT)
home.PDFToPresentation.title=PDF to 프리젠테이션
home.PDFToPresentation.desc=PDF를 프리젠테이션 형식으로 변환하세요. (PPT, PPTX, ODP)
home.PDFToText.title=PDF to 텍스트/RTF
home.PDFToText.desc=PDF를 텍스트 또는 RTF 형식으로 변환하세요.
home.PDFToHTML.title=PDF to HTML
home.PDFToHTML.desc=PDF를 HTML 형식으로 변환하세요.
home.PDFToXML.title=PDF to XML
home.PDFToXML.desc=PDF를 XML 형식으로 변환하세요.
home.ScannerImageSplit.title=스캔한 사진 감지/분할
home.ScannerImageSplit.desc=사진/PDF 내에서 여러 장의 사진을 분할합니다.
home.sign.title=서명
home.sign.desc=PDF에 그림, 텍스트, 이미지로 서명을 추가합니다.
home.flatten.title=합치기
home.flatten.desc=PDF에서 모든 인터랙션 요소와 양식을 제거하세요.
home.repair.title=복구
home.repair.desc=손상된 PDF의 복구를 시도합니다.
home.removeBlanks.title=빈 페이지 제거
home.removeBlanks.desc=문서에서 빈 페이지를 감지하고 제거합니다.
home.compare.title=비교
home.compare.desc=2개의 PDF 문서를 비교하고 차이를 표시합니다.
home.certSign.title=인증서로 서명
home.certSign.desc=PDF에 인증서/키로 서명합니다. (PEM/P12)
home.pageLayout.title=Multi-Page Layout
home.pageLayout.desc=Merge multiple pages of a PDF document into a single page
home.scalePages.title=Adjust page size/scale
home.scalePages.desc=Change the size/scale of page and/or its contents.
error.pdfPassword=The PDF Document is passworded and either the password was not provided or was incorrect
downloadPdf=PDF 다운로드
text=텍스트
font=폰트
selectFillter=-- 선택 --
pageNum=페이지 번호
pageLayout.title=Multi Page Layout
pageLayout.header=Multi Page Layout
pageLayout.pagesPerSheet=Pages per sheet:
pageLayout.submit=Submit
scalePages.title=Adjust page-scale
scalePages.header=Adjust page-scale
scalePages.pageSize=Size of a page of the document.
scalePages.scaleFactor=Zoom level (crop) of a page.
scalePages.submit=Submit
certSign.title=인증서로 서명
certSign.header=PDF에 당신의 인증서로 서명하세요 (개발 중)
certSign.selectPDF=서명할 PDF를 선택하세요:
certSign.selectKey=개인 키 파일을 선택하세요 (PKCS#8 형식, .pem 또는 .der):
certSign.selectCert=인증서 파일을 선택하세요 (X.509 형식, .pem 또는 .der):
certSign.selectP12=PKCS#12 키 저장소 파일을 선택하세요 (.p12 or .pfx) (선택 사항, 선택할 경우, 개인 키와 인증서를 포함하고 있어야 합니다):
certSign.certType=인증서 유형
certSign.password=키 저장소 또는 개인 키 비밀번호를 입력하세요 (있는 경우):
certSign.showSig=서명 보기
certSign.reason=이유
certSign.location=위치
certSign.name=이름
certSign.submit=PDF 서명
removeBlanks.title=빈 페이지 제거
removeBlanks.header=빈 페이지 제거
removeBlanks.threshold=임계값:
removeBlanks.thresholdDesc=흰색 픽셀이 얼마나 흰색이어야 하는지를 결정하는 임계값
removeBlanks.whitePercent=흰색 비율 (%):
removeBlanks.whitePercentDesc=제거될 페이지의 흰색 픽셀 비율
removeBlanks.submit=빈 페이지 제거
compare.title=비교
compare.header=PDF 비교
compare.document.1=문서 1
compare.document.2=문서 2
compare.submit=비교
sign.title=서명
sign.header=PDF에 서명
sign.upload=이미지 업로드
sign.draw=서명 그리기
sign.text=텍스트 입력
sign.clear=초기화
sign.add=추가
repair.title=복구
repair.header=PDF 복구
repair.submit=복구
flatten.title=합치기
flatten.header=PDF 합치기
flatten.submit=합치기
ScannerImageSplit.selectText.1=각도 임계값:
ScannerImageSplit.selectText.2=이미지를 회전하는 데 필요한 최소 절대 각도를 설정합니다(기본값: 10).
ScannerImageSplit.selectText.3=오차 범위:
ScannerImageSplit.selectText.4=예상 배경색 주변의 색상 변화 범위를 결정합니다(기본값: 30).
ScannerImageSplit.selectText.5=최소 면적:
ScannerImageSplit.selectText.6=사진의 최소 면적 임계값을 설정합니다 (기본값: 10000).
ScannerImageSplit.selectText.7=최소 윤곽 영역:
ScannerImageSplit.selectText.8=사진의 최소 윤곽선 영역 임계값을 설정합니다.
ScannerImageSplit.selectText.9=테두리 크기:
ScannerImageSplit.selectText.10=출력에서 흰색 테두리를 방지하기 위해 추가 및 제거되는 테두리의 크기를 설정합니다(기본값: 1).
navbar.settings=설정
settings.title=설정
settings.update=업데이트 가능
settings.appVersion=앱 버전:
settings.downloadOption.title=다운로드 옵션 선택 (zip 파일이 아닌 단일 파일 다운로드 시):
settings.downloadOption.1=현재 창에서 열기
settings.downloadOption.2=새 창에서 열기
settings.downloadOption.3=다운로드
settings.zipThreshold=다운로드한 파일 수가 초과된 경우 파일 압축하기
#OCR
ocr.title=OCR / 깔끔하게 스캔
ocr.header=깔끔하게 스캔 / OCR (광학 문자 인식)
ocr.selectText.1=PDF에서 감지할 언어를 선택하십시오 (현재 감지된 언어 목록):
ocr.selectText.2=OCR 텍스트가 포함된 텍스트 파일을 OCR 처리된 PDF와 함께 생성
ocr.selectText.3=비뚤어진 각도로 스캔한 페이지를 다시 제자리로 돌려 올바른 페이지로 스캔
ocr.selectText.4=페이지를 깨끗하게 정리하여 OCR이 배경의 이물질에서 텍스트를 찾을 가능성 줄이기 (출력 변경 없음)
ocr.selectText.5=페이지를 깨끗하게 정리하여 OCR이 배경의 이물질에서 텍스트를 찾을 가능성 줄이기 (출력 변경)
ocr.selectText.6=인터랙티브 텍스트가 있는 페이지는 건너뛰고 이미지만 OCR
ocr.selectText.7=OCR 강제(모든 페이지에서 원본 텍스트 제거하고 OCR로 대체)
ocr.selectText.8=일반 (PDF에 텍스트가 포함된 경우 오류 발생)
ocr.selectText.9=추가 설정
ocr.selectText.10=OCR 모드
ocr.selectText.11=OCR 후 이미지 제거(모든 이미지 제거, 변환 단계의 일부인 경우에만 유용)
ocr.selectText.12=렌더 유형(고급)
ocr.help=다른 언어 또는 Docker에 포함되지 않은 언어에 대해 사용하는 방법에 대해서는 이 문서를 참조하세요.
ocr.credit=이 서비스는 OCR에 OCRmyPDF와 Tesseract를 사용합니다.
ocr.submit=OCR로 PDF 처리
extractImages.title=이미지 추출
extractImages.header=이미지 추출
extractImages.selectText=추출된 이미지를 변환할 이미지 형식을 선택하세요.
extractImages.submit=추출
#File to PDF
fileToPDF.title=File to PDF
fileToPDF.header=모든 파일을 PDF로 변환
fileToPDF.credit=이 서비스는 파일 변환에 LibreOffice와 Unoconv를 사용합니다.
fileToPDF.supportedFileTypes=지원되는 파일 형식은 아래와 같지만, 지원되는 형식의 전체 업데이트 목록은 LibreOffice 설명서를 참조하세요.
fileToPDF.submit=PDF로 변환
#compress
compress.title=압축
compress.header=PDF 압축
compress.credit=이 서비스는 PDF 압축/최적화를 위해 Ghostscript를 사용합니다.
compress.selectText.1=수동 모드 - 1에서 4
compress.selectText.2=최적화 수준:
compress.selectText.3=4 (텍스트 이미지에 적합하지 않음)
compress.selectText.4=자동 - 정확한 크기의 PDF를 얻기 위해 품질 자동 조정
compress.selectText.5=예상 PDF 크기 (예: 25MB, 10.8MB, 25KB)
compress.submit=압축
#Add image
addImage.title=이미지 추가
addImage.header=PDF에 이미지 추가
addImage.everyPage=모든 페이지에 적용
addImage.submit=이미지 추가
#merge
merge.title=병합
merge.header=여러 개의 PDF 병합 (2개 이상)
merge.submit=병합
#pdfOrganiser
pdfOrganiser.title=페이지 정렬 도구
pdfOrganiser.header=PDF 페이지 정렬
pdfOrganiser.submit=페이지 재정렬
#multiTool
multiTool.title=PDF 멀티 툴
multiTool.header=PDF 멀티 툴
#pageRemover
pageRemover.title=페이지 제거 도구
pageRemover.header=PDF 페이지 제거 도구
pageRemover.pagesToDelete=제거할 페이지 (쉼표로 구분된 페이지 번호 입력):
pageRemover.submit=페이지 제거
#rotate
rotate.title=PDF 회전
rotate.header=PDF 회전
rotate.selectAngle=회전 각도 선택 (90도의 배수로):
rotate.submit=회전
#merge
split.title=PDF 분할
split.header=PDF 분할
split.desc.1=선택한 번호는 분할할 페이지 번호입니다.
split.desc.2=예를 들어, 1,3,7-8을 선택하면 10페이지 문서를 아래와 같이 6개의 별도의 PDF로 분할하게 됩니다.
split.desc.3=문서 #1: 페이지 1
split.desc.4=문서 #2: 페이지 2, 3
split.desc.5=문서 #3: 페이지 4, 5, 6
split.desc.6=문서 #4: 페이지 7
split.desc.7=문서 #5: 페이지 8
split.desc.8=문서 #6: 페이지 9, 10
split.splitPages=분할할 페이지 입력:
split.submit=분할
#imageToPdf
imageToPDF.title=Image to PDF
imageToPDF.header=이미지를 PDF로 변환
imageToPDF.submit=변환
imageToPDF.selectText.1=맞춤 크기로 늘리기
imageToPDF.selectText.2=PDF 자동 회전
imageToPDF.selectText.3=다중 파일 로직 (여러 이미지로 작업하는 경우에만 활성화됨)
imageToPDF.selectText.4=단일 PDF로 병합
imageToPDF.selectText.5=별개의 PDF로 변환
#pdfToImage
pdfToImage.title=PDF to Image
pdfToImage.header=PDF를 이미지로 변환
pdfToImage.selectText=이미지 형식
pdfToImage.singleOrMultiple=이미지 결과 유형
pdfToImage.single=단일 큰 이미지
pdfToImage.multi=여러 이미지
pdfToImage.colorType=색상 유형
pdfToImage.color=컬러
pdfToImage.grey=그레이스케일
pdfToImage.blackwhite=흑백 (데이터 손실 가능성 있음!)
pdfToImage.submit=변환하기
#imageToPdf
imageToPDF.title=이미지를 PDF로 변환
imageToPDF.header=이미지를 PDF로 변환
imageToPDF.submit=변환하기
imageToPDF.selectText.1=맞춤 크기로 늘리기
imageToPDF.selectText.2=PDF 자동 회전
imageToPDF.selectText.3=다중 파일 로직 (여러 이미지로 작업하는 경우에만 활성화됨)
imageToPDF.selectText.4=단일 PDF로 병합
imageToPDF.selectText.5=별도의 PDF로 변환
#pdfToImage
pdfToImage.title=PDF를 이미지로 변환
pdfToImage.header=PDF를 이미지로 변환
pdfToImage.selectText=이미지 형식
pdfToImage.singleOrMultiple=이미지 결과 유형
pdfToImage.single=단일 큰 이미지
pdfToImage.multi=여러 이미지
pdfToImage.colorType=색상 유형
pdfToImage.color=컬러
pdfToImage.grey=그레이스케일
pdfToImage.blackwhite=흑백 (데이터 손실 가능성 있음!)
pdfToImage.submit=변환하기
#addPassword
addPassword.title=암호 추가
addPassword.header=암호 추가 (암호화)
addPassword.selectText.1=암호화할 PDF 선택
addPassword.selectText.2=암호
addPassword.selectText.3=암호화 키 길이
addPassword.selectText.4=값이 높을수록 강력하지만, 값이 낮을수록 호환성이 더 좋습니다.
addPassword.selectText.5=설정할 권한
addPassword.selectText.6=문서 조립 방지
addPassword.selectText.7=콘텐츠 추출 방지
addPassword.selectText.8=접근성을 위한 추출 방지
addPassword.selectText.9=양식 작성 방지
addPassword.selectText.10=수정 방지
addPassword.selectText.11=주석 수정 방지
addPassword.selectText.12=인쇄 방지
addPassword.selectText.13=다른 형식으로 인쇄 방<><EBB0A9>
addPassword.selectText.14=Owner Password
addPassword.selectText.15=Restricts what can be done with the document once it is opened (Not supported by all readers)
addPassword.selectText.16=Restricts the opening of the document itself<6C>
addPassword.submit=암호화
#watermark
watermark.title=워터마크 추가
watermark.header=워터마크 추가
watermark.selectText.1=워터마크를 추가할 PDF 선택:
watermark.selectText.2=워터마크 텍스트:
watermark.selectText.3=폰트 크기:
watermark.selectText.4=회전 각도 (0-360):
watermark.selectText.5=가로 간격 (각 워터마크 사이의 가로 공간):
watermark.selectText.6=세로 간격 (각 워터마크 사이의 세로 공간):
watermark.selectText.7=투명도 (0% - 100%):
watermark.submit=워터마크 추가
#remove-watermark
remove-watermark.title=워터마크 제거
remove-watermark.header=워터마크 제거
remove-watermark.selectText.1=워터마크를 제거할 PDF 선택:
remove-watermark.selectText.2=워터마크 텍스트:
remove-watermark.submit=워터마크 제거
#Change permissions
permissions.title=권한 변경
permissions.header=권한 변경
permissions.warning=이 권한을 변경할 수 없도록 하기 위해서는 암호를 사용하여 비밀번호 추가 페이지에서 설정하는 것이 좋습니다.
permissions.selectText.1=권한을 변경할 PDF 선택
permissions.selectText.2=설정할 권한
permissions.selectText.3=문서 조립 방지
permissions.selectText.4=콘텐츠 추출 방지
permissions.selectText.5=접근성을 위한 추출 방지
permissions.selectText.6=양식 작성 방지
permissions.selectText.7=수정 방지
permissions.selectText.8=주석 수정 방지
permissions.selectText.9=인쇄 방지
permissions.selectText.10=다른 형식으로 인쇄 방지
permissions.submit=변경
#remove password
removePassword.title=암호 제거
removePassword.header=암호 제거 (복호화)
removePassword.selectText.1=복호화할 PDF 선택
removePassword.selectText.2=암호
removePassword.submit=제거
changeMetadata.title=메타데이터 변경
changeMetadata.header=메타데이터 변경
changeMetadata.selectText.1=변경하려는 변수를 편집해주세요
changeMetadata.selectText.2=모든 메타데이터 삭제
changeMetadata.selectText.3=사용자 정의 메타데이터 표시:
changeMetadata.author=저자:
changeMetadata.creationDate=작성일 (yyyy/MM/dd HH:mm:ss):
changeMetadata.creator=제작자:
changeMetadata.keywords=키워드:
changeMetadata.modDate=수정일 (yyyy/MM/dd HH:mm:ss):
changeMetadata.producer=생성자:
changeMetadata.subject=주제:
changeMetadata.title=제목:
changeMetadata.trapped=잠긴 상태:
changeMetadata.selectText.4=기타 메타데이터:
changeMetadata.selectText.5=사용자 정의 메타데이터 항목 추가
changeMetadata.submit=변경
xlsToPdf.title=Excel to PDF
xlsToPdf.header=Excel을 PDF로 변환
xlsToPdf.selectText.1=변환할 XLS 또는 XLSX Excel 시트 선택
xlsToPdf.convert=변환하기
pdfToPDFA.title=PDF To PDF/A
pdfToPDFA.header=PDF를 PDF/A로 변환
pdfToPDFA.credit=이 서비스는 PDF/A 변환을 위해 OCRmyPDF를 사용합니다.
pdfToPDFA.submit=변환
PDFToWord.title=PDF to Word
PDFToWord.header=PDF를 Word로 변환
PDFToWord.selectText.1=출력 파일 형식
PDFToWord.credit=이 서비스는 파일 변환을 위해 LibreOffice를 사용합니다.
PDFToWord.submit=변환
PDFToPresentation.title=PDF to Presentation
PDFToPresentation.header=PDF를 프레젠테이션으로 변환
PDFToPresentation.selectText.1=출력 파일 형식
PDFToPresentation.credit=이 서비스는 파일 변환을 위해 LibreOffice를 사용합니다.
PDFToPresentation.submit=변환
PDFToText.title=PDF to Text/RTF
PDFToText.header=PDF를 텍스트/RTF로 변환
PDFToText.selectText.1=출력 파일 형식
PDFToText.credit=이 서비스는 파일 변환을 위해 LibreOffice를 사용합니다.
PDFToText.submit=변환
PDFToHTML.title=PDF to HTML
PDFToHTML.header=PDF를 HTML로 변환
PDFToHTML.credit=이 서비스는 파일 변환을 위해 LibreOffice를 사용합니다.
PDFToHTML.submit=변환
PDFToXML.title=PDF to XML
PDFToXML.header=PDF를 XML로 변환
PDFToXML.credit=이 서비스는 파일 변환을 위해 LibreOffice를 사용합니다.
PDFToXML.submit=변환

View File

@@ -12,8 +12,8 @@ genericSubmit=Wyślij
processTimeWarning=Ostrzeżenie: Ten proces może potrwać do minuty, w zależności od rozmiaru pliku
pageOrderPrompt=Kolejność stron (wprowadź listę numerów stron oddzielonych przecinkami) :
goToPage=Idź
true=Prawda
false=Fałsz
true=Tak
false=Nie
unknown=Nieznany
save=Zapisz
close=Zamknij
@@ -54,7 +54,7 @@ home.pdfOrganiser.title=Uporządkuj
home.pdfOrganiser.desc=Usuń/Zmień kolejność stron w dowolnej kolejności
home.addImage.title=Dodaj obraz
home.addImage.desc=Dodaje obraz w wybranym miejscu w dokumencie PDF (komponent w budowie)
home.addImage.desc=Dodaje obraz w wybranym miejscu w dokumencie PDF (moduł w budowie)
home.watermark.title=Dodaj znak wodny
home.watermark.desc=Dodaj niestandardowy znak wodny do dokumentu PDF.
@@ -80,14 +80,14 @@ home.compressPdfs.desc=Kompresuj dokumenty PDF, aby zmniejszyć ich rozmiar.
home.changeMetadata.title=Zmień metadane
home.changeMetadata.desc=Zmień/Usuń/Dodaj metadane w dokumencie PDF
home.fileToPDF.title=Konwertuj plik do dokumentu PDF
home.fileToPDF.desc=Konwertuj prawie każdy plik do dokumentu PDF (DOCX, PNG, XLS, PPT, TXT i więcej)
home.fileToPDF.title=Konwertuj plik do PDF
home.fileToPDF.desc=Konwertuj dowolny plik do dokumentu PDF (DOCX, PNG, XLS, PPT, TXT i więcej)
home.ocr.title=OCR / Zamiana na tekst
home.ocr.desc=OCR skanuje i wykrywa tekst z obrazów w dokumencie PDF i zamienia go na tekst.
home.extractImages.title=Wyodrębnij obrazy
home.extractImages.desc=Wyodrębnia wszystkie obrazy z dokumentu PDF i zapisuje je w formacie zip
home.extractImages.desc=Wyodrębnia wszystkie obrazy z dokumentu PDF i zapisuje je w wybranym formacie
home.pdfToPDFA.title=PDF na PDF/A
home.pdfToPDFA.desc=Konwertuj dokument PDF na PDF/A w celu długoterminowego przechowywania
@@ -123,7 +123,18 @@ home.removeBlanks.title=Usuń puste strony
home.removeBlanks.desc=Wykrywa i usuwa puste strony z dokumentu PDF
home.compare.title=Porównaj
home.compare.desc=Porównuje i pokazuje różnice między 2 dokumentami PDF
home.compare.desc=Porównuje i pokazuje różnice między dwoma dokumentami PDF
home.certSign.title=Podpisz certyfikatem
home.certSign.desc=Podpisz dokument PDF za pomocą certyfikatu/klucza prywatnego (PEM/P12)
home.pageLayout.title=Układ wielu stron
home.pageLayout.desc=Scal wiele stron dokumentu PDF w jedną stronę
home.scalePages.title=Dopasuj rozmiar stron
home.scalePages.desc=Dopasuj rozmiar stron wybranego dokumentu PDF
error.pdfPassword=Dokument PDF jest zabezpieczony hasłem, musisz podać prawidłowe hasło.
downloadPdf=Pobierz PDF
text=Tekst
@@ -131,16 +142,27 @@ font=Czcionka
selectFillter=-- Wybierz --
pageNum=Numer strony
certSign.title=Podpisywanie certyfikatu
certSign.header=Podpisz plik PDF swoim certyfikatem (prace w toku)
certSign.selectPDF=Wybierz plik PDF do podpisania:
pageLayout.title=Układ wielu stron
pageLayout.header=Układ wielu stron
pageLayout.pagesPerSheet=Stron na jednym arkuszu:
pageLayout.submit=Wykonaj
scalePages.title=Dopasuj rozmiar stron
scalePages.header=Dopasuj rozmiar stron
scalePages.pageSize=Rozmiar stron dokumentu:
scalePages.scaleFactor=Poziom powiększenia (przycięcia) stron:
scalePages.submit=Wykonaj
certSign.title=Podpisywanie certyfikatem
certSign.header=Podpisz dokument PDF certyfikatem prywatnym (moduł w budowie)
certSign.selectPDF=Wybierz dokument PDF do podpisania:
certSign.selectKey=Wybierz plik klucza prywatnego (format PKCS#8, może to być .pem lub .der):
certSign.selectCert=Wybierz plik certyfikatu (format X.509, może to być .pem lub .der):
certSign.selectP12=Wybierz plik magazynu kluczy PKCS#12 (.p12 lub .pfx) (opcjonalnie, jeśli jest podany, powinien zawierać klucz prywatny i certyfikat):
certSign.certType=Typ certyfikatu
certSign.password=Wprowadź hasło do magazynu kluczy lub klucza prywatnego (jeśli istnieje):
certSign.showSig=Pokaż podpis
certSign.reason=Powód
certSign.showSig=Wyświetl podpis
certSign.reason=Organizacja
certSign.location=Lokalizacja
certSign.name=Nazwa
certSign.submit=Podpisz PDF
@@ -223,13 +245,13 @@ ocr.submit=Przetwarzaj PDF za pomocą OCR
extractImages.title=Wyodrębnij obrazy
extractImages.header=Wyodrębnij obrazy
extractImages.selectText=Wybierz format obrazu, aby je przekonwertować
extractImages.selectText=Wybierz format obrazu, na który chcesz przekonwertować wyodrębniony obraz.
extractImages.submit=Wyodrębnij
#File to PDF
fileToPDF.title=Plik na PDF
fileToPDF.header=Konwertuj kady plik na dokument PDF
fileToPDF.header=Konwertuj dowolny plik na dokument PDF
fileToPDF.credit=Ta usługa używa LibreOffice i Unoconv do konwersji plików.
fileToPDF.supportedFileTypes=Obsługiwane typy plików powinny być zgodne z poniższymi, jednak pełną zaktualizowaną listę obsługiwanych formatów można znaleźć w dokumentacji LibreOffice
fileToPDF.submit=Konwertuj na PDF
@@ -337,6 +359,9 @@ addPassword.selectText.10=Zablokuj modyfikacje
addPassword.selectText.11=Zablokuj modyfikacje adnotacji
addPassword.selectText.12=Zablokuj drukowanie
addPassword.selectText.13=Zablokuj drukowanie różnych formatów
addPassword.selectText.14=Owner Password
addPassword.selectText.15=Restricts what can be done with the document once it is opened (Not supported by all readers)
addPassword.selectText.16=Restricts the opening of the document itself
addPassword.submit=Zablokuj
#watermark
@@ -346,8 +371,8 @@ watermark.selectText.1=Wybierz dokument PDF, do którego chcesz dodać znak wodn
watermark.selectText.2=Treść znaku wodnego:
watermark.selectText.3=Rozmiar czcionki:
watermark.selectText.4=Obrót (0-360):
watermark.selectText.5=odstępy poziomo (odstęp między każdym znakiem wodnym w poziomie):
watermark.selectText.6=odstępy pionowo (odstęp między każdym znakiem wodnym w pionie):
watermark.selectText.5=Odstęp w poziomie (odstęp między każdym znakiem wodnym w poziomie):
watermark.selectText.6=Odstęp w pionie (odstęp między każdym znakiem wodnym w pionie):
watermark.selectText.7=Nieprzezroczystość (0% - 100%):
watermark.submit=Dodaj znak wodny
@@ -387,14 +412,14 @@ changeMetadata.selectText.1=Edytuj zmienne, które chcesz zmienić
changeMetadata.selectText.2=Usuń wszystkie metadane
changeMetadata.selectText.3=Pokaż niestandardowe metadane:
changeMetadata.author=Autor:
changeMetadata.creationDate=Data utworzenia (dd/MM/yyyy HH:mm:ss):
changeMetadata.creationDate=Data utworzenia (yyyy/MM/dd HH:mm:ss):
changeMetadata.creator=Twórca:
changeMetadata.keywords=Słowa kluczowe:
changeMetadata.modDate=Data modyfikacji (dd/MM/yyyy HH:mm:ss):
changeMetadata.modDate=Data modyfikacji (yyyy/MM/dd HH:mm:ss):
changeMetadata.producer=Producent:
changeMetadata.subject=Temat:
changeMetadata.title=Tytuł:
changeMetadata.trapped=Uwięziony:
changeMetadata.trapped=Zablokowany:
changeMetadata.selectText.4=Inne metadane:
changeMetadata.selectText.5=Dodaj niestandardowy wpis w metadanych
changeMetadata.submit=Zmień

View File

@@ -0,0 +1,486 @@
###########
# Generic #
###########
# the direction that the language is written (ltr =esquerda para a direita, rtl = direita para a esquerda)
language.direction=ltr
pdfPrompt=Selecione PDF(s)
multiPdfPrompt=Selecione PDFs (2+)
multiPdfDropPrompt=Selecione (ou arraste e solte) todos os PDFs necessários
imgPrompt=Selecione a(s) imagem(ns)
genericSubmit=Enviar
processTimeWarning=Aviso: esse processo pode levar até um minuto, dependendo do tamanho do arquivo
pageOrderPrompt=Ordem das páginas (digite uma lista separada por vírgulas de números de página):
goToPage=Ir
true=Verdadeiro
false=Falso
unknown=Desconhecido
save=Salvar
close=Fechar
filesSelected=arquivos selecionados
noFavourites=Nenhum favorito adicionado
bored=Entediado esperando?
#############
# HOME-PAGE #
#############
home.desc=Seu melhor utilitário para as necessidades de PDF.
navbar.convert=Converter
navbar.security=Segurança
navbar.other=Outro
navbar.darkmode=Modo Escuro
navbar.pageOps=Operações de página
home.multiTool.title=Multiferramenta de PDF
home.multiTool.desc=Mesclar, girar, reorganizar e remover páginas
home.merge.title=mesclar
home.merge.desc=Mescle facilmente vários PDFs em um.
home.split.title=Dividir
home.split.desc=Dividir PDFs em vários documentos
home.rotate.title=Girar
home.rotate.desc=Gire facilmente seus PDFs.
home.imageToPdf.title=Imagem para PDF
home.imageToPdf.desc=Converta uma imagem (PNG, JPEG, GIF) em PDF.
home.pdfToImage.title=PDF para imagem
home.pdfToImage.desc=Converta um PDF em uma imagem. (PNG, JPG, GIF)
home.pdfOrganiser.title=Organizar
home.pdfOrganiser.desc=Remova/reorganize as páginas em qualquer ordem
home.addImage.title=Adicionar imagem
home.addImage.desc=Adiciona uma imagem em um local definido no PDF (trabalho em andamento)
home.watermark.title=Adicione uma Marca d'água
home.watermark.desc=Adicione uma marca d'água personalizada ao seu documento PDF.
home.remove-watermark.title=Remover marca d'água
home.remove-watermark.desc=Remova marcas d'água do seu documento PDF.
home.permissions.title=Alterar permissões
home.permissions.desc=Altere as permissões do seu documento PDF
home.removePages.title=Remover
home.removePages.desc=Exclua as páginas indesejadas do seu documento PDF.
home.addPassword.title=Adicionar senha
home.addPassword.desc=Criptografe seu documento PDF com uma senha.
home.removePassword.title=Remover senha
home.removePassword.desc=Remova a proteção por senha do seu documento PDF.
home.compressPdfs.title=Comprimir
home.compressPdfs.desc=Comprima PDFs para reduzir o tamanho do arquivo.
home.changeMetadata.title=Alterar metadados
home.changeMetadata.desc=Alterar/remover/adicionar metadados de um documento PDF
home.fileToPDF.title=Converter arquivo para PDF
home.fileToPDF.desc=Converta praticamente qualquer arquivo em PDF (DOCX, PNG, XLS, PPT, TXT e mais)
home.ocr.title=OCR / Varreduras de limpeza
home.ocr.desc=A limpeza verifica e detecta texto de imagens em um PDF e o adiciona novamente como texto.
home.extractImages.title=Extrair imagens
home.extractImages.desc=Extrai todas as imagens de um PDF e as salva em zip
home.pdfToPDFA.title=PDF para PDF/A
home.pdfToPDFA.desc=Converta PDF para PDF/A para armazenamento de longo prazo
home.PDFToWord.title=PDF para Word
home.PDFToWord.desc=Converter PDF para formatos Word (DOC, DOCX e ODT)
home.PDFToPresentation.title=PDF para apresentação
home.PDFToPresentation.desc=Converter PDF para formatos de apresentação (PPT, PPTX e ODP)
home.PDFToText.title=PDF para Texto/RTF
home.PDFToText.desc=Converter PDF em formato de texto ou RTF
home.PDFToHTML.title=PDF para HTML
home.PDFToHTML.desc=Converter PDF para o formato HTML
home.PDFToXML.title=PDF para XML
home.PDFToXML.desc=Converter PDF para o formato XML
home.ScannerImageSplit.title=Detectar/dividir fotos digitalizadas
home.ScannerImageSplit.desc=Divide várias fotos de dentro de uma foto/PDF
home.sign.title=Sinal
home.sign.desc=Adiciona assinatura ao PDF por desenho, texto ou imagem
home.flatten.title=achatar
home.flatten.desc=Remova todos os elementos e formulários interativos de um PDF
home.repair.title=Reparar
home.repair.desc=Tenta reparar um PDF corrompido/quebrado
home.removeBlanks.title=Remover páginas em branco
home.removeBlanks.desc=Detecta e remove páginas em branco de um documento
home.compare.title=Comparar
home.compare.desc=Compara e mostra as diferenças entre 2 documentos PDF
home.certSign.title=Assinar com certificado
home.certSign.desc=Assina um PDF com um Certificado/Chave (PEM/P12)
home.pageLayout.title=Multi-Page Layout
home.pageLayout.desc=Merge multiple pages of a PDF document into a single page
home.scalePages.title=Adjust page size/scale
home.scalePages.desc=Change the size/scale of page and/or its contents.
error.pdfPassword=The PDF Document is passworded and either the password was not provided or was incorrect
downloadPdf=baixar PDF
text=Texto
font=Fonte
selectFillter=-- Selecione --
pageNum=Número de página
pageLayout.title=Multi Page Layout
pageLayout.header=Multi Page Layout
pageLayout.pagesPerSheet=Pages per sheet:
pageLayout.submit=Submit
scalePages.title=Adjust page-scale
scalePages.header=Adjust page-scale
scalePages.pageSize=Size of a page of the document.
scalePages.scaleFactor=Zoom level (crop) of a page.
scalePages.submit=Submit
certSign.title=Assinatura de certificado
certSign.header=Assine um PDF com seu certificado (Trabalho em andamento)
certSign.selectPDF=Selecione um arquivo PDF para assinatura:
certSign.selectKey=Selecione seu arquivo de chave privada (formato PKCS#8, pode ser .pem ou .der):
certSign.selectCert=Selecione seu arquivo de certificado (formato X.509, pode ser .pem ou .der):
certSign.selectP12=Selecione seu arquivo de armazenamento de chave PKCS#12 (.p12 ou .pfx) (opcional, se fornecido, deve conter sua chave privada e certificado):
certSign.certType=tipo de certificado
certSign.password=Digite seu armazenamento de chave ou senha de chave privada (se houver):
certSign.showSig=Mostrar assinatura
certSign.reason=Razão
certSign.location=Localização
certSign.name=Nome
certSign.submit=Assinar PDF
removeBlanks.title=Remover espaços em branco
removeBlanks.header=Remover páginas em branco
removeBlanks.threshold=Limite:
removeBlanks.thresholdDesc=Limiar para determinar quão branco um pixel branco deve ser
removeBlanks.whitePercent=Porcentagem branca (%):
removeBlanks.whitePercentDesc=Porcentagem da página que deve ser branca para ser removida
removeBlanks.submit=Remover espaços em branco
compare.title=Comparar
compare.header=Comparar PDFs
compare.document.1=Documento 1
compare.document.2=Documento 2
compare.submit=Comparar
sign.title=Sinal
sign.header=Assinar PDFs
sign.upload=Enviar Imagem
sign.draw=Desenhar Assinatura
sign.text=Entrada de texto
sign.clear=Claro
sign.add=Adicionar
repair.title=Reparar
repair.header=Reparar PDFs
repair.submit=Reparar
flatten.title=achatar
flatten.header=Achatar PDFs
flatten.submit=achatar
ScannerImageSplit.selectText.1=Limite de Ângulo:
ScannerImageSplit.selectText.2=Define o ângulo absoluto mínimo necessário para que a imagem seja girada (padrão: 10).
ScannerImageSplit.selectText.3=Tolerância:
ScannerImageSplit.selectText.4=Determina o intervalo de variação de cor em torno da cor de fundo estimada (padrão: 30).
ScannerImageSplit.selectText.5=Área Mínima:
ScannerImageSplit.selectText.6=Define o limite mínimo de área para uma foto (padrão: 10000).
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 da Borda:
ScannerImageSplit.selectText.10=Define o tamanho da borda adicionada e removida para evitar bordas brancas na saída (padrão: 1).
navbar.settings=Configurações
settings.title=Configurações
settings.update=Atualização disponível
settings.appVersion=Versão do aplicativo:
settings.downloadOption.title=Escolha a opção de download (para downloads não compactados de arquivo único):
settings.downloadOption.1=Abrir na mesma janela
settings.downloadOption.2=Abrir em nova janela
settings.downloadOption.3=⇬ Fazer download do arquivo
settings.zipThreshold=Compactar arquivos quando o número de arquivos baixados exceder
#OCR
ocr.title=OCR / Limpeza de digitalização
ocr.header=Varreduras de limpeza / OCR (reconhecimento óptico de caracteres)
ocr.selectText.1=Selecione os idiomas que devem ser detectados no PDF (os listados são os atualmente detectados):
ocr.selectText.2=Produzir arquivo de texto contendo texto OCR ao lado do PDF com OCR
ocr.selectText.3=As páginas corretas foram digitalizadas em um ângulo inclinado girando-as de volta ao lugar
ocr.selectText.4=Limpe a página para que seja menos provável que o OCR encontre o texto no ruído de fundo. (Sem mudança de saída)
ocr.selectText.5=Limpe a página para que seja menos provável que o OCR encontre texto no ruído de fundo, mantendo a limpeza na saída.
ocr.selectText.6=Ignora as páginas que contêm texto interativo, apenas as páginas de OCR que são imagens
ocr.selectText.7=Forçar OCR, irá OCR Todas as páginas removendo todos os elementos de texto originais
ocr.selectText.8=Normal (será um erro se o PDF contiver texto)
ocr.selectText.9=Configurações adicionais
ocr.selectText.10=Modo OCR
ocr.selectText.11=Remova as imagens após o OCR (remove TODAS as imagens, útil apenas se fizer parte da etapa de conversão)
ocr.selectText.12=Tipo de renderização (avançado)
ocr.help=Por favor, leia esta documentação sobre como usar isso para outros idiomas e/ou não usar no docker
ocr.credit=Este serviço usa OCRmyPDF e Tesseract para OCR.
ocr.submit=Processar PDF com OCR
extractImages.title=Extrair Imagens
extractImages.header=Extrair Imagens
extractImages.selectText=Selecione o formato de imagem para converter as imagens extraídas em
extractImages.submit=Extrair
#File to PDF
fileToPDF.title=Arquivo para PDF
fileToPDF.header=Converta qualquer arquivo para PDF
fileToPDF.credit=Este serviço usa LibreOffice e Unoconv para conversão de arquivos.
fileToPDF.supportedFileTypes=Os tipos de arquivo suportados devem incluir o abaixo, no entanto, para obter uma lista atualizada completa de formatos suportados, consulte a documentação do LibreOffice
fileToPDF.submit=Converter para PDF
#compress
compress.title=Comprimir
compress.header=Comprimir PDF
compress.credit=Este serviço usa o Ghostscript para compressão/otimização de PDF.
compress.selectText.1=Modo Manual - De 1 a 4
compress.selectText.2=Nível de otimização:
compress.selectText.3=4 (Péssimo para imagens de texto)
compress.selectText.4=Modo automático - Auto ajusta a qualidade para obter o tamanho exato do PDF
compress.selectText.5=Tamanho esperado do PDF (por exemplo, 25 MB, 10,8 MB, 25 KB)
compress.submit=Comprimir
#Add image
addImage.title=Adicionar imagem
addImage.header=Adicionar imagem ao PDF
addImage.everyPage=Cada página?
addImage.submit=Adicionar imagem
#merge
merge.title=mesclar
merge.header=Mesclar vários PDFs (2+)
merge.submit=mesclar
#pdfOrganiser
pdfOrganiser.title=Organizador de página
pdfOrganiser.header=Organizador de páginas PDF
pdfOrganiser.submit=Reorganizar páginas
#multiTool
multiTool.title=Multiferramenta de PDF
multiTool.header=Multiferramenta de PDF
#pageRemover
pageRemover.title=Removedor de página
pageRemover.header=Removedor de página PDF
pageRemover.pagesToDelete=Páginas a serem excluídas (digite uma lista separada por vírgulas de números de página):
pageRemover.submit=Excluir páginas
#rotate
rotate.title=Girar PDF
rotate.header=Girar PDF
rotate.selectAngle=Selecione o ângulo de rotação (em múltiplos de 90 graus):
rotate.submit=Girar
#merge
split.title=PDF dividido
split.header=PDF dividido
split.desc.1=Os números que você selecionar são o número da página na qual você deseja fazer uma divisão
split.desc.2=Assim, selecionar 1,3,7-8 dividiria um documento de 10 páginas em 6 PDFS separados com:
split.desc.3=Documento nº 1: página 1
split.desc.4=Documento nº 2: páginas 2 e 3
split.desc.5=Documento nº 3: Página 4, 5 e 6
split.desc.6=Documento nº 4: página 7
split.desc.7=Documento nº 5: página 8
split.desc.8=Documento nº 6: páginas 9 e 10
split.splitPages=Digite as páginas para dividir:
split.submit=Dividir
#merge
imageToPDF.title=Imagem para PDF
imageToPDF.header=Imagem para PDF
imageToPDF.submit=Converter
imageToPDF.selectText.1=Esticar para caber
imageToPDF.selectText.2=Girar PDF automaticamente
imageToPDF.selectText.3=Lógica de vários arquivos (Ativado apenas se estiver trabalhando com várias imagens)
imageToPDF.selectText.4=Mesclar em um único PDF
imageToPDF.selectText.5=Converter em PDFs separados
#pdfToImage
pdfToImage.title=PDF para imagem
pdfToImage.header=PDF para imagem
pdfToImage.selectText=Formato de imagem
pdfToImage.singleOrMultiple=Tipo de resultado de imagem
pdfToImage.single=Imagem grande única
pdfToImage.multi=Imagens múltiplas
pdfToImage.colorType=tipo de cor
pdfToImage.color=Cor
pdfToImage.grey=Escala de cinza
pdfToImage.blackwhite=Preto e branco (pode perder dados!)
pdfToImage.submit=Converter
#addPassword
addPassword.title=Adicionar senha
addPassword.header=Adicionar senha (Criptografar)
addPassword.selectText.1=Selecione PDF para criptografar
addPassword.selectText.2=Senha
addPassword.selectText.3=Comprimento da chave de criptografia
addPassword.selectText.4=Valores mais altos são mais fortes, mas valores mais baixos têm melhor compatibilidade.
addPassword.selectText.5=Permissões para definir
addPassword.selectText.6=Impedir a montagem do documento
addPassword.selectText.7=Impedir a extração de conteúdo
addPassword.selectText.8=Impedir a extração para acessibilidade
addPassword.selectText.9=Impedir o preenchimento do formulário
addPassword.selectText.10=Impedir modificação
addPassword.selectText.11=Impedir a modificação da anotação
addPassword.selectText.12=Impedir a impressão
addPassword.selectText.13=Impedir a impressão de formatos diferentes
addPassword.selectText.14=Owner Password
addPassword.selectText.15=Restricts what can be done with the document once it is opened (Not supported by all readers)
addPassword.selectText.16=Restricts the opening of the document itself
addPassword.submit=criptografar
#watermark
watermark.title=Adicione uma Marca d'água
watermark.header=Adicione uma Marca d'água
watermark.selectText.1=Selecione PDF para adicionar marca d'água a:
watermark.selectText.2=Texto da marca d'água:
watermark.selectText.3=Tamanho da fonte:
watermark.selectText.4=Rotação (0-360):
watermark.selectText.5=widthSpacer (espaço entre cada marca d'água horizontalmente):
watermark.selectText.6=heightSpacer (espaço entre cada marca d'água verticalmente):
watermark.selectText.7=Opacidade (0% - 100%):
watermark.submit=Adicione uma Marca d'água
#remove-watermark
remove-watermark.title=Remover marca d'água
remove-watermark.header=Remover marca d'água
remove-watermark.selectText.1=Selecione PDF para remover a marca d'água de:
remove-watermark.selectText.2=Texto da marca d'água:
remove-watermark.submit=Remover marca d'água
#Change permissions
permissions.title=Alterar permissões
permissions.header=Alterar permissões
permissions.warning=Aviso para que essas permissões sejam inalteráveis, é recomendável defini-las com uma senha por meio da página adicionar senha
permissions.selectText.1=Selecione o PDF para alterar as permissões
permissions.selectText.2=Permissões para definir
permissions.selectText.3=Impedir a montagem do documento
permissions.selectText.4=Impedir a extração de conteúdo
permissions.selectText.5=Impedir extração para acessibilidade
permissions.selectText.6=Impedir o preenchimento do formulário
permissions.selectText.7=Impedir modificações
permissions.selectText.8=Impedir a modificação da anotação
permissions.selectText.9=Impedir a impressão
permissions.selectText.10=Impedir a impressão de formatos diferentes
permissions.submit=Mudar
#remove password
removePassword.title=Remover senha
removePassword.header=Remover senha (Descriptografar)
removePassword.selectText.1=Selecione PDF para descriptografar
removePassword.selectText.2=Senha
removePassword.submit=Remover
changeMetadata.title=Alterar metadados
changeMetadata.header=Alterar metadados
changeMetadata.selectText.1=Edite as variáveis que deseja alterar
changeMetadata.selectText.2=Excluir todos os metadados
changeMetadata.selectText.3=Mostrar metadados personalizados:
changeMetadata.author=Autor:
changeMetadata.creationDate=Data de Criação (aaaa/MM/dd HH:mm:ss):
changeMetadata.creator=O Criador:
changeMetadata.keywords=Palavras-chave:
changeMetadata.modDate=Data de modificação (aaaa/MM/dd HH:mm:ss):
changeMetadata.producer=Produtor:
changeMetadata.subject=Assunto:
changeMetadata.title=Título:
changeMetadata.trapped=Encurralado:
changeMetadata.selectText.4=Outros metadados:
changeMetadata.selectText.5=Adicionar entrada de metadados personalizados
changeMetadata.submit=Mudar
xlsToPdf.title=Excel para PDF
xlsToPdf.header=Excel para PDF
xlsToPdf.selectText.1=Selecione planilha Excel XLS ou XLSX para converter
xlsToPdf.convert=converter
pdfToPDFA.title=PDF para PDF/A
pdfToPDFA.header=PDF para PDF/A
pdfToPDFA.credit=Este serviço usa OCRmyPDF para conversão de PDF/A
pdfToPDFA.submit=Converter
PDFToWord.title=PDF para Word
PDFToWord.header=PDF para Word
PDFToWord.selectText.1=Formato do arquivo de saída
PDFToWord.credit=Este serviço usa o LibreOffice para conversão de arquivos.
PDFToWord.submit=Converter
PDFToPresentation.title=PDF para apresentação
PDFToPresentation.header=PDF para apresentação
PDFToPresentation.selectText.1=Formato do arquivo de saída
PDFToPresentation.credit=Este serviço usa o LibreOffice para conversão de arquivos.
PDFToPresentation.submit=Converter
PDFToText.title=PDF para Texto/RTF
PDFToText.header=PDF para Texto/RTF
PDFToText.selectText.1=Formato do arquivo de saída
PDFToText.credit=Este serviço usa o LibreOffice para conversão de arquivos.
PDFToText.submit=Converter
PDFToHTML.title=PDF para HTML
PDFToHTML.header=PDF para HTML
PDFToHTML.credit=Este serviço usa o LibreOffice para conversão de arquivos.
PDFToHTML.submit=Converter
PDFToXML.title=PDF para XML
PDFToXML.header=PDF para XML
PDFToXML.credit=Este serviço usa o LibreOffice para conversão de arquivos.
PDFToXML.submit=Converter

View File

@@ -0,0 +1,439 @@
###########
# Generic #
###########
# the direction that the language is written (ltr = left to right, rtl = right to left)
language.direction=ltr
pdfPrompt=Selectează fișiere PDF
multiPdfPrompt=Selectează mai multe fișiere PDF (2+)
multiPdfDropPrompt=Selectează (sau trage și plasează) toate fișierele PDF de care ai nevoie
imgPrompt=Selectează imagini
genericSubmit=Trimite
processTimeWarning=Avertisment: Acest proces poate dura până la un minut în funcție de dimensiunea fișierului
pageOrderPrompt=Ordinea paginilor (Introdu o listă separată prin virgulă de numere de pagină):
goToPage=Mergi la pagină
true=Adevărat
false=Fals
unknown=Necunoscut
save=Salvează
close=Închide
filesSelected=fișiere selectate
noFavourites=Niciun favorit adăugat
bored=Plictisit așteptând?
#############
# HOME-PAGE #
#############
home.desc=Un singur punct de oprire găzduit local pentru toate nevoile tale legate de fișiere PDF.
navbar.convert=Converteste
navbar.security=Securitate
navbar.other=Altele
navbar.darkmode=Mod întunecat
navbar.pageOps=Operații pe pagină
home.multiTool.title=Instrument multiplu PDF
home.multiTool.desc=Unifică, rotește, rearanjează și elimină pagini
home.merge.title=Unifică
home.merge.desc=Unifică cu ușurință mai multe fișiere PDF într-unul singur.
home.split.title=Desparte
home.split.desc=Desparte fișierele PDF în mai multe documente.
home.rotate.title=Rotește
home.rotate.desc=Rotește cu ușurință fișierele PDF.
home.imageToPdf.title=Imagine în PDF
home.imageToPdf.desc=Convertește o imagine (PNG, JPEG, GIF) în PDF.
home.pdfToImage.title=PDF în Imagine
home.pdfToImage.desc=Convertește un fișier PDF în imagine (PNG, JPEG, GIF).
home.pdfOrganiser.title=Organizează
home.pdfOrganiser.desc=Elimină/rearanjează pagini în orice ordine
home.addImage.title=Adaugă imagine
home.addImage.desc=Adaugă o imagine într-o locație specifică pe PDF (în curs de dezvoltare)
home.watermark.title=Adaugă Filigran
home.watermark.desc=Adaugă un filigran personalizat la documentul PDF.
home.remove-watermark.title=Elimină Filigran
home.remove-watermark.desc=Elimină filigranele din documentul PDF.
home.permissions.title=Schimbă permisiuni
home.permissions.desc=Schimbă permisiunile documentului PDF
home.removePages.title=Elimină
home.removePages.desc=Șterge paginile nedorite din documentul PDF.
home.addPassword.title=Adaugă Parolă
home.addPassword.desc=Criptează documentul PDF cu o parolă.
home.removePassword.title=Elimină Parola
home.removePassword.desc=Elimină protecția cu parolă din documentul PDF.
home.compressPdfs.title=Comprimă
home.compressPdfs.desc=Comprimă fișierele PDF pentru a reduce dimensiunea lor.
home.changeMetadata.title=Schimbă Metadatele
home.changeMetadata.desc=Schimbă/Elimină/Adaugă metadate într-un document PDF.
home.fileToPDF.title=Convertește fișierul în PDF
home.fileToPDF.desc=Convertește aproape orice fișier în format PDF (DOCX, PNG, XLS, PPT, TXT și altele).
home.ocr.title=OCR / Curățare scanări
home.ocr.desc=Curăță scanările și detectează textul din imaginile dintr-un PDF și îl adaugă ca text.
home.extractImages.title=Extrage Imagini
home.extractImages.desc=Extrage toate imaginile dintr-un PDF și le salvează într-un fișier zip.
home.pdfToPDFA.title=PDF în PDF/A
home.pdfToPDFA.desc=Convertește un document PDF în format PDF/A pentru stocare pe termen lung.
home.PDFToWord.title=PDF în Word
home.PDFToWord.desc=Convertește un document PDF în formate Word (DOC, DOCX și ODT).
home.PDFToPresentation.title=PDF în Prezentare
home.PDFToPresentation.desc=Convertește un document PDF în formate de prezentare (PPT, PPTX și ODP).
home.PDFToText.title=PDF în Text/RTF
home.PDFToText.desc=Convertește un document PDF în format Text sau RTF.
home.PDFToHTML.title=PDF în HTML
home.PDFToHTML.desc=Convertește un document PDF în format HTML.
home.PDFToXML.title=PDF în XML
home.PDFToXML.desc=Convertește un document PDF în format XML.
home.ScannerImageSplit.title=Detectează/Împarte poze scanate
home.ScannerImageSplit.desc=Împarte mai multe poze dintr-o poză/PDF.
home.sign.title=Semnează
home.sign.desc=Adaugă o semnătură la documentul PDF prin desenare, text sau imagine.
home.flatten.title=Nivelare
home.flatten.desc=Elimină toate elementele interactive și formularele dintr-un PDF.
home.repair.title=Repară
home.repair.desc=Încearcă să repare un document PDF corupt/defect.
home.removeBlanks.title=Elimină pagini goale
home.removeBlanks.desc=Detectează și elimină paginile goale dintr-un document.
home.compare.title=Compară
home.compare.desc=Compară și arată diferențele dintre 2 documente PDF.
home.certSign.title=Semnare cu certificat
home.certSign.desc=Semnează un PDF cu un certificat/cheie (PEM/P12)
home.pageLayout.title=Multi-Page Layout
home.pageLayout.desc=Merge multiple pages of a PDF document into a single page
home.scalePages.title=Adjust page size/scale
home.scalePages.desc=Change the size/scale of page and/or its contents.
error.pdfPassword=The PDF Document is passworded and either the password was not provided or was incorrect
downloadPdf=Descarcă PDF
text=Text
font=Font
selectFillter=-- Selectează --
pageNum=Numărul paginii
pageLayout.title=Multi Page Layout
pageLayout.header=Multi Page Layout
pageLayout.pagesPerSheet=Pages per sheet:
pageLayout.submit=Submit
scalePages.title=Adjust page-scale
scalePages.header=Adjust page-scale
scalePages.pageSize=Size of a page of the document.
scalePages.scaleFactor=Zoom level (crop) of a page.
scalePages.submit=Submit
certSign.title = Semnare certificat
certSign.header = Semnează un fișier PDF cu certificatul tău (În curs de desfășurare)
certSign.selectPDF = Selectează un fișier PDF pentru semnare:
certSign.selectKey = Selectează fișierul cheie privată (format PKCS#8, poate fi .pem sau .der):
certSign.selectCert = Selectează fișierul de certificat (format X.509, poate fi .pem sau .der):
certSign.selectP12 = Selectează fișierul de stocare cheie PKCS#12 (.p12 sau .pfx) (Opțional, dacă este furnizat, ar trebui să conțină cheia privată și certificatul tău):
certSign.certType = Tipul certificatului
certSign.password = Introdu parola pentru stocarea cheie sau cheia privată (dacă există):
certSign.showSig = Afișează semnătura
certSign.reason = Motivul
certSign.location = Locația
certSign.name = Numele
certSign.submit = Semnează PDF
removeBlanks.title=Elimină pagini goale
removeBlanks.header=Elimină pagini goale
removeBlanks.threshold=Prag:
removeBlanks.thresholdDesc=Prag pentru determinarea cât de alb trebuie să fie un pixel alb
removeBlanks.whitePercent=Procent alb (%):
removeBlanks.whitePercentDesc=Procentul paginii care trebuie să fie alb pentru a fi eliminată
removeBlanks.submit=Elimină pagini goale
compare.title=Compară
compare.header=Compară PDF-uri
compare.document.1=Document 1
compare.document.2=Document 2
compare.submit=Compară
sign.title=Semnează
sign.header=Semnează documente PDF
sign.upload=Încarcă Imaginea
sign.draw=Desenează Semnătura
sign.text=Introdu Textul
sign.clear=Curăță
sign.add=Adaugă
repair.title=Repară
repair.header=Repară documente PDF
repair.submit=Repară
flatten.title=Nivelare
flatten.header=Nivelează documente PDF
flatten.submit=Nivelează
ScannerImageSplit.selectText.1=Prag unghi:
ScannerImageSplit.selectText.2=Stabilește unghiul absolut minim necesar pentru ca imaginea să fie rotită (implicit: 5).
ScannerImageSplit.selectText.3=Toleranță:
ScannerImageSplit.selectText.4=Determină intervalul de variație a culorii în jurul culorii de fundal estimate (implicit: 20).
ScannerImageSplit.selectText.5=Arie minimă:
ScannerImageSplit.selectText.6=Stabilește pragul minim de arie pentru o fotografie (implicit: 8000).
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).
navbar.settings=Setări
settings.title=Setări
settings.update=Actualizare disponibilă
settings.appVersion=Versiune aplicație:
settings.downloadOption.title=Alege opțiunea de descărcare (pentru descărcarea unui singur fișier non-zip):
settings.downloadOption.1=Deschide în aceeași fereastră
settings.downloadOption.2=Deschide într-o fereastră nouă
settings.downloadOption.3=Descarcă fișierul
settings.zipThreshold=Împachetează fișierele când numărul de fișiere descărcate depășește
#OCR
ocr.title=OCR / Curățare scanare
ocr.header=Curățare scanări / OCR (Recunoaștere optică a caracterelor)
ocr.selectText.1=Selectați limbile care trebuie detectate în PDF (Cele listate sunt cele detectate în prezent):
ocr.selectText.2=Produceți un fișier text care conține textul OCR împreună cu PDF-ul OCR
ocr.selectText.3=Corectați paginile care au fost scanate în unghi înclinat prin rotirea lor în poziție corectă
ocr.selectText.4=Curățați pagina astfel încât să fie mai puțin probabil ca OCR-ul să găsească text în zgomotul de fundal. (Nu se schimbă rezultatul)
ocr.selectText.5=Curățați pagina astfel încât să fie mai puțin probabil ca OCR-ul să găsească text în zgomotul de fundal, menține curățarea în rezultat.
ocr.selectText.6=Ignorați paginile care conțin text interactiv, OCR-ul se aplică doar paginilor care sunt imagini
ocr.selectText.7=Forțează OCR-ul, va aplica OCR pe fiecare pagină, înlăturând toate elementele de text originale
ocr.selectText.8=Normal (Va genera eroare dacă PDF-ul conține text)
ocr.selectText.9=Setări suplimentare
ocr.selectText.10=Mod OCR
ocr.selectText.11=Elimină imaginile după OCR (Elimină TOATE imaginile, util doar în etapa de conversie)
ocr.selectText.12=Tip de redare (Avansat)
ocr.help=Citiți documentația pentru a afla cum să utilizați acest serviciu pentru alte limbi și/sau în afara mediului Docker
ocr.credit=Acest serviciu utilizează OCRmyPDF și Tesseract pentru OCR.
ocr.submit=Procesează PDF-ul cu OCR
extractImages.title=Extrage Imagini
extractImages.header=Extrage Imagini
extractImages.selectText=Selectați formatul imaginii în care să se convertească imaginile extrase
extractImages.submit=Extrage
#File to PDF
fileToPDF.title=Fișier în PDF
fileToPDF.header=Convertiți orice fișier în PDF
fileToPDF.credit=Acest serviciu utilizează LibreOffice și Unoconv pentru conversia fișierelor.
fileToPDF.supportedFileTypes=Tipurile de fișiere suportate ar trebui să includă cele de mai jos. Pentru o listă completă și actualizată a formatelor suportate, consultați documentația LibreOffice.
fileToPDF.submit=Convertiți în PDF
#compress
compress.title=Comprimare
compress.header=Comprimare PDF
compress.credit=Acest serviciu utilizează OCRmyPDF pentru comprimarea/optimalizarea PDF-urilor.
compress.selectText.1=Nivel de optimizare:
compress.selectText.2=0 (Fără optimizare)
compress.selectText.3=1 (Implicit, optimizare fără pierdere)
compress.selectText.4=2 (Optimizare cu pierdere)
compress.selectText.5=3 (Optimizare cu pierdere, mai agresivă)
compress.selectText.6=Activare vizualizare rapidă pe web (linearizare PDF)
compress.selectText.7=Activare codificare JBIG2 cu pierdere
compress.submit=Comprimare
#Add image
addImage.title=Adăugare imagine
addImage.header=Adăugare imagine în PDF
addImage.everyPage=Pe fiecare pagină?
addImage.submit=Adăugare imagine
#merge
merge.title=Unire
merge.header=Unirea mai multor PDF-uri (2+)
merge.submit=Unire
#pdfOrganiser
pdfOrganiser.title=Organizator de pagini
pdfOrganiser.header=Organizator de pagini PDF
pdfOrganiser.submit=Rearanjați paginile
#multiTool
multiTool.title=Instrument PDF multiplu
multiTool.header=Instrument PDF multiplu
#pageRemover
pageRemover.title=Înlăturare pagini
pageRemover.header=Înlăturare pagini din PDF
pageRemover.pagesToDelete=Pagini de șters (Introduceți o listă de numere de pagini separate prin virgulă):
pageRemover.submit=Ștergere pagini
#rotate
rotate.title=Rotește PDF
rotate.header=Rotește PDF
rotate.selectAngle=Selectați un unghi de rotație (în multiplicate de 90 de grade):
rotate.submit=Rotește
#merge
split.title=Împarte PDF
split.header=Împarte PDF
split.desc.1=Numerele pe care le selectați reprezintă numărul paginii pe care doriți să o împărțiți
split.desc.2=Prin urmare, selectând 1,3,7-8, un document cu 10 pagini va fi împărțit în 6 PDF-uri separate, astfel:
split.desc.3=Documentul #1: Pagina 1
split.desc.4=Documentul #2: Paginile 2 și 3
split.desc.5=Documentul #3: Paginile 4, 5 și 6
split.desc.6=Documentul #4: Pagina 7
split.desc.7=Documentul #5: Pagina 8
split.desc.8=Documentul #6: Paginile 9 și 10
split.splitPages=Introduceți paginile pe care să le împărțiți:
split.submit=Împarte
#merge
imageToPDF.title=Imagine în PDF
imageToPDF.header=Imagine în PDF
imageToPDF.submit=Convertă
imageToPDF.selectText.1=Redimensionare pentru a se potrivi
imageToPDF.selectText.2=Rotire automată a PDF-ului
imageToPDF.selectText.3=Logica pentru mai multe fișiere (activată numai dacă se lucrează cu mai multe imagini)
imageToPDF.selectText.4=Unifică într-un singur PDF
imageToPDF.selectText.5=Convertă în PDF-uri separate
#pdfToImage
pdfToImage.title=PDF în Imagine
pdfToImage.header=PDF în Imagine
pdfToImage.selectText=Format imagine
pdfToImage.singleOrMultiple=Tip rezultat imagine
pdfToImage.single=O singură imagine mare
pdfToImage.multi=Mai multe imagini
pdfToImage.colorType=Tip culoare
pdfToImage.color=Culoare
pdfToImage.grey=Scală de gri
pdfToImage.blackwhite=Alb și negru (Poate pierde date!)
pdfToImage.submit=Convertă
#addPassword
addPassword.title=Adăugați parolă
addPassword.header=Adăugați o parolă (Criptare)
addPassword.selectText.1=Selectați PDF-ul pentru criptare
addPassword.selectText.2=Parolă
addPassword.selectText.3=Lungime cheie de criptare
addPassword.selectText.4=Valori mai mari sunt mai puternice, dar valorile mai mici au o compatibilitate mai bună.
addPassword.selectText.5=Permisiuni de setare
addPassword.selectText.6=Preveniți asamblarea documentului
addPassword.selectText.7=Preveniți extragerea conținutului
addPassword.selectText.8=Preveniți extragerea pentru accesibilitate
addPassword.selectText.9=Preveniți completarea formularului
addPassword.selectText.10=Preveniți modificarea
addPassword.selectText.11=Preveniți modificarea adnotărilor
addPassword.selectText.12=Preveniți tipărirea
addPassword.selectText.13=Preveniți tipărirea în formate diferite
addPassword.selectText.14=Owner Password
addPassword.selectText.15=Restricts what can be done with the document once it is opened (Not supported by all readers)
addPassword.selectText.16=Restricts the opening of the document itself
addPassword.submit=Criptare
#watermark
watermark.title=Adăugați Filigran
watermark.header=Adăugați Filigran
watermark.selectText.1=Selectați PDF-ul la care să adăugați filigranul:
watermark.selectText.2=Textul Filigranului:
watermark.selectText.3=Mărimea fontului:
watermark.selectText.4=Rotire (0-360):
watermark.selectText.5=widthSpacer (Spațiu între fiecare filigran pe orizontală):
watermark.selectText.6=heightSpacer (Spațiu între fiecare filigran pe verticală):
watermark.selectText.7=Opacitate (0% - 100%):
watermark.submit=Adăugați Filigran
#remove-watermark
remove-watermark.title=Eliminați Filigran
remove-watermark.header=Eliminați Filigran
remove-watermark.selectText.1=Selectați PDF-ul de la care să eliminați filigranul:
remove-watermark.selectText.2=Textul Filigranului:
remove-watermark.submit=Eliminați Filigran
#Change permissions
permissions.title=Schimbați Permisiunile
permissions.header=Schimbați Permisiunile
permissions.warning=Pentru a face aceste permisiuni neschimbabile, se recomandă să le setați cu o parolă prin intermediul paginii de adăugare a parolei
permissions.selectText.1=Selectați PDF-ul pentru a schimba permisiunile
permissions.selectText.2=Permisiunile de setat
permissions.selectText.3=Preveniți asamblarea documentului
permissions.selectText.4=Preveniți extragerea conținutului
permissions.selectText.5=Preveniți extragerea pentru accesibilitate
permissions.selectText.6=Preveniți completarea formularului
permissions.selectText.7=Preveniți modificarea
permissions.selectText.8=Preveniți modificarea adnotărilor
permissions.selectText.9=Preveniți tipărirea
permissions.selectText.10=Preveniți tipărirea în formate diferite
permissions.submit=Schimbare
#remove password
removePassword.title=Eliminați parola
removePassword.header=Eliminați parola (Decodificați)
removePassword.selectText.1=Selectați PDF-ul pentru decodificare
removePassword.selectText.2=Parolă
removePassword.submit=Eliminați
changeMetadata.title=Schimbați Metadatele
changeMetadata.header=Schimbați Metadatele
changeMetadata.selectText.1=Vă rugăm să editați variabilele pe care doriți să le schimbați
changeMetadata.selectText.2=Ștergeți toate metadatele
changeMetadata.selectText.3=Afișați Metadatele Personalizate:
changeMetadata.author=Autor:
changeMetadata.creationDate=Data creării (yyyy/MM/dd HH:mm:ss):
changeMetadata.creator=Creator:
changeMetadata.keywords=Cuvinte cheie:
changeMetadata.modDate=Data modificării (yyyy/MM/dd HH:mm:ss):
changeMetadata.producer=Producător:
changeMetadata.subject=Subiect:
changeMetadata.title=Titlu:
changeMetadata.trapped=Blocat:
changeMetadata.selectText.4=Alte Metadate:
changeMetadata.selectText.5=Adăugați Intrare Metadate Personalizate
changeMetadata.submit=Schimbare
xlsToPdf.title=Excel to PDF
xlsToPdf.header=Excel to PDF
xlsToPdf.selectText.1=Selectați fișierul Excel XLS sau XLSX pentru a converti
xlsToPdf.convert=convert
pdfToPDFA.title=PDF către PDF/A
pdfToPDFA.header=PDF către PDF/A
pdfToPDFA.credit=Acest serviciu utilizează OCRmyPDF pentru conversia în PDF/A
pdfToPDFA.submit=Convert
PDFToWord.title=PDF către Word
PDFToWord.header=PDF către Word
PDFToWord.selectText.1=Format fișier de ieșire
PDFToWord.credit=Acest serviciu utilizează LibreOffice pentru conversia fișierului.
PDFToWord.submit=Convert
PDFToPresentation.title=PDF către Prezentare
PDFToPresentation.header=PDF către Prezentare
PDFToPresentation.selectText.1=Format fișier de ieșire
PDFToPresentation.credit=Acest serviciu utilizează LibreOffice pentru conversia fișierului.
PDFToPresentation.submit=Convert
PDFToText.title=PDF către Text/RTF
PDFToText.header=PDF către Text/RTF
PDFToText.selectText.1=Format fișier de ieșire
PDFToText.credit=Acest serviciu utilizează LibreOffice pentru conversia fișierului.
PDFToText.submit=Convert
PDFToHTML.title=PDF către HTML
PDFToHTML.header=PDF către HTML
PDFToHTML.credit=Acest serviciu utilizează LibreOffice pentru conversia fișierului.
PDFToHTML.submit=Convert
PDFToXML.title=PDF către XML
PDFToXML.header=PDF către XML
PDFToXML.credit=Acest serviciu utilizează LibreOffice pentru conversia fișierului.
PDFToXML.submit=Convert

View File

@@ -125,12 +125,34 @@ home.removeBlanks.desc=Обнаруживает и удаляет пустые
home.compare.title=Сравнение
home.compare.desc=Сравнивает и показывает различия между двумя PDF-документами
home.certSign.title=Sign with Certificate
home.certSign.desc=Signs a PDF with a Certificate/Key (PEM/P12)
home.pageLayout.title=Multi-Page Layout
home.pageLayout.desc=Merge multiple pages of a PDF document into a single page
home.scalePages.title=Adjust page size/scale
home.scalePages.desc=Change the size/scale of page and/or its contents.
error.pdfPassword=The PDF Document is passworded and either the password was not provided or was incorrect
downloadPdf=Скачать PDF
text=Текст
font=Шрифт
selectFillter=-- Выбрать --
pageNum=номер страницы
pageLayout.title=Multi Page Layout
pageLayout.header=Multi Page Layout
pageLayout.pagesPerSheet=Pages per sheet:
pageLayout.submit=Submit
scalePages.title=Adjust page-scale
scalePages.header=Adjust page-scale
scalePages.pageSize=Size of a page of the document.
scalePages.scaleFactor=Zoom level (crop) of a page.
scalePages.submit=Submit
certSign.title=Подписание сертификата
certSign.header=Подпишите PDF своим сертификатом (работа в процессе)
certSign.selectPDF=Выберите файл PDF для подписи:
@@ -338,6 +360,9 @@ addPassword.selectText.10=Предотвратить модификацию
addPassword.selectText.11=Запретить модификацию аннотаций
addPassword.selectText.12=Запретить печать
addPassword.selectText.13=Запретить печать разных форматов
addPassword.selectText.14=Owner Password
addPassword.selectText.15=Restricts what can be done with the document once it is opened (Not supported by all readers)
addPassword.selectText.16=Restricts the opening of the document itself
addPassword.submit=Шифровать
#watermark

View File

@@ -125,12 +125,34 @@ home.removeBlanks.desc=Känner av och tar bort tomma sidor från ett dokument
home.compare.title=Jämför
home.compare.desc=Jämför och visar skillnaderna mellan 2 PDF-dokument
home.certSign.title=Sign with Certificate
home.certSign.desc=Signs a PDF with a Certificate/Key (PEM/P12)
home.pageLayout.title=Multi-Page Layout
home.pageLayout.desc=Merge multiple pages of a PDF document into a single page
home.scalePages.title=Adjust page size/scale
home.scalePages.desc=Change the size/scale of page and/or its contents.
error.pdfPassword=The PDF Document is passworded and either the password was not provided or was incorrect
downloadPdf=Ladda ner PDF
text=Text
font=Teckensnitt
selectFillter=-- Välj --
pageNum=Sidnummer
pageLayout.title=Multi Page Layout
pageLayout.header=Multi Page Layout
pageLayout.pagesPerSheet=Pages per sheet:
pageLayout.submit=Submit
scalePages.title=Adjust page-scale
scalePages.header=Adjust page-scale
scalePages.pageSize=Size of a page of the document.
scalePages.scaleFactor=Zoom level (crop) of a page.
scalePages.submit=Submit
certSign.title=Certifikatsignering
certSign.header=Skriv under en PDF med ditt certifikat (Pågående arbete)
certSign.selectPDF=Välj en PDF-fil för signering:
@@ -338,6 +360,9 @@ addPassword.selectText.10=Förhindra modifiering
addPassword.selectText.11=Förhindra anteckningsändring
addPassword.selectText.12=Förhindra utskrift
addPassword.selectText.13=Förhindra utskrift av olika format
addPassword.selectText.14=Owner Password
addPassword.selectText.15=Restricts what can be done with the document once it is opened (Not supported by all readers)
addPassword.selectText.16=Restricts the opening of the document itself
addPassword.submit=Kryptera
#vattenstämpel

View File

@@ -125,12 +125,34 @@ home.removeBlanks.desc=\u68C0\u6D4B\u5E76\u5220\u9664\u6587\u6863\u4E2D\u7684\u7
home.compare.title=\u6BD4\u8F83
home.compare.desc=\u6BD4\u8F83\u5E76\u663E\u793A 2 \u4E2A PDF \u6587\u6863\u4E4B\u95F4\u7684\u5DEE\u5F02
home.certSign.title=Sign with Certificate
home.certSign.desc=Signs a PDF with a Certificate/Key (PEM/P12)
home.pageLayout.title=Multi-Page Layout
home.pageLayout.desc=Merge multiple pages of a PDF document into a single page
home.scalePages.title=Adjust page size/scale
home.scalePages.desc=Change the size/scale of page and/or its contents.
error.pdfPassword=The PDF Document is passworded and either the password was not provided or was incorrect
downloadPdf=\u4E0B\u8F7DPDF
text=\u6587\u672C
font=\u5B57\u4F53
selectFillter=-- 閫夋嫨--
pageNum=椤电爜
pageLayout.title=Multi Page Layout
pageLayout.header=Multi Page Layout
pageLayout.pagesPerSheet=Pages per sheet:
pageLayout.submit=Submit
scalePages.title=Adjust page-scale
scalePages.header=Adjust page-scale
scalePages.pageSize=Size of a page of the document.
scalePages.scaleFactor=Zoom level (crop) of a page.
scalePages.submit=Submit
certSign.title=璇佷功绛惧悕
certSign.header=浣跨敤鎮ㄧ殑璇佷功绛剧讲 PDF锛堣繘琛屼腑锛<E88591>
certSign.selectPDF=閫夋嫨瑕佺<EFBFBD>鍚嶇殑 PDF 鏂囦欢锛<E6ACA2>
@@ -336,7 +358,10 @@ addPassword.selectText.9=防止填写表格
addPassword.selectText.10=闃叉<EFBFBD><EFBFBD>
addPassword.selectText.11=闃叉<EFBFBD><EFBFBD>敼娉ㄩ噴
addPassword.selectText.12=闃叉<EFBFBD>鎵撳嵃
addPassword.selectText.13=防止打印不同的格式
addPassword.selectText.13=闃叉<EFBFBD>鎵撳嵃涓嶅悓鐨勬牸寮
addPassword.selectText.14=Owner Password
addPassword.selectText.15=Restricts what can be done with the document once it is opened (Not supported by all readers)
addPassword.selectText.16=Restricts the opening of the document itself<6C>
addPassword.submit=鍔犲瘑
#watermark

View File

@@ -0,0 +1,94 @@
#errorContainer {
margin: 20px; /* adjust this value as needed */
}
#helpModalDialog {
width: 90%;
max-width: 800px;
}
#helpModal h1 {
text-align: center;
margin-top: 10%;
}
#helpModal p {
text-align: center;
margin-top: 2em;
}
#helpModal .button:hover {
background-color: #005b7f;
}
#helpModal .features-container {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(21rem, 3fr));
gap: 25px 30px;
}
#helpModal .feature-card {
border: 1px solid rgba(0, 0, 0, .125);
border-radius: 0.25rem;
padding: 1.25rem;
display: flex;
flex-direction: column;
align-items: flex-start;
}
#helpModal .feature-card .card-text {
flex: 1;
}
#support-section {
background-color: #f9f9f9;
padding: 4rem;
margin-top: 1rem;
text-align: center;
}
#support-section h1 {
margin-top: 0;
}
#support-section p {
margin-top: 0;
}
#button-group {
display: flex;
justify-content: center;
flex-wrap: wrap;
}
#github-button, #discord-button {
display: inline-block;
padding: 1rem 2rem;
margin: 1rem;
background-color: #008CBA;
color: #fff;
font-size: 1.2rem;
text-align: center;
text-decoration: none;
border-radius: 3rem;
transition: all 0.3s ease-in-out;
}
#github-button:hover, #discord-button:hover, #home-button:hover {
background-color: #005b7f;
}
#home-button {
display: block;
width: 200px;
height: 50px;
margin: 2em auto;
background-color: #008CBA;
color: white;
text-align: center;
line-height: 50px;
text-decoration: none;
font-weight: bold;
border-radius: 25px;
transition: all 0.3s ease-in-out;
}

View File

@@ -0,0 +1,10 @@
.custom-file-label {
padding-right: 90px;
}
.selected-files {
margin-top: 10px;
max-height: 150px;
overflow-y: auto;
white-space: pre-wrap;
}

View File

@@ -0,0 +1,49 @@
#game-container {
position: relative;
width: 100vh;
height: 0;
padding-bottom: 75%; /* 4:3 aspect ratio */
background-color: transparent;
margin: auto;
overflow: hidden;
border: 2px solid black; /* Add border */
}
.pdf, .player, .projectile {
position: absolute;
}
.pdf {
width: 50px;
height: 50px;
}
.player {
width: 50px;
height: 50px;
}
.projectile {
background-color: black !important;
width: 5px;
height: 10px;
}
#score, #level, #lives, #high-score {
color: black;
font-family: sans-serif;
position: absolute;
font-size: calc(14px + 0.25vw); /* Reduced font size */
}
#score {
top: 10px;
left: 10px;
}
#lives {
top: 10px;
left: calc(7vw); /* Adjusted position */
}
#high-score {
top: 10px;
left: calc(14vw); /* Adjusted position */
}
#level {
top: 10px;
right: 10px;
}

View File

@@ -0,0 +1,70 @@
.features-container {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(21rem, 3fr));
gap: 25px 30px;
}
.feature-card {
border: 2px solid rgba(0, 0, 0, .25);
border-radius: 0.25rem;
padding: 1.25rem;
display: flex;
flex-direction: column;
align-items: flex-start;
background: rgba(13, 110, 253, 0.05);
transition: transform 0.3s, border 0.3s;
}
.feature-card a {
text-decoration: none;
color: inherit;
display: flex;
flex-direction: column;
width: 100%;
height: 100%;
}
.feature-card .card-text {
flex: 1;
}
.feature-card:hover {
border: 1px solid rgba(0, 0, 0, .5);
cursor: pointer;
transform: scale(1.1);
}
.feature-card:hover .card-title {
text-decoration: underline;
}
.card-title.text-primary {
color: #000; /* Replace with your desired shade of blue */
}
.home-card-icon {
width: 30px;
height: 30px;
transform: translateY(-5px);
}
.home-card-icon-colour {
filter: invert(0.2) sepia(2) saturate(50) hue-rotate(190deg);
}
.favorite-icon {
display: none;
position: absolute;
top: 10px;
right: 10px;
}
/* Only show the favorite icons when the parent card is being hovered over */
.feature-card:hover .favorite-icon {
display: block;
}
.favorite-icon img {
filter: brightness(0);
}
.jumbotron {
padding: 3rem 3rem; /* Reduce vertical padding */
}

View File

@@ -0,0 +1,25 @@
.list-group-item {
display: flex;
justify-content: space-between;
align-items: center;
}
.filename {
flex-grow: 1;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
margin-right: 10px;
}
.arrows {
flex-shrink: 0;
display: flex;
justify-content: flex-end;
}
.move-up span,
.move-down span {
font-weight: bold;
font-size: 1.2em;
}

View File

@@ -0,0 +1,42 @@
.main-icon {
width: 36px;
height: 36px;
vertical-align: middle;
transform: translateY(-2px);
}
.icon {
width: 16px;
height: 16px;
vertical-align: middle;
transform: translateY(-2px);
}
.icon+.icon {
margin-left: -4px;
}
.icon-text {
margin-left: 4px;
}
.nav-item-separator {
position: relative;
margin: 0 4px; /* Adjust the margin as needed */
}
.nav-item-separator::before {
content: '';
position: absolute;
left: 0;
top: 10%; /* Adjust the top and bottom margins as needed */
bottom: 10%;
width: 1px;
background-color: #ccc; /* Adjust the color as needed */
}
.navbar-icon {
width: 20px;
height: 20px;
transform: translateY(-2px);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 201 KiB

View File

@@ -0,0 +1,24 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="flag-icons-kr" viewBox="0 0 640 480">
<defs>
<clipPath id="a">
<path fill-opacity=".7" d="M-95.8-.4h682.7v512H-95.8z"/>
</clipPath>
</defs>
<g fill-rule="evenodd" clip-path="url(#a)" transform="translate(89.8 .4) scale(.9375)">
<path fill="#fff" d="M-95.8-.4H587v512H-95.8Z"/>
<g transform="rotate(-56.3 361.6 -101.3) scale(10.66667)">
<g id="c">
<path id="b" d="M-6-26H6v2H-6Zm0 3H6v2H-6Zm0 3H6v2H-6Z"/>
<use xlink:href="#b" width="100%" height="100%" y="44"/>
</g>
<path stroke="#fff" d="M0 17v10"/>
<path fill="#cd2e3a" d="M0-12a12 12 0 0 1 0 24Z"/>
<path fill="#0047a0" d="M0-12a12 12 0 0 0 0 24A6 6 0 0 0 0 0Z"/>
<circle cy="-6" r="6" fill="#cd2e3a"/>
</g>
<g transform="rotate(-123.7 191.2 62.2) scale(10.66667)">
<use xlink:href="#c" width="100%" height="100%"/>
<path stroke="#fff" d="M0-23.5v3M0 17v3.5m0 3v3"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -0,0 +1,100 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="1060" height="742" version="1.1" viewBox="-2100 -1470 4200 2940" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<g id="G">
<clipPath id="gcut">
<path d="m-31.5 0v-70h63v70zm31.5-47v12h31.5v-12z"/>
</clipPath>
<use width="100%" height="100%" clip-path="url(#gcut)" xlink:href="#O"/>
<path d="M5-35H31.5V-25H5z"/>
<path d="m21.5-35h10v35h-10z"/>
</g>
<g id="R">
<use width="100%" height="100%" xlink:href="#P"/>
<path d="m28 0c0-10 0-32-15-32h-19c22 0 22 22 22 32"/>
</g>
<g id="star" fill="#fff">
<g id="c">
<path id="t" transform="rotate(18,0,-1)" d="m0-1v1h0.5"/>
<use transform="scale(-1,1)" width="100%" height="100%" xlink:href="#t"/>
</g>
<use transform="rotate(72)" width="100%" height="100%" xlink:href="#c"/>
<use transform="rotate(-72)" width="100%" height="100%" xlink:href="#c"/>
<use transform="rotate(144)" width="100%" height="100%" xlink:href="#c"/>
<use transform="rotate(216)" width="100%" height="100%" xlink:href="#c"/>
</g>
<g id="star1">
<use transform="scale(31.5)" width="100%" height="100%" xlink:href="#star"/>
</g>
<g id="star2">
<use transform="scale(26.25)" width="100%" height="100%" xlink:href="#star"/>
</g>
<g id="star3">
<use transform="scale(21)" width="100%" height="100%" xlink:href="#star"/>
</g>
<g id="star4">
<use transform="scale(15)" width="100%" height="100%" xlink:href="#star"/>
</g>
<g id="star5">
<use transform="scale(10.5)" width="100%" height="100%" xlink:href="#star"/>
</g>
<path id="D" d="m-31.5 0h33a30 30 0 0 0 30-30v-10a30 30 0 0 0-30-30h-33zm13-13h19a19 19 0 0 0 19-19v-6a19 19 0 0 0-19-19h-19z" fill-rule="evenodd"/>
<path id="E" transform="translate(-31.5)" d="m0 0h63v-13h-51v-18h40v-12h-40v-14h48v-13h-60z"/>
<path id="e" d="m-26.25 0h52.5v-12h-40.5v-16h33v-12h-33v-11h39.25v-12h-51.25z"/>
<path id="M" d="m-31.5 0h12v-48l14 48h11l14-48v48h12v-70h-17.5l-14 48-14-48h-17.5z"/>
<path id="O" d="m0 0a31.5 35 0 0 0 0-70 31.5 35 0 0 0 0 70m0-13a18.5 22 0 0 0 0-44 18.5 22 0 0 0 0 44" fill-rule="evenodd"/>
<path id="P" d="m-31.5 0h13v-26h28a22 22 0 0 0 0-44h-40zm13-39h27a9 9 0 0 0 0-18h-27z" fill-rule="evenodd"/>
<path id="S" d="m-15.75-22c0 7 6.75 10.5 16.75 10.5s14.74-3.25 14.75-7.75c0-14.25-46.75-5.25-46.5-30.25 0.25-21.5 24.75-20.5 33.75-20.5s26 4 25.75 21.25h-15.25c0-7.5-7-10.25-15-10.25-7.75 0-13.25 1.25-13.25 8.5-0.25 11.75 46.25 4 46.25 28.75 0 18.25-18 21.75-31.5 21.75-11.5 0-31.55-4.5-31.5-22z"/>
</defs>
<clipPath id="band">
<circle r="735"/>
</clipPath>
<path d="m-2100-1470h4200v2940h-4200z" fill="#009b3a"/>
<path d="M -1743,0 0,1113 1743,0 0,-1113 Z" fill="#fedf00"/>
<circle r="735" fill="#002776"/>
<path d="m-2205 1470a1785 1785 0 0 1 3570 0h-105a1680 1680 0 1 0-3360 0z" clip-path="url(#band)" fill="#fff"/>
<g transform="translate(-420,1470)" fill="#009b3a">
<use transform="rotate(-7)" y="-1697.5" width="100%" height="100%" xlink:href="#O"/>
<use transform="rotate(-4)" y="-1697.5" width="100%" height="100%" xlink:href="#R"/>
<use transform="rotate(-1)" y="-1697.5" width="100%" height="100%" xlink:href="#D"/>
<use transform="rotate(2)" y="-1697.5" width="100%" height="100%" xlink:href="#E"/>
<use transform="rotate(5)" y="-1697.5" width="100%" height="100%" xlink:href="#M"/>
<use transform="rotate(9.75)" y="-1697.5" width="100%" height="100%" xlink:href="#e"/>
<use transform="rotate(14.5)" y="-1697.5" width="100%" height="100%" xlink:href="#P"/>
<use transform="rotate(17.5)" y="-1697.5" width="100%" height="100%" xlink:href="#R"/>
<use transform="rotate(20.5)" y="-1697.5" width="100%" height="100%" xlink:href="#O"/>
<use transform="rotate(23.5)" y="-1697.5" width="100%" height="100%" xlink:href="#G"/>
<use transform="rotate(26.5)" y="-1697.5" width="100%" height="100%" xlink:href="#R"/>
<use transform="rotate(29.5)" y="-1697.5" width="100%" height="100%" xlink:href="#E"/>
<use transform="rotate(32.5)" y="-1697.5" width="100%" height="100%" xlink:href="#S"/>
<use transform="rotate(35.5)" y="-1697.5" width="100%" height="100%" xlink:href="#S"/>
<use transform="rotate(38.5)" y="-1697.5" width="100%" height="100%" xlink:href="#O"/>
</g>
<use id="αCMi" x="-600" y="-132" width="100%" height="100%" xlink:href="#star1"/>
<use id="αCMa" x="-535" y="177" width="100%" height="100%" xlink:href="#star1"/>
<use id="βCMa" x="-625" y="243" width="100%" height="100%" xlink:href="#star2"/>
<use id="γCMa" x="-463" y="132" width="100%" height="100%" xlink:href="#star4"/>
<use id="δCMa" x="-382" y="250" width="100%" height="100%" xlink:href="#star2"/>
<use id="εCMa" x="-404" y="323" width="100%" height="100%" xlink:href="#star3"/>
<use id="αVir" x="228" y="-228" width="100%" height="100%" xlink:href="#star1"/>
<use id="αSco" x="515" y="258" width="100%" height="100%" xlink:href="#star1"/>
<use id="βSco" x="617" y="265" width="100%" height="100%" xlink:href="#star3"/>
<use id="εSco" x="545" y="323" width="100%" height="100%" xlink:href="#star2"/>
<use id="θSco" x="368" y="477" width="100%" height="100%" xlink:href="#star2"/>
<use id="ιSco" x="367" y="551" width="100%" height="100%" xlink:href="#star3"/>
<use id="κSco" x="441" y="419" width="100%" height="100%" xlink:href="#star3"/>
<use id="λSco" x="500" y="382" width="100%" height="100%" xlink:href="#star2"/>
<use id="μSco" x="365" y="405" width="100%" height="100%" xlink:href="#star3"/>
<use id="αHya" x="-280" y="30" width="100%" height="100%" xlink:href="#star2"/>
<use id="γHya" x="200" y="-37" width="100%" height="100%" xlink:href="#star3"/>
<use id="αCru" y="330" width="100%" height="100%" xlink:href="#star1"/>
<use id="βCru" x="85" y="184" width="100%" height="100%" xlink:href="#star2"/>
<use id="γCru" y="118" width="100%" height="100%" xlink:href="#star2"/>
<use id="δCru" x="-74" y="184" width="100%" height="100%" xlink:href="#star3"/>
<use id="εCru" x="-37" y="235" width="100%" height="100%" xlink:href="#star4"/>
<use id="αTrA" x="220" y="495" width="100%" height="100%" xlink:href="#star2"/>
<use id="βTrA" x="283" y="430" width="100%" height="100%" xlink:href="#star3"/>
<use id="γTrA" x="162" y="412" width="100%" height="100%" xlink:href="#star3"/>
<use id="αCar" x="-295" y="390" width="100%" height="100%" xlink:href="#star1"/>
<use id="σOct" y="575" width="100%" height="100%" xlink:href="#star5"/>
</svg>

After

Width:  |  Height:  |  Size: 6.3 KiB

View File

@@ -0,0 +1,149 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Sodipodi ("http://www.sodipodi.com/") -->
<!-- /Creative Commons Public Domain -->
<!--
-->
<svg
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:cc="http://web.resource.org/cc/"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:ns1="http://sozi.baierouge.fr"
id="svg548"
viewBox="0 0 999 666"
sodipodi:version="0.31.1win"
sodipodi:docname="romania_flag.svg"
>
<sodipodi:namedview
id="base"
/>
<g
id="g555"
transform="scale(8.325)"
>
<rect
id="rect551"
style="fill-rule:evenodd;stroke-width:1pt;fill:#00319c"
height="80"
width="40"
y="0"
x="0"
/>
<rect
id="rect552"
style="fill-rule:evenodd;stroke-width:1pt;fill:#ffde00"
height="80"
width="40"
y="0"
x="40"
/>
<rect
id="rect553"
style="fill-rule:evenodd;stroke-width:1pt;fill:#de2110"
height="80"
width="40"
y="0"
x="80"
/>
</g
>
<metadata
>
<rdf:RDF
>
<cc:Work
>
<dc:format
>image/svg+xml</dc:format
>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage"
/>
<cc:license
rdf:resource="http://creativecommons.org/licenses/publicdomain/"
/>
<dc:publisher
>
<cc:Agent
rdf:about="http://openclipart.org/"
>
<dc:title
>Openclipart</dc:title
>
</cc:Agent
>
</dc:publisher
>
<dc:title
>Flag of Romania</dc:title
>
<dc:date
>2009-04-04T01:16:28</dc:date
>
<dc:description
>The Romania flag by Alexandru Popescu</dc:description
>
<dc:source
>https://openclipart.org/detail/23974/flag-of-romania-by-anonymous-23974</dc:source
>
<dc:creator
>
<cc:Agent
>
<dc:title
>Anonymous</dc:title
>
</cc:Agent
>
</dc:creator
>
<dc:subject
>
<rdf:Bag
>
<rdf:li
>blue</rdf:li
>
<rdf:li
>flag</rdf:li
>
<rdf:li
>red</rdf:li
>
<rdf:li
>romania</rdf:li
>
<rdf:li
>yellow</rdf:li
>
</rdf:Bag
>
</dc:subject
>
</cc:Work
>
<cc:License
rdf:about="http://creativecommons.org/licenses/publicdomain/"
>
<cc:permits
rdf:resource="http://creativecommons.org/ns#Reproduction"
/>
<cc:permits
rdf:resource="http://creativecommons.org/ns#Distribution"
/>
<cc:permits
rdf:resource="http://creativecommons.org/ns#DerivativeWorks"
/>
</cc:License
>
</rdf:RDF
>
</metadata
>
</svg
>

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-grid" viewBox="0 0 16 16">
<path d="M1 2.5A1.5 1.5 0 0 1 2.5 1h3A1.5 1.5 0 0 1 7 2.5v3A1.5 1.5 0 0 1 5.5 7h-3A1.5 1.5 0 0 1 1 5.5v-3zM2.5 2a.5.5 0 0 0-.5.5v3a.5.5 0 0 0 .5.5h3a.5.5 0 0 0 .5-.5v-3a.5.5 0 0 0-.5-.5h-3zm6.5.5A1.5 1.5 0 0 1 10.5 1h3A1.5 1.5 0 0 1 15 2.5v3A1.5 1.5 0 0 1 13.5 7h-3A1.5 1.5 0 0 1 9 5.5v-3zm1.5-.5a.5.5 0 0 0-.5.5v3a.5.5 0 0 0 .5.5h3a.5.5 0 0 0 .5-.5v-3a.5.5 0 0 0-.5-.5h-3zM1 10.5A1.5 1.5 0 0 1 2.5 9h3A1.5 1.5 0 0 1 7 10.5v3A1.5 1.5 0 0 1 5.5 15h-3A1.5 1.5 0 0 1 1 13.5v-3zm1.5-.5a.5.5 0 0 0-.5.5v3a.5.5 0 0 0 .5.5h3a.5.5 0 0 0 .5-.5v-3a.5.5 0 0 0-.5-.5h-3zm6.5.5A1.5 1.5 0 0 1 10.5 9h3a1.5 1.5 0 0 1 1.5 1.5v3a1.5 1.5 0 0 1-1.5 1.5h-3A1.5 1.5 0 0 1 9 13.5v-3zm1.5-.5a.5.5 0 0 0-.5.5v3a.5.5 0 0 0 .5.5h3a.5.5 0 0 0 .5-.5v-3a.5.5 0 0 0-.5-.5h-3z"/>
</svg>

After

Width:  |  Height:  |  Size: 880 B

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrows-fullscreen" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M5.828 10.172a.5.5 0 0 0-.707 0l-4.096 4.096V11.5a.5.5 0 0 0-1 0v3.975a.5.5 0 0 0 .5.5H4.5a.5.5 0 0 0 0-1H1.732l4.096-4.096a.5.5 0 0 0 0-.707zm4.344 0a.5.5 0 0 1 .707 0l4.096 4.096V11.5a.5.5 0 1 1 1 0v3.975a.5.5 0 0 1-.5.5H11.5a.5.5 0 0 1 0-1h2.768l-4.096-4.096a.5.5 0 0 1 0-.707zm0-4.344a.5.5 0 0 0 .707 0l4.096-4.096V4.5a.5.5 0 1 0 1 0V.525a.5.5 0 0 0-.5-.5H11.5a.5.5 0 0 0 0 1h2.768l-4.096 4.096a.5.5 0 0 0 0 .707zm-4.344 0a.5.5 0 0 1-.707 0L1.025 1.732V4.5a.5.5 0 0 1-1 0V.525a.5.5 0 0 1 .5-.5H4.5a.5.5 0 0 1 0 1H1.732l4.096 4.096a.5.5 0 0 1 0 .707z"/>
</svg>

After

Width:  |  Height:  |  Size: 730 B

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="43" height="43" fill="#007bff" class="bi bi-suit-heart-fill" viewBox="0 0 16 16">
<path d="M4 1c2.21 0 4 1.755 4 3.92C8 2.755 9.79 1 12 1s4 1.755 4 3.92c0 3.263-3.234 4.414-7.608 9.608a.513.513 0 0 1-.784 0C3.234 9.334 0 8.183 0 4.92 0 2.755 1.79 1 4 1z"/>
</svg>

After

Width:  |  Height:  |  Size: 312 B

View File

@@ -0,0 +1,76 @@
var toggleCount = 0;
var lastToggleTime = Date.now();
function toggleDarkMode() {
var currentTime = Date.now();
if (currentTime - lastToggleTime < 1000) {
toggleCount++;
} else {
toggleCount = 1;
}
lastToggleTime = currentTime;
var lightModeStyles = document.getElementById("light-mode-styles");
var darkModeStyles = document.getElementById("dark-mode-styles");
var rainbowModeStyles = document.getElementById("rainbow-mode-styles");
var darkModeIcon = document.getElementById("dark-mode-icon");
if (toggleCount >= 18) {
localStorage.setItem("dark-mode", "rainbow");
lightModeStyles.disabled = true;
darkModeStyles.disabled = true;
rainbowModeStyles.disabled = false;
darkModeIcon.src = "rainbow.svg";
} else if (localStorage.getItem("dark-mode") == "on") {
localStorage.setItem("dark-mode", "off");
lightModeStyles.disabled = false;
darkModeStyles.disabled = true;
rainbowModeStyles.disabled = true;
darkModeIcon.src = "sun.svg";
} else {
localStorage.setItem("dark-mode", "on");
lightModeStyles.disabled = true;
darkModeStyles.disabled = false;
rainbowModeStyles.disabled = true;
darkModeIcon.src = "moon.svg";
}
}
document.addEventListener("DOMContentLoaded", function() {
var lightModeStyles = document.getElementById("light-mode-styles");
var darkModeStyles = document.getElementById("dark-mode-styles");
var rainbowModeStyles = document.getElementById("rainbow-mode-styles");
var darkModeIcon = document.getElementById("dark-mode-icon");
if (localStorage.getItem("dark-mode") == "on") {
lightModeStyles.disabled = true;
darkModeStyles.disabled = false;
rainbowModeStyles.disabled = true;
darkModeIcon.src = "moon.svg";
} else if (localStorage.getItem("dark-mode") == "off") {
lightModeStyles.disabled = false;
darkModeStyles.disabled = true;
rainbowModeStyles.disabled = true;
darkModeIcon.src = "sun.svg";
} else if (localStorage.getItem("dark-mode") == "rainbow") {
lightModeStyles.disabled = true;
darkModeStyles.disabled = true;
rainbowModeStyles.disabled = false;
darkModeIcon.src = "rainbow.svg";
} else {
if (window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches) {
darkModeStyles.disabled = false;
rainbowModeStyles.disabled = true;
darkModeIcon.src = "moon.svg";
} else {
darkModeStyles.disabled = true;
rainbowModeStyles.disabled = true;
darkModeIcon.src = "sun.svg";
}
}
document.getElementById("dark-mode-toggle").addEventListener("click", function(event) {
event.preventDefault();
toggleDarkMode();
});
});

View File

@@ -0,0 +1,219 @@
function showErrorBanner(message, stackTrace) {
const errorContainer = document.getElementById("errorContainer");
errorContainer.style.display = "block"; // Display the banner
document.querySelector("#errorContainer .alert-heading").textContent = "Error";
document.querySelector("#errorContainer p").textContent = message;
document.querySelector("#traceContent").textContent = stackTrace;
}
let firstErrorOccurred = false;
$(document).ready(function() {
$('form').submit(async function(event) {
event.preventDefault();
firstErrorOccurred = false;
const url = this.action;
const files = $('#fileInput-input')[0].files;
const formData = new FormData(this);
const override = $('#override').val() || '';
const originalButtonText = $('#submitBtn').text();
$('#submitBtn').text('Processing...');
console.log(override);
try {
if(remoteCall === true) {
if (override === 'multi' || (!multiple && files.length > 1) && override !== 'single' ) {
await submitMultiPdfForm(url, files);
} else {
await handleSingleDownload(url, formData);
}
}
$('#submitBtn').text(originalButtonText);
} catch (error) {
handleDownloadError(error);
$('#submitBtn').text(originalButtonText);
console.error(error);
}
});
});
async function handleSingleDownload(url, formData, isMulti = false , isZip = false) {
try {
const response = await fetch(url, { method: 'POST', body: formData });
const contentType = response.headers.get('content-type');
if (!response.ok) {
if (contentType && contentType.includes('application/json')) {
return handleJsonResponse(response);
console.error('Throwing error banner, response was not okay');
}
throw new Error(`HTTP error! status: ${response.status}`);
}
const contentDisposition = response.headers.get('Content-Disposition');
let filename = getFilenameFromContentDisposition(contentDisposition);
const blob = await response.blob();
if (contentType.includes('application/pdf') || contentType.includes('image/')) {
return handleResponse(blob, filename, !isMulti, isZip);
} else {
return handleResponse(blob, filename, false, isZip);
}
} catch (error) {
console.error('Error in handleSingleDownload:', error);
throw error; // Re-throw the error if you want it to be handled higher up.
}
}
function getFilenameFromContentDisposition(contentDisposition) {
let filename;
if (contentDisposition && contentDisposition.indexOf('attachment') !== -1) {
filename = decodeURIComponent(contentDisposition.split('filename=')[1].replace(/"/g, '')).trim();
} else {
// If the Content-Disposition header is not present or does not contain the filename, use a default filename
filename = 'download';
}
return filename;
}
async function handleJsonResponse(response) {
const json = await response.json();
const errorMessage = JSON.stringify(json, null, 2);
if (errorMessage.toLowerCase().includes('the password is incorrect') || errorMessage.toLowerCase().includes('Password is not provided') || errorMessage.toLowerCase().includes('PDF contains an encryption dictionary')) {
if (!firstErrorOccurred) {
firstErrorOccurred = true;
alert(pdfPasswordPrompt);
}
} else {
showErrorBanner(json.error + ':' + json.message, json.trace);
}
}
async function handleResponse(blob, filename, considerViewOptions = false, isZip = false) {
if (!blob) return;
const downloadOption = localStorage.getItem('downloadOption');
if (considerViewOptions) {
if (downloadOption === 'sameWindow') {
const url = URL.createObjectURL(blob);
window.location.href = url;
return;
} else if (downloadOption === 'newWindow') {
const url = URL.createObjectURL(blob);
window.open(url, '_blank');
return;
}
}
if(!isZip){
downloadFile(blob, filename);
}
return { filename, blob };
}
function handleDownloadError(error) {
const errorMessage = error.message;
showErrorBanner(errorMessage);
}
let urls = []; // An array to hold all the URLs
function downloadFile(blob, filename) {
if (!(blob instanceof Blob)) {
console.error('Invalid blob passed to downloadFile function');
return;
}
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
a.click();
urls.push(url); // Store the URL so it doesn't get garbage collected too soon
return { filename, blob };
}
async function submitMultiPdfForm(url, files) {
const zipThreshold = parseInt(localStorage.getItem('zipThreshold'), 10) || 4;
const zipFiles = files.length > zipThreshold;
let jszip = null;
// Show the progress bar
$('#progressBarContainer').show();
// Initialize the progress bar
let progressBar = $('#progressBar');
progressBar.css('width', '0%');
progressBar.attr('aria-valuenow', 0);
progressBar.attr('aria-valuemax', files.length);
if (zipFiles) {
jszip = new JSZip();
}
// Get existing form data
let formData = new FormData($('form')[0]);
formData.delete('fileInput');
const CONCURRENCY_LIMIT = 8;
const chunks = [];
for (let i = 0; i < Array.from(files).length; i += CONCURRENCY_LIMIT) {
chunks.push(Array.from(files).slice(i, i + CONCURRENCY_LIMIT));
}
for (const chunk of chunks) {
const promises = chunk.map(async file => {
let fileFormData = new FormData();
fileFormData.append('fileInput', file);
// Add other form data
for (let pair of formData.entries()) {
fileFormData.append(pair[0], pair[1]);
}
try {
const downloadDetails = await handleSingleDownload(url, fileFormData, true, zipFiles);
console.log(downloadDetails);
if (zipFiles) {
jszip.file(downloadDetails.filename, downloadDetails.blob);
} else {
//downloadFile(downloadDetails.blob, downloadDetails.filename);
}
updateProgressBar(progressBar, Array.from(files).length);
} catch (error) {
handleDownloadError(error);
console.error(error);
}
});
await Promise.all(promises);
}
if (zipFiles) {
try {
const content = await jszip.generateAsync({ type: "blob" });
downloadFile(content, "files.zip");
} catch (error) {
console.error('Error generating ZIP file: ' + error);
}
}
progressBar.css('width', '100%');
progressBar.attr('aria-valuenow', Array.from(files).length);
}
function updateProgressBar(progressBar, files) {
let progress = ((progressBar.attr('aria-valuenow') / files.length) * 100) + (100 / files.length);
progressBar.css('width', progress + '%');
progressBar.attr('aria-valuenow', parseInt(progressBar.attr('aria-valuenow')) + 1);
}
window.addEventListener('unload', () => {
for (const url of urls) {
URL.revokeObjectURL(url);
}
});

View File

@@ -25,33 +25,50 @@ const DraggableUtils = {
},
})
.resizable({
edges: { left: true, right: true, bottom: true, top: true },
listeners: {
move: (event) => {
var target = event.target
var x = (parseFloat(target.getAttribute('data-x')) || 0)
var y = (parseFloat(target.getAttribute('data-y')) || 0)
edges: { left: true, right: true, bottom: true, top: true },
listeners: {
move: (event) => {
var target = event.target
var x = (parseFloat(target.getAttribute('data-x')) || 0)
var y = (parseFloat(target.getAttribute('data-y')) || 0)
// update the element's style
target.style.width = event.rect.width + 'px'
target.style.height = event.rect.height + 'px'
// check if control key is pressed
if (event.ctrlKey) {
const aspectRatio = target.offsetWidth / target.offsetHeight;
// preserve aspect ratio
let width = event.rect.width;
let height = event.rect.height;
// translate when resizing from top or left edges
x += event.deltaRect.left
y += event.deltaRect.top
if (Math.abs(event.deltaRect.width) >= Math.abs(event.deltaRect.height)) {
height = width / aspectRatio;
} else {
width = height * aspectRatio;
}
target.style.transform = 'translate(' + x + 'px,' + y + 'px)'
event.rect.width = width;
event.rect.height = height;
}
target.setAttribute('data-x', x)
target.setAttribute('data-y', y)
target.textContent = Math.round(event.rect.width) + '\u00D7' + Math.round(event.rect.height)
target.style.width = event.rect.width + 'px'
target.style.height = event.rect.height + 'px'
// translate when resizing from top or left edges
x += event.deltaRect.left
y += event.deltaRect.top
target.style.transform = 'translate(' + x + 'px,' + y + 'px)'
target.setAttribute('data-x', x)
target.setAttribute('data-y', y)
target.textContent = Math.round(event.rect.width) + '\u00D7' + Math.round(event.rect.height)
this.onInteraction(target);
},
},
this.onInteraction(target);
},
},
modifiers: [
interact.modifiers.restrictSize({
min: { width: 50, height: 50 },
min: { width: 5, height: 5 },
}),
],
inertia: true,

View File

@@ -0,0 +1,50 @@
var traceVisible = false;
function toggletrace() {
var traceDiv = document.getElementById("trace");
if (!traceVisible) {
traceDiv.style.maxHeight = "500px";
traceVisible = true;
} else {
traceDiv.style.maxHeight = "0px";
traceVisible = false;
}
adjustContainerHeight();
}
function copytrace() {
var flip = false
if (!traceVisible) {
toggletrace()
flip = true
}
var traceContent = document.getElementById("traceContent");
var range = document.createRange();
range.selectNode(traceContent);
window.getSelection().removeAllRanges();
window.getSelection().addRange(range);
document.execCommand("copy");
window.getSelection().removeAllRanges();
if (flip) {
toggletrace()
}
}
function dismissError() {
var errorContainer = document.getElementById("errorContainer");
errorContainer.style.display = "none";
errorContainer.style.height = "0";
}
function adjustContainerHeight() {
var errorContainer = document.getElementById("errorContainer");
var traceDiv = document.getElementById("trace");
if (traceVisible) {
errorContainer.style.height = errorContainer.scrollHeight - traceDiv.scrollHeight + traceDiv.offsetHeight + "px";
} else {
errorContainer.style.height = "auto";
}
}
function showHelp() {
$('#helpModal').modal('show');
}

View File

@@ -0,0 +1,37 @@
function updateFavoritesDropdown() {
var dropdown = document.querySelector('#favoritesDropdown');
dropdown.innerHTML = ''; // Clear the current favorites
var hasFavorites = false;
for (var i = 0; i < localStorage.length; i++) {
var key = localStorage.key(i);
if (localStorage.getItem(key) === 'favorite') {
// Find the corresponding navbar entry
var navbarEntry = document.querySelector(`a[href='${key}']`);
if (navbarEntry) {
// Create a new dropdown entry
var dropdownItem = document.createElement('a');
dropdownItem.className = 'dropdown-item';
dropdownItem.href = navbarEntry.href;
dropdownItem.innerHTML = navbarEntry.innerHTML;
dropdown.appendChild(dropdownItem);
hasFavorites = true;
}
}
}
// Show or hide the default item based on whether there are any favorites
if (!hasFavorites) {
var defaultItem = document.createElement('a');
defaultItem.className = 'dropdown-item';
defaultItem.textContent = noFavourites;
dropdown.appendChild(defaultItem);
}
}
document.addEventListener('DOMContentLoaded', function() {
updateFavoritesDropdown();
});

View File

@@ -0,0 +1,43 @@
document.addEventListener('DOMContentLoaded', function() {
const fileInput = document.getElementById(elementID);
// Prevent default behavior for drag events
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
fileInput.addEventListener(eventName, preventDefaults, false);
});
function preventDefaults(e) {
e.preventDefault();
e.stopPropagation();
}
// Add drop event listener
fileInput.addEventListener('drop', handleDrop, false);
function handleDrop(e) {
const dt = e.dataTransfer;
const files = dt.files;
fileInput.files = files;
handleFileInputChange(fileInput)
}
});
$("#"+elementID).on("change", function() {
handleFileInputChange(this);
});
function handleFileInputChange(inputElement) {
const files = $(inputElement).get(0).files;
const fileNames = Array.from(files).map(f => f.name);
const selectedFilesContainer = $(inputElement).siblings(".selected-files");
selectedFilesContainer.empty();
fileNames.forEach(fileName => {
selectedFilesContainer.append("<div>" + fileName + "</div>");
});
if (fileNames.length === 1) {
$(inputElement).siblings(".custom-file-label").addClass("selected").html(fileNames[0]);
} else if (fileNames.length > 1) {
$(inputElement).siblings(".custom-file-label").addClass("selected").html(fileNames.length + " " + filesSelected);
} else {
$(inputElement).siblings(".custom-file-label").addClass("selected").html(pdfPrompt);
}
}

View File

@@ -0,0 +1,41 @@
function compareVersions(version1, version2) {
const v1 = version1.split('.');
const v2 = version2.split('.');
for (let i = 0; i < v1.length || i < v2.length; i++) {
const n1 = parseInt(v1[i]) || 0;
const n2 = parseInt(v2[i]) || 0;
if (n1 > n2) {
return 1;
} else if (n1 < n2) {
return -1;
}
}
return 0;
}
async function getLatestReleaseVersion() {
const url = "https://api.github.com/repos/Frooodle/Stirling-PDF/releases/latest";
const response = await fetch(url);
const data = await response.json();
return data.tag_name.substring(1);
}
async function checkForUpdate() {
const latestVersion = await getLatestReleaseVersion();
console.log("latestVersion=" + latestVersion)
console.log("currentVersion=" + currentVersion)
console.log("compareVersions(latestVersion, currentVersion) > 0)=" + compareVersions(latestVersion, currentVersion))
if (latestVersion != null && latestVersion != "" && compareVersions(latestVersion, currentVersion) > 0) {
document.getElementById("update-btn").style.display = "block";
console.log("visible")
} else {
document.getElementById("update-btn").style.display = "none";
console.log("hidden")
}
}
checkForUpdate();

View File

@@ -0,0 +1,49 @@
function toggleFavorite(element) {
var img = element.querySelector('img');
var card = element.closest('.feature-card');
var cardId = card.id;
if (img.src.endsWith('star.svg')) {
img.src = 'images/star-fill.svg';
card.classList.add('favorite');
localStorage.setItem(cardId, 'favorite');
} else {
img.src = 'images/star.svg';
card.classList.remove('favorite');
localStorage.removeItem(cardId);
}
reorderCards();
updateFavoritesDropdown();
}
function reorderCards() {
var container = document.querySelector('.features-container');
var cards = Array.from(container.getElementsByClassName('feature-card'));
cards.sort(function(a, b) {
var aIsFavorite = localStorage.getItem(a.id) === 'favorite';
var bIsFavorite = localStorage.getItem(b.id) === 'favorite';
if (aIsFavorite && !bIsFavorite) {
return -1;
}
if (!aIsFavorite && bIsFavorite) {
return 1;
}
return 0;
});
cards.forEach(function(card) {
container.appendChild(card);
});
}
function initializeCards() {
var cards = document.querySelectorAll('.feature-card');
cards.forEach(function(card) {
var cardId = card.id;
var img = card.querySelector('.favorite-icon img');
if (localStorage.getItem(cardId) === 'favorite') {
img.src = 'images/star-fill.svg';
card.classList.add('favorite');
}
});
reorderCards();
updateFavoritesDropdown();
}
window.onload = initializeCards;

View File

@@ -0,0 +1,46 @@
document.addEventListener('DOMContentLoaded', function() {
const defaultLocale = document.documentElement.lang || 'en_GB';
const storedLocale = localStorage.getItem('languageCode') || defaultLocale;
const dropdownItems = document.querySelectorAll('.lang_dropdown-item');
for (let i = 0; i < dropdownItems.length; i++) {
const item = dropdownItems[i];
item.classList.remove('active');
if (item.dataset.languageCode === storedLocale) {
item.classList.add('active');
}
item.addEventListener('click', handleDropdownItemClick);
}
});
function handleDropdownItemClick(event) {
event.preventDefault();
const languageCode = this.dataset.languageCode;
localStorage.setItem('languageCode', languageCode);
const currentUrl = window.location.href;
if (currentUrl.indexOf('?lang=') === -1) {
window.location.href = currentUrl + '?lang=' + languageCode;
} else {
window.location.href = currentUrl.replace(/\?lang=\w{2,}/, '?lang=' + languageCode);
}
}
$(document).ready(function() {
$(".nav-item.dropdown").each(function() {
var $dropdownMenu = $(this).find(".dropdown-menu");
if ($dropdownMenu.children().length <= 2 && $dropdownMenu.children("hr.dropdown-divider").length === $dropdownMenu.children().length) {
$(this).prev('.nav-item.nav-item-separator').remove();
$(this).remove();
}
});
//Sort languages by alphabet
var list = $('.dropdown-menu[aria-labelledby="languageDropdown"]').children("a");
list.sort(function(a, b) {
var A = $(a).text().toUpperCase();
var B = $(b).text().toUpperCase();
return (A < B) ? -1 : (A > B) ? 1 : 0;
})
.appendTo('.dropdown-menu[aria-labelledby="languageDropdown"]');
});

View File

@@ -0,0 +1,63 @@
document.getElementById("fileInput-input").addEventListener("change", function() {
var files = this.files;
var list = document.getElementById("selectedFiles");
list.innerHTML = "";
for (var i = 0; i < files.length; i++) {
var item = document.createElement("li");
item.className = "list-group-item";
item.innerHTML = `
<div class="d-flex justify-content-between align-items-center w-100">
<div class="filename">${files[i].name}</div>
<div class="arrows d-flex">
<button class="btn btn-secondary move-up"><span>&uarr;</span></button>
<button class="btn btn-secondary move-down"><span>&darr;</span></button>
</div>
</div>
`;
list.appendChild(item);
}
var moveUpButtons = document.querySelectorAll(".move-up");
for (var i = 0; i < moveUpButtons.length; i++) {
moveUpButtons[i].addEventListener("click", function(event) {
event.preventDefault();
var parent = this.closest(".list-group-item");
var grandParent = parent.parentNode;
if (parent.previousElementSibling) {
grandParent.insertBefore(parent, parent.previousElementSibling);
updateFiles();
}
});
}
var moveDownButtons = document.querySelectorAll(".move-down");
for (var i = 0; i < moveDownButtons.length; i++) {
moveDownButtons[i].addEventListener("click", function(event) {
event.preventDefault();
var parent = this.closest(".list-group-item");
var grandParent = parent.parentNode;
if (parent.nextElementSibling) {
grandParent.insertBefore(parent.nextElementSibling, parent);
updateFiles();
}
});
}
function updateFiles() {
var dataTransfer = new DataTransfer();
var liElements = document.querySelectorAll("#selectedFiles li");
for (var i = 0; i < liElements.length; i++) {
var fileNameFromList = liElements[i].querySelector(".filename").innerText;
var fileFromFiles;
for (var j = 0; j < files.length; j++) {
var file = files[j];
if (file.name === fileNameFromList) {
dataTransfer.items.add(file);
break;
}
}
}
document.getElementById("fileInput-input").files = dataTransfer.files;
}
});

View File

@@ -62,7 +62,7 @@ class PdfContainer {
const files = e.target.files;
this.fileName = files[0].name;
for (var i=0; i < files.length; i++) {
this.addPdfFile(files[i], nextSiblingElement);
await this.addPdfFile(files[i], nextSiblingElement);
}
document.querySelectorAll(".enable-on-file").forEach(element => {

View File

@@ -0,0 +1,42 @@
// Get the download option from local storage, or set it to 'sameWindow' if it doesn't exist
var downloadOption = localStorage.getItem('downloadOption')
|| 'sameWindow';
// Set the selected option in the dropdown
document.getElementById('downloadOption').value = downloadOption;
// Save the selected option to local storage when the dropdown value changes
document.getElementById('downloadOption').addEventListener(
'change',
function() {
downloadOption = this.value;
localStorage.setItem('downloadOption',
downloadOption);
});
// Get the zipThreshold value from local storage, or set it to 0 if it doesn't exist
var zipThreshold = parseInt(localStorage.getItem('zipThreshold'), 10) || 4;
// Set the value of the slider and the display span
document.getElementById('zipThreshold').value = zipThreshold;
document.getElementById('zipThresholdValue').textContent = zipThreshold;
// Save the selected value to local storage when the slider value changes
document.getElementById('zipThreshold').addEventListener('input', function() {
zipThreshold = this.value;
document.getElementById('zipThresholdValue').textContent = zipThreshold;
localStorage.setItem('zipThreshold', zipThreshold);
});
var boredWaiting = localStorage.getItem('boredWaiting') || 'disabled';
document.getElementById('boredWaiting').checked = boredWaiting === 'enabled';
document.getElementById('boredWaiting').addEventListener('change', function() {
boredWaiting = this.checked ? 'enabled' : 'disabled';
localStorage.setItem('boredWaiting', boredWaiting);
});

View File

@@ -43,7 +43,7 @@
</div>
<div class="form-group">
<label for="dpi">DPI:</label>
<input type="number" name="dpi" class="form-control" id="dpi" min="1" max="100" step="1" value="30" required>
<input type="number" name="dpi" class="form-control" id="dpi" min="1" step="1" value="300" required>
</div>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{pdfToImage.submit}"></button>
</form>

View File

@@ -103,7 +103,7 @@ margin-top: 0;
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
<div th:insert="~{fragments/errorBanner.html :: errorBanner}"></div>
<div class="container">
<div id="support-section">

View File

@@ -8,14 +8,14 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- jQuery -->
<script src="js/jquery.min.js"></script>
<script src="js/thirdParty/jquery.min.js"></script>
<!-- jQuery -->
<script src="js/jszip.min.js"></script>
<script src="js/thirdParty/jszip.min.js"></script>
<!-- Bootstrap -->
<script src="js/popper.min.js"></script>
<script src="js/bootstrap.min.js"></script>
<script src="js/thirdParty/popper.min.js"></script>
<script src="js/thirdParty/bootstrap.min.js"></script>
<link rel="stylesheet" href="css/bootstrap.min.css">
<link rel="stylesheet" href="css/bootstrap-icons.css">
@@ -23,7 +23,7 @@
<script src="pdfjs/pdf.js"></script>
<!-- PDF-Lib -->
<script src="js/pdf-lib.min.js"></script>
<script src="js/thirdParty/pdf-lib.min.js"></script>
<!-- Custom -->
<link rel="stylesheet" href="css/general.css">
@@ -34,84 +34,7 @@
<script src="js/tab-container.js"></script>
<script>
var toggleCount = 0;
var lastToggleTime = Date.now();
function toggleDarkMode() {
var currentTime = Date.now();
if (currentTime - lastToggleTime < 1000) {
toggleCount++;
} else {
toggleCount = 1;
}
lastToggleTime = currentTime;
var lightModeStyles = document.getElementById("light-mode-styles");
var darkModeStyles = document.getElementById("dark-mode-styles");
var rainbowModeStyles = document.getElementById("rainbow-mode-styles");
var darkModeIcon = document.getElementById("dark-mode-icon");
if (toggleCount >= 18) {
localStorage.setItem("dark-mode", "rainbow");
lightModeStyles.disabled = true;
darkModeStyles.disabled = true;
rainbowModeStyles.disabled = false;
darkModeIcon.src = "rainbow.svg";
} else if (localStorage.getItem("dark-mode") == "on") {
localStorage.setItem("dark-mode", "off");
lightModeStyles.disabled = false;
darkModeStyles.disabled = true;
rainbowModeStyles.disabled = true;
darkModeIcon.src = "sun.svg";
} else {
localStorage.setItem("dark-mode", "on");
lightModeStyles.disabled = true;
darkModeStyles.disabled = false;
rainbowModeStyles.disabled = true;
darkModeIcon.src = "moon.svg";
}
}
document.addEventListener("DOMContentLoaded", function () {
var lightModeStyles = document.getElementById("light-mode-styles");
var darkModeStyles = document.getElementById("dark-mode-styles");
var rainbowModeStyles = document.getElementById("rainbow-mode-styles");
var darkModeIcon = document.getElementById("dark-mode-icon");
if (localStorage.getItem("dark-mode") == "on") {
lightModeStyles.disabled = true;
darkModeStyles.disabled = false;
rainbowModeStyles.disabled = true;
darkModeIcon.src = "moon.svg";
} else if (localStorage.getItem("dark-mode") == "off") {
lightModeStyles.disabled = false;
darkModeStyles.disabled = true;
rainbowModeStyles.disabled = true;
darkModeIcon.src = "sun.svg";
} else if (localStorage.getItem("dark-mode") == "rainbow") {
lightModeStyles.disabled = true;
darkModeStyles.disabled = true;
rainbowModeStyles.disabled = false;
darkModeIcon.src = "rainbow.svg";
} else {
if (window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches) {
darkModeStyles.disabled = false;
rainbowModeStyles.disabled = true;
darkModeIcon.src = "moon.svg";
} else {
darkModeStyles.disabled = true;
rainbowModeStyles.disabled = true;
darkModeIcon.src = "sun.svg";
}
}
document.getElementById("dark-mode-toggle").addEventListener("click", function (event) {
event.preventDefault();
toggleDarkMode();
});
});
</script>
<script src="js/darkmode.js"></script>
</head>
@@ -163,326 +86,17 @@ document.addEventListener("DOMContentLoaded", function () {
<div id="level">Level: 1</div>
<img src="favicon.svg" class="player" id="player">
</div>
<style>
#game-container {
position: relative;
width: 100vh;
height: 0;
padding-bottom: 75%; /* 4:3 aspect ratio */
background-color: transparent;
margin: auto;
overflow: hidden;
border: 2px solid black; /* Add border */
}
.pdf, .player, .projectile {
position: absolute;
}
.pdf {
width: 50px;
height: 50px;
}
.player {
width: 50px;
height: 50px;
}
.projectile {
background-color: black !important;
width: 5px;
height: 10px;
}
#score, #level, #lives, #high-score {
color: black;
font-family: sans-serif;
position: absolute;
font-size: calc(14px + 0.25vw); /* Reduced font size */
}
#score {
top: 10px;
left: 10px;
}
#lives {
top: 10px;
left: calc(7vw); /* Adjusted position */
}
#high-score {
top: 10px;
left: calc(14vw); /* Adjusted position */
}
#level {
top: 10px;
right: 10px;
}
</style>
<link rel="stylesheet" href="css/game.css">
</dialog>
</th:block>
<th:block th:fragment="fileSelector(name, multiple)" th:with="accept=${accept} ?: '*/*', inputText=${inputText} ?: #{pdfPrompt}, remoteCall=${remoteCall} ?: 'true', notRequired=${notRequired} ?: false">
<script>
$(document).ready(function() {
$('form').submit(async function(event) {
const boredWaiting = localStorage.getItem('boredWaiting');
if (boredWaiting === 'enabled') {
$('#show-game-btn').show();
}
var processing = "Processing..."
var submitButtonText = $('#submitBtn').text()
$('#submitBtn').text('Processing...');
console.log("start download code")
var files = $('#fileInput-input')[0].files;
var url = this.action;
console.log(url)
event.preventDefault(); // Prevent the default form handling behavior
/* Check if ${multiple} is false */
var multiple = [[${multiple}]] || false;
var override = $('#override').val() || '';
console.log("override=" + override)
if([[${remoteCall}]] === true) {
if (override === 'multi' || (!multiple && files.length > 1) && override !== 'single' ) {
console.log("multi parallel download")
await submitMultiPdfForm(event,url);
} else {
console.log("start single download")
// Get the selected download option from localStorage
const downloadOption = localStorage.getItem('downloadOption');
var formData = new FormData($('form')[0]);
// Send the request to the server using the fetch() API
const response = await fetch(url, {
method: 'POST',
body: formData
});
try {
if (!response) {
throw new Error('Received null response for file ' + i);
}
console.log("load single download")
// Extract the filename from the Content-Disposition header, if present
let filename = null;
const contentDispositionHeader = response.headers.get('Content-Disposition');
console.log(contentDispositionHeader)
if (contentDispositionHeader && contentDispositionHeader.indexOf('attachment') !== -1) {
filename = decodeURIComponent(contentDispositionHeader.split('filename=')[1].replace(/"/g, ''));
} else {
// If the Content-Disposition header is not present or does not contain the filename, use a default filename
filename = 'download';
}
console.log("filename=" + filename)
const contentType = response.headers.get('Content-Type');
console.log("contentType=" + contentType)
// Check if the response is a PDF or an image
if (contentType.includes('pdf') || contentType.includes('image')) {
const blob = await response.blob();
console.log("pdf/image")
// Perform the appropriate action based on the download option
if (downloadOption === 'sameWindow') {
console.log("same window")
// Open the file in the same window
window.location.href = URL.createObjectURL(blob);
} else if (downloadOption === 'newWindow') {
console.log("new window")
// Open the file in a new window
window.open(URL.createObjectURL(blob), '_blank');
} else {
console.log("else save")
// Download the file
const link = document.createElement('a');
link.href = URL.createObjectURL(blob);
link.download = filename;
link.click();
}
} else if (contentType.includes('json')) {
// Handle the JSON response
const json = await response.json();
// Format the error message
const errorMessage = JSON.stringify(json, null, 2);
// Display the error message in an alert
alert(`An error occurred: ${errorMessage}`);
} else {
const blob = await response.blob()
console.log("else save 2 zip")
// For ZIP files or other file types, just download the file
const link = document.createElement('a');
link.href = URL.createObjectURL(blob);
link.download = filename;
link.click();
}
} catch(error) {
console.log("error listener")
// Extract the error message and stack trace from the response
const errorMessage = error.message;
const stackTrace = error.stack;
// Create an error message to display to the user
const message = `${errorMessage}\n\n${stackTrace}`;
$('#submitBtn').text(submitButtonText);
// Display the error message to the user
alert(message);
};
}
} else {
//Offline do nothing and let other scripts handle everything
}
$('#submitBtn').text(submitButtonText);
});
});
async function submitMultiPdfForm(event, url) {
// Get the selected PDF files
let files = $('#fileInput-input')[0].files;
// Get the existing form data
let formData = new FormData($('form')[0]);
formData.delete('fileInput');
// Show the progress bar
$('#progressBarContainer').show();
// Initialize the progress bar
let progressBar = $('#progressBar');
progressBar.css('width', '0%');
progressBar.attr('aria-valuenow', 0);
progressBar.attr('aria-valuemax', files.length);
// Check the flag in localStorage, default to 4
const zipThreshold = parseInt(localStorage.getItem('zipThreshold'), 10) || 4;
const zipFiles = files.length > zipThreshold;
// Initialize JSZip instance if needed
let jszip = null;
if (zipFiles) {
jszip = new JSZip();
}
// Submit each PDF file in parallel
let promises = [];
for (let i = 0; i < files.length; i++) {
let promise = new Promise(async function(resolve, reject) {
let fileFormData = new FormData();
fileFormData.append('fileInput', files[i]);
for (let pair of formData.entries()) {
fileFormData.append(pair[0], pair[1]);
}
console.log(fileFormData);
try {
let response = await fetch(url, {
method: 'POST',
body: fileFormData
});
if (!response) {
throw new Error('Received null response for file ' + i);
}
if (!response.ok) {
throw new Error(`Error submitting request for file ${i}: ${response.status} ${response.statusText}`);
}
let contentDisposition = response.headers.get('content-disposition');
let fileName = "file.pdf"
if (!contentDisposition) {
//throw new Error('Content-Disposition header not found for file ' + i);
} else {
fileName = contentDisposition.split('filename=')[1].replace(/"/g, '');
}
console.log('Received response for file ' + i + ': ' + response);
let blob = await response.blob();
if (zipFiles) {
// Add the file to the ZIP archive
jszip.file(fileName, blob);
resolve();
} else {
// Download the file directly
let url = window.URL.createObjectURL(blob);
let a = document.createElement('a');
a.href = url;
a.download = fileName;
document.body.appendChild(a);
a.click();
a.remove();
resolve();
}
} catch (error) {
console.error('Error submitting request for file ' + i + ': ' + error);
// Set default values or fallbacks for error properties
let status = error && error.status || 500;
let statusText = error && error.statusText || 'Internal Server Error';
let message = error && error.message || 'An error occurred while processing your request.';
// Reject the Promise to signal that the request has failed
reject();
// Redirect to error page with Spring Boot error parameters
let url = '/error?status=' + status + '&error=' + encodeURIComponent(statusText) + '&message=' + encodeURIComponent(message);
window.location.href = url;
}
});
// Update the progress bar as each request finishes
promise.then(function() {
updateProgressBar(progressBar, files);
});
promises.push(promise);
}
// Wait for all requests to finish
try {
await Promise.all(promises);
} catch (error) {
console.error('Error while uploading files: ' + error);
}
// Update the progress bar
progressBar.css('width', '100%');
progressBar.attr('aria-valuenow', files.length);
// After all requests are finished, download the ZIP file if needed
if (zipFiles) {
try {
let content = await jszip.generateAsync({ type: "blob" });
let url = window.URL.createObjectURL(content);
let a = document.createElement('a');
a.href = url;
a.download = "files.zip";
document.body.appendChild(a);
a.click();
a.remove();
} catch (error) {
console.error('Error generating ZIP file: ' + error);
}
}
}
function updateProgressBar(progressBar, files) {
let progress = ((progressBar.attr('aria-valuenow') / files.length) * 100) + (100 / files.length);
progressBar.css('width', progress + '%');
progressBar.attr('aria-valuenow', parseInt(progressBar.attr('aria-valuenow')) + 1);
}
</script>
<th:block th:fragment="fileSelector(name, multiple)" th:with="accept=${accept} ?: '*/*', inputText=${inputText} ?: #{pdfPrompt}, remoteCall=${remoteCall} ?: true, notRequired=${notRequired} ?: false">
<script th:inline="javascript">
const pdfPasswordPrompt =/*[[#{error.pdfPassword}]]*/ '';
const multiple = [[${multiple}]] || false;
const remoteCall = [[${remoteCall}]] || true;
</script>
<script src="js/downloader.js"></script>
<div class="custom-file-chooser">
<div class="custom-file">
@@ -501,65 +115,12 @@ document.addEventListener("DOMContentLoaded", function () {
<button type="button" class="btn btn-primary" id="show-game-btn" style="display:none;">Bored waiting?</button>
<script th:inline="javascript">
document.addEventListener('DOMContentLoaded', function () {
const fileInput = document.getElementById([[${name+"-input"}]]);
// Prevent default behavior for drag events
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
fileInput.addEventListener(eventName, preventDefaults, false);
});
function preventDefaults(e) {
e.preventDefault();
e.stopPropagation();
}
// Add drop event listener
fileInput.addEventListener('drop', handleDrop, false);
function handleDrop(e) {
const dt = e.dataTransfer;
const files = dt.files;
fileInput.files = files;
handleFileInputChange(fileInput)
}
});
$([[${"#"+name+"-input"}]]).on("change", function() {
handleFileInputChange(this);
});
function handleFileInputChange(inputElement) {
const files = $(inputElement).get(0).files;
const fileNames = Array.from(files).map(f => f.name);
const selectedFilesContainer = $(inputElement).siblings(".selected-files");
selectedFilesContainer.empty();
fileNames.forEach(fileName => {
selectedFilesContainer.append("<div>" + fileName + "</div>");
});
console.log("fileNames.length=" + fileNames.length)
if (fileNames.length === 1) {
$(inputElement).siblings(".custom-file-label").addClass("selected").html(fileNames[0]);
} else if (fileNames.length > 1) {
$(inputElement).siblings(".custom-file-label").addClass("selected").html(fileNames.length + " " + [[#{filesSelected}]]);
} else {
$(inputElement).siblings(".custom-file-label").addClass("selected").html([[#{pdfPrompt}]]);
}
}
<script th:inline="javascript">
const elementID = /*[[${name+"-input"}]]*/ '';
const filesSelected = /*[[#{filesSelected}]]*/ '';
const pdfPrompt = /*[[#{pdfPrompt}]]*/ '';
</script>
<style>
.custom-file-label {
padding-right: 90px;
}
.selected-files {
margin-top: 10px;
max-height: 150px;
overflow-y: auto;
white-space: pre-wrap;
}
</style>
<script src="js/fileInput.js"></script>
<link rel="stylesheet" href="css/fileSelect.css">
</th:block>

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