Compare commits
91 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f2a65dc360 | ||
|
|
7b4a889ea7 | ||
|
|
f627d251c3 | ||
|
|
d5b7125415 | ||
|
|
67dd3cf0e3 | ||
|
|
b8b62bb5af | ||
|
|
b4a9d1ac18 | ||
|
|
8aae651c2c | ||
|
|
fc9465b324 | ||
|
|
579a50be2c | ||
|
|
9c5b967e4c | ||
|
|
d41deb729b | ||
|
|
a93a89f3f0 | ||
|
|
11d642a25f | ||
|
|
67448498ea | ||
|
|
489b8da713 | ||
|
|
728d4d0fa8 | ||
|
|
e70f4ff3a6 | ||
|
|
04032c0dfe | ||
|
|
ecba6461df | ||
|
|
433ba6c250 | ||
|
|
ff6c55d1d0 | ||
|
|
d9f8facf2e | ||
|
|
6bd3e5cc8f | ||
|
|
936fb5ae45 | ||
|
|
8e661e1260 | ||
|
|
987d9b0502 | ||
|
|
f6a9169446 | ||
|
|
d5c1c43eb1 | ||
|
|
20c74dac60 | ||
|
|
30161275a3 | ||
|
|
7e9479806e | ||
|
|
39b9ea9f1d | ||
|
|
5a9165d7c6 | ||
|
|
4f851156b7 | ||
|
|
b8fa278173 | ||
|
|
2cfb344e13 | ||
|
|
1d2bf92abe | ||
|
|
cefcda9f40 | ||
|
|
a4bc67ff8e | ||
|
|
48b3dea256 | ||
|
|
1f5231d905 | ||
|
|
c526e18992 | ||
|
|
4594765cbd | ||
|
|
e2a787e519 | ||
|
|
45b3e0aa6a | ||
|
|
7bdb2615d4 | ||
|
|
019a502490 | ||
|
|
6793fd5bc7 | ||
|
|
8ac5cf759e | ||
|
|
19880567ba | ||
|
|
5c5a3fefc1 | ||
|
|
90057828a5 | ||
|
|
7cfc3a09a2 | ||
|
|
b8bbee27f8 | ||
|
|
8e5c665e49 | ||
|
|
7b1e6fb953 | ||
|
|
79936e69c5 | ||
|
|
a19cd327f3 | ||
|
|
5c831c156b | ||
|
|
602df08df5 | ||
|
|
3b8d06a9e3 | ||
|
|
5fd43b50e0 | ||
|
|
15d39413f3 | ||
|
|
5e01946981 | ||
|
|
576d11f09a | ||
|
|
a43c296eef | ||
|
|
6015591e34 | ||
|
|
6f5f031b24 | ||
|
|
1499e78795 | ||
|
|
1b45ab7222 | ||
|
|
005b158ad3 | ||
|
|
26d003e840 | ||
|
|
192fb39302 | ||
|
|
1650dfcc29 | ||
|
|
fda83c4c1d | ||
|
|
1a6ebbb8e5 | ||
|
|
ea1f8912b8 | ||
|
|
a5885d2628 | ||
|
|
716d4c6f28 | ||
|
|
b3a36c82bb | ||
|
|
75fff1d52f | ||
|
|
2d88987cb3 | ||
|
|
8bbbdbd359 | ||
|
|
8a2aa44de8 | ||
|
|
3715c555d3 | ||
|
|
5e4de6cc5f | ||
|
|
4cadfc64f6 | ||
|
|
a3f0d47cad | ||
|
|
bc55b5fdda | ||
|
|
43474712eb |
49
.github/workflows/push-docker.yml
vendored
49
.github/workflows/push-docker.yml
vendored
@@ -8,7 +8,6 @@ on:
|
|||||||
- main
|
- main
|
||||||
jobs:
|
jobs:
|
||||||
push:
|
push:
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
|
|
||||||
@@ -46,13 +45,17 @@ jobs:
|
|||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ github.token }}
|
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
|
- name: Generate tags
|
||||||
id: meta
|
id: meta
|
||||||
uses: docker/metadata-action@v4.4.0
|
uses: docker/metadata-action@v4.4.0
|
||||||
with:
|
with:
|
||||||
images: |
|
images: |
|
||||||
${{ secrets.DOCKER_HUB_USERNAME }}/s-pdf
|
${{ secrets.DOCKER_HUB_USERNAME }}/s-pdf
|
||||||
ghcr.io/${{ github.repository_owner }}/s-pdf
|
ghcr.io/${{ steps.repoowner.outputs.lowercase }}/s-pdf
|
||||||
tags: |
|
tags: |
|
||||||
type=raw,value=${{ steps.versionNumber.outputs.versionNumber }},enable=${{ github.ref == 'refs/heads/master' }}
|
type=raw,value=${{ steps.versionNumber.outputs.versionNumber }},enable=${{ github.ref == 'refs/heads/master' }}
|
||||||
type=raw,value=latest,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 }}
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
platforms: linux/amd64,linux/arm64/v8
|
platforms: linux/amd64,linux/arm64/v8
|
||||||
|
|
||||||
- name: Generate tags
|
|
||||||
|
|
||||||
|
- name: Generate tags ultra-lite
|
||||||
id: meta2
|
id: meta2
|
||||||
uses: docker/metadata-action@v4.4.0
|
uses: docker/metadata-action@v4.4.0
|
||||||
with:
|
with:
|
||||||
images: |
|
images: |
|
||||||
${{ secrets.DOCKER_HUB_USERNAME }}/s-pdf
|
${{ secrets.DOCKER_HUB_USERNAME }}/s-pdf
|
||||||
ghcr.io/${{ github.repository_owner }}/s-pdf
|
ghcr.io/${{ steps.repoowner.outputs.lowercase }}/s-pdf
|
||||||
tags: |
|
tags: |
|
||||||
type=raw,value=${{ steps.versionNumber.outputs.versionNumber }}-ultra-light,enable=${{ github.ref == 'refs/heads/master' }}
|
type=raw,value=${{ steps.versionNumber.outputs.versionNumber }}-ultra-lite,enable=${{ github.ref == 'refs/heads/master' }}
|
||||||
type=raw,value=latest-ultra-light,enable=${{ github.ref == 'refs/heads/master' }}
|
type=raw,value=latest-ultra-lite,enable=${{ github.ref == 'refs/heads/master' }}
|
||||||
type=raw,value=alpha-ultra-light,enable=${{ github.ref == 'refs/heads/main' }}
|
|
||||||
|
|
||||||
- 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
|
uses: docker/build-push-action@v4.0.0
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
file: ./Dockerfile-ultralite
|
file: ./Dockerfile-ultra-lite
|
||||||
push: true
|
push: true
|
||||||
cache-from: type=gha
|
cache-from: type=gha
|
||||||
cache-to: type=gha,mode=max
|
cache-to: type=gha,mode=max
|
||||||
@@ -105,3 +106,27 @@ jobs:
|
|||||||
platforms: linux/amd64,linux/arm64/v8
|
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
45
.github/workflows/releaseArtifacts.yml
vendored
Normal 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
37
.github/workflows/swagger.yml
vendored
Normal 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
1
.gitignore
vendored
@@ -14,6 +14,7 @@ local.properties
|
|||||||
.recommenders
|
.recommenders
|
||||||
.classpath
|
.classpath
|
||||||
.project
|
.project
|
||||||
|
version.properties
|
||||||
|
|
||||||
# Gradle
|
# Gradle
|
||||||
.gradle
|
.gradle
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Build jbig2enc in a separate stage
|
# 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
|
# Create scripts folder and copy local scripts
|
||||||
RUN mkdir /scripts
|
RUN mkdir /scripts
|
||||||
|
|||||||
23
Dockerfile-lite
Normal file
23
Dockerfile-lite
Normal 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"]
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
# Build jbig2enc in a separate stage
|
# Build jbig2enc in a separate stage
|
||||||
FROM openjdk:17-jdk-slim
|
FROM bellsoft/liberica-openjdk-alpine:17
|
||||||
|
|
||||||
# Copy the application JAR file
|
# Copy the application JAR file
|
||||||
COPY build/libs/*.jar app.jar
|
COPY build/libs/*.jar app.jar
|
||||||
@@ -8,7 +8,7 @@ COPY build/libs/*.jar app.jar
|
|||||||
EXPOSE 8080
|
EXPOSE 8080
|
||||||
|
|
||||||
# Set environment variables
|
# Set environment variables
|
||||||
ENV GROUPS_TO_REMOVE=LibreOffice,CLI
|
ENV GROUPS_TO_REMOVE=CLI
|
||||||
|
|
||||||
# Run the application
|
# Run the application
|
||||||
CMD ["java", "-jar", "/app.jar"]
|
CMD ["java", "-jar", "/app.jar"]
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
# Main stage
|
# Main stage
|
||||||
FROM openjdk:17-jdk-slim AS base
|
FROM bellsoft/liberica-openjdk-debian:17 AS base
|
||||||
RUN apt-get update && \
|
RUN apt-get update && \
|
||||||
apt-get install -y --no-install-recommends \
|
apt-get install -y --no-install-recommends \
|
||||||
libreoffice-core-nogui \
|
libreoffice-core-nogui \
|
||||||
|
|||||||
35
Endpoint-groups.md
Normal file
35
Endpoint-groups.md
Normal 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 | | | | ✔️ | | | | | | | ✔️ |
|
||||||
@@ -123,7 +123,7 @@ This folder is required for the python scripts using OpenCV
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo mkdir /opt/Stirling-PDF &&\
|
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/ &&\
|
sudo mv scripts /opt/Stirling-PDF/ &&\
|
||||||
echo "Scripts installed."
|
echo "Scripts installed."
|
||||||
```
|
```
|
||||||
|
|||||||
14
README.md
14
README.md
@@ -21,7 +21,7 @@ Feel free to request any features or bug fixes either in github issues or our [D
|
|||||||

|

|
||||||
|
|
||||||
## Features
|
## 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.
|
- 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
|
- Merge multiple PDFs together into a single resultant file
|
||||||
- Convert PDFs to and from images
|
- 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
|
## Technologies used
|
||||||
- Spring Boot + Thymeleaf
|
- Spring Boot + Thymeleaf
|
||||||
- PDFBox
|
- PDFBox
|
||||||
|
- IText7
|
||||||
- [LibreOffice](https://www.libreoffice.org/discover/libreoffice/) for advanced conversions
|
- [LibreOffice](https://www.libreoffice.org/discover/libreoffice/) for advanced conversions
|
||||||
- [OcrMyPdf](https://github.com/ocrmypdf/OCRmyPDF)
|
- [OcrMyPdf](https://github.com/ocrmypdf/OCRmyPDF)
|
||||||
- HTML, CSS, JavaScript
|
- HTML, CSS, JavaScript
|
||||||
@@ -68,13 +69,20 @@ Please view https://github.com/Frooodle/Stirling-PDF/blob/main/LocalRunGuide.md
|
|||||||
### Docker
|
### Docker
|
||||||
https://hub.docker.com/r/frooodle/s-pdf
|
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 Run
|
Docker Run
|
||||||
```
|
```
|
||||||
docker run -d \
|
docker run -d \
|
||||||
-p 8080:8080 \
|
-p 8080:8080 \
|
||||||
-v /location/of/trainingData:/usr/share/tesseract-ocr/4.00/tessdata \
|
-v /location/of/trainingData:/usr/share/tesseract-ocr/4.00/tessdata \
|
||||||
--name stirling-pdf \
|
--name stirling-pdf \
|
||||||
frooodle/s-pdf
|
frooodle/s-pdf:latest
|
||||||
|
|
||||||
|
|
||||||
Can also add these for customisation but are not required
|
Can also add these for customisation but are not required
|
||||||
@@ -90,7 +98,7 @@ Docker Compose
|
|||||||
version: '3.3'
|
version: '3.3'
|
||||||
services:
|
services:
|
||||||
stirling-pdf:
|
stirling-pdf:
|
||||||
image: frooodle/s-pdf
|
image: frooodle/s-pdf:latest
|
||||||
ports:
|
ports:
|
||||||
- '8080:8080'
|
- '8080:8080'
|
||||||
volumes:
|
volumes:
|
||||||
|
|||||||
48
Version-groups.md
Normal file
48
Version-groups.md
Normal 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 | | | ✔️
|
||||||
61
build.gradle
61
build.gradle
@@ -1,21 +1,53 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id 'java'
|
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 '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'
|
group = 'stirling.software'
|
||||||
version = '0.9.1'
|
version = '0.10.2'
|
||||||
sourceCompatibility = '17'
|
sourceCompatibility = '17'
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
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 {
|
dependencies {
|
||||||
implementation 'org.springframework.boot:spring-boot-starter-web:3.0.6'
|
implementation 'org.springframework.boot:spring-boot-starter-web:3.1.0'
|
||||||
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf:3.0.6'
|
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf:3.1.0'
|
||||||
testImplementation 'org.springframework.boot:spring-boot-starter-test:3.0.6'
|
testImplementation 'org.springframework.boot:spring-boot-starter-test:3.1.0'
|
||||||
// https://mvnrepository.com/artifact/org.apache.pdfbox/jbig2-imageio
|
// https://mvnrepository.com/artifact/org.apache.pdfbox/jbig2-imageio
|
||||||
implementation group: 'org.apache.pdfbox', name: 'jbig2-imageio', version: '3.0.4'
|
implementation group: 'org.apache.pdfbox', name: 'jbig2-imageio', version: '3.0.4'
|
||||||
implementation 'commons-io:commons-io:2.11.0'
|
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 {
|
jar {
|
||||||
enabled = false
|
enabled = false
|
||||||
manifest {
|
manifest {
|
||||||
|
|||||||
32
groups.md
32
groups.md
@@ -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
36
lauch4jConfig.xml
Normal 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>
|
||||||
@@ -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)
|
blurred_image = cv2.GaussianBlur(image, (blur_size, blur_size), 0)
|
||||||
|
|
||||||
_, thresholded_image = cv2.threshold(blurred_image, white_value - threshold, white_value, cv2.THRESH_BINARY)
|
_, thresholded_image = cv2.threshold(blurred_image, white_value - threshold, white_value, cv2.THRESH_BINARY)
|
||||||
|
|
||||||
# Calculate the percentage of white pixels in the thresholded image
|
# 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
|
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}")
|
print(f"Page has white pixel percent of {white_pixel_percentage}")
|
||||||
return white_pixel_percentage > white_percent
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
rootProject.name = 'S-PDF'
|
rootProject.name = 'Stirling-PDF'
|
||||||
|
|||||||
@@ -2,10 +2,63 @@ package stirling.software.SPDF;
|
|||||||
|
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
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
|
@SpringBootApplication
|
||||||
public class SPdfApplication {
|
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) {
|
public static void main(String[] args) {
|
||||||
SpringApplication.run(SPdfApplication.class, 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -16,7 +16,7 @@ public class AppConfig {
|
|||||||
@Bean(name = "appVersion")
|
@Bean(name = "appVersion")
|
||||||
public String appVersion() {
|
public String appVersion() {
|
||||||
String version = getClass().getPackage().getImplementationVersion();
|
String version = getClass().getPackage().getImplementationVersion();
|
||||||
return (version != null) ? version : "0.3.3";
|
return (version != null) ? version : "0.0.0";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean(name = "homeText")
|
@Bean(name = "homeText")
|
||||||
|
|||||||
@@ -20,12 +20,14 @@ public class EndpointConfiguration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void enableEndpoint(String endpoint) {
|
public void enableEndpoint(String endpoint) {
|
||||||
endpointStatuses.put(endpoint, true);
|
endpointStatuses.put(endpoint, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void disableEndpoint(String endpoint) {
|
public void disableEndpoint(String endpoint) {
|
||||||
logger.info("Disabling {}", endpoint);
|
if(!endpointStatuses.containsKey(endpoint) || endpointStatuses.get(endpoint) != false) {
|
||||||
endpointStatuses.put(endpoint, false);
|
logger.info("Disabling {}", endpoint);
|
||||||
|
endpointStatuses.put(endpoint, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isEndpointEnabled(String endpoint) {
|
public boolean isEndpointEnabled(String endpoint) {
|
||||||
@@ -64,7 +66,9 @@ public class EndpointConfiguration {
|
|||||||
addEndpointToGroup("PageOps", "split-pdfs");
|
addEndpointToGroup("PageOps", "split-pdfs");
|
||||||
addEndpointToGroup("PageOps", "pdf-organizer");
|
addEndpointToGroup("PageOps", "pdf-organizer");
|
||||||
addEndpointToGroup("PageOps", "rotate-pdf");
|
addEndpointToGroup("PageOps", "rotate-pdf");
|
||||||
|
addEndpointToGroup("PageOps", "multi-page-layout");
|
||||||
|
addEndpointToGroup("PageOps", "scale-pages");
|
||||||
|
|
||||||
// Adding endpoints to "Convert" group
|
// Adding endpoints to "Convert" group
|
||||||
addEndpointToGroup("Convert", "pdf-to-img");
|
addEndpointToGroup("Convert", "pdf-to-img");
|
||||||
addEndpointToGroup("Convert", "img-to-pdf");
|
addEndpointToGroup("Convert", "img-to-pdf");
|
||||||
@@ -82,6 +86,9 @@ public class EndpointConfiguration {
|
|||||||
addEndpointToGroup("Security", "remove-password");
|
addEndpointToGroup("Security", "remove-password");
|
||||||
addEndpointToGroup("Security", "change-permissions");
|
addEndpointToGroup("Security", "change-permissions");
|
||||||
addEndpointToGroup("Security", "add-watermark");
|
addEndpointToGroup("Security", "add-watermark");
|
||||||
|
addEndpointToGroup("Security", "cert-sign");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Adding endpoints to "Other" group
|
// Adding endpoints to "Other" group
|
||||||
addEndpointToGroup("Other", "ocr-pdf");
|
addEndpointToGroup("Other", "ocr-pdf");
|
||||||
@@ -158,6 +165,9 @@ public class EndpointConfiguration {
|
|||||||
addEndpointToGroup("Java", "add-image");
|
addEndpointToGroup("Java", "add-image");
|
||||||
addEndpointToGroup("Java", "extract-images");
|
addEndpointToGroup("Java", "extract-images");
|
||||||
addEndpointToGroup("Java", "change-metadata");
|
addEndpointToGroup("Java", "change-metadata");
|
||||||
|
addEndpointToGroup("Java", "cert-sign");
|
||||||
|
addEndpointToGroup("Java", "multi-page-layout");
|
||||||
|
addEndpointToGroup("Java", "scale-pages");
|
||||||
|
|
||||||
|
|
||||||
//Javascript
|
//Javascript
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
package stirling.software.SPDF.config;
|
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.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
@@ -10,13 +14,23 @@ import io.swagger.v3.oas.models.info.Info;
|
|||||||
@Configuration
|
@Configuration
|
||||||
public class OpenApiConfig {
|
public class OpenApiConfig {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public OpenAPI customOpenAPI() {
|
public OpenAPI customOpenAPI() {
|
||||||
String version = getClass().getPackage().getImplementationVersion();
|
String version = getClass().getPackage().getImplementationVersion();
|
||||||
version = (version != null) ? version : "1.0.0";
|
if (version == null) {
|
||||||
|
Properties props = new Properties();
|
||||||
return new OpenAPI().components(new Components()).info(
|
try (InputStream input = getClass().getClassLoader().getResourceAsStream("version.properties")) {
|
||||||
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."));
|
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."));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import org.springframework.web.multipart.MultipartFile;
|
|||||||
|
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.Parameter;
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
import stirling.software.SPDF.utils.PdfUtils;
|
import stirling.software.SPDF.utils.WebResponseUtils;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
public class MergeController {
|
public class MergeController {
|
||||||
@@ -65,7 +65,7 @@ public class MergeController {
|
|||||||
|
|
||||||
|
|
||||||
// Return the merged PDF as a response
|
// 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) {
|
for (PDDocument doc : documents) {
|
||||||
// Close the document after processing
|
// Close the document after processing
|
||||||
|
|||||||
@@ -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");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -17,111 +17,191 @@ import org.springframework.web.multipart.MultipartFile;
|
|||||||
|
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.Parameter;
|
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
|
@RestController
|
||||||
public class RearrangePagesPDFController {
|
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")
|
PDDocument document = PDDocument.load(pdfFile.getBytes());
|
||||||
@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());
|
// 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
|
List<Integer> pagesToRemove = GeneralUtils.parsePageList(pageOrderArr, document.getNumberOfPages());
|
||||||
String[] pageOrderArr = pagesToDelete.split(",");
|
|
||||||
|
|
||||||
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) {
|
private List<Integer> removeFirst(int totalPages) {
|
||||||
List<Integer> newPageOrder = new ArrayList<>();
|
if (totalPages <= 1)
|
||||||
// loop through the page order array
|
return new ArrayList<>();
|
||||||
for (String element : pageOrderArr) {
|
List<Integer> newPageOrder = new ArrayList<>();
|
||||||
// check if the element contains a range of pages
|
for (int i = 2; i <= totalPages; i++) {
|
||||||
if (element.contains("-")) {
|
newPageOrder.add(i - 1);
|
||||||
// split the range into start and end page
|
}
|
||||||
String[] range = element.split("-");
|
return newPageOrder;
|
||||||
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;
|
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")
|
private List<Integer> removeFirstAndLast(int totalPages) {
|
||||||
@Operation(summary = "Rearrange pages in a PDF file",
|
if (totalPages <= 2)
|
||||||
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.")
|
return new ArrayList<>();
|
||||||
public ResponseEntity<byte[]> rearrangePages(
|
List<Integer> newPageOrder = new ArrayList<>();
|
||||||
@RequestPart(required = true, value = "fileInput")
|
for (int i = 2; i < totalPages; i++) {
|
||||||
@Parameter(description = "The input PDF file to rearrange pages")
|
newPageOrder.add(i - 1);
|
||||||
MultipartFile pdfFile,
|
}
|
||||||
@RequestParam("pageOrder")
|
return newPageOrder;
|
||||||
@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());
|
|
||||||
|
|
||||||
// Split the page order string into an array of page numbers or range of numbers
|
private List<Integer> reverseOrder(int totalPages) {
|
||||||
String[] pageOrderArr = pageOrder.split(",");
|
List<Integer> newPageOrder = new ArrayList<>();
|
||||||
// int[] newPageOrder = new int[pageOrderArr.length];
|
for (int i = totalPages; i >= 1; i--) {
|
||||||
int totalPages = document.getNumberOfPages();
|
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
|
private List<Integer> bookletSort(int totalPages) {
|
||||||
List<PDPage> newPages = new ArrayList<>();
|
List<Integer> newPageOrder = new ArrayList<>();
|
||||||
for (int i = 0; i < newPageOrder.size(); i++) {
|
for (int i = 0; i < totalPages / 2; i++) {
|
||||||
newPages.add(document.getPage(newPageOrder.get(i)));
|
newPageOrder.add(i);
|
||||||
}
|
newPageOrder.add(totalPages - i - 1);
|
||||||
|
}
|
||||||
|
return newPageOrder;
|
||||||
|
}
|
||||||
|
|
||||||
// Remove all the pages from the original document
|
private List<Integer> oddEvenSplit(int totalPages) {
|
||||||
for (int i = document.getNumberOfPages() - 1; i >= 0; i--) {
|
List<Integer> newPageOrder = new ArrayList<>();
|
||||||
document.removePage(i);
|
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
|
private List<Integer> processCustomMode(String customMode, int totalPages) {
|
||||||
for (PDPage page : newPages) {
|
try {
|
||||||
document.addPage(page);
|
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");
|
@PostMapping(consumes = "multipart/form-data", value = "/rearrange-pages")
|
||||||
} catch (IOException e) {
|
@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);
|
// Split the page order string into an array of page numbers or range of numbers
|
||||||
return null;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import org.springframework.web.multipart.MultipartFile;
|
|||||||
|
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.Parameter;
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
import stirling.software.SPDF.utils.PdfUtils;
|
import stirling.software.SPDF.utils.WebResponseUtils;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
public class RotationController {
|
public class RotationController {
|
||||||
@@ -46,7 +46,7 @@ public class RotationController {
|
|||||||
page.setRotation(page.getRotation() + angle);
|
page.setRotation(page.getRotation() + angle);
|
||||||
}
|
}
|
||||||
|
|
||||||
return PdfUtils.pdfDocToWebResponse(document, pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_rotated.pdf");
|
return WebResponseUtils.pdfDocToWebResponse(document, pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_rotated.pdf");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -6,7 +6,6 @@ import java.io.InputStream;
|
|||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.zip.ZipEntry;
|
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.Operation;
|
||||||
import io.swagger.v3.oas.annotations.Parameter;
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
|
import stirling.software.SPDF.utils.GeneralUtils;
|
||||||
|
import stirling.software.SPDF.utils.WebResponseUtils;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
public class SplitPDFController {
|
public class SplitPDFController {
|
||||||
@@ -38,7 +39,7 @@ public class SplitPDFController {
|
|||||||
@PostMapping(consumes = "multipart/form-data", value = "/split-pages")
|
@PostMapping(consumes = "multipart/form-data", value = "/split-pages")
|
||||||
@Operation(summary = "Split a PDF file into separate documents",
|
@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.")
|
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")
|
@RequestPart(required = true, value = "fileInput")
|
||||||
@Parameter(description = "The input PDF file to be split")
|
@Parameter(description = "The input PDF file to be split")
|
||||||
MultipartFile file,
|
MultipartFile file,
|
||||||
@@ -58,39 +59,28 @@ public class SplitPDFController {
|
|||||||
pageNumbers.add(i);
|
pageNumbers.add(i);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
List<String> pageNumbersStr = new ArrayList<>(Arrays.asList(pages.split(",")));
|
String[] splitPoints = pages.split(",");
|
||||||
if (!pageNumbersStr.contains(String.valueOf(document.getNumberOfPages()))) {
|
for (String splitPoint : splitPoints) {
|
||||||
String lastpage = String.valueOf(document.getNumberOfPages());
|
List<Integer> orderedPages = GeneralUtils.parsePageList(new String[] {splitPoint}, document.getNumberOfPages());
|
||||||
pageNumbersStr.add(lastpage);
|
pageNumbers.addAll(orderedPages);
|
||||||
}
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
// 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(",")));
|
logger.info("Splitting PDF into pages: {}", pageNumbers.stream().map(String::valueOf).collect(Collectors.joining(",")));
|
||||||
|
|
||||||
// split the document
|
// split the document
|
||||||
List<ByteArrayOutputStream> splitDocumentsBoas = new ArrayList<>();
|
List<ByteArrayOutputStream> splitDocumentsBoas = new ArrayList<>();
|
||||||
int currentPage = 0;
|
int previousPageNumber = 0;
|
||||||
for (int pageNumber : pageNumbers) {
|
for (int splitPoint : pageNumbers) {
|
||||||
try (PDDocument splitDocument = new PDDocument()) {
|
try (PDDocument splitDocument = new PDDocument()) {
|
||||||
for (int i = currentPage; i < pageNumber; i++) {
|
for (int i = previousPageNumber; i <= splitPoint; i++) {
|
||||||
PDPage page = document.getPage(i);
|
PDPage page = document.getPage(i);
|
||||||
splitDocument.addPage(page);
|
splitDocument.addPage(page);
|
||||||
logger.debug("Adding page {} to split document", i);
|
logger.debug("Adding page {} to split document", i);
|
||||||
}
|
}
|
||||||
currentPage = pageNumber;
|
previousPageNumber = splitPoint + 1;
|
||||||
logger.debug("Setting current page to {}", currentPage);
|
|
||||||
|
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
splitDocument.save(baos);
|
splitDocument.save(baos);
|
||||||
@@ -102,15 +92,17 @@ public class SplitPDFController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// closing the original document
|
// closing the original document
|
||||||
document.close();
|
document.close();
|
||||||
|
|
||||||
Path zipFile = Files.createTempFile("split_documents", ".zip");
|
Path zipFile = Files.createTempFile("split_documents", ".zip");
|
||||||
|
|
||||||
|
String filename = file.getOriginalFilename().replaceFirst("[.][^.]+$", "");
|
||||||
try (ZipOutputStream zipOut = new ZipOutputStream(Files.newOutputStream(zipFile))) {
|
try (ZipOutputStream zipOut = new ZipOutputStream(Files.newOutputStream(zipFile))) {
|
||||||
// loop through the split documents and write them to the zip file
|
// loop through the split documents and write them to the zip file
|
||||||
for (int i = 0; i < splitDocumentsBoas.size(); i++) {
|
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);
|
ByteArrayOutputStream baos = splitDocumentsBoas.get(i);
|
||||||
byte[] pdf = baos.toByteArray();
|
byte[] pdf = baos.toByteArray();
|
||||||
|
|
||||||
@@ -129,12 +121,11 @@ public class SplitPDFController {
|
|||||||
|
|
||||||
logger.info("Successfully created zip file with split documents: {}", zipFile.toString());
|
logger.info("Successfully created zip file with split documents: {}", zipFile.toString());
|
||||||
byte[] data = Files.readAllBytes(zipFile);
|
byte[] data = Files.readAllBytes(zipFile);
|
||||||
ByteArrayResource resource = new ByteArrayResource(data);
|
|
||||||
Files.delete(zipFile);
|
Files.delete(zipFile);
|
||||||
|
|
||||||
// return the Resource in the response
|
// return the Resource in the response
|
||||||
return ResponseEntity.ok().header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + file.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_split.zip")
|
return WebResponseUtils.bytesToWebResponse(data, filename + ".zip", MediaType.APPLICATION_OCTET_STREAM);
|
||||||
.contentType(MediaType.APPLICATION_OCTET_STREAM).contentLength(resource.contentLength()).body(resource);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import io.swagger.v3.oas.annotations.Operation;
|
|||||||
import io.swagger.v3.oas.annotations.Parameter;
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import stirling.software.SPDF.utils.PdfUtils;
|
import stirling.software.SPDF.utils.PdfUtils;
|
||||||
|
import stirling.software.SPDF.utils.WebResponseUtils;
|
||||||
@RestController
|
@RestController
|
||||||
public class ConvertImgPDFController {
|
public class ConvertImgPDFController {
|
||||||
|
|
||||||
@@ -56,8 +57,9 @@ public class ConvertImgPDFController {
|
|||||||
// returns bytes for image
|
// returns bytes for image
|
||||||
boolean singleImage = singleOrMultiple.equals("single");
|
boolean singleImage = singleOrMultiple.equals("single");
|
||||||
byte[] result = null;
|
byte[] result = null;
|
||||||
|
String filename = file.getOriginalFilename().replaceFirst("[.][^.]+$", "");
|
||||||
try {
|
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) {
|
} catch (IOException e) {
|
||||||
// TODO Auto-generated catch block
|
// TODO Auto-generated catch block
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
@@ -74,7 +76,7 @@ public class ConvertImgPDFController {
|
|||||||
ByteArrayResource resource = new ByteArrayResource(result);
|
ByteArrayResource resource = new ByteArrayResource(result);
|
||||||
// return the Resource in the response
|
// return the Resource in the response
|
||||||
return ResponseEntity.ok()
|
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);
|
.contentType(MediaType.APPLICATION_OCTET_STREAM).contentLength(resource.contentLength()).body(resource);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -97,7 +99,7 @@ public class ConvertImgPDFController {
|
|||||||
boolean autoRotate) throws IOException {
|
boolean autoRotate) throws IOException {
|
||||||
// Convert the file to PDF and get the resulting bytes
|
// Convert the file to PDF and get the resulting bytes
|
||||||
byte[] bytes = PdfUtils.imageToPdf(file, stretchToFit, autoRotate, colorType);
|
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) {
|
private String getMediaType(String imageFormat) {
|
||||||
|
|||||||
@@ -17,8 +17,8 @@ import org.springframework.web.multipart.MultipartFile;
|
|||||||
|
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.Parameter;
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
import stirling.software.SPDF.utils.PdfUtils;
|
|
||||||
import stirling.software.SPDF.utils.ProcessExecutor;
|
import stirling.software.SPDF.utils.ProcessExecutor;
|
||||||
|
import stirling.software.SPDF.utils.WebResponseUtils;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
public class ConvertOfficeController {
|
public class ConvertOfficeController {
|
||||||
@@ -72,7 +72,7 @@ public class ConvertOfficeController {
|
|||||||
// LibreOfficeListener.getInstance().start();
|
// LibreOfficeListener.getInstance().start();
|
||||||
|
|
||||||
byte[] pdfByteArray = convertToPdf(inputFile);
|
byte[] pdfByteArray = convertToPdf(inputFile);
|
||||||
return PdfUtils.bytesToWebResponse(pdfByteArray, inputFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_convertedToPDF.pdf");
|
return WebResponseUtils.bytesToWebResponse(pdfByteArray, inputFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_convertedToPDF.pdf");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,8 +14,8 @@ import org.springframework.web.multipart.MultipartFile;
|
|||||||
|
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.Parameter;
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
import stirling.software.SPDF.utils.PdfUtils;
|
|
||||||
import stirling.software.SPDF.utils.ProcessExecutor;
|
import stirling.software.SPDF.utils.ProcessExecutor;
|
||||||
|
import stirling.software.SPDF.utils.WebResponseUtils;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
public class ConvertPDFToPDFA {
|
public class ConvertPDFToPDFA {
|
||||||
@@ -58,7 +58,7 @@ public class ConvertPDFToPDFA {
|
|||||||
|
|
||||||
// Return the optimized PDF as a response
|
// Return the optimized PDF as a response
|
||||||
String outputFilename = inputFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_PDFA.pdf";
|
String outputFilename = inputFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_PDFA.pdf";
|
||||||
return PdfUtils.bytesToWebResponse(pdfBytes, outputFilename);
|
return WebResponseUtils.bytesToWebResponse(pdfBytes, outputFilename);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,9 +28,9 @@ import org.springframework.web.multipart.MultipartFile;
|
|||||||
|
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.Parameter;
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
import stirling.software.SPDF.utils.ImageFinder;
|
import stirling.software.SPDF.pdf.ImageFinder;
|
||||||
import stirling.software.SPDF.utils.PdfUtils;
|
|
||||||
import stirling.software.SPDF.utils.ProcessExecutor;
|
import stirling.software.SPDF.utils.ProcessExecutor;
|
||||||
|
import stirling.software.SPDF.utils.WebResponseUtils;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
public class BlankPageController {
|
public class BlankPageController {
|
||||||
@@ -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) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
|
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
|
||||||
|
|||||||
@@ -31,8 +31,9 @@ import org.springframework.web.multipart.MultipartFile;
|
|||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.Parameter;
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
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.ProcessExecutor;
|
||||||
|
import stirling.software.SPDF.utils.WebResponseUtils;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
public class CompressController {
|
public class CompressController {
|
||||||
@@ -55,7 +56,7 @@ public class CompressController {
|
|||||||
Long expectedOutputSize = 0L;
|
Long expectedOutputSize = 0L;
|
||||||
boolean autoMode = false;
|
boolean autoMode = false;
|
||||||
if (expectedOutputSizeString != null && expectedOutputSizeString.length() > 1 ) {
|
if (expectedOutputSizeString != null && expectedOutputSizeString.length() > 1 ) {
|
||||||
expectedOutputSize = PdfUtils.convertSizeToBytes(expectedOutputSizeString);
|
expectedOutputSize = GeneralUtils.convertSizeToBytes(expectedOutputSizeString);
|
||||||
autoMode = true;
|
autoMode = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -224,7 +225,7 @@ public class CompressController {
|
|||||||
|
|
||||||
// Return the optimized PDF as a response
|
// Return the optimized PDF as a response
|
||||||
String outputFilename = inputFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_Optimized.pdf";
|
String outputFilename = inputFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_Optimized.pdf";
|
||||||
return PdfUtils.bytesToWebResponse(pdfBytes, outputFilename);
|
return WebResponseUtils.bytesToWebResponse(pdfBytes, outputFilename);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,8 +31,8 @@ import org.springframework.web.multipart.MultipartFile;
|
|||||||
|
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.Parameter;
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
import stirling.software.SPDF.utils.PdfUtils;
|
|
||||||
import stirling.software.SPDF.utils.ProcessExecutor;
|
import stirling.software.SPDF.utils.ProcessExecutor;
|
||||||
|
import stirling.software.SPDF.utils.WebResponseUtils;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
public class ExtractImageScansController {
|
public class ExtractImageScansController {
|
||||||
@@ -147,11 +147,11 @@ public class ExtractImageScansController {
|
|||||||
// Clean up the temporary zip file
|
// Clean up the temporary zip file
|
||||||
Files.delete(tempZipFile);
|
Files.delete(tempZipFile);
|
||||||
|
|
||||||
return PdfUtils.bytesToWebResponse(zipBytes, outputZipFilename, MediaType.APPLICATION_OCTET_STREAM);
|
return WebResponseUtils.bytesToWebResponse(zipBytes, outputZipFilename, MediaType.APPLICATION_OCTET_STREAM);
|
||||||
} else {
|
} else {
|
||||||
// Return the processed image as a response
|
// Return the processed image as a response
|
||||||
byte[] imageBytes = processedImageBytes.get(0);
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ import org.springframework.web.multipart.MultipartFile;
|
|||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.Parameter;
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import stirling.software.SPDF.utils.PdfUtils;
|
import stirling.software.SPDF.utils.WebResponseUtils;
|
||||||
@RestController
|
@RestController
|
||||||
public class ExtractImagesController {
|
public class ExtractImagesController {
|
||||||
|
|
||||||
@@ -59,7 +59,7 @@ public class ExtractImagesController {
|
|||||||
zos.setLevel(Deflater.BEST_COMPRESSION);
|
zos.setLevel(Deflater.BEST_COMPRESSION);
|
||||||
|
|
||||||
int imageIndex = 1;
|
int imageIndex = 1;
|
||||||
|
String filename = file.getOriginalFilename().replaceFirst("[.][^.]+$", "");
|
||||||
int pageNum = 1;
|
int pageNum = 1;
|
||||||
// Iterate over each page
|
// Iterate over each page
|
||||||
for (PDPage page : document.getPages()) {
|
for (PDPage page : document.getPages()) {
|
||||||
@@ -81,7 +81,7 @@ public class ExtractImagesController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Write image to zip file
|
// Write image to zip file
|
||||||
String imageName = "Image " + imageIndex + " (Page " + pageNum + ")." + format;
|
String imageName = filename + "_" + imageIndex + " (Page " + pageNum + ")." + format;
|
||||||
ZipEntry zipEntry = new ZipEntry(imageName);
|
ZipEntry zipEntry = new ZipEntry(imageName);
|
||||||
zos.putNextEntry(zipEntry);
|
zos.putNextEntry(zipEntry);
|
||||||
|
|
||||||
@@ -106,7 +106,7 @@ public class ExtractImagesController {
|
|||||||
// Create ByteArrayResource from byte array
|
// Create ByteArrayResource from byte array
|
||||||
byte[] zipContents = baos.toByteArray();
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -19,7 +19,7 @@ import org.springframework.web.multipart.MultipartFile;
|
|||||||
|
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.Parameter;
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
import stirling.software.SPDF.utils.PdfUtils;
|
import stirling.software.SPDF.utils.WebResponseUtils;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
public class MetadataController {
|
public class MetadataController {
|
||||||
@@ -159,7 +159,7 @@ public class MetadataController {
|
|||||||
info.setTrapped(trapped);
|
info.setTrapped(trapped);
|
||||||
|
|
||||||
document.setDocumentInformation(info);
|
document.setDocumentInformation(info);
|
||||||
return PdfUtils.pdfDocToWebResponse(document, pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_metadata.pdf");
|
return WebResponseUtils.pdfDocToWebResponse(document, pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_metadata.pdf");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,8 +27,8 @@ import org.springframework.web.multipart.MultipartFile;
|
|||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.Parameter;
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
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.ProcessExecutor;
|
||||||
|
import stirling.software.SPDF.utils.WebResponseUtils;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
public class OCRController {
|
public class OCRController {
|
||||||
@@ -189,11 +189,11 @@ public class OCRController {
|
|||||||
Files.delete(sidecarTextPath);
|
Files.delete(sidecarTextPath);
|
||||||
|
|
||||||
// Return the zip file containing both the PDF and the text file
|
// Return the zip file containing both the PDF and the text file
|
||||||
return PdfUtils.bytesToWebResponse(zipBytes, outputZipFilename, MediaType.APPLICATION_OCTET_STREAM);
|
return WebResponseUtils.bytesToWebResponse(zipBytes, outputZipFilename, MediaType.APPLICATION_OCTET_STREAM);
|
||||||
} else {
|
} else {
|
||||||
// Return the OCR processed PDF as a response
|
// Return the OCR processed PDF as a response
|
||||||
Files.delete(tempOutputFile);
|
Files.delete(tempOutputFile);
|
||||||
return PdfUtils.bytesToWebResponse(pdfBytes, outputFilename);
|
return WebResponseUtils.bytesToWebResponse(pdfBytes, outputFilename);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import org.springframework.web.multipart.MultipartFile;
|
|||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.Parameter;
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
import stirling.software.SPDF.utils.PdfUtils;
|
import stirling.software.SPDF.utils.PdfUtils;
|
||||||
|
import stirling.software.SPDF.utils.WebResponseUtils;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
public class OverlayImageController {
|
public class OverlayImageController {
|
||||||
@@ -47,7 +48,7 @@ public class OverlayImageController {
|
|||||||
byte[] imageBytes = imageFile.getBytes();
|
byte[] imageBytes = imageFile.getBytes();
|
||||||
byte[] result = PdfUtils.overlayImage(pdfBytes, imageBytes, x, y, everyPage);
|
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) {
|
} catch (IOException e) {
|
||||||
logger.error("Failed to add image to PDF", e);
|
logger.error("Failed to add image to PDF", e);
|
||||||
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
|
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ import org.springframework.web.multipart.MultipartFile;
|
|||||||
|
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.Parameter;
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
import stirling.software.SPDF.utils.PdfUtils;
|
|
||||||
import stirling.software.SPDF.utils.ProcessExecutor;
|
import stirling.software.SPDF.utils.ProcessExecutor;
|
||||||
|
import stirling.software.SPDF.utils.WebResponseUtils;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
public class RepairController {
|
public class RepairController {
|
||||||
@@ -60,7 +60,7 @@ public class RepairController {
|
|||||||
|
|
||||||
// Return the optimized PDF as a response
|
// Return the optimized PDF as a response
|
||||||
String outputFilename = inputFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_repaired.pdf";
|
String outputFilename = inputFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_repaired.pdf";
|
||||||
return PdfUtils.bytesToWebResponse(pdfBytes, outputFilename);
|
return WebResponseUtils.bytesToWebResponse(pdfBytes, outputFilename);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package stirling.software.SPDF.controller.api.security;
|
package stirling.software.SPDF.controller.api.security;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
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.Operation;
|
||||||
import io.swagger.v3.oas.annotations.Parameter;
|
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
|
@RestController
|
||||||
public class CertSignController {
|
public class CertSignController {
|
||||||
|
|
||||||
@@ -177,8 +177,17 @@ public class CertSignController {
|
|||||||
String signingDate = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss z").format(new Date());
|
String signingDate = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss z").format(new Date());
|
||||||
|
|
||||||
// Prepare the text for the digital signature
|
// 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
|
// Get the PDF font and measure the width and height of the text block
|
||||||
PdfFont font = PdfFontFactory.createFont(StandardFonts.HELVETICA_BOLD);
|
PdfFont font = PdfFontFactory.createFont(StandardFonts.HELVETICA_BOLD);
|
||||||
float textWidth = Arrays.stream(layer2Text.split("\n"))
|
float textWidth = Arrays.stream(layer2Text.split("\n"))
|
||||||
@@ -206,12 +215,12 @@ public class CertSignController {
|
|||||||
|
|
||||||
// Configure the appearance of the digital signature
|
// Configure the appearance of the digital signature
|
||||||
appearance.setPageRect(rect)
|
appearance.setPageRect(rect)
|
||||||
.setContact(name)
|
.setContact(name != null ? name : "")
|
||||||
.setPageNumber(pageNumber)
|
.setPageNumber(pageNumber)
|
||||||
.setReason(reason)
|
.setReason(reason != null ? reason : "")
|
||||||
.setLocation(location)
|
.setLocation(location != null ? location : "")
|
||||||
.setReuseAppearance(false)
|
.setReuseAppearance(false)
|
||||||
.setLayer2Text(layer2Text);
|
.setLayer2Text(layer2Text.toString());
|
||||||
|
|
||||||
signer.setFieldName("sig");
|
signer.setFieldName("sig");
|
||||||
} else {
|
} else {
|
||||||
@@ -230,7 +239,7 @@ public class CertSignController {
|
|||||||
System.out.println("Signed PDF size: " + signedPdf.size());
|
System.out.println("Signed PDF size: " + signedPdf.size());
|
||||||
|
|
||||||
System.out.println("PDF signed = " + isPdfSigned(signedPdf.toByteArray()));
|
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 {
|
public boolean isPdfSigned(byte[] pdfData) throws IOException {
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import org.springframework.web.multipart.MultipartFile;
|
|||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.Parameter;
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import stirling.software.SPDF.utils.PdfUtils;
|
import stirling.software.SPDF.utils.WebResponseUtils;
|
||||||
@RestController
|
@RestController
|
||||||
public class PasswordController {
|
public class PasswordController {
|
||||||
|
|
||||||
@@ -38,7 +38,7 @@ public class PasswordController {
|
|||||||
String password) throws IOException {
|
String password) throws IOException {
|
||||||
PDDocument document = PDDocument.load(fileInput.getBytes(), password);
|
PDDocument document = PDDocument.load(fileInput.getBytes(), password);
|
||||||
document.setAllSecurityToBeRemoved(true);
|
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")
|
@PostMapping(consumes = "multipart/form-data", value = "/add-password")
|
||||||
@@ -50,8 +50,11 @@ public class PasswordController {
|
|||||||
@RequestPart(required = true, value = "fileInput")
|
@RequestPart(required = true, value = "fileInput")
|
||||||
@Parameter(description = "The input PDF file to which the password should be added", required = true)
|
@Parameter(description = "The input PDF file to which the password should be added", required = true)
|
||||||
MultipartFile fileInput,
|
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")
|
@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,
|
String password,
|
||||||
@RequestParam(defaultValue = "128", name = "keyLength")
|
@RequestParam(defaultValue = "128", name = "keyLength")
|
||||||
@Parameter(description = "The length of the encryption key", schema = @Schema(allowableValues = {"40", "128", "256"}))
|
@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());
|
PDDocument document = PDDocument.load(fileInput.getBytes());
|
||||||
AccessPermission ap = new AccessPermission();
|
AccessPermission ap = new AccessPermission();
|
||||||
|
|
||||||
ap.setCanAssembleDocument(!canAssembleDocument);
|
ap.setCanAssembleDocument(!canAssembleDocument);
|
||||||
ap.setCanExtractContent(!canExtractContent);
|
ap.setCanExtractContent(!canExtractContent);
|
||||||
ap.setCanExtractForAccessibility(!canExtractForAccessibility);
|
ap.setCanExtractForAccessibility(!canExtractForAccessibility);
|
||||||
@@ -93,14 +95,17 @@ public class PasswordController {
|
|||||||
ap.setCanModifyAnnotations(!canModifyAnnotations);
|
ap.setCanModifyAnnotations(!canModifyAnnotations);
|
||||||
ap.setCanPrint(!canPrint);
|
ap.setCanPrint(!canPrint);
|
||||||
ap.setCanPrintFaithful(!canPrintFaithful);
|
ap.setCanPrintFaithful(!canPrintFaithful);
|
||||||
StandardProtectionPolicy spp = new StandardProtectionPolicy(password, password, ap);
|
StandardProtectionPolicy spp = new StandardProtectionPolicy(ownerPassword, password, ap);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
spp.setEncryptionKeyLength(keyLength);
|
spp.setEncryptionKeyLength(keyLength);
|
||||||
|
|
||||||
spp.setPermissions(ap);
|
spp.setPermissions(ap);
|
||||||
|
|
||||||
document.protect(spp);
|
document.protect(spp);
|
||||||
|
|
||||||
return PdfUtils.pdfDocToWebResponse(document, fileInput.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_passworded.pdf");
|
return WebResponseUtils.pdfDocToWebResponse(document, fileInput.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_passworded.pdf");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import org.springframework.web.multipart.MultipartFile;
|
|||||||
|
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.Parameter;
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
import stirling.software.SPDF.utils.PdfUtils;
|
import stirling.software.SPDF.utils.WebResponseUtils;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
public class WatermarkController {
|
public class WatermarkController {
|
||||||
@@ -91,7 +91,7 @@ public class WatermarkController {
|
|||||||
// Close the content stream
|
// Close the content stream
|
||||||
contentStream.close();
|
contentStream.close();
|
||||||
}
|
}
|
||||||
return PdfUtils.pdfDocToWebResponse(document, pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_watermarked.pdf");
|
return WebResponseUtils.pdfDocToWebResponse(document, pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_watermarked.pdf");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,7 +74,9 @@ public class OtherWebController {
|
|||||||
@Hidden
|
@Hidden
|
||||||
public ModelAndView ocrPdfPage() {
|
public ModelAndView ocrPdfPage() {
|
||||||
ModelAndView modelAndView = new ModelAndView("other/ocr-pdf");
|
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");
|
modelAndView.addObject("currentPage", "ocr-pdf");
|
||||||
return modelAndView;
|
return modelAndView;
|
||||||
}
|
}
|
||||||
@@ -108,4 +110,25 @@ public class OtherWebController {
|
|||||||
return "other/remove-blanks";
|
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";
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package stirling.software.SPDF.utils;
|
package stirling.software.SPDF.pdf;
|
||||||
|
|
||||||
import java.awt.geom.Point2D;
|
import java.awt.geom.Point2D;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
91
src/main/java/stirling/software/SPDF/utils/GeneralUtils.java
Normal file
91
src/main/java/stirling/software/SPDF/utils/GeneralUtils.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package stirling.software.SPDF.utils;
|
||||||
|
|
||||||
|
public class PDFManipulationUtils {
|
||||||
|
|
||||||
|
}
|
||||||
@@ -92,6 +92,6 @@ public class PDFToFile {
|
|||||||
if (tempOutputDir != null)
|
if (tempOutputDir != null)
|
||||||
FileUtils.deleteDirectory(tempOutputDir.toFile());
|
FileUtils.deleteDirectory(tempOutputDir.toFile());
|
||||||
}
|
}
|
||||||
return PdfUtils.bytesToWebResponse(fileBytes, fileName, MediaType.APPLICATION_OCTET_STREAM);
|
return WebResponseUtils.bytesToWebResponse(fileBytes, fileName, MediaType.APPLICATION_OCTET_STREAM);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,15 +8,7 @@ import java.io.File;
|
|||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.net.URLEncoder;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.nio.file.Files;
|
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.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
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.PDPage;
|
||||||
import org.apache.pdfbox.pdmodel.PDPageContentStream;
|
import org.apache.pdfbox.pdmodel.PDPageContentStream;
|
||||||
import org.apache.pdfbox.pdmodel.common.PDRectangle;
|
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.LosslessFactory;
|
||||||
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
|
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
|
||||||
import org.apache.pdfbox.rendering.ImageType;
|
import org.apache.pdfbox.rendering.ImageType;
|
||||||
import org.apache.pdfbox.rendering.PDFRenderer;
|
import org.apache.pdfbox.rendering.PDFRenderer;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
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;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
public class PdfUtils {
|
public class PdfUtils {
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(PdfUtils.class);
|
private static final Logger logger = LoggerFactory.getLogger(PdfUtils.class);
|
||||||
|
|
||||||
public static ResponseEntity<byte[]> boasToWebResponse(ByteArrayOutputStream baos, String docName) throws IOException {
|
public static byte[] convertFromPdf(byte[] inputStream, String imageType, ImageType colorType, boolean singleImage, int DPI, String filename) throws IOException, Exception {
|
||||||
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 {
|
|
||||||
try (PDDocument document = PDDocument.load(new ByteArrayInputStream(inputStream))) {
|
try (PDDocument document = PDDocument.load(new ByteArrayInputStream(inputStream))) {
|
||||||
PDFRenderer pdfRenderer = new PDFRenderer(document);
|
PDFRenderer pdfRenderer = new PDFRenderer(document);
|
||||||
int pageCount = document.getNumberOfPages();
|
int pageCount = document.getNumberOfPages();
|
||||||
@@ -107,7 +73,7 @@ public class PdfUtils {
|
|||||||
ImageIO.write(image, imageType, baosImage);
|
ImageIO.write(image, imageType, baosImage);
|
||||||
|
|
||||||
// Add the image to the zip file
|
// 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());
|
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 {
|
public static byte[] imageToPdf(MultipartFile[] files, boolean stretchToFit, boolean autoRotate, String colorType) throws IOException {
|
||||||
try (PDDocument doc = new PDDocument()) {
|
try (PDDocument doc = new PDDocument()) {
|
||||||
for (MultipartFile file : files) {
|
for (MultipartFile file : files) {
|
||||||
|
String contentType = file.getContentType();
|
||||||
String originalFilename = file.getOriginalFilename();
|
String originalFilename = file.getOriginalFilename();
|
||||||
if (originalFilename != null && (originalFilename.toLowerCase().endsWith(".tiff") || originalFilename.toLowerCase().endsWith(".tif")) ) {
|
if (originalFilename != null && (originalFilename.toLowerCase().endsWith(".tiff") || originalFilename.toLowerCase().endsWith(".tif")) ) {
|
||||||
ImageReader reader = ImageIO.getImageReadersByFormatName("tiff").next();
|
ImageReader reader = ImageIO.getImageReadersByFormatName("tiff").next();
|
||||||
@@ -132,7 +99,7 @@ public class PdfUtils {
|
|||||||
int numPages = reader.getNumImages(true);
|
int numPages = reader.getNumImages(true);
|
||||||
for (int i = 0; i < numPages; i++) {
|
for (int i = 0; i < numPages; i++) {
|
||||||
BufferedImage pageImage = reader.read(i);
|
BufferedImage pageImage = reader.read(i);
|
||||||
BufferedImage convertedImage = convertColorType(pageImage, colorType);
|
BufferedImage convertedImage = ImageProcessingUtils.convertColorType(pageImage, colorType);
|
||||||
PDImageXObject pdImage = LosslessFactory.createFromImage(doc, convertedImage);
|
PDImageXObject pdImage = LosslessFactory.createFromImage(doc, convertedImage);
|
||||||
addImageToDocument(doc, pdImage, stretchToFit, autoRotate);
|
addImageToDocument(doc, pdImage, stretchToFit, autoRotate);
|
||||||
}
|
}
|
||||||
@@ -145,8 +112,13 @@ public class PdfUtils {
|
|||||||
fos.write(buffer, 0, len);
|
fos.write(buffer, 0, len);
|
||||||
}
|
}
|
||||||
BufferedImage image = ImageIO.read(imageFile);
|
BufferedImage image = ImageIO.read(imageFile);
|
||||||
BufferedImage convertedImage = convertColorType(image, colorType);
|
BufferedImage convertedImage = ImageProcessingUtils.convertColorType(image, colorType);
|
||||||
PDImageXObject pdImage = LosslessFactory.createFromImage(doc, convertedImage);
|
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);
|
addImageToDocument(doc, pdImage, stretchToFit, autoRotate);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
logger.error("Error writing image to file: {}", imageFile.getAbsolutePath(), 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 {
|
private static void addImageToDocument(PDDocument doc, PDImageXObject image, boolean stretchToFit, boolean autoRotate) throws IOException {
|
||||||
boolean imageIsLandscape = image.getWidth() > image.getHeight();
|
boolean imageIsLandscape = image.getWidth() > image.getHeight();
|
||||||
PDRectangle pageSize = PDRectangle.A4;
|
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 {
|
public static byte[] overlayImage(byte[] pdfBytes, byte[] imageBytes, float x, float y, boolean everyPage) throws IOException {
|
||||||
|
|
||||||
PDDocument document = PDDocument.load(new ByteArrayInputStream(pdfBytes));
|
PDDocument document = PDDocument.load(new ByteArrayInputStream(pdfBytes));
|
||||||
@@ -275,41 +202,7 @@ public class PdfUtils {
|
|||||||
return baos.toByteArray();
|
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -129,15 +129,37 @@ home.repair.desc = يحاول إصلاح ملف PDF تالف / معطل
|
|||||||
home.removeBlanks.title = إزالة الصفحات الفارغة
|
home.removeBlanks.title = إزالة الصفحات الفارغة
|
||||||
home.removeBlanks.desc = يكتشف ويزيل الصفحات الفارغة من المستند
|
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.title = قارن
|
||||||
home.compare.desc = يقارن ويظهر الاختلافات بين 2 من مستندات PDF
|
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
|
downloadPdf = تنزيل PDF
|
||||||
text=نص
|
text=نص
|
||||||
font=الخط
|
font=الخط
|
||||||
selectFillter = - حدد -
|
selectFillter = - حدد -
|
||||||
pageNum = رقم الصفحة
|
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.title = توقيع الشهادة
|
||||||
certSign.header = قم بتوقيع ملف PDF بشهادتك (العمل قيد التقدم)
|
certSign.header = قم بتوقيع ملف PDF بشهادتك (العمل قيد التقدم)
|
||||||
certSign.selectPDF = حدد ملف PDF للتوقيع:
|
certSign.selectPDF = حدد ملف PDF للتوقيع:
|
||||||
@@ -339,6 +361,9 @@ addPassword.selectText.10=منع التعديل
|
|||||||
addPassword.selectText.11=منع تعديل التعليقات التوضيحية
|
addPassword.selectText.11=منع تعديل التعليقات التوضيحية
|
||||||
addPassword.selectText.12=منع الطباعة
|
addPassword.selectText.12=منع الطباعة
|
||||||
addPassword.selectText.13=منع طباعة تنسيقات مختلفة
|
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=تشفير
|
addPassword.submit=تشفير
|
||||||
|
|
||||||
#watermark
|
#watermark
|
||||||
|
|||||||
@@ -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.title=Elimina les pàgines en blanc
|
||||||
home.removeBlanks.desc=Detecta i elimina les pàgines en blanc d'un document
|
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.title=Compara
|
||||||
home.compare.desc=Compara i mostra les diferències entre 2 documents PDF
|
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
|
downloadPdf=Descarregueu PDF
|
||||||
text=Text
|
text=Text
|
||||||
font=Tipus de lletra
|
font=Tipus de lletra
|
||||||
selectFillter=-- Selecciona --
|
selectFillter=-- Selecciona --
|
||||||
pageNum=Número de pàgina
|
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.title=Significació del certificat
|
||||||
certSign.header=Firmar un PDF amb el vostre certificat (Treball en curs)
|
certSign.header=Firmar un PDF amb el vostre certificat (Treball en curs)
|
||||||
certSign.selectPDF=Seleccioneu un fitxer PDF per signar:
|
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.11=Evita modificacions d'annotacions
|
||||||
addPassword.selectText.12=Evita impressió
|
addPassword.selectText.12=Evita impressió
|
||||||
addPassword.selectText.13=Evita impressió de diferents formats
|
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
|
addPassword.submit=Encripta
|
||||||
|
|
||||||
#watermark
|
#watermark
|
||||||
|
|||||||
@@ -121,15 +121,37 @@ home.repair.desc=Versucht, ein beschädigtes/kaputtes PDF zu reparieren
|
|||||||
home.removeBlanks.title=Leere Seiten entfernen
|
home.removeBlanks.title=Leere Seiten entfernen
|
||||||
home.removeBlanks.desc=Erkennt und entfernt leere Seiten aus einem Dokument
|
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.title=Vergleichen
|
||||||
home.compare.desc=Vergleicht und zeigt die Unterschiede zwischen zwei PDF-Dokumenten an
|
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
|
downloadPdf=PDF herunterladen
|
||||||
text=Text
|
text=Text
|
||||||
font=Schriftart
|
font=Schriftart
|
||||||
selectFillter=-- Auswählen --
|
selectFillter=-- Auswählen --
|
||||||
pageNum=Seitenzahl
|
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.title=Zertifikatsignierung
|
||||||
certSign.header=Signieren Sie ein PDF mit Ihrem Zertifikat (in Arbeit)
|
certSign.header=Signieren Sie ein PDF mit Ihrem Zertifikat (in Arbeit)
|
||||||
certSign.selectPDF=Wählen Sie eine PDF-Datei zum Signieren aus:
|
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.11=Ändern von Kommentaren verhindern
|
||||||
addPassword.selectText.12=Drucken verhindern
|
addPassword.selectText.12=Drucken verhindern
|
||||||
addPassword.selectText.13=Drucken verschiedener Formate 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
|
addPassword.submit=Verschlüsseln
|
||||||
|
|
||||||
#watermark
|
#watermark
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ multiPdfDropPrompt=Select (or drag & drop) all PDFs you require
|
|||||||
imgPrompt=Select Image(s)
|
imgPrompt=Select Image(s)
|
||||||
genericSubmit=Submit
|
genericSubmit=Submit
|
||||||
processTimeWarning=Warning: This process can take up to a minute depending on file-size
|
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
|
goToPage=Go
|
||||||
true=True
|
true=True
|
||||||
false=False
|
false=False
|
||||||
@@ -54,14 +54,11 @@ home.pdfOrganiser.title=Organise
|
|||||||
home.pdfOrganiser.desc=Remove/Rearrange pages in any order
|
home.pdfOrganiser.desc=Remove/Rearrange pages in any order
|
||||||
|
|
||||||
home.addImage.title=Add image
|
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.title=Add Watermark
|
||||||
home.watermark.desc=Add a custom watermark to your PDF document.
|
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.title=Change Permissions
|
||||||
home.permissions.desc=Change the permissions of your PDF document
|
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.title=Sign with Certificate
|
||||||
home.certSign.desc=Signs a PDF with a Certificate/Key (PEM/P12)
|
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
|
downloadPdf=Download PDF
|
||||||
text=Text
|
text=Text
|
||||||
@@ -135,6 +139,17 @@ font=Font
|
|||||||
selectFillter=-- Select --
|
selectFillter=-- Select --
|
||||||
pageNum=Page Number
|
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.title=Certificate Signing
|
||||||
certSign.header=Sign a PDF with your certificate (Work in progress)
|
certSign.header=Sign a PDF with your certificate (Work in progress)
|
||||||
certSign.selectPDF=Select a PDF File for Signing:
|
certSign.selectPDF=Select a PDF File for Signing:
|
||||||
@@ -333,10 +348,10 @@ pdfToImage.submit=Convert
|
|||||||
addPassword.title=Add Password
|
addPassword.title=Add Password
|
||||||
addPassword.header=Add password (Encrypt)
|
addPassword.header=Add password (Encrypt)
|
||||||
addPassword.selectText.1=Select PDF to 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.3=Encryption Key Length
|
||||||
addPassword.selectText.4=Higher values are stronger, but lower values have better compatibility.
|
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.6=Prevent assembly of document
|
||||||
addPassword.selectText.7=Prevent content extraction
|
addPassword.selectText.7=Prevent content extraction
|
||||||
addPassword.selectText.8=Prevent extraction for accessibility
|
addPassword.selectText.8=Prevent extraction for accessibility
|
||||||
@@ -345,6 +360,9 @@ addPassword.selectText.10=Prevent modification
|
|||||||
addPassword.selectText.11=Prevent annotation modification
|
addPassword.selectText.11=Prevent annotation modification
|
||||||
addPassword.selectText.12=Prevent printing
|
addPassword.selectText.12=Prevent printing
|
||||||
addPassword.selectText.13=Prevent printing different formats
|
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
|
addPassword.submit=Encrypt
|
||||||
|
|
||||||
#watermark
|
#watermark
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ imgPrompt=Seleccionar Imagen(es)
|
|||||||
genericSubmit=Enviar
|
genericSubmit=Enviar
|
||||||
processTimeWarning=Advertencia: este proceso puede tardar hasta un minuto dependiendo del tamaño del archivo
|
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):
|
pageOrderPrompt=Orden de páginas (Introduzca una lista de números de página separados por coma):
|
||||||
goToPage=Ir
|
goToPage=Ir a
|
||||||
true=Verdadero
|
true=Verdadero
|
||||||
false=Falso
|
false=Falso
|
||||||
unknown=Desconocido
|
unknown=Desconocido
|
||||||
@@ -23,7 +23,7 @@ bored=¿Aburrido de esperar?
|
|||||||
#############
|
#############
|
||||||
# HOME-PAGE #
|
# 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.convert=Convertir
|
||||||
navbar.security=Seguridad
|
navbar.security=Seguridad
|
||||||
@@ -35,19 +35,19 @@ home.multiTool.title=Multi-herramienta PDF
|
|||||||
home.multiTool.desc=Combinar, rotar, reorganizar y eliminar páginas
|
home.multiTool.desc=Combinar, rotar, reorganizar y eliminar páginas
|
||||||
|
|
||||||
home.merge.title=Unir
|
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.title=Dividir
|
||||||
home.split.desc=Dividir PDFs en múltiples documentos
|
home.split.desc=Dividir PDFs en múltiples documentos
|
||||||
|
|
||||||
home.rotate.title=Rotar
|
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.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.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.title=Organizador
|
||||||
home.pdfOrganiser.desc=Eliminar/Reorganizar páginas en cualquier orden
|
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.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.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.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.title=Cambiar permisos
|
||||||
home.permissions.desc=Cambiar los permisos de tu documento PDF
|
home.permissions.desc=Cambiar los permisos del documento PDF
|
||||||
|
|
||||||
home.removePages.title=Eliminar
|
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.title=Añadir contraseña
|
||||||
home.addPassword.desc=Encriptar el documento PDF con una contraseña.
|
home.addPassword.desc=Encriptar el documento PDF con una contraseña
|
||||||
|
|
||||||
home.removePassword.title=Eliminar Contraseña
|
home.removePassword.title=Eliminar contraseña
|
||||||
home.removePassword.desc=Eliminar la contraseña del documento PDF.
|
home.removePassword.desc=Eliminar la contraseña del documento PDF
|
||||||
|
|
||||||
home.compressPdfs.title=Comprimir
|
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.title=Cambiar metadatos
|
||||||
home.changeMetadata.desc=Cambiar/Eliminar/Añadir metadatos al documento PDF.
|
home.changeMetadata.desc=Cambiar/Eliminar/Añadir metadatos al documento PDF
|
||||||
|
|
||||||
home.fileToPDF.title=Convertir fichero a 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.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.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.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.title=Convertir PDF a PDF/A
|
||||||
home.pdfToPDFA.desc=Convertir PDF a PDF/A para almacenamiento a largo plazo
|
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.ScannerImageSplit.desc=Dividir varias fotos dentro de una foto/PDF
|
||||||
|
|
||||||
home.sign.title=Firmar
|
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.title=Aplanar
|
||||||
home.flatten.desc=Eliminar todos los elementos y formularios interactivos de un PDF
|
home.flatten.desc=Eliminar todos los elementos y formularios interactivos de un PDF
|
||||||
|
|
||||||
home.repair.title=Reparar
|
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.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.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
|
downloadPdf=Descargar PDF
|
||||||
text=Texto
|
text=Texto
|
||||||
@@ -130,8 +141,19 @@ font=Fuente
|
|||||||
selectFilter=-- Seleccionar --
|
selectFilter=-- Seleccionar --
|
||||||
pageNum=Número de página
|
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.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.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.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):
|
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
|
flatten.submit=Aplanar
|
||||||
|
|
||||||
ScannerImageSplit.selectText.1=Umbral de ángulo:
|
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.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.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.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.9=Tamaño del borde:
|
||||||
ScannerImageSplit.selectText.10=Establece el tamaño del borde agregado y eliminado para evitar bordes blancos en la salida (predeterminado: 1).
|
ScannerImageSplit.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.title=Ajustes
|
||||||
settings.update=Actualización disponible
|
settings.update=Actualización disponible
|
||||||
settings.appVersion=Versión de la aplicación:
|
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.title=Elegir la opción de descarga (para descargas de un solo archivo sin ZIP):
|
||||||
settings.downloadOption.1=Abre en la misma ventana
|
settings.downloadOption.1=Abrir en la misma ventana
|
||||||
settings.downloadOption.2=Abre en una nueva ventana
|
settings.downloadOption.2=Abrir en una nueva ventana
|
||||||
settings.downloadOption.3=Descarga el fichero
|
settings.downloadOption.3=Descargar el fichero
|
||||||
settings.zipThreshold=Ficheros ZIP cuando excede el número de ficheros descargados
|
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
|
||||||
ocr.title=OCR / Escaneo de limpieza
|
ocr.title=OCR / Escaneo de limpieza
|
||||||
ocr.header=Escaneos de limpieza / OCR (Reconocimiento óptico de caracteres)
|
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.1=Seleccionar 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.2=Producir 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.3=Corregir 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.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=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.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=Ignora las páginas que tienen texto interactivo, solo las páginas OCR que son imágenes
|
ocr.selectText.6=Ignorar 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.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.8=Normal (se producirá un error si el PDF contiene texto)
|
||||||
ocr.selectText.9=Ajustes Adicionales
|
ocr.selectText.9=Ajustes adicionales
|
||||||
ocr.selectText.10=Modo OCR
|
ocr.selectText.10=Modo OCR
|
||||||
ocr.selectText.11=Eliminar imágenes después de OCR (Elimina TODAS las imágenes, solo es útil si es parte del paso de conversión)
|
ocr.selectText.11=Eliminar imágenes después de OCR (Elimina TODAS las imágenes, solo es útil si es parte del paso de conversión)
|
||||||
ocr.selectText.12=Tipo de procesamiento (avanzado)
|
ocr.selectText.12=Tipo de procesamiento (avanzado)
|
||||||
ocr.help=Lea esta documentación sobre cómo usar esto para otros idiomas y/o no usarlo en docker
|
ocr.help=Lea esta documentación sobre cómo usar esto para otros idiomas y/o no usarlo en Docker
|
||||||
ocr.credit=Este servicio utiliza OCRmyPDF y Tesseract para OCR.
|
ocr.credit=Este servicio utiliza OCRmyPDF y Tesseract para OCR
|
||||||
ocr.submit=Procesa PDF con OCR
|
ocr.submit=Procesar PDF con OCR
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
extractImages.title=Extraer imágenes
|
extractImages.title=Extraer imágenes
|
||||||
extractImages.header=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
|
extractImages.submit=Extraer
|
||||||
|
|
||||||
|
|
||||||
#File to PDF
|
#File to PDF
|
||||||
fileToPDF.title=Fichero a PDF
|
fileToPDF.title=Archivo a PDF
|
||||||
fileToPDF.header=Convierte cualquier fichero a PDF
|
fileToPDF.header=Convertir cualquier archivo a PDF
|
||||||
fileToPDF.credit=Este servicio usa LibreOffice y Unoconv para la conversión de ficheros.
|
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.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
|
fileToPDF.submit=Convertir a PDF
|
||||||
|
|
||||||
|
|
||||||
#compress
|
#compress
|
||||||
compress.title=Comprimir
|
compress.title=Comprimir
|
||||||
compress.header=Comprimir PDF
|
compress.header=Comprimir PDF
|
||||||
compress.credit=Este servicio utiliza Ghostscript para compresión/optimización de PDF.
|
compress.credit=Este servicio utiliza Ghostscript para compresión/optimización de PDF
|
||||||
compress.selectText.1=Modo manual - De 1 a 4
|
compress.selectText.1=Modo manual - De 1 a 4
|
||||||
compress.selectText.2=Nivel de optimización:
|
compress.selectText.2=Nivel de optimización:
|
||||||
compress.selectText.3=4 (Terrible para imágenes de texto)
|
compress.selectText.3=4 (Terrible para imágenes de texto)
|
||||||
compress.selectText.4=Modo automático: ajusta automáticamente la calidad para que el PDF tenga el tamaño exacto
|
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
|
compress.submit=Comprimir
|
||||||
|
|
||||||
|
|
||||||
#Add image
|
#Add image
|
||||||
addImage.title=Añadir Imagen
|
addImage.title=Añadir imagen
|
||||||
addImage.header=Añadir image de PDF
|
addImage.header=Añadir imagen de PDF
|
||||||
addImage.everyPage=¿Todas las páginas?
|
addImage.everyPage=¿Todas las páginas?
|
||||||
addImage.submit=Añadir imagen
|
addImage.submit=Añadir imagen
|
||||||
|
|
||||||
@@ -269,13 +291,13 @@ multiTool.header=Multi-herramienta PDF
|
|||||||
#pageRemover
|
#pageRemover
|
||||||
pageRemover.title=Eliminador de páginas
|
pageRemover.title=Eliminador de páginas
|
||||||
pageRemover.header=Eliminador de páginas PDF
|
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
|
pageRemover.submit=Eliminar Páginas
|
||||||
|
|
||||||
#rotate
|
#rotate
|
||||||
rotate.title=Rotar PDF
|
rotate.title=Rotar PDF
|
||||||
rotate.header=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
|
rotate.submit=Rotar
|
||||||
|
|
||||||
|
|
||||||
@@ -284,7 +306,7 @@ rotate.submit=Rotar
|
|||||||
#merge
|
#merge
|
||||||
split.title=Dividir PDF
|
split.title=Dividir PDF
|
||||||
split.header=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.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.3=Documento #1: Página 1
|
||||||
split.desc.4=Documento #2: Páginas 2 y 3
|
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.6=Documento #4: Página 7
|
||||||
split.desc.7=Documento #5: Página 8
|
split.desc.7=Documento #5: Página 8
|
||||||
split.desc.8=Documento #6: Páginas 9 y 10
|
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
|
split.submit=Dividir
|
||||||
|
|
||||||
|
|
||||||
@@ -302,7 +324,7 @@ imageToPDF.header=Imagen a PDF
|
|||||||
imageToPDF.submit=Convertir
|
imageToPDF.submit=Convertir
|
||||||
imageToPDF.selectText.1=Estirar para ajustar
|
imageToPDF.selectText.1=Estirar para ajustar
|
||||||
imageToPDF.selectText.2=Rotación automática del PDF
|
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.4=Unir en un único archivo PDF
|
||||||
imageToPDF.selectText.5=Convertir a PDFs separados
|
imageToPDF.selectText.5=Convertir a PDFs separados
|
||||||
|
|
||||||
@@ -311,21 +333,21 @@ pdfToImage.title=PDF a Imagen
|
|||||||
pdfToImage.header=PDF a Imagen
|
pdfToImage.header=PDF a Imagen
|
||||||
pdfToImage.selectText=Formato de Imagen
|
pdfToImage.selectText=Formato de Imagen
|
||||||
pdfToImage.singleOrMultiple=Tipo resultante de imagen
|
pdfToImage.singleOrMultiple=Tipo resultante de imagen
|
||||||
pdfToImage.single=Una Imagen Grande Única
|
pdfToImage.single=Una única imagen grande
|
||||||
pdfToImage.multi=Múltiples Imágenes
|
pdfToImage.multi=Múltiples imágenes
|
||||||
pdfToImage.colorType=Tipo de color
|
pdfToImage.colorType=Tipo de color
|
||||||
pdfToImage.color=Color
|
pdfToImage.color=Color
|
||||||
pdfToImage.grey=Escala de Grises
|
pdfToImage.grey=Escala de grises
|
||||||
pdfToImage.blackwhite=Blanco y Negro (¡Puedes perder datos!)
|
pdfToImage.blackwhite=Blanco y Negro (¡Puede perder datos!)
|
||||||
pdfToImage.submit=Convertir
|
pdfToImage.submit=Convertir
|
||||||
|
|
||||||
#addPassword
|
#addPassword
|
||||||
addPassword.title=Añadir Contraseña
|
addPassword.title=Añadir contraseña
|
||||||
addPassword.header=Añadir contraseña (Encriptar)
|
addPassword.header=Añadir contraseña (encriptar)
|
||||||
addPassword.selectText.1=Seleccionar PDF para encriptar
|
addPassword.selectText.1=Seleccionar PDF para encriptar
|
||||||
addPassword.selectText.2=Contraseña
|
addPassword.selectText.2=Contraseña
|
||||||
addPassword.selectText.3=Longitud de la clave de cifrado
|
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.5=Permisos para establecer
|
||||||
addPassword.selectText.6=Impedir el ensamblaje del documento
|
addPassword.selectText.6=Impedir el ensamblaje del documento
|
||||||
addPassword.selectText.7=Impedir la extracción de contenido
|
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.11=Impedir modificación de anotaciones
|
||||||
addPassword.selectText.12=Impedir imprimir
|
addPassword.selectText.12=Impedir imprimir
|
||||||
addPassword.selectText.13=Impedir imprimir diferentes formatos
|
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
|
addPassword.submit=Encriptar
|
||||||
|
|
||||||
#watermark
|
#watermark
|
||||||
@@ -357,8 +382,8 @@ remove-watermark.selectText.2=Texto de la marca de agua:
|
|||||||
remove-watermark.submit=Eliminar marca de agua
|
remove-watermark.submit=Eliminar marca de agua
|
||||||
|
|
||||||
#Change permissions
|
#Change permissions
|
||||||
permissions.title=Cambiar Permisos
|
permissions.title=Cambiar permisos
|
||||||
permissions.header=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.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.1=Seleccionar PDF para cambiar los permisos
|
||||||
permissions.selectText.2=Permisos a establecer
|
permissions.selectText.2=Permisos a establecer
|
||||||
@@ -374,21 +399,21 @@ permissions.submit=Cambiar
|
|||||||
|
|
||||||
#remove password
|
#remove password
|
||||||
removePassword.title=Eliminar contraseña
|
removePassword.title=Eliminar contraseña
|
||||||
removePassword.header=Eliminar contraseña (Desencriptar)
|
removePassword.header=Eliminar contraseña (desencriptar)
|
||||||
removePassword.selectText.1=Seleccionar PDF para Desencriptar
|
removePassword.selectText.1=Seleccionar PDF para desencriptar
|
||||||
removePassword.selectText.2=Contraseña
|
removePassword.selectText.2=Contraseña
|
||||||
removePassword.submit=Eliminar
|
removePassword.submit=Eliminar
|
||||||
|
|
||||||
changeMetadata.title=Cambiar Metadatos
|
changeMetadata.title=Cambiar metadatos
|
||||||
changeMetadata.header=Cambiar Metadatos
|
changeMetadata.header=Cambiar metadatos
|
||||||
changeMetadata.selectText.1=Editar las variables que desea cambiar
|
changeMetadata.selectText.1=Editar las variables que desea cambiar
|
||||||
changeMetadata.selectText.2=Eliminar todos los metadatos
|
changeMetadata.selectText.2=Eliminar todos los metadatos
|
||||||
changeMetadata.selectText.3=Mostrar metadatos personalizados:
|
changeMetadata.selectText.3=Mostrar metadatos personalizados:
|
||||||
changeMetadata.author=Autor:
|
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.creator=Creador:
|
||||||
changeMetadata.keywords=Palabras clave:
|
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.producer=Productor:
|
||||||
changeMetadata.subject=Asunto:
|
changeMetadata.subject=Asunto:
|
||||||
changeMetadata.title=Título:
|
changeMetadata.title=Título:
|
||||||
@@ -415,29 +440,29 @@ pdfToPDFA.submit=Convertir
|
|||||||
PDFToWord.title=PDF a Word
|
PDFToWord.title=PDF a Word
|
||||||
PDFToWord.header=PDF a Word
|
PDFToWord.header=PDF a Word
|
||||||
PDFToWord.selectText.1=Formato de archivo de salida
|
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
|
PDFToWord.submit=Convertir
|
||||||
|
|
||||||
PDFToPresentation.title=PDF a presentación
|
PDFToPresentation.title=PDF a presentación
|
||||||
PDFToPresentation.header=PDF a presentación
|
PDFToPresentation.header=PDF a presentación
|
||||||
PDFToPresentation.selectText.1=Formato de archivo de salida
|
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
|
PDFToPresentation.submit=Convertir
|
||||||
|
|
||||||
|
|
||||||
PDFToText.title=PDF a TXT/RTF
|
PDFToText.title=PDF a TXT/RTF
|
||||||
PDFToText.header=PDF a TXT/RTF
|
PDFToText.header=PDF a TXT/RTF
|
||||||
PDFToText.selectText.1=Formato de archivo de salida
|
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
|
PDFToText.submit=Convertir
|
||||||
|
|
||||||
|
|
||||||
PDFToHTML.title=PDF a HTML
|
PDFToHTML.title=PDF a HTML
|
||||||
PDFToHTML.header=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
|
PDFToHTML.submit=Convertir
|
||||||
|
|
||||||
PDFToXML.title=PDF a XML
|
PDFToXML.title=PDF a XML
|
||||||
PDFToXML.header=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
|
PDFToXML.submit=Convertir
|
||||||
|
|||||||
@@ -127,15 +127,37 @@ home.repair.desc=Essaye de réparer un PDF corrompu/cassé
|
|||||||
home.removeBlanks.title=Supprimer les pages vierges
|
home.removeBlanks.title=Supprimer les pages vierges
|
||||||
home.removeBlanks.desc=Détecte et supprime les pages vierges d'un document
|
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.title=Comparer
|
||||||
home.compare.desc=Compare et affiche les différences entre 2 documents PDF
|
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
|
downloadPdf=Télécharger le PDF
|
||||||
text=Texte
|
text=Texte
|
||||||
font=Police
|
font=Police
|
||||||
selectFilter=-- Sélectionner --
|
selectFilter=-- Sélectionner --
|
||||||
pageNum=numéro de page
|
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.title=Signature du certificat
|
||||||
certSign.header=Signer un PDF avec votre certificat (Travail en cours)
|
certSign.header=Signer un PDF avec votre certificat (Travail en cours)
|
||||||
certSign.selectPDF=Sélectionnez un fichier PDF à signer :
|
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.11=Empêcher la modification des annotations
|
||||||
addPassword.selectText.12=Empêcher l'impression
|
addPassword.selectText.12=Empêcher l'impression
|
||||||
addPassword.selectText.13=Empêcher l'impression de différents formats
|
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
|
addPassword.submit=Crypter
|
||||||
|
|
||||||
#watermark
|
#watermark
|
||||||
|
|||||||
@@ -122,15 +122,37 @@ home.repair.desc=Prova a riparare un PDF corrotto.
|
|||||||
home.removeBlanks.title=Rimuovi pagine vuote
|
home.removeBlanks.title=Rimuovi pagine vuote
|
||||||
home.removeBlanks.desc=Trova e rimuovi pagine vuote da un PDF.
|
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.title=Compara
|
||||||
home.compare.desc=Vedi e compara le differenze tra due PDF.
|
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
|
downloadPdf=Scarica PDF
|
||||||
text=Testo
|
text=Testo
|
||||||
font=Font
|
font=Font
|
||||||
selectFillter=-- Seleziona --
|
selectFillter=-- Seleziona --
|
||||||
pageNum=Numero pagina
|
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.title=Firma del certificato
|
||||||
certSign.header=Firma un PDF con il tuo certificato (Lavoro in corso)
|
certSign.header=Firma un PDF con il tuo certificato (Lavoro in corso)
|
||||||
certSign.selectPDF=Seleziona un file PDF per la firma:
|
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.11=Previeni annotazioni
|
||||||
addPassword.selectText.12=Previeni stampa
|
addPassword.selectText.12=Previeni stampa
|
||||||
addPassword.selectText.13=Previeni stampa in diversi formati
|
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
|
addPassword.submit=Crittografa
|
||||||
|
|
||||||
#watermark
|
#watermark
|
||||||
|
|||||||
@@ -128,6 +128,13 @@ home.compare.desc=2개의 PDF 문서를 비교하고 차이를 표시합니다.
|
|||||||
home.certSign.title=인증서로 서명
|
home.certSign.title=인증서로 서명
|
||||||
home.certSign.desc=PDF에 인증서/키로 서명합니다. (PEM/P12)
|
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 다운로드
|
downloadPdf=PDF 다운로드
|
||||||
text=텍스트
|
text=텍스트
|
||||||
@@ -135,6 +142,17 @@ font=폰트
|
|||||||
selectFillter=-- 선택 --
|
selectFillter=-- 선택 --
|
||||||
pageNum=페이지 번호
|
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.title=인증서로 서명
|
||||||
certSign.header=PDF에 당신의 인증서로 서명하세요 (개발 중)
|
certSign.header=PDF에 당신의 인증서로 서명하세요 (개발 중)
|
||||||
certSign.selectPDF=서명할 PDF를 선택하세요:
|
certSign.selectPDF=서명할 PDF를 선택하세요:
|
||||||
@@ -368,7 +386,10 @@ addPassword.selectText.9=양식 작성 방지
|
|||||||
addPassword.selectText.10=수정 방지
|
addPassword.selectText.10=수정 방지
|
||||||
addPassword.selectText.11=주석 수정 방지
|
addPassword.selectText.11=주석 수정 방지
|
||||||
addPassword.selectText.12=인쇄 방지
|
addPassword.selectText.12=인쇄 방지
|
||||||
addPassword.selectText.13=다른 형식으로 인쇄 방지
|
addPassword.selectText.13=다른 형식으로 인쇄 방<EFBFBD><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=암호화
|
addPassword.submit=암호화
|
||||||
|
|
||||||
#watermark
|
#watermark
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ genericSubmit=Wyślij
|
|||||||
processTimeWarning=Ostrzeżenie: Ten proces może potrwać do minuty, w zależności od rozmiaru pliku
|
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) :
|
pageOrderPrompt=Kolejność stron (wprowadź listę numerów stron oddzielonych przecinkami) :
|
||||||
goToPage=Idź
|
goToPage=Idź
|
||||||
true=Prawda
|
true=Tak
|
||||||
false=Fałsz
|
false=Nie
|
||||||
unknown=Nieznany
|
unknown=Nieznany
|
||||||
save=Zapisz
|
save=Zapisz
|
||||||
close=Zamknij
|
close=Zamknij
|
||||||
@@ -54,7 +54,7 @@ home.pdfOrganiser.title=Uporządkuj
|
|||||||
home.pdfOrganiser.desc=Usuń/Zmień kolejność stron w dowolnej kolejności
|
home.pdfOrganiser.desc=Usuń/Zmień kolejność stron w dowolnej kolejności
|
||||||
|
|
||||||
home.addImage.title=Dodaj obraz
|
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.title=Dodaj znak wodny
|
||||||
home.watermark.desc=Dodaj niestandardowy znak wodny do dokumentu PDF.
|
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.title=Zmień metadane
|
||||||
home.changeMetadata.desc=Zmień/Usuń/Dodaj metadane w dokumencie PDF
|
home.changeMetadata.desc=Zmień/Usuń/Dodaj metadane w dokumencie PDF
|
||||||
|
|
||||||
home.fileToPDF.title=Konwertuj plik do dokumentu PDF
|
home.fileToPDF.title=Konwertuj plik do PDF
|
||||||
home.fileToPDF.desc=Konwertuj prawie każdy plik do dokumentu PDF (DOCX, PNG, XLS, PPT, TXT i więcej)
|
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.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.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.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.title=PDF na PDF/A
|
||||||
home.pdfToPDFA.desc=Konwertuj dokument PDF na PDF/A w celu długoterminowego przechowywania
|
home.pdfToPDFA.desc=Konwertuj dokument PDF na PDF/A w celu długoterminowego przechowywania
|
||||||
@@ -123,10 +123,18 @@ home.removeBlanks.title=Usuń puste strony
|
|||||||
home.removeBlanks.desc=Wykrywa i usuwa puste strony z dokumentu PDF
|
home.removeBlanks.desc=Wykrywa i usuwa puste strony z dokumentu PDF
|
||||||
|
|
||||||
home.compare.title=Porównaj
|
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 osobistym
|
home.certSign.title=Podpisz certyfikatem
|
||||||
home.certSign.desc=Podpisz dokument PDF za pomocą certyfikatu/klucza osobistego (PEM/P12)
|
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
|
downloadPdf=Pobierz PDF
|
||||||
text=Tekst
|
text=Tekst
|
||||||
@@ -134,16 +142,27 @@ font=Czcionka
|
|||||||
selectFillter=-- Wybierz --
|
selectFillter=-- Wybierz --
|
||||||
pageNum=Numer strony
|
pageNum=Numer strony
|
||||||
|
|
||||||
|
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.title=Podpisywanie certyfikatem
|
||||||
certSign.header=Podpisz dokument PDF certyfikatem osobistym (prace w toku)
|
certSign.header=Podpisz dokument PDF certyfikatem prywatnym (moduł w budowie)
|
||||||
certSign.selectPDF=Wybierz dokument PDF do podpisania:
|
certSign.selectPDF=Wybierz dokument PDF do podpisania:
|
||||||
certSign.selectKey=Wybierz plik klucza prywatnego (format PKCS#8, może to być .pem lub .der):
|
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.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.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.certType=Typ certyfikatu
|
||||||
certSign.password=Wprowadź hasło do magazynu kluczy lub klucza prywatnego (jeśli istnieje):
|
certSign.password=Wprowadź hasło do magazynu kluczy lub klucza prywatnego (jeśli istnieje):
|
||||||
certSign.showSig=Pokaż podpis
|
certSign.showSig=Wyświetl podpis
|
||||||
certSign.reason=Powód
|
certSign.reason=Organizacja
|
||||||
certSign.location=Lokalizacja
|
certSign.location=Lokalizacja
|
||||||
certSign.name=Nazwa
|
certSign.name=Nazwa
|
||||||
certSign.submit=Podpisz PDF
|
certSign.submit=Podpisz PDF
|
||||||
@@ -226,13 +245,13 @@ ocr.submit=Przetwarzaj PDF za pomocą OCR
|
|||||||
|
|
||||||
extractImages.title=Wyodrębnij obrazy
|
extractImages.title=Wyodrębnij obrazy
|
||||||
extractImages.header=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
|
extractImages.submit=Wyodrębnij
|
||||||
|
|
||||||
|
|
||||||
#File to PDF
|
#File to PDF
|
||||||
fileToPDF.title=Plik na 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.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.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
|
fileToPDF.submit=Konwertuj na PDF
|
||||||
@@ -340,6 +359,9 @@ addPassword.selectText.10=Zablokuj modyfikacje
|
|||||||
addPassword.selectText.11=Zablokuj modyfikacje adnotacji
|
addPassword.selectText.11=Zablokuj modyfikacje adnotacji
|
||||||
addPassword.selectText.12=Zablokuj drukowanie
|
addPassword.selectText.12=Zablokuj drukowanie
|
||||||
addPassword.selectText.13=Zablokuj drukowanie różnych formatów
|
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
|
addPassword.submit=Zablokuj
|
||||||
|
|
||||||
#watermark
|
#watermark
|
||||||
@@ -349,8 +371,8 @@ watermark.selectText.1=Wybierz dokument PDF, do którego chcesz dodać znak wodn
|
|||||||
watermark.selectText.2=Treść znaku wodnego:
|
watermark.selectText.2=Treść znaku wodnego:
|
||||||
watermark.selectText.3=Rozmiar czcionki:
|
watermark.selectText.3=Rozmiar czcionki:
|
||||||
watermark.selectText.4=Obrót (0-360):
|
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.5=Odstęp w poziomie (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.6=Odstęp w pionie (odstęp między każdym znakiem wodnym w pionie):
|
||||||
watermark.selectText.7=Nieprzezroczystość (0% - 100%):
|
watermark.selectText.7=Nieprzezroczystość (0% - 100%):
|
||||||
watermark.submit=Dodaj znak wodny
|
watermark.submit=Dodaj znak wodny
|
||||||
|
|
||||||
@@ -390,14 +412,14 @@ changeMetadata.selectText.1=Edytuj zmienne, które chcesz zmienić
|
|||||||
changeMetadata.selectText.2=Usuń wszystkie metadane
|
changeMetadata.selectText.2=Usuń wszystkie metadane
|
||||||
changeMetadata.selectText.3=Pokaż niestandardowe metadane:
|
changeMetadata.selectText.3=Pokaż niestandardowe metadane:
|
||||||
changeMetadata.author=Autor:
|
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.creator=Twórca:
|
||||||
changeMetadata.keywords=Słowa kluczowe:
|
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.producer=Producent:
|
||||||
changeMetadata.subject=Temat:
|
changeMetadata.subject=Temat:
|
||||||
changeMetadata.title=Tytuł:
|
changeMetadata.title=Tytuł:
|
||||||
changeMetadata.trapped=Uwięziony:
|
changeMetadata.trapped=Zablokowany:
|
||||||
changeMetadata.selectText.4=Inne metadane:
|
changeMetadata.selectText.4=Inne metadane:
|
||||||
changeMetadata.selectText.5=Dodaj niestandardowy wpis w metadanych
|
changeMetadata.selectText.5=Dodaj niestandardowy wpis w metadanych
|
||||||
changeMetadata.submit=Zmień
|
changeMetadata.submit=Zmień
|
||||||
|
|||||||
486
src/main/resources/messages_pt_BR.properties
Normal file
486
src/main/resources/messages_pt_BR.properties
Normal 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
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -109,12 +109,31 @@ home.compare.desc=Compară și arată diferențele dintre 2 documente PDF.
|
|||||||
home.certSign.title=Semnare cu certificat
|
home.certSign.title=Semnare cu certificat
|
||||||
home.certSign.desc=Semnează un PDF cu un certificat/cheie (PEM/P12)
|
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
|
downloadPdf=Descarcă PDF
|
||||||
text=Text
|
text=Text
|
||||||
font=Font
|
font=Font
|
||||||
selectFillter=-- Selectează --
|
selectFillter=-- Selectează --
|
||||||
pageNum=Numărul paginii
|
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.title = Semnare certificat
|
||||||
certSign.header = Semnează un fișier PDF cu certificatul tău (În curs de desfășurare)
|
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.selectPDF = Selectează un fișier PDF pentru semnare:
|
||||||
@@ -316,6 +335,9 @@ addPassword.selectText.10=Preveniți modificarea
|
|||||||
addPassword.selectText.11=Preveniți modificarea adnotărilor
|
addPassword.selectText.11=Preveniți modificarea adnotărilor
|
||||||
addPassword.selectText.12=Preveniți tipărirea
|
addPassword.selectText.12=Preveniți tipărirea
|
||||||
addPassword.selectText.13=Preveniți tipărirea în formate diferite
|
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
|
addPassword.submit=Criptare
|
||||||
|
|
||||||
#watermark
|
#watermark
|
||||||
|
|||||||
@@ -125,12 +125,34 @@ home.removeBlanks.desc=Обнаруживает и удаляет пустые
|
|||||||
home.compare.title=Сравнение
|
home.compare.title=Сравнение
|
||||||
home.compare.desc=Сравнивает и показывает различия между двумя PDF-документами
|
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
|
downloadPdf=Скачать PDF
|
||||||
text=Текст
|
text=Текст
|
||||||
font=Шрифт
|
font=Шрифт
|
||||||
selectFillter=-- Выбрать --
|
selectFillter=-- Выбрать --
|
||||||
pageNum=номер страницы
|
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.title=Подписание сертификата
|
||||||
certSign.header=Подпишите PDF своим сертификатом (работа в процессе)
|
certSign.header=Подпишите PDF своим сертификатом (работа в процессе)
|
||||||
certSign.selectPDF=Выберите файл PDF для подписи:
|
certSign.selectPDF=Выберите файл PDF для подписи:
|
||||||
@@ -338,6 +360,9 @@ addPassword.selectText.10=Предотвратить модификацию
|
|||||||
addPassword.selectText.11=Запретить модификацию аннотаций
|
addPassword.selectText.11=Запретить модификацию аннотаций
|
||||||
addPassword.selectText.12=Запретить печать
|
addPassword.selectText.12=Запретить печать
|
||||||
addPassword.selectText.13=Запретить печать разных форматов
|
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=Шифровать
|
addPassword.submit=Шифровать
|
||||||
|
|
||||||
#watermark
|
#watermark
|
||||||
|
|||||||
@@ -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.title=Jämför
|
||||||
home.compare.desc=Jämför och visar skillnaderna mellan 2 PDF-dokument
|
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
|
downloadPdf=Ladda ner PDF
|
||||||
text=Text
|
text=Text
|
||||||
font=Teckensnitt
|
font=Teckensnitt
|
||||||
selectFillter=-- Välj --
|
selectFillter=-- Välj --
|
||||||
pageNum=Sidnummer
|
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.title=Certifikatsignering
|
||||||
certSign.header=Skriv under en PDF med ditt certifikat (Pågående arbete)
|
certSign.header=Skriv under en PDF med ditt certifikat (Pågående arbete)
|
||||||
certSign.selectPDF=Välj en PDF-fil för signering:
|
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.11=Förhindra anteckningsändring
|
||||||
addPassword.selectText.12=Förhindra utskrift
|
addPassword.selectText.12=Förhindra utskrift
|
||||||
addPassword.selectText.13=Förhindra utskrift av olika format
|
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
|
addPassword.submit=Kryptera
|
||||||
|
|
||||||
#vattenstämpel
|
#vattenstämpel
|
||||||
|
|||||||
@@ -125,12 +125,34 @@ home.removeBlanks.desc=\u68C0\u6D4B\u5E76\u5220\u9664\u6587\u6863\u4E2D\u7684\u7
|
|||||||
home.compare.title=\u6BD4\u8F83
|
home.compare.title=\u6BD4\u8F83
|
||||||
home.compare.desc=\u6BD4\u8F83\u5E76\u663E\u793A 2 \u4E2A PDF \u6587\u6863\u4E4B\u95F4\u7684\u5DEE\u5F02
|
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
|
downloadPdf=\u4E0B\u8F7DPDF
|
||||||
text=\u6587\u672C
|
text=\u6587\u672C
|
||||||
font=\u5B57\u4F53
|
font=\u5B57\u4F53
|
||||||
selectFillter=-- 閫夋嫨--
|
selectFillter=-- 閫夋嫨--
|
||||||
pageNum=椤电爜
|
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.title=璇佷功绛惧悕
|
||||||
certSign.header=浣跨敤鎮ㄧ殑璇佷功绛剧讲 PDF锛堣繘琛屼腑锛<E88591>
|
certSign.header=浣跨敤鎮ㄧ殑璇佷功绛剧讲 PDF锛堣繘琛屼腑锛<E88591>
|
||||||
certSign.selectPDF=閫夋嫨瑕佺<EFBFBD>鍚嶇殑 PDF 鏂囦欢锛<E6ACA2>
|
certSign.selectPDF=閫夋嫨瑕佺<EFBFBD>鍚嶇殑 PDF 鏂囦欢锛<E6ACA2>
|
||||||
@@ -336,7 +358,10 @@ addPassword.selectText.9=防止填写表格
|
|||||||
addPassword.selectText.10=闃叉<EFBFBD>淇<EFBFBD>敼
|
addPassword.selectText.10=闃叉<EFBFBD>淇<EFBFBD>敼
|
||||||
addPassword.selectText.11=闃叉<EFBFBD>淇<EFBFBD>敼娉ㄩ噴
|
addPassword.selectText.11=闃叉<EFBFBD>淇<EFBFBD>敼娉ㄩ噴
|
||||||
addPassword.selectText.12=闃叉<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=鍔犲瘑
|
addPassword.submit=鍔犲瘑
|
||||||
|
|
||||||
#watermark
|
#watermark
|
||||||
|
|||||||
94
src/main/resources/static/css/errorBanner.css
Normal file
94
src/main/resources/static/css/errorBanner.css
Normal 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;
|
||||||
|
}
|
||||||
10
src/main/resources/static/css/fileSelect.css
Normal file
10
src/main/resources/static/css/fileSelect.css
Normal 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;
|
||||||
|
}
|
||||||
49
src/main/resources/static/css/game.css
Normal file
49
src/main/resources/static/css/game.css
Normal 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;
|
||||||
|
}
|
||||||
70
src/main/resources/static/css/home.css
Normal file
70
src/main/resources/static/css/home.css
Normal 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 */
|
||||||
|
}
|
||||||
25
src/main/resources/static/css/merge.css
Normal file
25
src/main/resources/static/css/merge.css
Normal 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;
|
||||||
|
}
|
||||||
42
src/main/resources/static/css/navbar.css
Normal file
42
src/main/resources/static/css/navbar.css
Normal 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);
|
||||||
|
}
|
||||||
BIN
src/main/resources/static/favicon.ico
Normal file
BIN
src/main/resources/static/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 201 KiB |
100
src/main/resources/static/images/flags/pt_br.svg
Normal file
100
src/main/resources/static/images/flags/pt_br.svg
Normal 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 |
3
src/main/resources/static/images/page-layout.svg
Normal file
3
src/main/resources/static/images/page-layout.svg
Normal 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 |
3
src/main/resources/static/images/scale-pages.svg
Normal file
3
src/main/resources/static/images/scale-pages.svg
Normal 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 |
3
src/main/resources/static/images/suit-heart-fill.svg
Normal file
3
src/main/resources/static/images/suit-heart-fill.svg
Normal 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 |
76
src/main/resources/static/js/darkmode.js
Normal file
76
src/main/resources/static/js/darkmode.js
Normal 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();
|
||||||
|
});
|
||||||
|
});
|
||||||
219
src/main/resources/static/js/downloader.js
Normal file
219
src/main/resources/static/js/downloader.js
Normal 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);
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -25,33 +25,50 @@ const DraggableUtils = {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
.resizable({
|
.resizable({
|
||||||
edges: { left: true, right: true, bottom: true, top: true },
|
edges: { left: true, right: true, bottom: true, top: true },
|
||||||
listeners: {
|
listeners: {
|
||||||
move: (event) => {
|
move: (event) => {
|
||||||
var target = event.target
|
var target = event.target
|
||||||
var x = (parseFloat(target.getAttribute('data-x')) || 0)
|
var x = (parseFloat(target.getAttribute('data-x')) || 0)
|
||||||
var y = (parseFloat(target.getAttribute('data-y')) || 0)
|
var y = (parseFloat(target.getAttribute('data-y')) || 0)
|
||||||
|
|
||||||
// update the element's style
|
// check if control key is pressed
|
||||||
target.style.width = event.rect.width + 'px'
|
if (event.ctrlKey) {
|
||||||
target.style.height = event.rect.height + 'px'
|
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
|
if (Math.abs(event.deltaRect.width) >= Math.abs(event.deltaRect.height)) {
|
||||||
x += event.deltaRect.left
|
height = width / aspectRatio;
|
||||||
y += event.deltaRect.top
|
} 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.style.width = event.rect.width + 'px'
|
||||||
target.setAttribute('data-y', y)
|
target.style.height = event.rect.height + 'px'
|
||||||
target.textContent = Math.round(event.rect.width) + '\u00D7' + Math.round(event.rect.height)
|
|
||||||
|
// 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: [
|
modifiers: [
|
||||||
interact.modifiers.restrictSize({
|
interact.modifiers.restrictSize({
|
||||||
min: { width: 50, height: 50 },
|
min: { width: 5, height: 5 },
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
inertia: true,
|
inertia: true,
|
||||||
|
|||||||
50
src/main/resources/static/js/errorBanner.js
Normal file
50
src/main/resources/static/js/errorBanner.js
Normal 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');
|
||||||
|
}
|
||||||
37
src/main/resources/static/js/favourites.js
Normal file
37
src/main/resources/static/js/favourites.js
Normal 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();
|
||||||
|
});
|
||||||
43
src/main/resources/static/js/fileInput.js
Normal file
43
src/main/resources/static/js/fileInput.js
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
41
src/main/resources/static/js/githubVersion.js
Normal file
41
src/main/resources/static/js/githubVersion.js
Normal 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();
|
||||||
49
src/main/resources/static/js/homecard.js
Normal file
49
src/main/resources/static/js/homecard.js
Normal 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;
|
||||||
46
src/main/resources/static/js/languageSelection.js
Normal file
46
src/main/resources/static/js/languageSelection.js
Normal 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"]');
|
||||||
|
});
|
||||||
63
src/main/resources/static/js/merge.js
Normal file
63
src/main/resources/static/js/merge.js
Normal 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>↑</span></button>
|
||||||
|
<button class="btn btn-secondary move-down"><span>↓</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;
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -62,7 +62,7 @@ class PdfContainer {
|
|||||||
const files = e.target.files;
|
const files = e.target.files;
|
||||||
this.fileName = files[0].name;
|
this.fileName = files[0].name;
|
||||||
for (var i=0; i < files.length; i++) {
|
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 => {
|
document.querySelectorAll(".enable-on-file").forEach(element => {
|
||||||
|
|||||||
42
src/main/resources/static/js/settings.js
Normal file
42
src/main/resources/static/js/settings.js
Normal 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);
|
||||||
|
});
|
||||||
@@ -43,7 +43,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="dpi">DPI:</label>
|
<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>
|
</div>
|
||||||
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{pdfToImage.submit}"></button>
|
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{pdfToImage.submit}"></button>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ margin-top: 0;
|
|||||||
<div id="content-wrap">
|
<div id="content-wrap">
|
||||||
|
|
||||||
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
|
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
|
||||||
|
<div th:insert="~{fragments/errorBanner.html :: errorBanner}"></div>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
|
|
||||||
<div id="support-section">
|
<div id="support-section">
|
||||||
|
|||||||
@@ -8,14 +8,14 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
|
||||||
<!-- jQuery -->
|
<!-- jQuery -->
|
||||||
<script src="js/jquery.min.js"></script>
|
<script src="js/thirdParty/jquery.min.js"></script>
|
||||||
|
|
||||||
<!-- jQuery -->
|
<!-- jQuery -->
|
||||||
<script src="js/jszip.min.js"></script>
|
<script src="js/thirdParty/jszip.min.js"></script>
|
||||||
|
|
||||||
<!-- Bootstrap -->
|
<!-- Bootstrap -->
|
||||||
<script src="js/popper.min.js"></script>
|
<script src="js/thirdParty/popper.min.js"></script>
|
||||||
<script src="js/bootstrap.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.min.css">
|
||||||
<link rel="stylesheet" href="css/bootstrap-icons.css">
|
<link rel="stylesheet" href="css/bootstrap-icons.css">
|
||||||
|
|
||||||
@@ -23,7 +23,7 @@
|
|||||||
<script src="pdfjs/pdf.js"></script>
|
<script src="pdfjs/pdf.js"></script>
|
||||||
|
|
||||||
<!-- PDF-Lib -->
|
<!-- PDF-Lib -->
|
||||||
<script src="js/pdf-lib.min.js"></script>
|
<script src="js/thirdParty/pdf-lib.min.js"></script>
|
||||||
|
|
||||||
<!-- Custom -->
|
<!-- Custom -->
|
||||||
<link rel="stylesheet" href="css/general.css">
|
<link rel="stylesheet" href="css/general.css">
|
||||||
@@ -34,84 +34,7 @@
|
|||||||
<script src="js/tab-container.js"></script>
|
<script src="js/tab-container.js"></script>
|
||||||
|
|
||||||
|
|
||||||
<script>
|
<script src="js/darkmode.js"></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>
|
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
@@ -163,326 +86,17 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
<div id="level">Level: 1</div>
|
<div id="level">Level: 1</div>
|
||||||
<img src="favicon.svg" class="player" id="player">
|
<img src="favicon.svg" class="player" id="player">
|
||||||
</div>
|
</div>
|
||||||
<style>
|
<link rel="stylesheet" href="css/game.css">
|
||||||
#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>
|
|
||||||
</dialog>
|
</dialog>
|
||||||
</th:block>
|
</th:block>
|
||||||
|
|
||||||
<th:block th:fragment="fileSelector(name, multiple)" th:with="accept=${accept} ?: '*/*', inputText=${inputText} ?: #{pdfPrompt}, remoteCall=${remoteCall} ?: 'true', notRequired=${notRequired} ?: false">
|
<th:block th:fragment="fileSelector(name, multiple)" th:with="accept=${accept} ?: '*/*', inputText=${inputText} ?: #{pdfPrompt}, remoteCall=${remoteCall} ?: true, notRequired=${notRequired} ?: false">
|
||||||
<script>
|
<script th:inline="javascript">
|
||||||
$(document).ready(function() {
|
const pdfPasswordPrompt =/*[[#{error.pdfPassword}]]*/ '';
|
||||||
$('form').submit(async function(event) {
|
const multiple = [[${multiple}]] || false;
|
||||||
const boredWaiting = localStorage.getItem('boredWaiting');
|
const remoteCall = [[${remoteCall}]] || true;
|
||||||
if (boredWaiting === 'enabled') {
|
</script>
|
||||||
$('#show-game-btn').show();
|
<script src="js/downloader.js"></script>
|
||||||
}
|
|
||||||
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>
|
|
||||||
|
|
||||||
<div class="custom-file-chooser">
|
<div class="custom-file-chooser">
|
||||||
<div class="custom-file">
|
<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>
|
<button type="button" class="btn btn-primary" id="show-game-btn" style="display:none;">Bored waiting?</button>
|
||||||
|
|
||||||
|
|
||||||
|
<script th:inline="javascript">
|
||||||
<script th:inline="javascript">
|
const elementID = /*[[${name+"-input"}]]*/ '';
|
||||||
document.addEventListener('DOMContentLoaded', function () {
|
const filesSelected = /*[[#{filesSelected}]]*/ '';
|
||||||
const fileInput = document.getElementById([[${name+"-input"}]]);
|
const pdfPrompt = /*[[#{pdfPrompt}]]*/ '';
|
||||||
|
|
||||||
// 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>
|
</script>
|
||||||
|
|
||||||
<style>
|
<script src="js/fileInput.js"></script>
|
||||||
.custom-file-label {
|
<link rel="stylesheet" href="css/fileSelect.css">
|
||||||
padding-right: 90px;
|
|
||||||
}
|
|
||||||
.selected-files {
|
|
||||||
margin-top: 10px;
|
|
||||||
max-height: 150px;
|
|
||||||
overflow-y: auto;
|
|
||||||
white-space: pre-wrap;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
</th:block>
|
</th:block>
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
<th:block th:fragment="errorBannerPerPage">
|
||||||
|
|
||||||
|
|
||||||
|
<div id="errorContainer" class="alert alert-danger alert-dismissible fade show" role="alert" style="display: none;">
|
||||||
|
<h4 class="alert-heading">Error</h4>
|
||||||
|
<p></p>
|
||||||
|
<button type="button" class="btn btn-danger" onclick="toggletrace()">Show Stack Trace</button>
|
||||||
|
<button type="button" class="btn btn-secondary" onclick="copytrace()">Copy Stack Trace</button>
|
||||||
|
<button type="button" class="btn btn-info" onclick="showHelp()">Help</button>
|
||||||
|
<button type="button" class="close" data-dismiss="alert" aria-label="Close" onclick="dismissError()">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
<!-- Stack trace section -->
|
||||||
|
<div id="trace" style="max-height: 0; overflow: hidden;">
|
||||||
|
<div style="background-color: #f8d7da; border: 1px solid #f5c6cb; border-radius: 3px; padding: 10px; margin-top: 5px;">
|
||||||
|
<pre id="traceContent"></pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Help Modal -->
|
||||||
|
<link rel="stylesheet" href="css/errorBanner.css">
|
||||||
|
|
||||||
|
<div class="modal fade" id="helpModal" tabindex="-1" role="dialog" aria-labelledby="helpModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-dialog-centered" role="document" id="helpModalDialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="helpModalLabel">Help</h5>
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div id="support-section">
|
||||||
|
<h1 class="display-2">Oops!</h1>
|
||||||
|
<p class="lead">Sorry for the issue!.</p>
|
||||||
|
<br>
|
||||||
|
<h2>Need help / Found an issue?</h2>
|
||||||
|
<p>If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord:</p>
|
||||||
|
<div id="button-group">
|
||||||
|
<a href="https://github.com/Frooodle/Stirling-PDF/issues" id="github-button" target="_blank">GitHub - Submit a ticket</a>
|
||||||
|
<a href="https://discord.gg/Cn8pWhQRxZ" id="discord-button" target="_blank">Discord - Submit Support post</a>
|
||||||
|
</div>
|
||||||
|
<a href="/" id="home-button">Go to Homepage</a>
|
||||||
|
<a data-dismiss="modal" id="home-button">Close</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script src="js/errorBanner.js"></script>
|
||||||
|
</th:block>
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
<div th:fragment="footer">
|
<div th:fragment="footer">
|
||||||
<footer id="footer" class="text-center py-3">
|
<footer id="footer" class="text-center py-3">
|
||||||
<a href="https://github.com/Frooodle/Stirling-PDF" target="_blank" class="mx-1"><img src="images/github.svg"></img></a>
|
<a href="https://github.com/Frooodle/Stirling-PDF" target="_blank" class="mx-1" title="Visit Github Repository"><img src="images/github.svg"></img></a>
|
||||||
<a href="https://hub.docker.com/r/frooodle/s-pdf" target="_blank" class="mx-1"><img src="images/docker.svg"></img></a>
|
<a href="https://hub.docker.com/r/frooodle/s-pdf" target="_blank" class="mx-1" title="See Docker Hub"><img src="images/docker.svg"></img></a>
|
||||||
<a href="https://discord.gg/Cn8pWhQRxZ" target="_blank" class="mx-1"><img src="images/discord.svg"></img></a>
|
<a href="https://discord.gg/Cn8pWhQRxZ" target="_blank" class="mx-1" title="Join Discord Channel"><img src="images/discord.svg"></img></a>
|
||||||
|
<a href="https://github.com/sponsors/Frooodle" target="_blank" class="mx-1" title="Donate"><img src="images/suit-heart-fill.svg"></img></a>
|
||||||
<div th:if="${@appName} != 'Stirling PDF'" class="mt-2" style="color: grey;">Powered by Stirling PDF</div>
|
<div th:if="${@appName} != 'Stirling PDF'" class="mt-2" style="color: grey;">Powered by Stirling PDF</div>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
@@ -1,124 +1,13 @@
|
|||||||
<div th:fragment="navbar" class="mx-auto">
|
<div th:fragment="navbar" class="mx-auto">
|
||||||
<script>
|
<script src="js/languageSelection.js"></script>
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script th:inline="javascript">
|
<script th:inline="javascript">
|
||||||
function compareVersions(version1, version2) {
|
const currentVersion = /*[[${@appVersion}]]*/ '';
|
||||||
const v1 = version1.split('.');
|
const noFavourites = /*[[#{noFavourites}]]*/ '';
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
const currentVersion = /*[[${@appVersion}]]*/ ''; // Replace with your current version number
|
|
||||||
|
|
||||||
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();
|
|
||||||
</script>
|
</script>
|
||||||
|
<script th:src="@{/js/githubVersion.js}"></script>
|
||||||
|
|
||||||
<style>
|
<link rel="stylesheet" href="css/navbar.css">
|
||||||
|
|
||||||
.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);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<nav class="navbar navbar-expand-lg navbar-light bg-light">
|
<nav class="navbar navbar-expand-lg navbar-light bg-light">
|
||||||
<div class="container ">
|
<div class="container ">
|
||||||
@@ -156,7 +45,9 @@ function compareVersions(version1, version2) {
|
|||||||
<div th:replace="~{fragments/navbarEntry :: navbarEntry ( 'pdf-organizer', 'images/sort-numeric-down.svg', 'home.pdfOrganiser.title', 'home.pdfOrganiser.desc')}"></div>
|
<div th:replace="~{fragments/navbarEntry :: navbarEntry ( 'pdf-organizer', 'images/sort-numeric-down.svg', 'home.pdfOrganiser.title', 'home.pdfOrganiser.desc')}"></div>
|
||||||
<div th:replace="~{fragments/navbarEntry :: navbarEntry ( 'rotate-pdf', 'images/arrow-clockwise.svg', 'home.rotate.title', 'home.rotate.desc')}"></div>
|
<div th:replace="~{fragments/navbarEntry :: navbarEntry ( 'rotate-pdf', 'images/arrow-clockwise.svg', 'home.rotate.title', 'home.rotate.desc')}"></div>
|
||||||
<div th:replace="~{fragments/navbarEntry :: navbarEntry ( 'remove-pages', 'images/file-earmark-x.svg', 'home.removePages.title', 'home.removePages.desc')}"></div>
|
<div th:replace="~{fragments/navbarEntry :: navbarEntry ( 'remove-pages', 'images/file-earmark-x.svg', 'home.removePages.title', 'home.removePages.desc')}"></div>
|
||||||
|
<div th:replace="~{fragments/navbarEntry :: navbarEntry ( 'multi-page-layout', 'images/page-layout.svg', 'home.pageLayout.title', 'home.pageLayout.desc')}"></div>
|
||||||
|
<div th:replace="~{fragments/navbarEntry :: navbarEntry ( 'scale-pages', 'images/scale-pages.svg', 'home.scalePages.title', 'home.scalePages.desc')}"></div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item nav-item-separator"></li>
|
<li class="nav-item nav-item-separator"></li>
|
||||||
@@ -273,6 +164,9 @@ function compareVersions(version1, version2) {
|
|||||||
<a class="dropdown-item lang_dropdown-item" href="" data-language-code="pl_PL">
|
<a class="dropdown-item lang_dropdown-item" href="" data-language-code="pl_PL">
|
||||||
<img src="images/flags/pl.svg" alt="icon" width="20" height="15"> Polski
|
<img src="images/flags/pl.svg" alt="icon" width="20" height="15"> Polski
|
||||||
</a>
|
</a>
|
||||||
|
<a class="dropdown-item lang_dropdown-item" href="" data-language-code="pt_BR">
|
||||||
|
<img src="images/flags/pt_br.svg" alt="icon" width="20" height="15"> Português (BR)
|
||||||
|
</a>
|
||||||
<a class="dropdown-item lang_dropdown-item" href="" data-language-code="ro_RO">
|
<a class="dropdown-item lang_dropdown-item" href="" data-language-code="ro_RO">
|
||||||
<img src="images/flags/ro.svg" alt="icon" width="20" height="15"> Romanian
|
<img src="images/flags/ro.svg" alt="icon" width="20" height="15"> Romanian
|
||||||
</a>
|
</a>
|
||||||
@@ -302,51 +196,10 @@ function compareVersions(version1, version2) {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<script th:inline="javascript">
|
<script src="js/favourites.js"></script>
|
||||||
|
|
||||||
|
|
||||||
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();
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
</nav>
|
</nav>
|
||||||
<div th:insert="~{fragments/errorBanner.html :: errorBanner}"></div>
|
|
||||||
|
<div th:insert="~{fragments/errorBannerPerPage.html :: errorBannerPerPage}"></div>
|
||||||
<div class="modal fade" id="settingsModal" tabindex="-1" role="dialog" aria-labelledby="settingsModalLabel" aria-hidden="true">
|
<div class="modal fade" id="settingsModal" tabindex="-1" role="dialog" aria-labelledby="settingsModalLabel" aria-hidden="true">
|
||||||
<div class="modal-dialog modal-dialog-centered" role="document">
|
<div class="modal-dialog modal-dialog-centered" role="document">
|
||||||
<div class="modal-content dark-card">
|
<div class="modal-content dark-card">
|
||||||
@@ -359,9 +212,13 @@ function compareVersions(version1, version2) {
|
|||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||||
<p class="mb-0" th:utext="#{settings.appVersion} + ' ' + ${@appVersion}"></p>
|
<p class="mb-0" th:utext="#{settings.appVersion} + ' ' + ${@appVersion}"></p>
|
||||||
<a href="swagger-ui/index.html" target="_blank">
|
<a href="https://github.com/sponsors/Frooodle" target="_blank">
|
||||||
<button type="button" class="btn btn-sm btn-outline-primary"> API </button>
|
<button type="button" class="btn btn-sm btn-outline-primary">Sponsor Stirling-PDF</button>
|
||||||
</a>
|
</a>
|
||||||
|
<a href="swagger-ui/index.html" target="_blank">
|
||||||
|
<button type="button" class="btn btn-sm btn-outline-primary">API</button>
|
||||||
|
</a>
|
||||||
|
|
||||||
<a href="https://github.com/Frooodle/Stirling-PDF/releases" target="_blank">
|
<a href="https://github.com/Frooodle/Stirling-PDF/releases" target="_blank">
|
||||||
<button type="button" class="btn btn-sm btn-outline-primary" id="update-btn" th:utext="#{settings.update}"></button>
|
<button type="button" class="btn btn-sm btn-outline-primary" id="update-btn" th:utext="#{settings.update}"></button>
|
||||||
</a>
|
</a>
|
||||||
@@ -392,78 +249,13 @@ function compareVersions(version1, version2) {
|
|||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-secondary" data-dismiss="modal" th:text="#{close}"></button>
|
<button type="button" class="btn btn-secondary" data-dismiss="modal" th:text="#{close}"></button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<script>
|
<script src="js/settings.js"></script>
|
||||||
$(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"]');
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
});
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user