Compare commits
213 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ccee0eecb5 | ||
|
|
2b7e97a09f | ||
|
|
a2926b8fe9 | ||
|
|
9009317f83 | ||
|
|
67772e7221 | ||
|
|
cc02da47d7 | ||
|
|
af540183a6 | ||
|
|
6581bb4ab4 | ||
|
|
53e5edcb75 | ||
|
|
ae22066bad | ||
|
|
6ee5daf884 | ||
|
|
93edc56f1d | ||
|
|
41970348cd | ||
|
|
815a823104 | ||
|
|
c27f99ab58 | ||
|
|
ed35a878a5 | ||
|
|
bd1ab5e80b | ||
|
|
a1fae66800 | ||
|
|
c4d1761687 | ||
|
|
a647347e10 | ||
|
|
2d42ae9a36 | ||
|
|
8ce900acca | ||
|
|
60ee55b602 | ||
|
|
75cd62b7c2 | ||
|
|
55fe47d4f3 | ||
|
|
440d5072b7 | ||
|
|
80968ee6c4 | ||
|
|
99254c964b | ||
|
|
d2a4cf74e8 | ||
|
|
fd918ef15f | ||
|
|
e16ed5ca1a | ||
|
|
25e5470c5e | ||
|
|
59320e6828 | ||
|
|
f9aacb4c66 | ||
|
|
28faf3888c | ||
|
|
a9e22947ef | ||
|
|
7585fc80b4 | ||
|
|
5139af9f48 | ||
|
|
f2521717ae | ||
|
|
0f935b92cb | ||
|
|
5e3a2b456e | ||
|
|
632f29d1d6 | ||
|
|
7353d69f1a | ||
|
|
0b1cdf6a68 | ||
|
|
e0350b2837 | ||
|
|
1a521505a6 | ||
|
|
12d457e3ee | ||
|
|
d58933ea8d | ||
|
|
5bbfd15f38 | ||
|
|
acf4662d2f | ||
|
|
1d55ee7f93 | ||
|
|
b86afd7fc9 | ||
|
|
a5164dc0b6 | ||
|
|
cc919ea614 | ||
|
|
42d0d49682 | ||
|
|
07e81a117b | ||
|
|
a56bcb09b0 | ||
|
|
28b3272cd0 | ||
|
|
5c7221d0d7 | ||
|
|
0aa79d28f8 | ||
|
|
4b4cdb85ff | ||
|
|
654f7742e4 | ||
|
|
f082bca6c9 | ||
|
|
f7ace9d92a | ||
|
|
9759b44cbb | ||
|
|
5dfe8a83cd | ||
|
|
3c47f21337 | ||
|
|
b75360bdb1 | ||
|
|
32bace863c | ||
|
|
acda1e4dd8 | ||
|
|
a342def43f | ||
|
|
b100435d9c | ||
|
|
b2a29e2b13 | ||
|
|
fc0af56136 | ||
|
|
fd246aac93 | ||
|
|
7d90ecf91f | ||
|
|
d2aa72d873 | ||
|
|
d0b57c0419 | ||
|
|
5bee714437 | ||
|
|
9af537c985 | ||
|
|
30c56a0ec9 | ||
|
|
563a72ad95 | ||
|
|
2d4aff3b08 | ||
|
|
e0783cad60 | ||
|
|
feaad367df | ||
|
|
6b5b58ea94 | ||
|
|
60d1d336d6 | ||
|
|
2b93407e64 | ||
|
|
d5dec31b39 | ||
|
|
2608aa4c97 | ||
|
|
c174ca1c7e | ||
|
|
80c26a9550 | ||
|
|
f4ad6b963f | ||
|
|
b470cdf60c | ||
|
|
585bf4ccb4 | ||
|
|
6b0fedfabf | ||
|
|
9a1510a4f1 | ||
|
|
bcb4594afa | ||
|
|
0ebec74bed | ||
|
|
d10f5734fb | ||
|
|
91171727e4 | ||
|
|
e8a91d2631 | ||
|
|
cd020e536b | ||
|
|
c05605a286 | ||
|
|
63a698b679 | ||
|
|
3d3ef6f37b | ||
|
|
9e839e130e | ||
|
|
3e2ca2fd5f | ||
|
|
ee26262b6e | ||
|
|
4327af5133 | ||
|
|
6cce2ee70a | ||
|
|
70d07433d5 | ||
|
|
8109c63250 | ||
|
|
32ba7361ac | ||
|
|
a9da1b648f | ||
|
|
1fcd64a1ab | ||
|
|
f8362b6bfa | ||
|
|
fb0541fddf | ||
|
|
61b645c51c | ||
|
|
84b5a69074 | ||
|
|
374f445953 | ||
|
|
bf78db9b75 | ||
|
|
e3882b78db | ||
|
|
5389d4fc13 | ||
|
|
01529cc981 | ||
|
|
93fb571725 | ||
|
|
ab4aea315a | ||
|
|
90ca50ae7a | ||
|
|
3cb36d36bf | ||
|
|
8f51f025d0 | ||
|
|
e048fc6653 | ||
|
|
a44fc62fee | ||
|
|
9da29bf99a | ||
|
|
1edb669583 | ||
|
|
8e4e2469b0 | ||
|
|
f91953f67a | ||
|
|
0bb61149e8 | ||
|
|
e5f7d1077d | ||
|
|
7c91a77442 | ||
|
|
fb24398b01 | ||
|
|
43107965a9 | ||
|
|
67b7435624 | ||
|
|
78d3fd3768 | ||
|
|
243e4889b9 | ||
|
|
42564e683b | ||
|
|
96097d1cd3 | ||
|
|
4f35a8d79f | ||
|
|
3b8bed5b0a | ||
|
|
5897f0e3ed | ||
|
|
2aba80d0ca | ||
|
|
af6cd2e38b | ||
|
|
5ab2664c70 | ||
|
|
39c31ef5d9 | ||
|
|
bd1c7a35e2 | ||
|
|
0eed041986 | ||
|
|
1958c34bcb | ||
|
|
f5ceede3cd | ||
|
|
d98473f5e3 | ||
|
|
19d7027361 | ||
|
|
a34c2863bd | ||
|
|
4521dce1a9 | ||
|
|
3564c1a45c | ||
|
|
b6e2d25462 | ||
|
|
256ce50c49 | ||
|
|
fb0ad73035 | ||
|
|
71636c3413 | ||
|
|
5ef28bab9a | ||
|
|
757433629a | ||
|
|
050ae2a512 | ||
|
|
4c81eac8fb | ||
|
|
a4544d7943 | ||
|
|
9d65537317 | ||
|
|
b6a284e2bc | ||
|
|
f4e5690841 | ||
|
|
fa84479254 | ||
|
|
6ef1ca0a0b | ||
|
|
927a1481a9 | ||
|
|
7f9f45c720 | ||
|
|
7ddc607fd2 | ||
|
|
4e1b0c6abe | ||
|
|
e4d7b53112 | ||
|
|
f9ff57a26e | ||
|
|
2af9f19c3a | ||
|
|
dbb8e2b245 | ||
|
|
689ad18c71 | ||
|
|
4422787d4e | ||
|
|
8d1057477b | ||
|
|
b3dff3a520 | ||
|
|
6141be7310 | ||
|
|
2762459acf | ||
|
|
53d8d4dbb4 | ||
|
|
5ae72f3e6f | ||
|
|
872b36124f | ||
|
|
f762ab6aa7 | ||
|
|
c311f9a4ed | ||
|
|
0a7517ecdd | ||
|
|
03febd9484 | ||
|
|
655b97bfd5 | ||
|
|
fdbc7f4621 | ||
|
|
f9fe303671 | ||
|
|
d4459eb6d6 | ||
|
|
9087a3ebbd | ||
|
|
56669f4247 | ||
|
|
22be1a1104 | ||
|
|
a80c585086 | ||
|
|
6d5dbd9729 | ||
|
|
0b4e3de455 | ||
|
|
8faef235a6 | ||
|
|
559bc7c731 | ||
|
|
3d7fdd0f35 | ||
|
|
726fcf861c | ||
|
|
839105f41d | ||
|
|
a2a27e2216 |
2
.github/FUNDING.yml
vendored
@@ -1,6 +1,6 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||
github: Frooodle # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||
patreon: # Replace with a single Patreon username
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
ko_fi: # Replace with a single Ko-fi username
|
||||
|
||||
2
.github/workflows/codeql.yml
vendored
@@ -46,7 +46,7 @@ jobs:
|
||||
# with:
|
||||
# languages: java
|
||||
|
||||
- uses: gradle/gradle-build-action@v2.3.3
|
||||
- uses: gradle/gradle-build-action@v2.4.2
|
||||
with:
|
||||
gradle-version: 7.6
|
||||
arguments: assemble --no-build-cache
|
||||
|
||||
78
.github/workflows/push-docker.yml
vendored
@@ -1,10 +1,10 @@
|
||||
name: Push Docker Image with VersionNumber
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- testGit
|
||||
- main
|
||||
jobs:
|
||||
push:
|
||||
@@ -12,16 +12,16 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v3.5.2
|
||||
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v3
|
||||
uses: actions/setup-java@v3.11.0
|
||||
with:
|
||||
java-version: '17'
|
||||
distribution: 'temurin'
|
||||
|
||||
|
||||
- uses: gradle/gradle-build-action@v2.3.3
|
||||
- uses: gradle/gradle-build-action@v2.4.2
|
||||
with:
|
||||
gradle-version: 7.6
|
||||
arguments: clean build
|
||||
@@ -37,43 +37,39 @@ jobs:
|
||||
uses: docker/login-action@v2.1.0
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_HUB_API }}
|
||||
|
||||
# - name: Check if tag exists
|
||||
# id: checkIdExists
|
||||
# continue-on-error: true
|
||||
# run: |
|
||||
# response=$(curl -s https://hub.docker.com/v2/repositories/frooodle/s-pdf/tags/?name=${{ steps.versionNumber.outputs.versionNumber }})
|
||||
# result=$(echo $response | jq ".results")
|
||||
# if [ "$result" == "[]" ]; then
|
||||
# echo "Tag ${{ steps.versionNumber.outputs.versionNumber }} doesnt exist. Continuing with build and push."
|
||||
# else
|
||||
# echo "Tag ${{ steps.versionNumber.outputs.versionNumber }} already exists. Skipping build and push."
|
||||
# exit 1;
|
||||
# fi
|
||||
|
||||
|
||||
|
||||
- name: Setup buildx
|
||||
run: |
|
||||
docker buildx create --name mybuilder
|
||||
docker buildx use mybuilder
|
||||
|
||||
- name: Build and push versioned amd64 and v8
|
||||
if: github.ref == 'refs/heads/main'
|
||||
run: |
|
||||
docker buildx build --platform="linux/amd64,linux/arm64/v8" --push --tag "frooodle/s-pdf:${{ steps.versionNumber.outputs.versionNumber }}-alpha" .
|
||||
|
||||
password: ${{ secrets.DOCKER_HUB_API }}
|
||||
|
||||
- name: Build and push versioned amd64 and v8
|
||||
if: github.ref == 'refs/heads/master'
|
||||
run: |
|
||||
docker buildx build --platform="linux/amd64,linux/arm64/v8" --push --tag "frooodle/s-pdf:${{ steps.versionNumber.outputs.versionNumber }}" .
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v2.1.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ github.token }}
|
||||
|
||||
|
||||
- name: Build and push latest amd64 and v8
|
||||
if: github.ref == 'refs/heads/master'
|
||||
run: |
|
||||
docker buildx build --platform="linux/amd64,linux/arm64/v8" --push --tag "frooodle/s-pdf:latest" .
|
||||
|
||||
- name: Generate tags
|
||||
id: meta
|
||||
uses: docker/metadata-action@v4.4.0
|
||||
with:
|
||||
images: |
|
||||
${{ secrets.DOCKER_HUB_USERNAME }}/s-pdf
|
||||
ghcr.io/${{ github.repository_owner }}/s-pdf
|
||||
tags: |
|
||||
${{ steps.versionNumber.outputs.versionNumber }}${{ github.ref == 'refs/heads/main' && '-alpha' || '' }}
|
||||
type=raw,value=latest,enable=${{ github.ref == 'refs/heads/master' }}
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2.1.0
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2.5.0
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v4.0.0
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
platforms: linux/amd64,linux/arm64/v8
|
||||
|
||||
4
.gitignore
vendored
@@ -109,4 +109,6 @@ local.properties
|
||||
*.tar.gz
|
||||
*.rar
|
||||
|
||||
/build
|
||||
/build
|
||||
|
||||
/.vscode
|
||||
56
Dockerfile
@@ -1,46 +1,9 @@
|
||||
# Build jbig2enc in a separate stage
|
||||
FROM debian:bullseye-slim as jbig2enc_builder
|
||||
FROM frooodle/stirling-pdf-base:beta2
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y --no-install-recommends \
|
||||
git \
|
||||
automake \
|
||||
autoconf \
|
||||
libtool \
|
||||
libleptonica-dev \
|
||||
pkg-config \
|
||||
ca-certificates \
|
||||
zlib1g-dev \
|
||||
make \
|
||||
g++
|
||||
|
||||
RUN git clone https://github.com/agl/jbig2enc && \
|
||||
cd jbig2enc && \
|
||||
./autogen.sh && \
|
||||
./configure && \
|
||||
make && \
|
||||
make install
|
||||
|
||||
# Main stage
|
||||
FROM openjdk:17-jdk-slim
|
||||
|
||||
# Install necessary dependencies
|
||||
RUN apt-get update && \
|
||||
apt-get install -y --no-install-recommends \
|
||||
libreoffice-core \
|
||||
libreoffice-common \
|
||||
libreoffice-writer \
|
||||
libreoffice-calc \
|
||||
libreoffice-impress \
|
||||
python3-uno \
|
||||
python3-pip \
|
||||
unoconv \
|
||||
pngquant \
|
||||
ocrmypdf && \
|
||||
pip install --user --upgrade ocrmypdf
|
||||
|
||||
# Copy the jbig2enc binary from the builder stage
|
||||
COPY --from=jbig2enc_builder /usr/local/bin/jbig2 /usr/local/bin/jbig2
|
||||
# Create scripts folder and copy local scripts
|
||||
RUN mkdir /scripts
|
||||
COPY ./scripts/* /scripts/
|
||||
|
||||
# Copy the application JAR file
|
||||
COPY build/libs/*.jar app.jar
|
||||
@@ -49,10 +12,11 @@ COPY build/libs/*.jar app.jar
|
||||
EXPOSE 8080
|
||||
|
||||
# Set environment variables
|
||||
ENV LOG_LEVEL=INFO
|
||||
ENV APP_HOME_NAME="Stirling PDF"
|
||||
#ENV APP_HOME_DESCRIPTION="Personal PDF Website!"
|
||||
#ENV APP_NAVBAR_NAME="Stirling PDF"
|
||||
|
||||
# Run the application
|
||||
ENTRYPOINT ["java","-jar","/app.jar","-Dlogging.level=${LOG_LEVEL}"]
|
||||
|
||||
|
||||
|
||||
RUN chmod +x /scripts/init.sh
|
||||
ENTRYPOINT ["/scripts/init.sh"]
|
||||
CMD ["java", "-jar", "/app.jar"]
|
||||
|
||||
62
DockerfileBase
Normal file
@@ -0,0 +1,62 @@
|
||||
# Build jbig2enc in a separate stage
|
||||
FROM debian:bullseye-slim as jbig2enc_builder
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y --no-install-recommends \
|
||||
git \
|
||||
automake \
|
||||
autoconf \
|
||||
libtool \
|
||||
libleptonica-dev \
|
||||
pkg-config \
|
||||
ca-certificates \
|
||||
zlib1g-dev \
|
||||
make \
|
||||
g++
|
||||
|
||||
RUN git clone https://github.com/agl/jbig2enc && \
|
||||
cd jbig2enc && \
|
||||
./autogen.sh && \
|
||||
./configure && \
|
||||
make && \
|
||||
make install
|
||||
|
||||
|
||||
# Main stage
|
||||
FROM openjdk:17-jdk-slim AS base
|
||||
RUN apt-get update && \
|
||||
apt-get install -y --no-install-recommends \
|
||||
libreoffice-core \
|
||||
libreoffice-common \
|
||||
libreoffice-writer \
|
||||
libreoffice-calc \
|
||||
libreoffice-impress \
|
||||
python3-uno \
|
||||
python3-pip \
|
||||
unoconv \
|
||||
pngquant \
|
||||
unpaper \
|
||||
ocrmypdf && \
|
||||
rm -rf /var/lib/apt/lists/* && \
|
||||
mkdir /usr/share/tesseract-ocr-original && \
|
||||
cp -r /usr/share/tesseract-ocr/* /usr/share/tesseract-ocr-original && \
|
||||
rm -rf /usr/share/tesseract-ocr
|
||||
|
||||
# Python packages stage
|
||||
FROM base AS python-packages
|
||||
RUN apt-get update && \
|
||||
apt-get install -y --no-install-recommends \
|
||||
build-essential \
|
||||
libffi-dev \
|
||||
libssl-dev \
|
||||
zlib1g-dev \
|
||||
libjpeg-dev && \
|
||||
pip install --upgrade pip && \
|
||||
pip install --no-cache-dir \
|
||||
opencv-python-headless && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Final stage: Copy necessary files from the previous stage
|
||||
FROM base
|
||||
COPY --from=python-packages /usr/local /usr/local
|
||||
COPY --from=jbig2enc_builder /usr/local/bin/jbig2 /usr/local/bin/jbig2
|
||||
@@ -8,11 +8,18 @@ Fork Stirling-PDF and make a new branch out of Main
|
||||
|
||||
Then add reference to the language in the navbar by adding a new language entry to the dropdown
|
||||
|
||||
https://github.com/Frooodle/Stirling-PDF/blob/main/src/main/resources/templates/fragments/navbar.html#L80
|
||||
https://github.com/Frooodle/Stirling-PDF/blob/main/src/main/resources/templates/fragments/navbar.html#L306
|
||||
and add a flag svg file to
|
||||
https://github.com/Frooodle/Stirling-PDF/tree/main/src/main/resources/static/images/flags
|
||||
Any SVG flags are fine, i got most of mine from [here](https://flagicons.lipis.dev/)
|
||||
If your language isnt represented by a flag just find whichever closely matches it, such as for Arabic i chose Saudi Arabia
|
||||
|
||||
|
||||
For example to add Polish you would add
|
||||
```
|
||||
<a class="dropdown-item lang_dropdown-item" href="" data-language-code="pl_PL">Polish</a>
|
||||
<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
|
||||
</a>
|
||||
```
|
||||
The data-language-code is the code used to reference the file in the next step.
|
||||
|
||||
|
||||
@@ -20,6 +20,8 @@ Depending on your requirements, you can choose the appropriate language pack for
|
||||
1. Download the desired language pack(s) by selecting the `.traineddata` file(s) for the language(s) you need.
|
||||
2. Place the `.traineddata` files in the Tesseract tessdata directory: `/usr/share/tesseract-ocr/4.00/tessdata`
|
||||
|
||||
# DO NOT REMOVE EXISTING ENG.TRAINEDDATA, ITS REQUIRED.
|
||||
|
||||
#### Docker
|
||||
|
||||
If you are using Docker, you need to expose the Tesseract tessdata directory as a volume in order to use the additional language packs.
|
||||
@@ -32,14 +34,14 @@ services:
|
||||
your_service_name:
|
||||
image: your_docker_image_name
|
||||
volumes:
|
||||
- /usr/share/tesseract-ocr/4.00/tessdata:/location/of/trainingData
|
||||
- /location/of/trainingData:/usr/share/tesseract-ocr/4.00/tessdata
|
||||
```
|
||||
|
||||
|
||||
#### Docker run
|
||||
Add the following to your existing docker run command
|
||||
```bash
|
||||
-v /usr/share/tesseract-ocr/4.00/tessdata:/location/of/trainingData
|
||||
-v /location/of/trainingData:/usr/share/tesseract-ocr/4.00/tessdata
|
||||
```
|
||||
|
||||
#### Non-Docker
|
||||
|
||||
137
LocalRunGuide.md
Normal file
@@ -0,0 +1,137 @@
|
||||
|
||||
To run the application without Docker, you will need to manually install all dependencies and build the necessary components.
|
||||
|
||||
Note that some dependencies might not be available in the standard repositories of all Linux distributions, and may require additional steps to install.
|
||||
|
||||
The following guide assumes you have a basic understanding of using a command line interface in your operating system.
|
||||
|
||||
It should work on most Linux distributions and MacOS. For Windows, you might need to use Windows Subsystem for Linux (WSL) for certain steps.
|
||||
The amount of dependencies is to actually reduce overall size, ie installing LibreOffice sub components rather than full LibreOffice package.
|
||||
|
||||
### Step 1: Prerequisites
|
||||
|
||||
Install the following software, if not already installed:
|
||||
|
||||
- Java 17 or later
|
||||
|
||||
- Gradle 7.0 or later (included within repo so not needed on server)
|
||||
|
||||
- Git
|
||||
|
||||
- Python 3 (with pip)
|
||||
|
||||
- Make
|
||||
|
||||
- GCC/G++
|
||||
|
||||
- Automake
|
||||
|
||||
- Autoconf
|
||||
|
||||
- libtool
|
||||
|
||||
- pkg-config
|
||||
|
||||
- zlib1g-dev
|
||||
|
||||
- libleptonica-dev
|
||||
|
||||
For Debian-based systems, you can use the following command:
|
||||
|
||||
```bash
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y git automake autoconf libtool libleptonica-dev pkg-config zlib1g-dev make g++ java-17-openjdk python3 python3-pip
|
||||
```
|
||||
|
||||
### Step 2: Clone and Build jbig2enc (Only required for certain OCR functionality)
|
||||
|
||||
```bash
|
||||
git clone https:github.com/agl/jbig2enc
|
||||
cd jbig2enc
|
||||
./autogen.sh
|
||||
./configure
|
||||
make
|
||||
sudo make install
|
||||
```
|
||||
|
||||
### Step 3: Install Additional Software
|
||||
Next we need to install LibreOffice for conversions, ocrmypdf for OCR, and opencv for patern recognition functionality.
|
||||
|
||||
Install the following software:
|
||||
|
||||
- libreoffice-core
|
||||
|
||||
- libreoffice-common
|
||||
|
||||
- libreoffice-writer
|
||||
|
||||
- libreoffice-calc
|
||||
|
||||
- libreoffice-impress
|
||||
|
||||
- python3-uno
|
||||
|
||||
- unoconv
|
||||
|
||||
- pngquant
|
||||
|
||||
- unpaper
|
||||
|
||||
- ocrmypdf
|
||||
|
||||
- opencv-python-headless
|
||||
|
||||
For Debian-based systems, you can use the following command:
|
||||
|
||||
```bash
|
||||
sudo apt-get install -y libreoffice-core libreoffice-common libreoffice-writer libreoffice-calc libreoffice-impress python3-uno unoconv pngquant unpaper ocrmypdf
|
||||
pip3 install opencv-python-headless
|
||||
```
|
||||
|
||||
### Step 4: Clone and Build Stirling-PDF
|
||||
|
||||
```bash
|
||||
git clone https://github.com/Frooodle/Stirling-PDF.git
|
||||
cd Stirling-PDF
|
||||
./gradlew build
|
||||
```
|
||||
|
||||
|
||||
### Step 5: Move jar to desired location
|
||||
|
||||
After the build process, a `.jar` file will be generated in the `build/libs` directory.
|
||||
You can move this file to a desired location, for example, `/opt/Stirling-PDF/`.
|
||||
You must also move the Script folder within the Stirling-PDF repo that you have downloaded to this directory.
|
||||
This folder is required for the python scripts using OpenCV
|
||||
|
||||
### Step 6: Other files
|
||||
#### OCR
|
||||
If you plan to use the OCR (Optical Character Recognition) functionality, you might need to install language packs for Tesseract if running none english scanning.
|
||||
|
||||
##### Installing Language Packs
|
||||
|
||||
1. Download the desired language pack(s) by selecting the `.traineddata` file(s) for the language(s) you need.
|
||||
2. Place the `.traineddata` files in the Tesseract tessdata directory: `/usr/share/tesseract-ocr/4.00/tessdata`
|
||||
Please view [OCRmyPDF install guide](https:ocrmypdf.readthedocs.io/en/latest/installation.html) for more info.
|
||||
**IMPORTANT:** DO NOT REMOVE EXISTING `eng.traineddata`, IT'S REQUIRED.
|
||||
|
||||
|
||||
|
||||
### Step 7: Run Stirling-PDF
|
||||
|
||||
```bash
|
||||
./gradlew bootRun
|
||||
or
|
||||
java -jar build/libs/app.jar
|
||||
```
|
||||
|
||||
Remember to set the necessary environment variables before running the project if you want to customize the application the list can be seen in the main readme.
|
||||
|
||||
You can do this in the terminal by using the `export` command or -D arguements to java -jar command:
|
||||
|
||||
```bash
|
||||
export APP_HOME_NAME="Stirling PDF"
|
||||
or
|
||||
-DAPP_HOME_NAME="Stirling PDF"
|
||||
```
|
||||
|
||||
109
README.md
@@ -6,78 +6,135 @@
|
||||
[](https://github.com/Frooodle/Stirling-PDF/)
|
||||
[](https://github.com/Frooodle/stirling-pdf)
|
||||
[](https://www.paypal.com/paypalme/froodleplex)
|
||||
[](https://github.com/sponsors/Frooodle)
|
||||
|
||||
This is a locally hosted web application that allows you to perform various operations on PDF files, such as splitting and adding images.
|
||||
This is a powerful locally hosted web based PDF manipulation tool using docker that allows you to perform various operations on PDF files, such as splitting merging, converting, reorganizing, adding images, rotating, compressing, and more. This locally hosted web application started as a 100% ChatGPT-made application and has evolved to include a wide range of features to handle all your PDF needs.
|
||||
|
||||
Started off as a 100% ChatGPT made application, slowly moving away from that as more features are added
|
||||
|
||||
I will support and fix/add things to this if there is a demand [Discord](https://discord.gg/Cn8pWhQRxZ)
|
||||
Feel free to request any features or bug fixes either in github issues or our [Discord](https://discord.gg/Cn8pWhQRxZ)
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
## Features
|
||||
|
||||
- Full intractable GUI for merging/splitting/rotating/moving PDFs and their pages.
|
||||
- Split PDFs into multiple files at specified page numbers or extract all pages as individual files.
|
||||
- Merge multiple PDFs together into a single resultant file
|
||||
- Convert PDFs to and from images
|
||||
- Reorganize PDF pages into different orders.
|
||||
- Add images to PDFs at specified locations. (WIP)
|
||||
- Add/Generate signatures
|
||||
- Flatten PDFs
|
||||
- Repair PDFs
|
||||
- Detect and remove blank pages
|
||||
- Compare 2 PDFs and show differences in text
|
||||
- Add images to PDFs
|
||||
- Rotating PDFs in 90 degree increments.
|
||||
- Compressing PDFs to decrease their filesize. (Using OCRMyPDF)
|
||||
- Add and remove passwords
|
||||
- Set PDF Permissions
|
||||
- Add watermark(s)
|
||||
- Convert Any common file to PDF (using LibreOffice)
|
||||
- Convert PDF to Word/Powerpoint/Others (using LibreOffice)
|
||||
- Extract images from PDF
|
||||
- OCR on PDF (Using OCRMyPDF)
|
||||
- Edit metadata
|
||||
- Dark mode support.
|
||||
- Custom download options
|
||||
- Custom download options (see [here](https://github.com/Frooodle/Stirling-PDF/blob/main/images/settings.png) for example)
|
||||
- Parallel file processing and downloads
|
||||
- API for integration with external scripts
|
||||
|
||||
Hosted instance/demo of the app can be seen [here](https://pdf.adminforge.de/) hosted by the team at adminforge.de
|
||||
|
||||
## Technologies used
|
||||
- Spring Boot + Thymeleaf
|
||||
- PDFBox
|
||||
- LibreOffice for advanced conversions
|
||||
- OcrMyPdf https://github.com/ocrmypdf/OCRmyPDF
|
||||
- [LibreOffice](https://www.libreoffice.org/discover/libreoffice/) for advanced conversions
|
||||
- [OcrMyPdf](https://github.com/ocrmypdf/OCRmyPDF)
|
||||
- HTML, CSS, JavaScript
|
||||
- Docker
|
||||
- PDF.js
|
||||
- PDF-LIB.js
|
||||
|
||||
## How to use
|
||||
|
||||
### Locally
|
||||
|
||||
Prerequisites
|
||||
- Java 17 or later
|
||||
- Gradle 7.0 or later
|
||||
|
||||
1. Clone or download the repository.
|
||||
2. Build the project using Gradle by running `./gradlew build`
|
||||
3. Start the application by running `./gradlew bootRun` or by calling the build jar in build/libs with java -jar jarName.jar
|
||||
|
||||
Please view https://github.com/Frooodle/Stirling-PDF/blob/main/LocalRunGuide.md
|
||||
|
||||
### Docker
|
||||
https://hub.docker.com/r/frooodle/s-pdf
|
||||
|
||||
Docker Run
|
||||
```
|
||||
docker run -p 8080:8080 frooodle/s-pdf
|
||||
docker run -d \
|
||||
-p 8080:8080 \
|
||||
-v /location/of/trainingData:/usr/share/tesseract-ocr/4.00/tessdata \
|
||||
--name stirling-pdf \
|
||||
frooodle/s-pdf
|
||||
|
||||
|
||||
Can also add these for customisation but are not required
|
||||
-e APP_HOME_NAME="Stirling PDF" \
|
||||
-e APP_HOME_DESCRIPTION="Your locally hosted one-stop-shop for all your PDF needs." \
|
||||
-e APP_NAVBAR_NAME="Stirling PDF" \
|
||||
-e ALLOW_GOOGLE_VISABILITY="true" \
|
||||
-e APP_ROOT_PATH="/" \
|
||||
-e APP_LOCALE="en_GB" \
|
||||
```
|
||||
Docker Compose
|
||||
```
|
||||
version: '3.3'
|
||||
services:
|
||||
s-pdf:
|
||||
ports:
|
||||
- '8080:8080'
|
||||
image: frooodle/s-pdf
|
||||
stirling-pdf:
|
||||
image: frooodle/s-pdf
|
||||
ports:
|
||||
- '8080:8080'
|
||||
volumes:
|
||||
- /location/of/trainingData:/usr/share/tesseract-ocr/4.00/tessdata #Required for extra OCR languages
|
||||
# - /location/of/extraConfigs:/configs
|
||||
# environment:
|
||||
# APP_LOCALE: en_GB
|
||||
# APP_HOME_NAME: Stirling PDF
|
||||
# APP_HOME_DESCRIPTION: Your locally hosted one-stop-shop for all your PDF needs.
|
||||
# APP_NAVBAR_NAME: Stirling PDF
|
||||
# APP_ROOT_PATH: /
|
||||
# ALLOW_GOOGLE_VISABILITY: true
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Enable OCR/Compression feature
|
||||
Please view https://github.com/Frooodle/Stirling-PDF/blob/main/HowToUseOCR.md
|
||||
|
||||
## Want to add your own language?
|
||||
Stirling PDF currently supports
|
||||
- English
|
||||
- Arabic (العربية)
|
||||
- German (Deutsch)
|
||||
- French (Français)
|
||||
- Spanish (Español)
|
||||
- Chinese (简体中文)
|
||||
|
||||
If you want to add your own language to Stirling-PDF please refer
|
||||
https://github.com/Frooodle/Stirling-PDF/blob/main/HowToAddNewLanguage.md
|
||||
|
||||
And please create a PR to merge it back in so others can use it!
|
||||
|
||||
Also please note as i add new features i will google translate existing languages so that they dont lose support. This could mean that new features need grammer corrections as added.
|
||||
|
||||
## How to View
|
||||
1. Open a web browser and navigate to `http://localhost:8080/`
|
||||
2. Use the application by following the instructions on the website.
|
||||
|
||||
## Note
|
||||
The application is currently not thread-safe
|
||||
|
||||
## Customize App
|
||||
Stirling PDF allows easy customization of the visible application name.
|
||||
Simply use environment variables APP_HOME_NAME, APP_HOME_DESCRIPTION and APP_NAVBAR_NAME with Docker or Java.
|
||||
If running Java directly, you can also pass these as properties using -D arguments.
|
||||
|
||||
Using the same method you can also change
|
||||
- The default language by providing APP_LOCALE with values like de-DE fr-FR or ar-AR to select your default language (Will always default to English on invalid locale)
|
||||
- Enable/Disable search engine visablility with ALLOW_GOOGLE_VISABILITY with true / false values. Default disable visability.
|
||||
- Change root URI for Stirling-PDF ie change server.com/ to server.com/pdf-app by running APP_ROOT_PATH as pdf-app
|
||||
|
||||
## API
|
||||
For those wanting to use Stirling-PDFs backend API to link with their own custom scripting to edit PDFs you can view all existing API documentation
|
||||
[here](https://app.swaggerhub.com/apis-docs/Frooodle/Stirling-PDF/) or navigate to /swagger-ui/index.html of your stirling-pdf instance for your versions documentation
|
||||
|
||||
22
build.gradle
@@ -1,11 +1,11 @@
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'org.springframework.boot' version '3.0.4'
|
||||
id 'org.springframework.boot' version '3.0.6'
|
||||
id 'io.spring.dependency-management' version '1.1.0'
|
||||
}
|
||||
|
||||
group = 'stirling.software'
|
||||
version = '0.4.2'
|
||||
version = '0.8.1'
|
||||
sourceCompatibility = '17'
|
||||
|
||||
repositories {
|
||||
@@ -13,16 +13,18 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'org.springframework.boot:spring-boot-starter-web'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
|
||||
testImplementation 'org.springframework.boot:spring-boot-starter-test'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-web:3.0.6'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf:3.0.6'
|
||||
testImplementation 'org.springframework.boot:spring-boot-starter-test:3.0.6'
|
||||
// https://mvnrepository.com/artifact/org.apache.pdfbox/jbig2-imageio
|
||||
implementation group: 'org.apache.pdfbox', name: 'jbig2-imageio', version: '3.0.4'
|
||||
implementation 'commons-io:commons-io:2.11.0'
|
||||
|
||||
implementation 'org.apache.logging.log4j:log4j-core:2.20.0'
|
||||
|
||||
//general PDF
|
||||
implementation 'org.apache.pdfbox:pdfbox:2.0.27'
|
||||
implementation 'com.itextpdf:itextpdf:5.5.13.3'
|
||||
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.1.0'
|
||||
|
||||
//general PDF
|
||||
implementation 'org.apache.pdfbox:pdfbox:2.0.28'
|
||||
developmentOnly("org.springframework.boot:spring-boot-devtools")
|
||||
|
||||
}
|
||||
|
||||
|
||||
BIN
images/DemoGif.gif
Normal file
|
After Width: | Height: | Size: 1.4 MiB |
BIN
images/settings.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 92 KiB After Width: | Height: | Size: 134 KiB |
40
scripts/detect-blank-pages.py
Normal file
@@ -0,0 +1,40 @@
|
||||
import cv2
|
||||
import numpy as np
|
||||
import sys
|
||||
import argparse
|
||||
|
||||
def is_blank_image(image_path, threshold=10, white_percent=99, white_value=255, blur_size=5):
|
||||
image = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
|
||||
|
||||
if image is None:
|
||||
print(f"Error: Unable to read the image file: {image_path}")
|
||||
return False
|
||||
|
||||
# Apply Gaussian blur to reduce noise
|
||||
blurred_image = cv2.GaussianBlur(image, (blur_size, blur_size), 0)
|
||||
|
||||
_, thresholded_image = cv2.threshold(blurred_image, white_value - threshold, white_value, cv2.THRESH_BINARY)
|
||||
|
||||
# Calculate the percentage of white pixels in the thresholded image
|
||||
white_pixels = np.sum(thresholded_image == white_value)
|
||||
total_pixels = thresholded_image.size
|
||||
white_pixel_percentage = (white_pixels / total_pixels) * 100
|
||||
print(f"Page has white pixel percent of {white_pixel_percentage}")
|
||||
return white_pixel_percentage > white_percent
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description='Detect if an image is considered blank or not.')
|
||||
parser.add_argument('image_path', help='The path to the image file.')
|
||||
parser.add_argument('-t', '--threshold', type=int, default=10, help='Threshold for determining white pixels. The default value is 10.')
|
||||
parser.add_argument('-w', '--white_percent', type=float, default=99, help='The percentage of white pixels for an image to be considered blank. The default value is 99.')
|
||||
args = parser.parse_args()
|
||||
|
||||
blank = is_blank_image(args.image_path, args.threshold, args.white_percent)
|
||||
|
||||
if blank:
|
||||
# Return code 1: The image is considered blank.
|
||||
sys.exit(1)
|
||||
else:
|
||||
# Return code 0: The image is not considered blank.
|
||||
sys.exit(0)
|
||||
9
scripts/init.sh
Normal file
@@ -0,0 +1,9 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copy the original tesseract-ocr files to the volume directory without overwriting existing files
|
||||
echo "Copying original files without overwriting existing files"
|
||||
mkdir -p /usr/share/tesseract-ocr
|
||||
cp -rn /usr/share/tesseract-ocr-original/* /usr/share/tesseract-ocr
|
||||
|
||||
# Run the main command
|
||||
exec "$@"
|
||||
134
scripts/split_photos.py
Normal file
@@ -0,0 +1,134 @@
|
||||
import sys
|
||||
import cv2
|
||||
import numpy as np
|
||||
import os
|
||||
|
||||
def find_photo_boundaries(image, background_color, tolerance=30, min_area=10000, min_contour_area=500):
|
||||
mask = cv2.inRange(image, background_color - tolerance, background_color + tolerance)
|
||||
mask = cv2.bitwise_not(mask)
|
||||
kernel = np.ones((5,5),np.uint8)
|
||||
mask = cv2.dilate(mask, kernel, iterations=2)
|
||||
contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
|
||||
|
||||
photo_boundaries = []
|
||||
for contour in contours:
|
||||
x, y, w, h = cv2.boundingRect(contour)
|
||||
area = w * h
|
||||
contour_area = cv2.contourArea(contour)
|
||||
if area >= min_area and contour_area >= min_contour_area:
|
||||
photo_boundaries.append((x, y, w, h))
|
||||
|
||||
return photo_boundaries
|
||||
|
||||
def estimate_background_color(image, sample_points=5):
|
||||
h, w, _ = image.shape
|
||||
points = [
|
||||
(0, 0),
|
||||
(w - 1, 0),
|
||||
(w - 1, h - 1),
|
||||
(0, h - 1),
|
||||
(w // 2, h // 2),
|
||||
]
|
||||
|
||||
colors = []
|
||||
for x, y in points:
|
||||
colors.append(image[y, x])
|
||||
|
||||
return np.median(colors, axis=0)
|
||||
|
||||
def auto_rotate(image, angle_threshold=10):
|
||||
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
|
||||
ret, binary = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
|
||||
contours, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
|
||||
|
||||
if len(contours) == 0:
|
||||
return image
|
||||
|
||||
largest_contour = max(contours, key=cv2.contourArea)
|
||||
mu = cv2.moments(largest_contour)
|
||||
|
||||
if mu["m00"] == 0:
|
||||
return image
|
||||
|
||||
x_centroid = int(mu["m10"] / mu["m00"])
|
||||
y_centroid = int(mu["m01"] / mu["m00"])
|
||||
|
||||
coords = np.column_stack(np.where(binary > 0))
|
||||
u, _, vt = np.linalg.svd(coords - np.array([[y_centroid, x_centroid]]), full_matrices=False)
|
||||
|
||||
angle = np.arctan2(u[1, 0], u[0, 0]) * 180 / np.pi
|
||||
|
||||
if angle < -45:
|
||||
angle = -(90 + angle)
|
||||
else:
|
||||
angle = -angle
|
||||
|
||||
if abs(angle) < angle_threshold:
|
||||
return image
|
||||
|
||||
(h, w) = image.shape[:2]
|
||||
center = (w // 2, h // 2)
|
||||
M = cv2.getRotationMatrix2D(center, angle, 1.0)
|
||||
return cv2.warpAffine(image, M, (w, h), flags=cv2.INTER_CUBIC, borderMode=cv2.BORDER_REPLICATE)
|
||||
|
||||
|
||||
|
||||
def crop_borders(image, border_color, tolerance=30):
|
||||
mask = cv2.inRange(image, border_color - tolerance, border_color + tolerance)
|
||||
|
||||
contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
|
||||
if len(contours) == 0:
|
||||
return image
|
||||
|
||||
largest_contour = max(contours, key=cv2.contourArea)
|
||||
x, y, w, h = cv2.boundingRect(largest_contour)
|
||||
|
||||
return image[y:y+h, x:x+w]
|
||||
|
||||
def split_photos(input_file, output_directory, tolerance=30, min_area=10000, min_contour_area=500, angle_threshold=10, border_size=0):
|
||||
image = cv2.imread(input_file)
|
||||
background_color = estimate_background_color(image)
|
||||
|
||||
# Add a constant border around the image
|
||||
image = cv2.copyMakeBorder(image, border_size, border_size, border_size, border_size, cv2.BORDER_CONSTANT, value=background_color)
|
||||
|
||||
photo_boundaries = find_photo_boundaries(image, background_color, tolerance)
|
||||
|
||||
if not os.path.exists(output_directory):
|
||||
os.makedirs(output_directory)
|
||||
|
||||
# Get the input file's base name without the extension
|
||||
input_file_basename = os.path.splitext(os.path.basename(input_file))[0]
|
||||
|
||||
for idx, (x, y, w, h) in enumerate(photo_boundaries):
|
||||
cropped_image = image[y:y+h, x:x+w]
|
||||
cropped_image = auto_rotate(cropped_image, angle_threshold)
|
||||
|
||||
# Remove the added border
|
||||
cropped_image = cropped_image[border_size:-border_size, border_size:-border_size]
|
||||
|
||||
output_path = os.path.join(output_directory, f"{input_file_basename}_{idx+1}.png")
|
||||
cv2.imwrite(output_path, cropped_image)
|
||||
print(f"Saved {output_path}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage: python3 split_photos.py <input_file> <output_directory> [tolerance] [min_area] [min_contour_area] [angle_threshold] [border_size]")
|
||||
print("\nParameters:")
|
||||
print(" <input_file> - The input scanned image containing multiple photos.")
|
||||
print(" <output_directory> - The directory where the result images should be placed.")
|
||||
print(" [tolerance] - Optional. Determines the range of color variation around the estimated background color (default: 30).")
|
||||
print(" [min_area] - Optional. Sets the minimum area threshold for a photo (default: 10000).")
|
||||
print(" [min_contour_area] - Optional. Sets the minimum contour area threshold for a photo (default: 500).")
|
||||
print(" [angle_threshold] - Optional. Sets the minimum absolute angle required for the image to be rotated (default: 10).")
|
||||
print(" [border_size] - Optional. Sets the size of the border added and removed to prevent white borders in the output (default: 0).")
|
||||
sys.exit(1)
|
||||
|
||||
input_file = sys.argv[1]
|
||||
output_directory = sys.argv[2]
|
||||
tolerance = int(sys.argv[3]) if len(sys.argv) > 3 else 20
|
||||
min_area = int(sys.argv[4]) if len(sys.argv) > 4 else 8000
|
||||
min_contour_area = int(sys.argv[5]) if len(sys.argv) > 5 else 500
|
||||
angle_threshold = int(sys.argv[6]) if len(sys.argv) > 6 else 60
|
||||
border_size = int(sys.argv[7]) if len(sys.argv) > 7 else 0
|
||||
split_photos(input_file, output_directory, tolerance=tolerance, min_area=min_area, min_contour_area=min_contour_area, angle_threshold=angle_threshold, border_size=border_size)
|
||||
@@ -1,4 +1,5 @@
|
||||
package stirling.software.SPDF;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Socket;
|
||||
@@ -6,32 +7,46 @@ import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
public class LibreOfficeListener {
|
||||
|
||||
private static final LibreOfficeListener INSTANCE = new LibreOfficeListener();
|
||||
|
||||
|
||||
private static final long ACTIVITY_TIMEOUT = 20 * 60 * 1000; // 20 minutes
|
||||
|
||||
private static final LibreOfficeListener INSTANCE = new LibreOfficeListener();
|
||||
private static final int LISTENER_PORT = 2002;
|
||||
|
||||
private ExecutorService executorService;
|
||||
private Process process;
|
||||
private long lastActivityTime;
|
||||
|
||||
private LibreOfficeListener() {}
|
||||
|
||||
|
||||
public static LibreOfficeListener getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
|
||||
private ExecutorService executorService;
|
||||
private long lastActivityTime;
|
||||
|
||||
private Process process;
|
||||
|
||||
private LibreOfficeListener() {
|
||||
}
|
||||
|
||||
private boolean isListenerRunning() {
|
||||
try {
|
||||
System.out.println("waiting for listener to start");
|
||||
Socket socket = new Socket();
|
||||
socket.connect(new InetSocketAddress("localhost", 2002), 1000); // Timeout after 1 second
|
||||
socket.close();
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void start() throws IOException {
|
||||
// Check if the listener is already running
|
||||
if (process != null && process.isAlive()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Start the listener process
|
||||
process = Runtime.getRuntime().exec("unoconv --listener");
|
||||
lastActivityTime = System.currentTimeMillis();
|
||||
|
||||
|
||||
// Start a background thread to monitor the activity timeout
|
||||
executorService = Executors.newSingleThreadExecutor();
|
||||
executorService.submit(() -> {
|
||||
@@ -49,46 +64,33 @@ public class LibreOfficeListener {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// Wait for the listener to start up
|
||||
|
||||
// Wait for the listener to start up
|
||||
long startTime = System.currentTimeMillis();
|
||||
long timeout = 30000; // Timeout after 30 seconds
|
||||
while (System.currentTimeMillis() - startTime < timeout) {
|
||||
if (isListenerRunning()) {
|
||||
|
||||
|
||||
lastActivityTime = System.currentTimeMillis();
|
||||
return;
|
||||
}
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
} // Check every 1 second
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
} // Check every 1 second
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isListenerRunning() {
|
||||
try {
|
||||
System.out.println("waiting for listener to start");
|
||||
Socket socket = new Socket();
|
||||
socket.connect(new InetSocketAddress("localhost", 2002), 1000); // Timeout after 1 second
|
||||
socket.close();
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public synchronized void stop() {
|
||||
// Stop the activity timeout monitor thread
|
||||
executorService.shutdownNow();
|
||||
|
||||
|
||||
// Stop the listener process
|
||||
if (process != null && process.isAlive()) {
|
||||
process.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -5,9 +5,7 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class SPdfApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SPdfApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -3,12 +3,40 @@ package stirling.software.SPDF.config;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
|
||||
@Configuration
|
||||
public class AppConfig {
|
||||
public class AppConfig {
|
||||
@Bean(name = "appName")
|
||||
public String appName() {
|
||||
String appName = System.getProperty("APP_HOME_NAME");
|
||||
if (appName == null)
|
||||
appName = System.getenv("APP_HOME_NAME");
|
||||
return (appName != null) ? appName : "Stirling PDF";
|
||||
}
|
||||
|
||||
@Bean(name = "appVersion")
|
||||
public String appVersion() {
|
||||
String version = getClass().getPackage().getImplementationVersion();
|
||||
return (version != null) ? version : "0.3.3";
|
||||
}
|
||||
|
||||
@Bean(name = "homeText")
|
||||
public String homeText() {
|
||||
String homeText = System.getProperty("APP_HOME_DESCRIPTION");
|
||||
if (homeText == null)
|
||||
homeText = System.getenv("APP_HOME_DESCRIPTION");
|
||||
return (homeText != null) ? homeText : "null";
|
||||
}
|
||||
|
||||
@Bean(name = "navBarText")
|
||||
public String navBarText() {
|
||||
String navBarText = System.getProperty("APP_NAVBAR_NAME");
|
||||
if (navBarText == null)
|
||||
navBarText = System.getenv("APP_NAVBAR_NAME");
|
||||
if (navBarText == null)
|
||||
navBarText = System.getProperty("APP_HOME_NAME");
|
||||
if (navBarText == null)
|
||||
navBarText = System.getenv("APP_HOME_NAME");
|
||||
|
||||
return (navBarText != null) ? navBarText : "Stirling PDF";
|
||||
}
|
||||
}
|
||||
@@ -13,11 +13,10 @@ import org.springframework.web.servlet.i18n.SessionLocaleResolver;
|
||||
@Configuration
|
||||
public class Beans implements WebMvcConfigurer {
|
||||
|
||||
@Bean
|
||||
public LocaleResolver localeResolver() {
|
||||
SessionLocaleResolver slr = new SessionLocaleResolver();
|
||||
slr.setDefaultLocale(Locale.US);
|
||||
return slr;
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
registry.addInterceptor(localeChangeInterceptor());
|
||||
registry.addInterceptor(new CleanUrlInterceptor());
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -27,9 +26,28 @@ public class Beans implements WebMvcConfigurer {
|
||||
return lci;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
registry.addInterceptor(localeChangeInterceptor());
|
||||
@Bean
|
||||
public LocaleResolver localeResolver() {
|
||||
SessionLocaleResolver slr = new SessionLocaleResolver();
|
||||
|
||||
String appLocaleEnv = System.getProperty("APP_LOCALE");
|
||||
if (appLocaleEnv == null)
|
||||
appLocaleEnv = System.getenv("APP_LOCALE");
|
||||
Locale defaultLocale = Locale.UK; // Fallback to UK locale if environment variable is not set
|
||||
|
||||
if (appLocaleEnv != null && !appLocaleEnv.isEmpty()) {
|
||||
Locale tempLocale = Locale.forLanguageTag(appLocaleEnv);
|
||||
String tempLanguageTag = tempLocale.toLanguageTag();
|
||||
|
||||
if (appLocaleEnv.equalsIgnoreCase(tempLanguageTag)) {
|
||||
defaultLocale = tempLocale;
|
||||
} else {
|
||||
System.err.println("Invalid APP_LOCALE environment variable value. Falling back to default Locale.UK.");
|
||||
}
|
||||
}
|
||||
|
||||
slr.setDefaultLocale(defaultLocale);
|
||||
return slr;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
package stirling.software.SPDF.config;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
|
||||
public class CleanUrlInterceptor implements HandlerInterceptor {
|
||||
|
||||
private static final Pattern LANG_PATTERN = Pattern.compile("&?lang=([^&]+)");
|
||||
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
|
||||
String queryString = request.getQueryString();
|
||||
if (queryString != null && !queryString.isEmpty()) {
|
||||
String requestURI = request.getRequestURI();
|
||||
|
||||
// Keep the lang parameter if it exists
|
||||
Matcher langMatcher = LANG_PATTERN.matcher(queryString);
|
||||
String langQueryString = langMatcher.find() ? "lang=" + langMatcher.group(1) : "";
|
||||
|
||||
// Check if there are any other query parameters besides the lang parameter
|
||||
String remainingQueryString = queryString.replaceAll(LANG_PATTERN.pattern(), "").replaceAll("&+", "&").replaceAll("^&|&$", "");
|
||||
|
||||
if (!remainingQueryString.isEmpty()) {
|
||||
// Redirect to the URL without other query parameters
|
||||
String redirectUrl = requestURI + (langQueryString.isEmpty() ? "" : "?" + langQueryString);
|
||||
response.sendRedirect(redirectUrl);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package stirling.software.SPDF.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import io.swagger.v3.oas.models.Components;
|
||||
import io.swagger.v3.oas.models.OpenAPI;
|
||||
import io.swagger.v3.oas.models.info.Info;
|
||||
|
||||
@Configuration
|
||||
public class OpenApiConfig {
|
||||
|
||||
@Bean
|
||||
public OpenAPI customOpenAPI() {
|
||||
String version = getClass().getPackage().getImplementationVersion();
|
||||
version = (version != null) ? version : "1.0.0";
|
||||
|
||||
return new OpenAPI().components(new Components()).info(
|
||||
new Info().title("Stirling PDF API").version(version).description("API documentation for all Server-Side processing.\nPlease note some functionality might be UI only and missing from here."));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
package stirling.software.SPDF.controller;
|
||||
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
import org.apache.pdfbox.cos.COSName;
|
||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||
import org.apache.pdfbox.pdmodel.PDPage;
|
||||
import org.apache.pdfbox.pdmodel.PDResources;
|
||||
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import com.itextpdf.text.DocumentException;
|
||||
import com.itextpdf.text.pdf.PdfReader;
|
||||
import com.itextpdf.text.pdf.PdfStamper;
|
||||
|
||||
import stirling.software.SPDF.utils.PdfUtils;
|
||||
import stirling.software.SPDF.utils.ProcessExecutor;
|
||||
|
||||
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.multipart.MultipartFile;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
@Controller
|
||||
public class CompressController {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(CompressController.class);
|
||||
|
||||
@GetMapping("/compress-pdf")
|
||||
public String compressPdfForm(Model model) {
|
||||
model.addAttribute("currentPage", "compress-pdf");
|
||||
return "compress-pdf";
|
||||
}
|
||||
|
||||
|
||||
@PostMapping("/compress-pdf")
|
||||
public ResponseEntity<byte[]> optimizePdf(
|
||||
@RequestParam("fileInput") MultipartFile inputFile,
|
||||
@RequestParam("optimizeLevel") int optimizeLevel,
|
||||
@RequestParam(name = "fastWebView", required = false) Boolean fastWebView,
|
||||
@RequestParam(name = "jbig2Lossy", required = false) Boolean jbig2Lossy) throws IOException, InterruptedException {
|
||||
|
||||
// Save the uploaded file to a temporary location
|
||||
Path tempInputFile = Files.createTempFile("input_", ".pdf");
|
||||
inputFile.transferTo(tempInputFile.toFile());
|
||||
|
||||
// Prepare the output file path
|
||||
Path tempOutputFile = Files.createTempFile("output_", ".pdf");
|
||||
|
||||
// Prepare the OCRmyPDF command
|
||||
List<String> command = new ArrayList<>();
|
||||
command.add("ocrmypdf");
|
||||
command.add("--skip-text");
|
||||
command.add("--tesseract-timeout=0");
|
||||
command.add("--optimize");
|
||||
command.add(String.valueOf(optimizeLevel));
|
||||
|
||||
if (fastWebView != null && fastWebView) {
|
||||
long fileSize = inputFile.getSize();
|
||||
long fastWebViewSize = (long) (fileSize * 1.25); // 25% higher than file size
|
||||
command.add("--fast-web-view");
|
||||
command.add(String.valueOf(fastWebViewSize));
|
||||
}
|
||||
|
||||
if (jbig2Lossy != null && jbig2Lossy) {
|
||||
command.add("--jbig2-lossy");
|
||||
}
|
||||
|
||||
command.add(tempInputFile.toString());
|
||||
command.add(tempOutputFile.toString());
|
||||
|
||||
int returnCode = ProcessExecutor.runCommandWithOutputHandling(command);
|
||||
|
||||
// Read the optimized PDF file
|
||||
byte[] pdfBytes = Files.readAllBytes(tempOutputFile);
|
||||
|
||||
// Clean up the temporary files
|
||||
Files.delete(tempInputFile);
|
||||
Files.delete(tempOutputFile);
|
||||
|
||||
// Return the optimized PDF as a response
|
||||
String outputFilename = inputFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_Optimized.pdf";
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_PDF);
|
||||
headers.setContentDispositionFormData("attachment", outputFilename);
|
||||
return ResponseEntity.ok().headers(headers).body(pdfBytes);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,143 +0,0 @@
|
||||
package stirling.software.SPDF.controller;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
import stirling.software.SPDF.utils.ProcessExecutor;
|
||||
|
||||
import java.io.FileOutputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
//import com.spire.pdf.*;
|
||||
@Controller
|
||||
public class OCRController {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(OCRController.class);
|
||||
|
||||
@GetMapping("/ocr-pdf")
|
||||
public ModelAndView ocrPdfPage() {
|
||||
ModelAndView modelAndView = new ModelAndView("ocr-pdf");
|
||||
modelAndView.addObject("languages", getAvailableTesseractLanguages());
|
||||
modelAndView.addObject("currentPage", "ocr-pdf");
|
||||
return modelAndView;
|
||||
}
|
||||
|
||||
@PostMapping("/ocr-pdf")
|
||||
public ResponseEntity<byte[]> processPdfWithOCR(@RequestParam("fileInput") MultipartFile inputFile,
|
||||
@RequestParam("languages") List<String> selectedLanguages,
|
||||
@RequestParam(name = "sidecar", required = false) Boolean sidecar) throws IOException, InterruptedException {
|
||||
|
||||
//--output-type pdfa
|
||||
if (selectedLanguages == null || selectedLanguages.size() < 1) {
|
||||
throw new IOException("Please select at least one language.");
|
||||
}
|
||||
|
||||
// Save the uploaded file to a temporary location
|
||||
Path tempInputFile = Files.createTempFile("input_", ".pdf");
|
||||
inputFile.transferTo(tempInputFile.toFile());
|
||||
|
||||
// Prepare the output file path
|
||||
Path tempOutputFile = Files.createTempFile("output_", ".pdf");
|
||||
|
||||
// Run OCR Command
|
||||
String languageOption = String.join("+", selectedLanguages);
|
||||
List<String> command = new ArrayList<>(Arrays.asList("ocrmypdf","--verbose", "2", "--language", languageOption,
|
||||
tempInputFile.toString(), tempOutputFile.toString()));
|
||||
String sidecarFile = tempOutputFile.toString().replace(".pdf", ".txt");
|
||||
if (sidecar != null && sidecar) {
|
||||
command.add("--sidecar");
|
||||
command.add(sidecarFile);
|
||||
}
|
||||
int returnCode = ProcessExecutor.runCommandWithOutputHandling(command);
|
||||
|
||||
// Read the OCR processed PDF file
|
||||
byte[] pdfBytes = Files.readAllBytes(tempOutputFile);
|
||||
|
||||
// Clean up the temporary files
|
||||
Files.delete(tempInputFile);
|
||||
// Return the OCR processed PDF as a response
|
||||
String outputFilename = inputFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_OCR.pdf";
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
|
||||
if (sidecar != null && sidecar) {
|
||||
// Create a zip file containing both the PDF and the text file
|
||||
String outputZipFilename = inputFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_OCR.zip";
|
||||
Path tempZipFile = Files.createTempFile("output_", ".zip");
|
||||
|
||||
try (ZipOutputStream zipOut = new ZipOutputStream(new FileOutputStream(tempZipFile.toFile()))) {
|
||||
// Add PDF file to the zip
|
||||
ZipEntry pdfEntry = new ZipEntry(outputFilename);
|
||||
zipOut.putNextEntry(pdfEntry);
|
||||
Files.copy(tempOutputFile, zipOut);
|
||||
zipOut.closeEntry();
|
||||
|
||||
// Add text file to the zip
|
||||
ZipEntry txtEntry = new ZipEntry(sidecarFile);
|
||||
zipOut.putNextEntry(txtEntry);
|
||||
Files.copy(Paths.get(sidecarFile), zipOut);
|
||||
zipOut.closeEntry();
|
||||
}
|
||||
|
||||
byte[] zipBytes = Files.readAllBytes(tempZipFile);
|
||||
|
||||
// Clean up the temporary zip file
|
||||
Files.delete(tempZipFile);
|
||||
Files.delete(tempOutputFile);
|
||||
Files.delete(Paths.get(sidecarFile));
|
||||
|
||||
// Return the zip file containing both the PDF and the text file
|
||||
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
|
||||
headers.setContentDispositionFormData("attachment", outputZipFilename);
|
||||
return ResponseEntity.ok().headers(headers).body(zipBytes);
|
||||
} else {
|
||||
// Return the OCR processed PDF as a response
|
||||
Files.delete(tempOutputFile);
|
||||
headers.setContentType(MediaType.APPLICATION_PDF);
|
||||
headers.setContentDispositionFormData("attachment", outputFilename);
|
||||
return ResponseEntity.ok().headers(headers).body(pdfBytes);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public List<String> getAvailableTesseractLanguages() {
|
||||
String tessdataDir = "/usr/share/tesseract-ocr/4.00/tessdata";
|
||||
File[] files = new File(tessdataDir).listFiles();
|
||||
if (files == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return Arrays.stream(files)
|
||||
.filter(file -> file.getName().endsWith(".traineddata"))
|
||||
.map(file -> file.getName().replace(".traineddata", ""))
|
||||
.filter(lang -> !lang.equalsIgnoreCase("osd"))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
package stirling.software.SPDF.controller;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import stirling.software.SPDF.utils.PdfUtils;
|
||||
|
||||
@Controller
|
||||
public class OverlayImageController {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(OverlayImageController.class);
|
||||
|
||||
@GetMapping("/add-image")
|
||||
public String overlayImage(Model model) {
|
||||
model.addAttribute("currentPage", "add-image");
|
||||
return "add-image";
|
||||
}
|
||||
|
||||
@PostMapping("/add-image")
|
||||
public ResponseEntity<byte[]> overlayImage(@RequestParam("fileInput") MultipartFile pdfFile, @RequestParam("fileInput2") MultipartFile imageFile, @RequestParam("x") float x,
|
||||
@RequestParam("y") float y) {
|
||||
try {
|
||||
byte[] pdfBytes = pdfFile.getBytes();
|
||||
byte[] imageBytes = imageFile.getBytes();
|
||||
byte[] result = PdfUtils.overlayImage(pdfBytes, imageBytes, x, y);
|
||||
|
||||
return PdfUtils.bytesToWebResponse(result, pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_overlayed.pdf");
|
||||
} catch (IOException e) {
|
||||
logger.error("Failed to add image to PDF", e);
|
||||
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
package stirling.software.SPDF.controller;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
|
||||
@Controller
|
||||
public class PdfController {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(PdfController.class);
|
||||
|
||||
@GetMapping("/home")
|
||||
public String root(Model model) {
|
||||
return "redirect:/";
|
||||
}
|
||||
|
||||
@GetMapping("/")
|
||||
public String home(Model model) {
|
||||
model.addAttribute("currentPage", "home");
|
||||
return "home";
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -1,75 +1,78 @@
|
||||
package stirling.software.SPDF.controller;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||
import org.apache.pdfbox.pdmodel.PDPage;
|
||||
import org.apache.pdfbox.pdmodel.PDPageTree;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.core.io.InputStreamResource;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
@Controller
|
||||
public class MergeController {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(MergeController.class);
|
||||
|
||||
@GetMapping("/merge-pdfs")
|
||||
public String hello(Model model) {
|
||||
model.addAttribute("currentPage", "merge-pdfs");
|
||||
return "merge-pdfs";
|
||||
}
|
||||
|
||||
@PostMapping("/merge-pdfs")
|
||||
public ResponseEntity<InputStreamResource> mergePdfs(@RequestParam("fileInput") MultipartFile[] files) throws IOException {
|
||||
// Read the input PDF files into PDDocument objects
|
||||
List<PDDocument> documents = new ArrayList<>();
|
||||
|
||||
// Loop through the files array and read each file into a PDDocument
|
||||
for (MultipartFile file : files) {
|
||||
documents.add(PDDocument.load(file.getInputStream()));
|
||||
}
|
||||
|
||||
PDDocument mergedDoc = mergeDocuments(documents);
|
||||
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
||||
mergedDoc.save(byteArrayOutputStream);
|
||||
mergedDoc.close();
|
||||
|
||||
// Create an InputStreamResource from the merged PDF
|
||||
InputStreamResource resource = new InputStreamResource(new ByteArrayInputStream(byteArrayOutputStream.toByteArray()));
|
||||
|
||||
// Return the merged PDF as a response
|
||||
return ResponseEntity.ok().contentType(MediaType.APPLICATION_PDF).body(resource);
|
||||
}
|
||||
|
||||
private PDDocument mergeDocuments(List<PDDocument> documents) throws IOException {
|
||||
// Create a new empty document
|
||||
PDDocument mergedDoc = new PDDocument();
|
||||
|
||||
// Iterate over the list of documents and add their pages to the merged document
|
||||
for (PDDocument doc : documents) {
|
||||
// Get all pages from the current document
|
||||
PDPageTree pages = doc.getPages();
|
||||
// Iterate over the pages and add them to the merged document
|
||||
for (PDPage page : pages) {
|
||||
mergedDoc.addPage(page);
|
||||
}
|
||||
}
|
||||
|
||||
// Return the merged document
|
||||
return mergedDoc;
|
||||
}
|
||||
|
||||
package stirling.software.SPDF.controller.api;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||
import org.apache.pdfbox.pdmodel.PDPage;
|
||||
import org.apache.pdfbox.pdmodel.PDPageTree;
|
||||
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 io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import stirling.software.SPDF.utils.PdfUtils;
|
||||
|
||||
@RestController
|
||||
public class MergeController {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(MergeController.class);
|
||||
|
||||
private PDDocument mergeDocuments(List<PDDocument> documents) throws IOException {
|
||||
// Create a new empty document
|
||||
PDDocument mergedDoc = new PDDocument();
|
||||
|
||||
// Iterate over the list of documents and add their pages to the merged document
|
||||
for (PDDocument doc : documents) {
|
||||
// Get all pages from the current document
|
||||
PDPageTree pages = doc.getPages();
|
||||
// Iterate over the pages and add them to the merged document
|
||||
for (PDPage page : pages) {
|
||||
mergedDoc.addPage(page);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// Return the merged document
|
||||
return mergedDoc;
|
||||
}
|
||||
|
||||
@PostMapping(consumes = "multipart/form-data", value = "/merge-pdfs")
|
||||
@Operation(
|
||||
summary = "Merge multiple PDF files into one",
|
||||
description = "This endpoint merges multiple PDF files into a single PDF file. The merged file will contain all pages from the input files in the order they were provided."
|
||||
)
|
||||
public ResponseEntity<byte[]> mergePdfs(
|
||||
@RequestPart(required = true, value = "fileInput")
|
||||
@Parameter(description = "The input PDF files to be merged into a single file", required = true)
|
||||
MultipartFile[] files) throws IOException {
|
||||
// Read the input PDF files into PDDocument objects
|
||||
List<PDDocument> documents = new ArrayList<>();
|
||||
|
||||
// Loop through the files array and read each file into a PDDocument
|
||||
for (MultipartFile file : files) {
|
||||
documents.add(PDDocument.load(file.getInputStream()));
|
||||
}
|
||||
|
||||
PDDocument mergedDoc = mergeDocuments(documents);
|
||||
|
||||
|
||||
// Return the merged PDF as a response
|
||||
ResponseEntity<byte[]> response = PdfUtils.pdfDocToWebResponse(mergedDoc, files[0].getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_merged.pdf");
|
||||
|
||||
for (PDDocument doc : documents) {
|
||||
// Close the document after processing
|
||||
doc.close();
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,121 +1,127 @@
|
||||
package stirling.software.SPDF.controller;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||
import org.apache.pdfbox.pdmodel.PDPage;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import stirling.software.SPDF.utils.PdfUtils;
|
||||
|
||||
@Controller
|
||||
public class RearrangePagesPDFController {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(RearrangePagesPDFController.class);
|
||||
|
||||
@GetMapping("/pdf-organizer")
|
||||
public String pageOrganizer(Model model) {
|
||||
model.addAttribute("currentPage", "pdf-organizer");
|
||||
return "pdf-organizer";
|
||||
}
|
||||
|
||||
@GetMapping("/remove-pages")
|
||||
public String pageDeleter(Model model) {
|
||||
model.addAttribute("currentPage", "remove-pages");
|
||||
return "remove-pages";
|
||||
}
|
||||
|
||||
@PostMapping("/remove-pages")
|
||||
public ResponseEntity<byte[]> deletePages(@RequestParam("fileInput") MultipartFile pdfFile, @RequestParam("pagesToDelete") 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(",");
|
||||
|
||||
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 PdfUtils.pdfDocToWebResponse(document, pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_removed_pages.pdf");
|
||||
|
||||
}
|
||||
|
||||
private List<Integer> pageOrderToString(String[] pageOrderArr, int totalPages) {
|
||||
List<Integer> newPageOrder = new ArrayList<>();
|
||||
// loop through the page order array
|
||||
for (String element : pageOrderArr) {
|
||||
// check if the element contains a range of pages
|
||||
if (element.contains("-")) {
|
||||
// split the range into start and end page
|
||||
String[] range = element.split("-");
|
||||
int start = Integer.parseInt(range[0]);
|
||||
int end = Integer.parseInt(range[1]);
|
||||
// check if the end page is greater than total pages
|
||||
if (end > totalPages) {
|
||||
end = totalPages;
|
||||
}
|
||||
// loop through the range of pages
|
||||
for (int j = start; j <= end; j++) {
|
||||
// print the current index
|
||||
newPageOrder.add(j - 1);
|
||||
}
|
||||
} else {
|
||||
// if the element is a single page
|
||||
newPageOrder.add(Integer.parseInt(element) - 1);
|
||||
}
|
||||
}
|
||||
|
||||
return newPageOrder;
|
||||
}
|
||||
|
||||
@PostMapping("/rearrange-pages")
|
||||
public ResponseEntity<byte[]> rearrangePages(@RequestParam("fileInput") MultipartFile pdfFile, @RequestParam("pageOrder") 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
|
||||
String[] pageOrderArr = pageOrder.split(",");
|
||||
// int[] newPageOrder = new int[pageOrderArr.length];
|
||||
int totalPages = document.getNumberOfPages();
|
||||
|
||||
List<Integer> newPageOrder = pageOrderToString(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 PdfUtils.pdfDocToWebResponse(document, pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_rearranged.pdf");
|
||||
} catch (IOException e) {
|
||||
|
||||
logger.error("Failed rearranging documents", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
package stirling.software.SPDF.controller.api;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||
import org.apache.pdfbox.pdmodel.PDPage;
|
||||
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.RequestPart;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import stirling.software.SPDF.utils.PdfUtils;
|
||||
|
||||
@RestController
|
||||
public class RearrangePagesPDFController {
|
||||
|
||||
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 {
|
||||
|
||||
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(",");
|
||||
|
||||
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 PdfUtils.pdfDocToWebResponse(document, pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_removed_pages.pdf");
|
||||
|
||||
}
|
||||
|
||||
private List<Integer> pageOrderToString(String[] pageOrderArr, int totalPages) {
|
||||
List<Integer> newPageOrder = new ArrayList<>();
|
||||
// loop through the page order array
|
||||
for (String element : pageOrderArr) {
|
||||
// check if the element contains a range of pages
|
||||
if (element.contains("-")) {
|
||||
// split the range into start and end page
|
||||
String[] range = element.split("-");
|
||||
int start = Integer.parseInt(range[0]);
|
||||
int end = Integer.parseInt(range[1]);
|
||||
// check if the end page is greater than total pages
|
||||
if (end > totalPages) {
|
||||
end = totalPages;
|
||||
}
|
||||
// loop through the range of pages
|
||||
for (int j = start; j <= end; j++) {
|
||||
// print the current index
|
||||
newPageOrder.add(j - 1);
|
||||
}
|
||||
} else {
|
||||
// if the element is a single page
|
||||
newPageOrder.add(Integer.parseInt(element) - 1);
|
||||
}
|
||||
}
|
||||
|
||||
return newPageOrder;
|
||||
}
|
||||
|
||||
@PostMapping(consumes = "multipart/form-data", value = "/rearrange-pages")
|
||||
@Operation(summary = "Rearrange pages in a PDF file",
|
||||
description = "This endpoint rearranges pages in a given PDF file based on the specified page order. Users can provide a page order as a comma-separated list of page numbers or page ranges.")
|
||||
public ResponseEntity<byte[]> rearrangePages(
|
||||
@RequestPart(required = true, value = "fileInput")
|
||||
@Parameter(description = "The input PDF file to rearrange pages")
|
||||
MultipartFile pdfFile,
|
||||
@RequestParam("pageOrder")
|
||||
@Parameter(description = "The new page order as a comma-separated list of page numbers or page ranges (e.g., '1,3,5-7')")
|
||||
String pageOrder) {
|
||||
try {
|
||||
// Load the input PDF
|
||||
PDDocument document = PDDocument.load(pdfFile.getInputStream());
|
||||
|
||||
// Split the page order string into an array of page numbers or range of numbers
|
||||
String[] pageOrderArr = pageOrder.split(",");
|
||||
// int[] newPageOrder = new int[pageOrderArr.length];
|
||||
int totalPages = document.getNumberOfPages();
|
||||
|
||||
List<Integer> newPageOrder = pageOrderToString(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 PdfUtils.pdfDocToWebResponse(document, pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_rearranged.pdf");
|
||||
} catch (IOException e) {
|
||||
|
||||
logger.error("Failed rearranging documents", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,48 +1,53 @@
|
||||
package stirling.software.SPDF.controller;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||
import org.apache.pdfbox.pdmodel.PDPage;
|
||||
import org.apache.pdfbox.pdmodel.PDPageTree;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import stirling.software.SPDF.utils.PdfUtils;
|
||||
|
||||
@Controller
|
||||
public class RotationController {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(RotationController.class);
|
||||
|
||||
@GetMapping("/rotate-pdf")
|
||||
public String rotatePdfForm(Model model) {
|
||||
model.addAttribute("currentPage", "rotate-pdf");
|
||||
return "rotate-pdf";
|
||||
}
|
||||
|
||||
@PostMapping("/rotate-pdf")
|
||||
public ResponseEntity<byte[]> rotatePDF(@RequestParam("fileInput") MultipartFile pdfFile, @RequestParam("angle") Integer angle) throws IOException {
|
||||
|
||||
// Load the PDF document
|
||||
PDDocument document = PDDocument.load(pdfFile.getBytes());
|
||||
|
||||
// Get the list of pages in the document
|
||||
PDPageTree pages = document.getPages();
|
||||
|
||||
for (PDPage page : pages) {
|
||||
page.setRotation(page.getRotation() + angle);
|
||||
}
|
||||
|
||||
return PdfUtils.pdfDocToWebResponse(document, pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_rotated.pdf");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
package stirling.software.SPDF.controller.api;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||
import org.apache.pdfbox.pdmodel.PDPage;
|
||||
import org.apache.pdfbox.pdmodel.PDPageTree;
|
||||
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.RequestPart;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import stirling.software.SPDF.utils.PdfUtils;
|
||||
|
||||
@RestController
|
||||
public class RotationController {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(RotationController.class);
|
||||
|
||||
@PostMapping(consumes = "multipart/form-data", value = "/rotate-pdf")
|
||||
@Operation(
|
||||
summary = "Rotate a PDF file",
|
||||
description = "This endpoint rotates a given PDF file by a specified angle. The angle must be a multiple of 90."
|
||||
)
|
||||
public ResponseEntity<byte[]> rotatePDF(
|
||||
@RequestPart(required = true, value = "fileInput")
|
||||
@Parameter(description = "The PDF file to be rotated", required = true)
|
||||
MultipartFile pdfFile,
|
||||
@RequestParam("angle")
|
||||
@Parameter(description = "The angle by which to rotate the PDF file. This should be a multiple of 90.", example = "90", required = true)
|
||||
Integer angle) throws IOException {
|
||||
|
||||
// Load the PDF document
|
||||
PDDocument document = PDDocument.load(pdfFile.getBytes());
|
||||
|
||||
// Get the list of pages in the document
|
||||
PDPageTree pages = document.getPages();
|
||||
|
||||
for (PDPage page : pages) {
|
||||
page.setRotation(page.getRotation() + angle);
|
||||
}
|
||||
|
||||
return PdfUtils.pdfDocToWebResponse(document, pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_rotated.pdf");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,140 +1,140 @@
|
||||
package stirling.software.SPDF.controller;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URI;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||
import org.apache.pdfbox.pdmodel.PDPage;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.core.io.ByteArrayResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
@Controller
|
||||
public class SplitPDFController {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(SplitPDFController.class);
|
||||
|
||||
@GetMapping("/split-pdfs")
|
||||
public String splitPdfForm(Model model) {
|
||||
model.addAttribute("currentPage", "split-pdfs");
|
||||
return "split-pdfs";
|
||||
}
|
||||
|
||||
@PostMapping("/split-pages")
|
||||
public ResponseEntity<Resource> splitPdf(@RequestParam("fileInput") MultipartFile file, @RequestParam("pages") String pages) throws IOException {
|
||||
// parse user input
|
||||
|
||||
// open the pdf document
|
||||
InputStream inputStream = file.getInputStream();
|
||||
PDDocument document = PDDocument.load(inputStream);
|
||||
|
||||
List<Integer> pageNumbers = new ArrayList<>();
|
||||
pages = pages.replaceAll("\\s+", ""); // remove whitespaces
|
||||
if (pages.toLowerCase().equals("all")) {
|
||||
for (int i = 0; i < document.getNumberOfPages(); i++) {
|
||||
pageNumbers.add(i);
|
||||
}
|
||||
} else {
|
||||
List<String> pageNumbersStr = new ArrayList<>(Arrays.asList(pages.split(",")));
|
||||
if (!pageNumbersStr.contains(String.valueOf(document.getNumberOfPages()))) {
|
||||
String lastpage = String.valueOf(document.getNumberOfPages());
|
||||
pageNumbersStr.add(lastpage);
|
||||
}
|
||||
for (String page : pageNumbersStr) {
|
||||
if (page.contains("-")) {
|
||||
String[] range = page.split("-");
|
||||
int start = Integer.parseInt(range[0]);
|
||||
int end = Integer.parseInt(range[1]);
|
||||
for (int i = start; i <= end; i++) {
|
||||
pageNumbers.add(i);
|
||||
}
|
||||
} else {
|
||||
pageNumbers.add(Integer.parseInt(page));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
logger.info("Splitting PDF into pages: {}", pageNumbers.stream().map(String::valueOf).collect(Collectors.joining(",")));
|
||||
|
||||
// split the document
|
||||
List<ByteArrayOutputStream> splitDocumentsBoas = new ArrayList<>();
|
||||
int currentPage = 0;
|
||||
for (int pageNumber : pageNumbers) {
|
||||
try (PDDocument splitDocument = new PDDocument()) {
|
||||
for (int i = currentPage; i < pageNumber; i++) {
|
||||
PDPage page = document.getPage(i);
|
||||
splitDocument.addPage(page);
|
||||
logger.debug("Adding page {} to split document", i);
|
||||
}
|
||||
currentPage = pageNumber;
|
||||
logger.debug("Setting current page to {}", currentPage);
|
||||
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
splitDocument.save(baos);
|
||||
|
||||
splitDocumentsBoas.add(baos);
|
||||
} catch (Exception e) {
|
||||
logger.error("Failed splitting documents and saving them", e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
// closing the original document
|
||||
document.close();
|
||||
|
||||
// create the zip file
|
||||
Path zipFile = Paths.get("split_documents.zip");
|
||||
URI uri = URI.create("jar:file:" + zipFile.toUri().getPath());
|
||||
Map<String, String> env = new HashMap<>();
|
||||
env.put("create", "true");
|
||||
FileSystem zipfs = FileSystems.newFileSystem(uri, env);
|
||||
|
||||
// loop through the split documents and write them to the zip file
|
||||
for (int i = 0; i < splitDocumentsBoas.size(); i++) {
|
||||
String fileName = "split_document_" + (i + 1) + ".pdf";
|
||||
ByteArrayOutputStream baos = splitDocumentsBoas.get(i);
|
||||
byte[] pdf = baos.toByteArray();
|
||||
Path pathInZipfile = zipfs.getPath(fileName);
|
||||
try (OutputStream os = Files.newOutputStream(pathInZipfile)) {
|
||||
os.write(pdf);
|
||||
logger.info("Wrote split document {} to zip file", fileName);
|
||||
} catch (Exception e) {
|
||||
logger.error("Failed writing to zip", e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
zipfs.close();
|
||||
logger.info("Successfully created zip file with split documents: {}", zipFile.toString());
|
||||
byte[] data = Files.readAllBytes(zipFile);
|
||||
ByteArrayResource resource = new ByteArrayResource(data);
|
||||
new File("split_documents.zip").delete();
|
||||
// return the Resource in the response
|
||||
return ResponseEntity.ok().header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + file.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_split.zip").contentType(MediaType.APPLICATION_OCTET_STREAM)
|
||||
.contentLength(resource.contentLength()).body(resource);
|
||||
}
|
||||
}
|
||||
package stirling.software.SPDF.controller.api;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||
import org.apache.pdfbox.pdmodel.PDPage;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.core.io.ByteArrayResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
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.RequestPart;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
|
||||
@RestController
|
||||
public class SplitPDFController {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(SplitPDFController.class);
|
||||
|
||||
@PostMapping(consumes = "multipart/form-data", value = "/split-pages")
|
||||
@Operation(summary = "Split a PDF file into separate documents",
|
||||
description = "This endpoint splits a given PDF file into separate documents based on the specified page numbers or ranges. Users can specify pages using individual numbers, ranges, or 'all' for every page.")
|
||||
public ResponseEntity<Resource> splitPdf(
|
||||
@RequestPart(required = true, value = "fileInput")
|
||||
@Parameter(description = "The input PDF file to be split")
|
||||
MultipartFile file,
|
||||
@RequestParam("pages")
|
||||
@Parameter(description = "The pages to be included in separate documents. Specify individual page numbers (e.g., '1,3,5'), ranges (e.g., '1-3,5-7'), or 'all' for every page.")
|
||||
String pages) throws IOException {
|
||||
// parse user input
|
||||
|
||||
// open the pdf document
|
||||
InputStream inputStream = file.getInputStream();
|
||||
PDDocument document = PDDocument.load(inputStream);
|
||||
|
||||
List<Integer> pageNumbers = new ArrayList<>();
|
||||
pages = pages.replaceAll("\\s+", ""); // remove whitespaces
|
||||
if (pages.toLowerCase().equals("all")) {
|
||||
for (int i = 0; i < document.getNumberOfPages(); i++) {
|
||||
pageNumbers.add(i);
|
||||
}
|
||||
} else {
|
||||
List<String> pageNumbersStr = new ArrayList<>(Arrays.asList(pages.split(",")));
|
||||
if (!pageNumbersStr.contains(String.valueOf(document.getNumberOfPages()))) {
|
||||
String lastpage = String.valueOf(document.getNumberOfPages());
|
||||
pageNumbersStr.add(lastpage);
|
||||
}
|
||||
for (String page : pageNumbersStr) {
|
||||
if (page.contains("-")) {
|
||||
String[] range = page.split("-");
|
||||
int start = Integer.parseInt(range[0]);
|
||||
int end = Integer.parseInt(range[1]);
|
||||
for (int i = start; i <= end; i++) {
|
||||
pageNumbers.add(i);
|
||||
}
|
||||
} else {
|
||||
pageNumbers.add(Integer.parseInt(page));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
logger.info("Splitting PDF into pages: {}", pageNumbers.stream().map(String::valueOf).collect(Collectors.joining(",")));
|
||||
|
||||
// split the document
|
||||
List<ByteArrayOutputStream> splitDocumentsBoas = new ArrayList<>();
|
||||
int currentPage = 0;
|
||||
for (int pageNumber : pageNumbers) {
|
||||
try (PDDocument splitDocument = new PDDocument()) {
|
||||
for (int i = currentPage; i < pageNumber; i++) {
|
||||
PDPage page = document.getPage(i);
|
||||
splitDocument.addPage(page);
|
||||
logger.debug("Adding page {} to split document", i);
|
||||
}
|
||||
currentPage = pageNumber;
|
||||
logger.debug("Setting current page to {}", currentPage);
|
||||
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
splitDocument.save(baos);
|
||||
|
||||
splitDocumentsBoas.add(baos);
|
||||
} catch (Exception e) {
|
||||
logger.error("Failed splitting documents and saving them", e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
// closing the original document
|
||||
document.close();
|
||||
|
||||
Path zipFile = Files.createTempFile("split_documents", ".zip");
|
||||
|
||||
try (ZipOutputStream zipOut = new ZipOutputStream(Files.newOutputStream(zipFile))) {
|
||||
// loop through the split documents and write them to the zip file
|
||||
for (int i = 0; i < splitDocumentsBoas.size(); i++) {
|
||||
String fileName = "split_document_" + (i + 1) + ".pdf";
|
||||
ByteArrayOutputStream baos = splitDocumentsBoas.get(i);
|
||||
byte[] pdf = baos.toByteArray();
|
||||
|
||||
// Add PDF file to the zip
|
||||
ZipEntry pdfEntry = new ZipEntry(fileName);
|
||||
zipOut.putNextEntry(pdfEntry);
|
||||
zipOut.write(pdf);
|
||||
zipOut.closeEntry();
|
||||
|
||||
logger.info("Wrote split document {} to zip file", fileName);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("Failed writing to zip", e);
|
||||
throw e;
|
||||
}
|
||||
|
||||
logger.info("Successfully created zip file with split documents: {}", zipFile.toString());
|
||||
byte[] data = Files.readAllBytes(zipFile);
|
||||
ByteArrayResource resource = new ByteArrayResource(data);
|
||||
Files.delete(zipFile);
|
||||
|
||||
// return the Resource in the response
|
||||
return ResponseEntity.ok().header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + file.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_split.zip")
|
||||
.contentType(MediaType.APPLICATION_OCTET_STREAM).contentLength(resource.contentLength()).body(resource);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
package stirling.software.SPDF.controller.api.converters;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.pdfbox.rendering.ImageType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.core.io.ByteArrayResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
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.RequestPart;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import stirling.software.SPDF.utils.PdfUtils;
|
||||
@RestController
|
||||
public class ConvertImgPDFController {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(ConvertImgPDFController.class);
|
||||
|
||||
@PostMapping(consumes = "multipart/form-data", value = "/pdf-to-img")
|
||||
@Operation(summary = "Convert PDF to image(s)",
|
||||
description = "This endpoint converts a PDF file to image(s) with the specified image format, color type, and DPI. Users can choose to get a single image or multiple images.")
|
||||
public ResponseEntity<Resource> convertToImage(
|
||||
@RequestPart(required = true, value = "fileInput")
|
||||
@Parameter(description = "The input PDF file to be converted")
|
||||
MultipartFile file,
|
||||
@RequestParam("imageFormat")
|
||||
@Parameter(description = "The output image format", schema = @Schema(allowableValues = {"png", "jpeg", "jpg", "gif"}))
|
||||
String imageFormat,
|
||||
@RequestParam("singleOrMultiple")
|
||||
@Parameter(description = "Choose between a single image containing all pages or separate images for each page", schema = @Schema(allowableValues = {"single", "multiple"}))
|
||||
String singleOrMultiple,
|
||||
@RequestParam("colorType")
|
||||
@Parameter(description = "The color type of the output image(s)", schema = @Schema(allowableValues = {"rgb", "greyscale", "blackwhite"}))
|
||||
String colorType,
|
||||
@RequestParam("dpi")
|
||||
@Parameter(description = "The DPI (dots per inch) for the output image(s)")
|
||||
String dpi) throws IOException {
|
||||
|
||||
byte[] pdfBytes = file.getBytes();
|
||||
ImageType colorTypeResult = ImageType.RGB;
|
||||
if ("greyscale".equals(colorType)) {
|
||||
colorTypeResult = ImageType.GRAY;
|
||||
} else if ("blackwhite".equals(colorType)) {
|
||||
colorTypeResult = ImageType.BINARY;
|
||||
}
|
||||
// returns bytes for image
|
||||
boolean singleImage = singleOrMultiple.equals("single");
|
||||
byte[] result = null;
|
||||
try {
|
||||
result = PdfUtils.convertFromPdf(pdfBytes, imageFormat.toUpperCase(), colorTypeResult, singleImage, Integer.valueOf(dpi));
|
||||
} catch (IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
} catch (Exception e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (singleImage) {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.parseMediaType(getMediaType(imageFormat)));
|
||||
ResponseEntity<Resource> response = new ResponseEntity<>(new ByteArrayResource(result), headers, HttpStatus.OK);
|
||||
return response;
|
||||
} else {
|
||||
ByteArrayResource resource = new ByteArrayResource(result);
|
||||
// return the Resource in the response
|
||||
return ResponseEntity.ok()
|
||||
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + file.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_convertedToImages.zip")
|
||||
.contentType(MediaType.APPLICATION_OCTET_STREAM).contentLength(resource.contentLength()).body(resource);
|
||||
}
|
||||
}
|
||||
|
||||
@PostMapping(consumes = "multipart/form-data", value = "/img-to-pdf")
|
||||
@Operation(summary = "Convert images to a PDF file",
|
||||
description = "This endpoint converts one or more images to a PDF file. Users can specify whether to stretch the images to fit the PDF page, and whether to automatically rotate the images.")
|
||||
public ResponseEntity<byte[]> convertToPdf(
|
||||
@RequestPart(required = true, value = "fileInput")
|
||||
@Parameter(description = "The input images to be converted to a PDF file")
|
||||
MultipartFile[] file,
|
||||
@RequestParam(defaultValue = "false", name = "stretchToFit")
|
||||
@Parameter(description = "Whether to stretch the images to fit the PDF page or maintain the aspect ratio", example = "false")
|
||||
boolean stretchToFit,
|
||||
@RequestParam("colorType")
|
||||
@Parameter(description = "The color type of the output image(s)", schema = @Schema(allowableValues = {"rgb", "greyscale", "blackwhite"}))
|
||||
String colorType,
|
||||
@RequestParam(defaultValue = "false", name = "autoRotate")
|
||||
@Parameter(description = "Whether to automatically rotate the images to better fit the PDF page", example = "true")
|
||||
boolean autoRotate) throws IOException {
|
||||
// Convert the file to PDF and get the resulting bytes
|
||||
byte[] bytes = PdfUtils.imageToPdf(file, stretchToFit, autoRotate, colorType);
|
||||
return PdfUtils.bytesToWebResponse(bytes, file[0].getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_coverted.pdf");
|
||||
}
|
||||
|
||||
private String getMediaType(String imageFormat) {
|
||||
if (imageFormat.equalsIgnoreCase("PNG"))
|
||||
return "image/png";
|
||||
else if (imageFormat.equalsIgnoreCase("JPEG") || imageFormat.equalsIgnoreCase("JPG"))
|
||||
return "image/jpeg";
|
||||
else if (imageFormat.equalsIgnoreCase("GIF"))
|
||||
return "image/gif";
|
||||
else
|
||||
return "application/octet-stream";
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package stirling.software.SPDF.controller.api.converters;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
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 io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import stirling.software.SPDF.utils.PdfUtils;
|
||||
import stirling.software.SPDF.utils.ProcessExecutor;
|
||||
|
||||
@RestController
|
||||
public class ConvertOfficeController {
|
||||
|
||||
public byte[] convertToPdf(MultipartFile inputFile) throws IOException, InterruptedException {
|
||||
// Check for valid file extension
|
||||
String originalFilename = inputFile.getOriginalFilename();
|
||||
if (originalFilename == null || !isValidFileExtension(FilenameUtils.getExtension(originalFilename))) {
|
||||
throw new IllegalArgumentException("Invalid file extension");
|
||||
}
|
||||
|
||||
// Save the uploaded file to a temporary location
|
||||
Path tempInputFile = Files.createTempFile("input_", "." + FilenameUtils.getExtension(originalFilename));
|
||||
Files.copy(inputFile.getInputStream(), tempInputFile, StandardCopyOption.REPLACE_EXISTING);
|
||||
|
||||
// Prepare the output file path
|
||||
Path tempOutputFile = Files.createTempFile("output_", ".pdf");
|
||||
|
||||
// Run the LibreOffice command
|
||||
List<String> command = new ArrayList<>(Arrays.asList("unoconv", "-vvv", "-f", "pdf", "-o", tempOutputFile.toString(), tempInputFile.toString()));
|
||||
int returnCode = ProcessExecutor.getInstance(ProcessExecutor.Processes.LIBRE_OFFICE).runCommandWithOutputHandling(command);
|
||||
|
||||
// Read the converted PDF file
|
||||
byte[] pdfBytes = Files.readAllBytes(tempOutputFile);
|
||||
|
||||
// Clean up the temporary files
|
||||
Files.delete(tempInputFile);
|
||||
Files.delete(tempOutputFile);
|
||||
|
||||
return pdfBytes;
|
||||
}
|
||||
private boolean isValidFileExtension(String fileExtension) {
|
||||
String extensionPattern = "^(?i)[a-z0-9]{2,4}$";
|
||||
return fileExtension.matches(extensionPattern);
|
||||
}
|
||||
|
||||
@PostMapping(consumes = "multipart/form-data", value = "/file-to-pdf")
|
||||
@Operation(
|
||||
summary = "Convert a file to a PDF using OCR",
|
||||
description = "This endpoint converts a given file to a PDF using Optical Character Recognition (OCR). The filename of the resulting PDF will be the original filename with '_convertedToPDF.pdf' appended."
|
||||
)
|
||||
public ResponseEntity<byte[]> processPdfWithOCR(
|
||||
@RequestPart(required = true, value = "fileInput")
|
||||
@Parameter(
|
||||
description = "The input file to be converted to a PDF file using OCR",
|
||||
required = true
|
||||
)
|
||||
MultipartFile inputFile
|
||||
) throws IOException, InterruptedException {
|
||||
// unused but can start server instance if startup time is to long
|
||||
// LibreOfficeListener.getInstance().start();
|
||||
|
||||
byte[] pdfByteArray = convertToPdf(inputFile);
|
||||
return PdfUtils.bytesToWebResponse(pdfByteArray, inputFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_convertedToPDF.pdf");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package stirling.software.SPDF.controller.api.converters;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
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.RequestPart;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import stirling.software.SPDF.utils.PDFToFile;
|
||||
|
||||
@RestController
|
||||
public class ConvertPDFToOffice {
|
||||
|
||||
@PostMapping(consumes = "multipart/form-data", value = "/pdf-to-html")
|
||||
@Operation(summary = "Convert PDF to HTML", description = "This endpoint converts a PDF file to HTML format.")
|
||||
public ResponseEntity<byte[]> processPdfToHTML(
|
||||
@RequestPart(required = true, value = "fileInput") @Parameter(description = "The input PDF file to be converted to HTML format", required = true) MultipartFile inputFile)
|
||||
throws IOException, InterruptedException {
|
||||
PDFToFile pdfToFile = new PDFToFile();
|
||||
return pdfToFile.processPdfToOfficeFormat(inputFile, "html", "writer_pdf_import");
|
||||
}
|
||||
|
||||
@PostMapping(consumes = "multipart/form-data", value = "/pdf-to-presentation")
|
||||
@Operation(summary = "Convert PDF to Presentation format", description = "This endpoint converts a given PDF file to a Presentation format.")
|
||||
public ResponseEntity<byte[]> processPdfToPresentation(
|
||||
@RequestPart(required = true, value = "fileInput") @Parameter(description = "The input PDF file") MultipartFile inputFile,
|
||||
@RequestParam("outputFormat") @Parameter(description = "The output Presentation format", schema = @Schema(allowableValues = {
|
||||
"ppt", "pptx", "odp" })) String outputFormat)
|
||||
throws IOException, InterruptedException {
|
||||
PDFToFile pdfToFile = new PDFToFile();
|
||||
return pdfToFile.processPdfToOfficeFormat(inputFile, outputFormat, "impress_pdf_import");
|
||||
}
|
||||
|
||||
@PostMapping(consumes = "multipart/form-data", value = "/pdf-to-text")
|
||||
@Operation(summary = "Convert PDF to Text or RTF format", description = "This endpoint converts a given PDF file to Text or RTF format.")
|
||||
public ResponseEntity<byte[]> processPdfToRTForTXT(
|
||||
@RequestPart(required = true, value = "fileInput") @Parameter(description = "The input PDF file") MultipartFile inputFile,
|
||||
@RequestParam("outputFormat") @Parameter(description = "The output Text or RTF format", schema = @Schema(allowableValues = {
|
||||
"rtf", "txt:Text" })) String outputFormat)
|
||||
throws IOException, InterruptedException {
|
||||
PDFToFile pdfToFile = new PDFToFile();
|
||||
return pdfToFile.processPdfToOfficeFormat(inputFile, outputFormat, "writer_pdf_import");
|
||||
}
|
||||
|
||||
@PostMapping(consumes = "multipart/form-data", value = "/pdf-to-word")
|
||||
@Operation(summary = "Convert PDF to Word document", description = "This endpoint converts a given PDF file to a Word document format.")
|
||||
public ResponseEntity<byte[]> processPdfToWord(
|
||||
@RequestPart(required = true, value = "fileInput") @Parameter(description = "The input PDF file") MultipartFile inputFile,
|
||||
@RequestParam("outputFormat") @Parameter(description = "The output Word document format", schema = @Schema(allowableValues = {
|
||||
"doc", "docx", "odt" })) String outputFormat)
|
||||
throws IOException, InterruptedException {
|
||||
PDFToFile pdfToFile = new PDFToFile();
|
||||
return pdfToFile.processPdfToOfficeFormat(inputFile, outputFormat, "writer_pdf_import");
|
||||
}
|
||||
|
||||
@PostMapping(consumes = "multipart/form-data", value = "/pdf-to-xml")
|
||||
@Operation(summary = "Convert PDF to XML", description = "This endpoint converts a PDF file to an XML file.")
|
||||
public ResponseEntity<byte[]> processPdfToXML(
|
||||
@RequestPart(required = true, value = "fileInput") @Parameter(description = "The input PDF file to be converted to an XML file", required = true) MultipartFile inputFile)
|
||||
throws IOException, InterruptedException {
|
||||
|
||||
PDFToFile pdfToFile = new PDFToFile();
|
||||
return pdfToFile.processPdfToOfficeFormat(inputFile, "xml", "writer_pdf_import");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package stirling.software.SPDF.controller.api.converters;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
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 io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import stirling.software.SPDF.utils.PdfUtils;
|
||||
import stirling.software.SPDF.utils.ProcessExecutor;
|
||||
|
||||
@RestController
|
||||
public class ConvertPDFToPDFA {
|
||||
|
||||
@PostMapping(consumes = "multipart/form-data", value = "/pdf-to-pdfa")
|
||||
@Operation(
|
||||
summary = "Convert a PDF to a PDF/A",
|
||||
description = "This endpoint converts a PDF file to a PDF/A file. PDF/A is a format designed for long-term archiving of digital documents."
|
||||
)
|
||||
public ResponseEntity<byte[]> pdfToPdfA(
|
||||
@RequestPart(required = true, value = "fileInput")
|
||||
@Parameter(description = "The input PDF file to be converted to a PDF/A file", required = true)
|
||||
MultipartFile inputFile) throws IOException, InterruptedException {
|
||||
|
||||
// Save the uploaded file to a temporary location
|
||||
Path tempInputFile = Files.createTempFile("input_", ".pdf");
|
||||
inputFile.transferTo(tempInputFile.toFile());
|
||||
|
||||
// Prepare the output file path
|
||||
Path tempOutputFile = Files.createTempFile("output_", ".pdf");
|
||||
|
||||
// Prepare the OCRmyPDF command
|
||||
List<String> command = new ArrayList<>();
|
||||
command.add("ocrmypdf");
|
||||
command.add("--skip-text");
|
||||
command.add("--tesseract-timeout=0");
|
||||
command.add("--output-type");
|
||||
command.add("pdfa");
|
||||
command.add(tempInputFile.toString());
|
||||
command.add(tempOutputFile.toString());
|
||||
|
||||
int returnCode = ProcessExecutor.getInstance(ProcessExecutor.Processes.OCR_MY_PDF).runCommandWithOutputHandling(command);
|
||||
|
||||
// Read the optimized PDF file
|
||||
byte[] pdfBytes = Files.readAllBytes(tempOutputFile);
|
||||
|
||||
// Clean up the temporary files
|
||||
Files.delete(tempInputFile);
|
||||
Files.delete(tempOutputFile);
|
||||
|
||||
// Return the optimized PDF as a response
|
||||
String outputFilename = inputFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_PDFA.pdf";
|
||||
return PdfUtils.bytesToWebResponse(pdfBytes, outputFilename);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
package stirling.software.SPDF.controller.api.other;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||
import org.apache.pdfbox.pdmodel.PDPage;
|
||||
import org.apache.pdfbox.pdmodel.PDPageTree;
|
||||
import org.apache.pdfbox.rendering.PDFRenderer;
|
||||
import org.apache.pdfbox.text.PDFTextStripper;
|
||||
import org.springframework.http.HttpStatus;
|
||||
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.RequestPart;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import stirling.software.SPDF.utils.ImageFinder;
|
||||
import stirling.software.SPDF.utils.PdfUtils;
|
||||
import stirling.software.SPDF.utils.ProcessExecutor;
|
||||
|
||||
@RestController
|
||||
public class BlankPageController {
|
||||
|
||||
@PostMapping(consumes = "multipart/form-data", value = "/remove-blanks")
|
||||
@Operation(
|
||||
summary = "Remove blank pages from a PDF file",
|
||||
description = "This endpoint removes blank pages from a given PDF file. Users can specify the threshold and white percentage to tune the detection of blank pages."
|
||||
)
|
||||
public ResponseEntity<byte[]> removeBlankPages(
|
||||
@RequestPart(required = true, value = "fileInput")
|
||||
@Parameter(description = "The input PDF file from which blank pages will be removed", required = true)
|
||||
MultipartFile inputFile,
|
||||
@RequestParam(defaultValue = "10", name = "threshold")
|
||||
@Parameter(description = "The threshold value to determine blank pages", example = "10")
|
||||
int threshold,
|
||||
@RequestParam(defaultValue = "99.9", name = "whitePercent")
|
||||
@Parameter(description = "The percentage of white color on a page to consider it as blank", example = "99.9")
|
||||
float whitePercent) throws IOException, InterruptedException {
|
||||
|
||||
PDDocument document = null;
|
||||
try {
|
||||
document = PDDocument.load(inputFile.getInputStream());
|
||||
PDPageTree pages = document.getDocumentCatalog().getPages();
|
||||
PDFTextStripper textStripper = new PDFTextStripper();
|
||||
|
||||
List<Integer> pagesToKeepIndex = new ArrayList<>();
|
||||
int pageIndex = 0;
|
||||
PDFRenderer pdfRenderer = new PDFRenderer(document);
|
||||
|
||||
for (PDPage page : pages) {
|
||||
System.out.println("checking page " + pageIndex);
|
||||
textStripper.setStartPage(pageIndex + 1);
|
||||
textStripper.setEndPage(pageIndex + 1);
|
||||
String pageText = textStripper.getText(document);
|
||||
boolean hasText = !pageText.trim().isEmpty();
|
||||
if (hasText) {
|
||||
pagesToKeepIndex.add(pageIndex);
|
||||
System.out.println("page " + pageIndex + " has text");
|
||||
} else {
|
||||
boolean hasImages = hasImagesOnPage(page);
|
||||
if (hasImages) {
|
||||
System.out.println("page " + pageIndex + " has image");
|
||||
|
||||
Path tempFile = Files.createTempFile("image_", ".png");
|
||||
|
||||
// Render image and save as temp file
|
||||
BufferedImage image = pdfRenderer.renderImageWithDPI(pageIndex, 300);
|
||||
ImageIO.write(image, "png", tempFile.toFile());
|
||||
|
||||
List<String> command = new ArrayList<>(Arrays.asList("python3", System.getProperty("user.dir") + "scripts/detect-blank-pages.py", tempFile.toString() ,"--threshold", String.valueOf(threshold), "--white_percent", String.valueOf(whitePercent)));
|
||||
|
||||
// Run CLI command
|
||||
int returnCode = ProcessExecutor.getInstance(ProcessExecutor.Processes.PYTHON_OPENCV).runCommandWithOutputHandling(command);
|
||||
|
||||
// does contain data
|
||||
if (returnCode == 0) {
|
||||
System.out.println("page " + pageIndex + " has image which is not blank");
|
||||
pagesToKeepIndex.add(pageIndex);
|
||||
} else {
|
||||
System.out.println("Skipping, Image was blank for page #" + pageIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
pageIndex++;
|
||||
|
||||
}
|
||||
System.out.print("pagesToKeep=" + pagesToKeepIndex.size());
|
||||
|
||||
// Remove pages not present in pagesToKeepIndex
|
||||
List<Integer> pageIndices = IntStream.range(0, pages.getCount()).boxed().collect(Collectors.toList());
|
||||
Collections.reverse(pageIndices); // Reverse to prevent index shifting during removal
|
||||
for (Integer i : pageIndices) {
|
||||
if (!pagesToKeepIndex.contains(i)) {
|
||||
pages.remove(i);
|
||||
}
|
||||
}
|
||||
|
||||
return PdfUtils.pdfDocToWebResponse(document, inputFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_blanksRemoved.pdf");
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
} finally {
|
||||
if (document != null)
|
||||
document.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static boolean hasImagesOnPage(PDPage page) throws IOException {
|
||||
ImageFinder imageFinder = new ImageFinder(page);
|
||||
imageFinder.processPage(page);
|
||||
return imageFinder.hasImages();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
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.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.RequestPart;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import stirling.software.SPDF.utils.PdfUtils;
|
||||
import stirling.software.SPDF.utils.ProcessExecutor;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
@RestController
|
||||
public class CompressController {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(CompressController.class);
|
||||
|
||||
@PostMapping(consumes = "multipart/form-data", value = "/compress-pdf")
|
||||
@Operation(
|
||||
summary = "Optimize PDF file",
|
||||
description = "This endpoint accepts a PDF file and optimizes it based on the provided parameters."
|
||||
)
|
||||
public ResponseEntity<byte[]> optimizePdf(
|
||||
@RequestPart(required = true, value = "fileInput")
|
||||
@Parameter(description = "The input PDF file to be optimized.", required = true)
|
||||
MultipartFile inputFile,
|
||||
@RequestParam("optimizeLevel")
|
||||
@Parameter(description = "The level of optimization to apply to the PDF file. Higher values indicate greater compression but may reduce quality.",
|
||||
schema = @Schema(allowableValues = {"0", "1", "2", "3"}), example = "1")
|
||||
int optimizeLevel,
|
||||
@RequestParam(name = "fastWebView", required = false)
|
||||
@Parameter(description = "If true, optimize the PDF for fast web view. This increases the file size by about 25%.", example = "false")
|
||||
Boolean fastWebView,
|
||||
@RequestParam(name = "jbig2Lossy", required = false)
|
||||
@Parameter(description = "If true, apply lossy JB2 compression to the PDF file.", example = "false")
|
||||
Boolean jbig2Lossy)
|
||||
throws IOException, InterruptedException {
|
||||
|
||||
// Save the uploaded file to a temporary location
|
||||
Path tempInputFile = Files.createTempFile("input_", ".pdf");
|
||||
inputFile.transferTo(tempInputFile.toFile());
|
||||
|
||||
// Prepare the output file path
|
||||
Path tempOutputFile = Files.createTempFile("output_", ".pdf");
|
||||
|
||||
// Prepare the OCRmyPDF command
|
||||
List<String> command = new ArrayList<>();
|
||||
command.add("ocrmypdf");
|
||||
command.add("--skip-text");
|
||||
command.add("--tesseract-timeout=0");
|
||||
command.add("--optimize");
|
||||
command.add(String.valueOf(optimizeLevel));
|
||||
command.add("--output-type");
|
||||
command.add("pdf");
|
||||
|
||||
if (fastWebView != null && fastWebView) {
|
||||
long fileSize = inputFile.getSize();
|
||||
long fastWebViewSize = (long) (fileSize * 1.25); // 25% higher than file size
|
||||
command.add("--fast-web-view");
|
||||
command.add(String.valueOf(fastWebViewSize));
|
||||
}
|
||||
|
||||
if (jbig2Lossy != null && jbig2Lossy) {
|
||||
command.add("--jbig2-lossy");
|
||||
}
|
||||
|
||||
command.add(tempInputFile.toString());
|
||||
command.add(tempOutputFile.toString());
|
||||
|
||||
int returnCode = ProcessExecutor.getInstance(ProcessExecutor.Processes.OCR_MY_PDF).runCommandWithOutputHandling(command);
|
||||
|
||||
// Read the optimized PDF file
|
||||
byte[] pdfBytes = Files.readAllBytes(tempOutputFile);
|
||||
|
||||
// Clean up the temporary files
|
||||
Files.delete(tempInputFile);
|
||||
Files.delete(tempOutputFile);
|
||||
|
||||
// Return the optimized PDF as a response
|
||||
String outputFilename = inputFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_Optimized.pdf";
|
||||
return PdfUtils.bytesToWebResponse(pdfBytes, outputFilename);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
package stirling.software.SPDF.controller.api.other;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||
import org.apache.pdfbox.rendering.PDFRenderer;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
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.RequestPart;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import stirling.software.SPDF.utils.PdfUtils;
|
||||
import stirling.software.SPDF.utils.ProcessExecutor;
|
||||
|
||||
@RestController
|
||||
public class ExtractImageScansController {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(ExtractImageScansController.class);
|
||||
|
||||
@PostMapping(consumes = "multipart/form-data", value = "/extract-image-scans")
|
||||
@Operation(summary = "Extract image scans from an input file",
|
||||
description = "This endpoint extracts image scans from a given file based on certain parameters. Users can specify angle threshold, tolerance, minimum area, minimum contour area, and border size.")
|
||||
public ResponseEntity<byte[]> extractImageScans(
|
||||
@RequestPart(required = true, value = "fileInput")
|
||||
@Parameter(description = "The input file containing image scans")
|
||||
MultipartFile inputFile,
|
||||
@RequestParam(name = "angle_threshold", defaultValue = "5")
|
||||
@Parameter(description = "The angle threshold for the image scan extraction", example = "5")
|
||||
int angleThreshold,
|
||||
@RequestParam(name = "tolerance", defaultValue = "20")
|
||||
@Parameter(description = "The tolerance for the image scan extraction", example = "20")
|
||||
int tolerance,
|
||||
@RequestParam(name = "min_area", defaultValue = "8000")
|
||||
@Parameter(description = "The minimum area for the image scan extraction", example = "8000")
|
||||
int minArea,
|
||||
@RequestParam(name = "min_contour_area", defaultValue = "500")
|
||||
@Parameter(description = "The minimum contour area for the image scan extraction", example = "500")
|
||||
int minContourArea,
|
||||
@RequestParam(name = "border_size", defaultValue = "1")
|
||||
@Parameter(description = "The border size for the image scan extraction", example = "1")
|
||||
int borderSize) throws IOException, InterruptedException {
|
||||
|
||||
String fileName = inputFile.getOriginalFilename();
|
||||
String extension = fileName.substring(fileName.lastIndexOf(".") + 1);
|
||||
|
||||
List<String> images = new ArrayList<>();
|
||||
|
||||
// Check if input file is a PDF
|
||||
if (extension.equalsIgnoreCase("pdf")) {
|
||||
// Load PDF document
|
||||
try (PDDocument document = PDDocument.load(new ByteArrayInputStream(inputFile.getBytes()))) {
|
||||
PDFRenderer pdfRenderer = new PDFRenderer(document);
|
||||
int pageCount = document.getNumberOfPages();
|
||||
images = new ArrayList<>();
|
||||
|
||||
// Create images of all pages
|
||||
for (int i = 0; i < pageCount; i++) {
|
||||
// Create temp file to save the image
|
||||
Path tempFile = Files.createTempFile("image_", ".png");
|
||||
|
||||
// Render image and save as temp file
|
||||
BufferedImage image = pdfRenderer.renderImageWithDPI(i, 300);
|
||||
ImageIO.write(image, "png", tempFile.toFile());
|
||||
|
||||
// Add temp file path to images list
|
||||
images.add(tempFile.toString());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Path tempInputFile = Files.createTempFile("input_", "." + extension);
|
||||
Files.copy(inputFile.getInputStream(), tempInputFile, StandardCopyOption.REPLACE_EXISTING);
|
||||
// Add input file path to images list
|
||||
images.add(tempInputFile.toString());
|
||||
}
|
||||
|
||||
List<byte[]> processedImageBytes = new ArrayList<>();
|
||||
|
||||
// Process each image
|
||||
for (int i = 0; i < images.size(); i++) {
|
||||
|
||||
Path tempDir = Files.createTempDirectory("openCV_output");
|
||||
List<String> command = new ArrayList<>(Arrays.asList(
|
||||
"python3",
|
||||
"./scripts/split_photos.py",
|
||||
images.get(i),
|
||||
tempDir.toString(),
|
||||
"--angle_threshold", String.valueOf(angleThreshold),
|
||||
"--tolerance", String.valueOf(tolerance),
|
||||
"--min_area", String.valueOf(minArea),
|
||||
"--min_contour_area", String.valueOf(minContourArea),
|
||||
"--border_size", String.valueOf(borderSize)
|
||||
));
|
||||
|
||||
|
||||
// Run CLI command
|
||||
int returnCode = ProcessExecutor.getInstance(ProcessExecutor.Processes.PYTHON_OPENCV).runCommandWithOutputHandling(command);
|
||||
|
||||
// Read the output photos in temp directory
|
||||
List<Path> tempOutputFiles = Files.list(tempDir).sorted().collect(Collectors.toList());
|
||||
for (Path tempOutputFile : tempOutputFiles) {
|
||||
byte[] imageBytes = Files.readAllBytes(tempOutputFile);
|
||||
processedImageBytes.add(imageBytes);
|
||||
}
|
||||
// Clean up the temporary directory
|
||||
FileUtils.deleteDirectory(tempDir.toFile());
|
||||
}
|
||||
|
||||
// Create zip file if multiple images
|
||||
if (processedImageBytes.size() > 1) {
|
||||
String outputZipFilename = fileName.replaceFirst("[.][^.]+$", "") + "_processed.zip";
|
||||
Path tempZipFile = Files.createTempFile("output_", ".zip");
|
||||
|
||||
try (ZipOutputStream zipOut = new ZipOutputStream(new FileOutputStream(tempZipFile.toFile()))) {
|
||||
// Add processed images to the zip
|
||||
for (int i = 0; i < processedImageBytes.size(); i++) {
|
||||
ZipEntry entry = new ZipEntry(fileName.replaceFirst("[.][^.]+$", "") + "_" + (i + 1) + ".png");
|
||||
zipOut.putNextEntry(entry);
|
||||
zipOut.write(processedImageBytes.get(i));
|
||||
zipOut.closeEntry();
|
||||
}
|
||||
}
|
||||
|
||||
byte[] zipBytes = Files.readAllBytes(tempZipFile);
|
||||
|
||||
// Clean up the temporary zip file
|
||||
Files.delete(tempZipFile);
|
||||
|
||||
return PdfUtils.bytesToWebResponse(zipBytes, outputZipFilename, MediaType.APPLICATION_OCTET_STREAM);
|
||||
} else {
|
||||
// Return the processed image as a response
|
||||
byte[] imageBytes = processedImageBytes.get(0);
|
||||
return PdfUtils.bytesToWebResponse(imageBytes, fileName.replaceFirst("[.][^.]+$", "") + ".png", MediaType.IMAGE_PNG);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package stirling.software.SPDF.controller;
|
||||
package stirling.software.SPDF.controller.api.other;
|
||||
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Image;
|
||||
@@ -18,33 +18,34 @@ import org.apache.pdfbox.pdmodel.PDPage;
|
||||
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.core.io.ByteArrayResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RequestPart;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
@Controller
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import stirling.software.SPDF.utils.PdfUtils;
|
||||
@RestController
|
||||
public class ExtractImagesController {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(ExtractImagesController.class);
|
||||
|
||||
@GetMapping("/extract-images")
|
||||
public String extractImagesForm(Model model) {
|
||||
model.addAttribute("currentPage", "extract-images");
|
||||
return "extract-images";
|
||||
}
|
||||
@PostMapping(consumes = "multipart/form-data", value = "/extract-images")
|
||||
@Operation(summary = "Extract images from a PDF file",
|
||||
description = "This endpoint extracts images from a given PDF file and returns them in a zip file. Users can specify the output image format.")
|
||||
public ResponseEntity<byte[]> extractImages(
|
||||
@RequestPart(required = true, value = "fileInput")
|
||||
@Parameter(description = "The input PDF file containing images")
|
||||
MultipartFile file,
|
||||
@RequestParam("format")
|
||||
@Parameter(description = "The output image format e.g., 'png', 'jpeg', or 'gif'", schema = @Schema(allowableValues = {"png", "jpeg", "gif"}))
|
||||
String format) throws IOException {
|
||||
|
||||
@PostMapping("/extract-images")
|
||||
public ResponseEntity<Resource> extractImages(@RequestParam("fileInput") MultipartFile file, @RequestParam("format") String format) throws IOException {
|
||||
|
||||
System.out.println(System.currentTimeMillis() + "file=" + file.getName() + ", format=" + format);
|
||||
PDDocument document = PDDocument.load(file.getBytes());
|
||||
|
||||
@@ -58,7 +59,7 @@ public class ExtractImagesController {
|
||||
zos.setLevel(Deflater.BEST_COMPRESSION);
|
||||
|
||||
int imageIndex = 1;
|
||||
|
||||
|
||||
int pageNum = 1;
|
||||
// Iterate over each page
|
||||
for (PDPage page : document.getPages()) {
|
||||
@@ -72,21 +73,18 @@ public class ExtractImagesController {
|
||||
RenderedImage renderedImage = image.getImage();
|
||||
BufferedImage bufferedImage = null;
|
||||
if (format.equalsIgnoreCase("png")) {
|
||||
bufferedImage = new BufferedImage(renderedImage.getWidth(), renderedImage.getHeight(),
|
||||
BufferedImage.TYPE_INT_ARGB);
|
||||
bufferedImage = new BufferedImage(renderedImage.getWidth(), renderedImage.getHeight(), BufferedImage.TYPE_INT_ARGB);
|
||||
} else if (format.equalsIgnoreCase("jpeg") || format.equalsIgnoreCase("jpg")) {
|
||||
bufferedImage = new BufferedImage(renderedImage.getWidth(), renderedImage.getHeight(),
|
||||
BufferedImage.TYPE_INT_RGB);
|
||||
bufferedImage = new BufferedImage(renderedImage.getWidth(), renderedImage.getHeight(), BufferedImage.TYPE_INT_RGB);
|
||||
} else if (format.equalsIgnoreCase("gif")) {
|
||||
bufferedImage = new BufferedImage(renderedImage.getWidth(), renderedImage.getHeight(),
|
||||
BufferedImage.TYPE_BYTE_INDEXED);
|
||||
}
|
||||
bufferedImage = new BufferedImage(renderedImage.getWidth(), renderedImage.getHeight(), BufferedImage.TYPE_BYTE_INDEXED);
|
||||
}
|
||||
|
||||
// Write image to zip file
|
||||
String imageName = "Image " + imageIndex + " (Page " + pageNum + ")." + format;
|
||||
ZipEntry zipEntry = new ZipEntry(imageName);
|
||||
zos.putNextEntry(zipEntry);
|
||||
|
||||
|
||||
Graphics2D g = bufferedImage.createGraphics();
|
||||
g.drawImage((Image) renderedImage, 0, 0, null);
|
||||
g.dispose();
|
||||
@@ -94,12 +92,11 @@ public class ExtractImagesController {
|
||||
ByteArrayOutputStream imageBaos = new ByteArrayOutputStream();
|
||||
ImageIO.write(bufferedImage, format, imageBaos);
|
||||
zos.write(imageBaos.toByteArray());
|
||||
|
||||
|
||||
|
||||
zos.closeEntry();
|
||||
imageIndex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Close ZipOutputStream and PDDocument
|
||||
@@ -108,22 +105,8 @@ public class ExtractImagesController {
|
||||
|
||||
// Create ByteArrayResource from byte array
|
||||
byte[] zipContents = baos.toByteArray();
|
||||
ByteArrayResource resource = new ByteArrayResource(zipContents);
|
||||
|
||||
// Set content disposition header to indicate that the response should be downloaded as a file
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentLength(zipContents.length);
|
||||
headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + file.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_extracted-images.zip");
|
||||
|
||||
// Return ResponseEntity with ByteArrayResource and headers
|
||||
return ResponseEntity
|
||||
.status(HttpStatus.OK)
|
||||
.headers(headers)
|
||||
|
||||
.header("Cache-Control", "no-cache")
|
||||
.contentType(MediaType.APPLICATION_OCTET_STREAM)
|
||||
.body(resource);
|
||||
return PdfUtils.boasToWebResponse(baos, file.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_extracted-images.zip", MediaType.APPLICATION_OCTET_STREAM);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package stirling.software.SPDF.controller.security;
|
||||
package stirling.software.SPDF.controller.api.other;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.text.ParseException;
|
||||
@@ -11,49 +11,77 @@ import org.apache.pdfbox.cos.COSName;
|
||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||
import org.apache.pdfbox.pdmodel.PDDocumentInformation;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RequestPart;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import stirling.software.SPDF.utils.PdfUtils;
|
||||
|
||||
@Controller
|
||||
@RestController
|
||||
public class MetadataController {
|
||||
|
||||
@GetMapping("/change-metadata")
|
||||
public String addWatermarkForm(Model model) {
|
||||
model.addAttribute("currentPage", "change-metadata");
|
||||
return "security/change-metadata";
|
||||
}
|
||||
|
||||
private String checkUndefined(String entry) {
|
||||
// Check if the string is "undefined"
|
||||
if("undefined".equals(entry)) {
|
||||
if ("undefined".equals(entry)) {
|
||||
// Return null if it is
|
||||
return null;
|
||||
}
|
||||
// Return the original string if it's not "undefined"
|
||||
return entry;
|
||||
|
||||
|
||||
}
|
||||
@PostMapping("/update-metadata")
|
||||
public ResponseEntity<byte[]> metadata(@RequestParam("fileInput") MultipartFile pdfFile,
|
||||
@RequestParam(value = "deleteAll", required = false, defaultValue = "false") Boolean deleteAll, @RequestParam(value = "author", required = false) String author,
|
||||
@RequestParam(value = "creationDate", required = false) String creationDate, @RequestParam(value = "creator", required = false) String creator,
|
||||
@RequestParam(value = "keywords", required = false) String keywords, @RequestParam(value = "modificationDate", required = false) String modificationDate,
|
||||
@RequestParam(value = "producer", required = false) String producer, @RequestParam(value = "subject", required = false) String subject,
|
||||
@RequestParam(value = "title", required = false) String title, @RequestParam(value = "trapped", required = false) String trapped,
|
||||
@RequestParam Map<String, String> allRequestParams) throws IOException {
|
||||
|
||||
@PostMapping(consumes = "multipart/form-data", value = "/update-metadata")
|
||||
@Operation(summary = "Update metadata of a PDF file",
|
||||
description = "This endpoint allows you to update the metadata of a given PDF file. You can add, modify, or delete standard and custom metadata fields.")
|
||||
public ResponseEntity<byte[]> metadata(
|
||||
@RequestPart(required = true, value = "fileInput")
|
||||
@Parameter(description = "The input PDF file to update metadata")
|
||||
MultipartFile pdfFile,
|
||||
@RequestParam(value = "deleteAll", required = false, defaultValue = "false")
|
||||
@Parameter(description = "Delete all metadata if set to true")
|
||||
Boolean deleteAll,
|
||||
@RequestParam(value = "author", required = false)
|
||||
@Parameter(description = "The author of the document")
|
||||
String author,
|
||||
@RequestParam(value = "creationDate", required = false)
|
||||
@Parameter(description = "The creation date of the document (format: yyyy/MM/dd HH:mm:ss)")
|
||||
String creationDate,
|
||||
@RequestParam(value = "creator", required = false)
|
||||
@Parameter(description = "The creator of the document")
|
||||
String creator,
|
||||
@RequestParam(value = "keywords", required = false)
|
||||
@Parameter(description = "The keywords for the document")
|
||||
String keywords,
|
||||
@RequestParam(value = "modificationDate", required = false)
|
||||
@Parameter(description = "The modification date of the document (format: yyyy/MM/dd HH:mm:ss)")
|
||||
String modificationDate,
|
||||
@RequestParam(value = "producer", required = false)
|
||||
@Parameter(description = "The producer of the document")
|
||||
String producer,
|
||||
@RequestParam(value = "subject", required = false)
|
||||
@Parameter(description = "The subject of the document")
|
||||
String subject,
|
||||
@RequestParam(value = "title", required = false)
|
||||
@Parameter(description = "The title of the document")
|
||||
String title,
|
||||
@RequestParam(value = "trapped", required = false)
|
||||
@Parameter(description = "The trapped status of the document")
|
||||
String trapped,
|
||||
@RequestParam Map<String, String> allRequestParams)
|
||||
throws IOException {
|
||||
|
||||
// Load the PDF file into a PDDocument
|
||||
PDDocument document = PDDocument.load(pdfFile.getBytes());
|
||||
|
||||
|
||||
// Get the document information from the PDF
|
||||
PDDocumentInformation info = document.getDocumentInformation();
|
||||
|
||||
|
||||
// Check if each metadata value is "undefined" and set it to null if it is
|
||||
author = checkUndefined(author);
|
||||
creationDate = checkUndefined(creationDate);
|
||||
@@ -64,8 +92,9 @@ public class MetadataController {
|
||||
subject = checkUndefined(subject);
|
||||
title = checkUndefined(title);
|
||||
trapped = checkUndefined(trapped);
|
||||
|
||||
// If the "deleteAll" flag is set, remove all metadata from the document information
|
||||
|
||||
// If the "deleteAll" flag is set, remove all metadata from the document
|
||||
// information
|
||||
if (deleteAll) {
|
||||
for (String key : info.getMetadataKeys()) {
|
||||
info.setCustomMetadataValue(key, null);
|
||||
@@ -83,7 +112,7 @@ public class MetadataController {
|
||||
title = null;
|
||||
trapped = null;
|
||||
} else {
|
||||
// Iterate through the request parameters and set the metadata values
|
||||
// Iterate through the request parameters and set the metadata values
|
||||
for (Entry<String, String> entry : allRequestParams.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
// Check if the key is a standard metadata key
|
||||
@@ -128,11 +157,9 @@ public class MetadataController {
|
||||
info.setSubject(subject);
|
||||
info.setTitle(title);
|
||||
info.setTrapped(trapped);
|
||||
|
||||
|
||||
document.setDocumentInformation(info);
|
||||
return PdfUtils.pdfDocToWebResponse(document, pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_metadata.pdf");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
package stirling.software.SPDF.controller.api.other;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
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.RequestPart;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import stirling.software.SPDF.utils.PdfUtils;
|
||||
import stirling.software.SPDF.utils.ProcessExecutor;
|
||||
|
||||
@RestController
|
||||
public class OCRController {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(OCRController.class);
|
||||
|
||||
public List<String> getAvailableTesseractLanguages() {
|
||||
String tessdataDir = "/usr/share/tesseract-ocr/4.00/tessdata";
|
||||
File[] files = new File(tessdataDir).listFiles();
|
||||
if (files == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return Arrays.stream(files).filter(file -> file.getName().endsWith(".traineddata")).map(file -> file.getName().replace(".traineddata", ""))
|
||||
.filter(lang -> !lang.equalsIgnoreCase("osd")).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@PostMapping(consumes = "multipart/form-data", value = "/ocr-pdf")
|
||||
@Operation(summary = "Process a PDF file with OCR",
|
||||
description = "This endpoint processes a PDF file using OCR (Optical Character Recognition). Users can specify languages, sidecar, deskew, clean, cleanFinal, ocrType, ocrRenderType, and removeImagesAfter options.")
|
||||
public ResponseEntity<byte[]> processPdfWithOCR(
|
||||
@RequestPart(required = true, value = "fileInput")
|
||||
@Parameter(description = "The input PDF file to be processed with OCR")
|
||||
MultipartFile inputFile,
|
||||
@RequestParam("languages")
|
||||
@Parameter(description = "List of languages to use in OCR processing")
|
||||
List<String> selectedLanguages,
|
||||
@RequestParam(name = "sidecar", required = false)
|
||||
@Parameter(description = "Include OCR text in a sidecar text file if set to true")
|
||||
Boolean sidecar,
|
||||
@RequestParam(name = "deskew", required = false)
|
||||
@Parameter(description = "Deskew the input file if set to true")
|
||||
Boolean deskew,
|
||||
@RequestParam(name = "clean", required = false)
|
||||
@Parameter(description = "Clean the input file if set to true")
|
||||
Boolean clean,
|
||||
@RequestParam(name = "clean-final", required = false)
|
||||
@Parameter(description = "Clean the final output if set to true")
|
||||
Boolean cleanFinal,
|
||||
@RequestParam(name = "ocrType", required = false)
|
||||
@Parameter(description = "Specify the OCR type, e.g., 'skip-text', 'force-ocr', or 'Normal'", schema = @Schema(allowableValues = {"skip-text", "force-ocr", "Normal"}))
|
||||
String ocrType,
|
||||
@RequestParam(name = "ocrRenderType", required = false, defaultValue = "hocr")
|
||||
@Parameter(description = "Specify the OCR render type, either 'hocr' or 'sandwich'", schema = @Schema(allowableValues = {"hocr", "sandwich"}))
|
||||
String ocrRenderType,
|
||||
@RequestParam(name = "removeImagesAfter", required = false)
|
||||
@Parameter(description = "Remove images from the output PDF if set to true")
|
||||
Boolean removeImagesAfter) throws IOException, InterruptedException {
|
||||
|
||||
// --output-type pdfa
|
||||
if (selectedLanguages == null || selectedLanguages.isEmpty()) {
|
||||
throw new IOException("Please select at least one language.");
|
||||
}
|
||||
|
||||
if(!ocrRenderType.equals("hocr") && !ocrRenderType.equals("sandwich")) {
|
||||
throw new IOException("ocrRenderType wrong");
|
||||
}
|
||||
|
||||
// Get available Tesseract languages
|
||||
List<String> availableLanguages = getAvailableTesseractLanguages();
|
||||
|
||||
// Validate selected languages
|
||||
selectedLanguages = selectedLanguages.stream().filter(availableLanguages::contains).toList();
|
||||
|
||||
if (selectedLanguages.isEmpty()) {
|
||||
throw new IOException("None of the selected languages are valid.");
|
||||
}
|
||||
// Save the uploaded file to a temporary location
|
||||
Path tempInputFile = Files.createTempFile("input_", ".pdf");
|
||||
Files.copy(inputFile.getInputStream(), tempInputFile, StandardCopyOption.REPLACE_EXISTING);
|
||||
|
||||
// Prepare the output file path
|
||||
Path tempOutputFile = Files.createTempFile("output_", ".pdf");
|
||||
|
||||
// Prepare the output file path
|
||||
Path sidecarTextPath = null;
|
||||
|
||||
// Run OCR Command
|
||||
String languageOption = String.join("+", selectedLanguages);
|
||||
|
||||
|
||||
List<String> command = new ArrayList<>(Arrays.asList("ocrmypdf", "--verbose", "2", "--output-type", "pdf", "--pdf-renderer" , ocrRenderType));
|
||||
|
||||
if (sidecar != null && sidecar) {
|
||||
sidecarTextPath = Files.createTempFile("sidecar", ".txt");
|
||||
command.add("--sidecar");
|
||||
command.add(sidecarTextPath.toString());
|
||||
}
|
||||
|
||||
if (deskew != null && deskew) {
|
||||
command.add("--deskew");
|
||||
}
|
||||
if (clean != null && clean) {
|
||||
command.add("--clean");
|
||||
}
|
||||
if (cleanFinal != null && cleanFinal) {
|
||||
command.add("--clean-final");
|
||||
}
|
||||
if (ocrType != null && !ocrType.equals("")) {
|
||||
if ("skip-text".equals(ocrType)) {
|
||||
command.add("--skip-text");
|
||||
} else if ("force-ocr".equals(ocrType)) {
|
||||
command.add("--force-ocr");
|
||||
} else if ("Normal".equals(ocrType)) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
command.addAll(Arrays.asList("--language", languageOption, tempInputFile.toString(), tempOutputFile.toString()));
|
||||
|
||||
// Run CLI command
|
||||
int returnCode = ProcessExecutor.getInstance(ProcessExecutor.Processes.OCR_MY_PDF).runCommandWithOutputHandling(command);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Remove images from the OCR processed PDF if the flag is set to true
|
||||
if (removeImagesAfter != null && removeImagesAfter) {
|
||||
Path tempPdfWithoutImages = Files.createTempFile("output_", "_no_images.pdf");
|
||||
|
||||
List<String> gsCommand = Arrays.asList("gs", "-sDEVICE=pdfwrite", "-dFILTERIMAGE", "-o", tempPdfWithoutImages.toString(), tempOutputFile.toString());
|
||||
|
||||
int gsReturnCode = ProcessExecutor.getInstance(ProcessExecutor.Processes.GHOSTSCRIPT).runCommandWithOutputHandling(gsCommand);
|
||||
tempOutputFile = tempPdfWithoutImages;
|
||||
}
|
||||
// Read the OCR processed PDF file
|
||||
byte[] pdfBytes = Files.readAllBytes(tempOutputFile);
|
||||
// Clean up the temporary files
|
||||
Files.delete(tempInputFile);
|
||||
|
||||
// Return the OCR processed PDF as a response
|
||||
String outputFilename = inputFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_OCR.pdf";
|
||||
|
||||
if (sidecar != null && sidecar) {
|
||||
// Create a zip file containing both the PDF and the text file
|
||||
String outputZipFilename = inputFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_OCR.zip";
|
||||
Path tempZipFile = Files.createTempFile("output_", ".zip");
|
||||
|
||||
try (ZipOutputStream zipOut = new ZipOutputStream(new FileOutputStream(tempZipFile.toFile()))) {
|
||||
// Add PDF file to the zip
|
||||
ZipEntry pdfEntry = new ZipEntry(outputFilename);
|
||||
zipOut.putNextEntry(pdfEntry);
|
||||
Files.copy(tempOutputFile, zipOut);
|
||||
zipOut.closeEntry();
|
||||
|
||||
// Add text file to the zip
|
||||
ZipEntry txtEntry = new ZipEntry(outputFilename.replace(".pdf", ".txt"));
|
||||
zipOut.putNextEntry(txtEntry);
|
||||
Files.copy(sidecarTextPath, zipOut);
|
||||
zipOut.closeEntry();
|
||||
}
|
||||
|
||||
byte[] zipBytes = Files.readAllBytes(tempZipFile);
|
||||
|
||||
// Clean up the temporary zip file
|
||||
Files.delete(tempZipFile);
|
||||
Files.delete(tempOutputFile);
|
||||
Files.delete(sidecarTextPath);
|
||||
|
||||
// Return the zip file containing both the PDF and the text file
|
||||
return PdfUtils.bytesToWebResponse(pdfBytes, outputZipFilename, MediaType.APPLICATION_OCTET_STREAM);
|
||||
} else {
|
||||
// Return the OCR processed PDF as a response
|
||||
Files.delete(tempOutputFile);
|
||||
return PdfUtils.bytesToWebResponse(pdfBytes, outputFilename);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package stirling.software.SPDF.controller.api.other;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.HttpStatus;
|
||||
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.RequestPart;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import stirling.software.SPDF.utils.PdfUtils;
|
||||
|
||||
@RestController
|
||||
public class OverlayImageController {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(OverlayImageController.class);
|
||||
|
||||
@PostMapping(consumes = "multipart/form-data", value = "/add-image")
|
||||
@Operation(
|
||||
summary = "Overlay image onto a PDF file",
|
||||
description = "This endpoint overlays an image onto a PDF file at the specified coordinates. The image can be overlaid on every page of the PDF if specified."
|
||||
)
|
||||
public ResponseEntity<byte[]> overlayImage(
|
||||
@RequestPart(required = true, value = "fileInput")
|
||||
@Parameter(description = "The input PDF file to overlay the image onto.", required = true)
|
||||
MultipartFile pdfFile,
|
||||
@RequestParam("fileInput2")
|
||||
@Parameter(description = "The image file to be overlaid onto the PDF.", required = true)
|
||||
MultipartFile imageFile,
|
||||
@RequestParam("x")
|
||||
@Parameter(description = "The x-coordinate at which to place the top-left corner of the image.", example = "0")
|
||||
float x,
|
||||
@RequestParam("y")
|
||||
@Parameter(description = "The y-coordinate at which to place the top-left corner of the image.", example = "0")
|
||||
float y,
|
||||
@RequestParam("everyPage")
|
||||
@Parameter(description = "Whether to overlay the image onto every page of the PDF.", example = "false")
|
||||
boolean everyPage) {
|
||||
try {
|
||||
byte[] pdfBytes = pdfFile.getBytes();
|
||||
byte[] imageBytes = imageFile.getBytes();
|
||||
byte[] result = PdfUtils.overlayImage(pdfBytes, imageBytes, x, y, everyPage);
|
||||
|
||||
return PdfUtils.bytesToWebResponse(result, pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_overlayed.pdf");
|
||||
} catch (IOException e) {
|
||||
logger.error("Failed to add image to PDF", e);
|
||||
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
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.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 io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import stirling.software.SPDF.utils.PdfUtils;
|
||||
import stirling.software.SPDF.utils.ProcessExecutor;
|
||||
|
||||
@RestController
|
||||
public class RepairController {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(RepairController.class);
|
||||
|
||||
@PostMapping(consumes = "multipart/form-data", value = "/repair")
|
||||
@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 {
|
||||
|
||||
// Save the uploaded file to a temporary location
|
||||
Path tempInputFile = Files.createTempFile("input_", ".pdf");
|
||||
inputFile.transferTo(tempInputFile.toFile());
|
||||
|
||||
// Prepare the output file path
|
||||
Path tempOutputFile = Files.createTempFile("output_", ".pdf");
|
||||
|
||||
List<String> command = new ArrayList<>();
|
||||
command.add("gs");
|
||||
command.add("-o");
|
||||
command.add(tempOutputFile.toString());
|
||||
command.add("-sDEVICE=pdfwrite");
|
||||
command.add(tempInputFile.toString());
|
||||
|
||||
|
||||
int returnCode = ProcessExecutor.getInstance(ProcessExecutor.Processes.GHOSTSCRIPT).runCommandWithOutputHandling(command);
|
||||
|
||||
// Read the optimized PDF file
|
||||
byte[] pdfBytes = Files.readAllBytes(tempOutputFile);
|
||||
|
||||
// Clean up the temporary files
|
||||
Files.delete(tempInputFile);
|
||||
Files.delete(tempOutputFile);
|
||||
|
||||
// Return the optimized PDF as a response
|
||||
String outputFilename = inputFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_repaired.pdf";
|
||||
return PdfUtils.bytesToWebResponse(pdfBytes, outputFilename);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
package stirling.software.SPDF.controller.api.security;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||
import org.apache.pdfbox.pdmodel.encryption.AccessPermission;
|
||||
import org.apache.pdfbox.pdmodel.encryption.StandardProtectionPolicy;
|
||||
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.RequestPart;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import stirling.software.SPDF.utils.PdfUtils;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
@RestController
|
||||
public class PasswordController {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(PasswordController.class);
|
||||
|
||||
|
||||
@PostMapping(consumes = "multipart/form-data", value = "/remove-password")
|
||||
@Operation(
|
||||
summary = "Remove password from a PDF file",
|
||||
description = "This endpoint removes the password from a protected PDF file. Users need to provide the existing password."
|
||||
)
|
||||
public ResponseEntity<byte[]> removePassword(
|
||||
@RequestPart(required = true, value = "fileInput")
|
||||
@Parameter(description = "The input PDF file from which the password should be removed", required = true)
|
||||
MultipartFile fileInput,
|
||||
@RequestParam(name = "password")
|
||||
@Parameter(description = "The password of the PDF file", required = true)
|
||||
String password) throws IOException {
|
||||
PDDocument document = PDDocument.load(fileInput.getBytes(), password);
|
||||
document.setAllSecurityToBeRemoved(true);
|
||||
return PdfUtils.pdfDocToWebResponse(document, fileInput.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_password_removed.pdf");
|
||||
}
|
||||
|
||||
@PostMapping(consumes = "multipart/form-data", value = "/add-password")
|
||||
@Operation(
|
||||
summary = "Add password to a PDF file",
|
||||
description = "This endpoint adds password protection to a PDF file. Users can specify a set of permissions that should be applied to the file."
|
||||
)
|
||||
public ResponseEntity<byte[]> addPassword(
|
||||
@RequestPart(required = true, value = "fileInput")
|
||||
@Parameter(description = "The input PDF file to which the password should be added", required = true)
|
||||
MultipartFile fileInput,
|
||||
@RequestParam(defaultValue = "", name = "password")
|
||||
@Parameter(description = "The password to be added to the PDF file")
|
||||
String password,
|
||||
@RequestParam(defaultValue = "128", name = "keyLength")
|
||||
@Parameter(description = "The length of the encryption key", schema = @Schema(allowableValues = {"40", "128", "256"}))
|
||||
int keyLength,
|
||||
@RequestParam(defaultValue = "false", name = "canAssembleDocument")
|
||||
@Parameter(description = "Whether the document assembly is allowed", example = "false")
|
||||
boolean canAssembleDocument,
|
||||
@RequestParam(defaultValue = "false", name = "canExtractContent")
|
||||
@Parameter(description = "Whether content extraction for accessibility is allowed", example = "false")
|
||||
boolean canExtractContent,
|
||||
@RequestParam(defaultValue = "false", name = "canExtractForAccessibility")
|
||||
@Parameter(description = "Whether content extraction for accessibility is allowed", example = "false")
|
||||
boolean canExtractForAccessibility,
|
||||
@RequestParam(defaultValue = "false", name = "canFillInForm")
|
||||
@Parameter(description = "Whether form filling is allowed", example = "false")
|
||||
boolean canFillInForm,
|
||||
@RequestParam(defaultValue = "false", name = "canModify")
|
||||
@Parameter(description = "Whether the document modification is allowed", example = "false")
|
||||
boolean canModify,
|
||||
@RequestParam(defaultValue = "false", name = "canModifyAnnotations")
|
||||
@Parameter(description = "Whether modification of annotations is allowed", example = "false")
|
||||
boolean canModifyAnnotations,
|
||||
@RequestParam(defaultValue = "false", name = "canPrint")
|
||||
@Parameter(description = "Whether printing of the document is allowed", example = "false")
|
||||
boolean canPrint,
|
||||
@RequestParam(defaultValue = "false", name = "canPrintFaithful")
|
||||
@Parameter(description = "Whether faithful printing is allowed", example = "false")
|
||||
boolean canPrintFaithful
|
||||
) throws IOException {
|
||||
|
||||
PDDocument document = PDDocument.load(fileInput.getBytes());
|
||||
AccessPermission ap = new AccessPermission();
|
||||
|
||||
ap.setCanAssembleDocument(!canAssembleDocument);
|
||||
ap.setCanExtractContent(!canExtractContent);
|
||||
ap.setCanExtractForAccessibility(!canExtractForAccessibility);
|
||||
ap.setCanFillInForm(!canFillInForm);
|
||||
ap.setCanModify(!canModify);
|
||||
ap.setCanModifyAnnotations(!canModifyAnnotations);
|
||||
ap.setCanPrint(!canPrint);
|
||||
ap.setCanPrintFaithful(!canPrintFaithful);
|
||||
StandardProtectionPolicy spp = new StandardProtectionPolicy(password, password, ap);
|
||||
spp.setEncryptionKeyLength(keyLength);
|
||||
|
||||
spp.setPermissions(ap);
|
||||
|
||||
document.protect(spp);
|
||||
|
||||
return PdfUtils.pdfDocToWebResponse(document, fileInput.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_passworded.pdf");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
package stirling.software.SPDF.controller.api.security;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||
import org.apache.pdfbox.pdmodel.PDPage;
|
||||
import org.apache.pdfbox.pdmodel.PDPageContentStream;
|
||||
import org.apache.pdfbox.pdmodel.font.PDFont;
|
||||
import org.apache.pdfbox.pdmodel.font.PDType1Font;
|
||||
import org.apache.pdfbox.pdmodel.graphics.state.PDExtendedGraphicsState;
|
||||
import org.apache.pdfbox.util.Matrix;
|
||||
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.RequestPart;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import stirling.software.SPDF.utils.PdfUtils;
|
||||
|
||||
@RestController
|
||||
public class WatermarkController {
|
||||
|
||||
@PostMapping(consumes = "multipart/form-data", value = "/add-watermark")
|
||||
@Operation(summary = "Add watermark to a PDF file",
|
||||
description = "This endpoint adds a watermark to a given PDF file. Users can specify the watermark text, font size, rotation, opacity, width spacer, and height spacer.")
|
||||
public ResponseEntity<byte[]> addWatermark(
|
||||
@RequestPart(required = true, value = "fileInput")
|
||||
@Parameter(description = "The input PDF file to add a watermark")
|
||||
MultipartFile pdfFile,
|
||||
@RequestParam("watermarkText")
|
||||
@Parameter(description = "The watermark text to add to the PDF file")
|
||||
String watermarkText,
|
||||
@RequestParam(defaultValue = "30", name = "fontSize")
|
||||
@Parameter(description = "The font size of the watermark text", example = "30")
|
||||
float fontSize,
|
||||
@RequestParam(defaultValue = "0", name = "rotation")
|
||||
@Parameter(description = "The rotation of the watermark text in degrees", example = "0")
|
||||
float rotation,
|
||||
@RequestParam(defaultValue = "0.5", name = "opacity")
|
||||
@Parameter(description = "The opacity of the watermark text (0.0 - 1.0)", example = "0.5")
|
||||
float opacity,
|
||||
@RequestParam(defaultValue = "50", name = "widthSpacer")
|
||||
@Parameter(description = "The width spacer between watermark texts", example = "50")
|
||||
int widthSpacer,
|
||||
@RequestParam(defaultValue = "50", name = "heightSpacer")
|
||||
@Parameter(description = "The height spacer between watermark texts", example = "50")
|
||||
int heightSpacer) throws IOException {
|
||||
|
||||
// Load the input PDF
|
||||
PDDocument document = PDDocument.load(pdfFile.getInputStream());
|
||||
|
||||
// Create a page in the document
|
||||
for (PDPage page : document.getPages()) {
|
||||
|
||||
// Get the page's content stream
|
||||
PDPageContentStream contentStream = new PDPageContentStream(document, page, PDPageContentStream.AppendMode.APPEND, true);
|
||||
|
||||
// Set transparency
|
||||
PDExtendedGraphicsState graphicsState = new PDExtendedGraphicsState();
|
||||
graphicsState.setNonStrokingAlphaConstant(opacity);
|
||||
contentStream.setGraphicsStateParameters(graphicsState);
|
||||
|
||||
// Set font of watermark
|
||||
PDFont font = PDType1Font.HELVETICA_BOLD;
|
||||
contentStream.beginText();
|
||||
contentStream.setFont(font, fontSize);
|
||||
contentStream.setNonStrokingColor(Color.LIGHT_GRAY);
|
||||
|
||||
// Set size and location of watermark
|
||||
float pageWidth = page.getMediaBox().getWidth();
|
||||
float pageHeight = page.getMediaBox().getHeight();
|
||||
float watermarkWidth = widthSpacer + font.getStringWidth(watermarkText) * fontSize / 1000;
|
||||
float watermarkHeight = heightSpacer + fontSize;
|
||||
int watermarkRows = (int) (pageHeight / watermarkHeight + 1);
|
||||
int watermarkCols = (int) (pageWidth / watermarkWidth + 1);
|
||||
|
||||
// Add the watermark text
|
||||
for (int i = 0; i < watermarkRows; i++) {
|
||||
for (int j = 0; j < watermarkCols; j++) {
|
||||
contentStream.setTextMatrix(Matrix.getRotateInstance((float) Math.toRadians(rotation), j * watermarkWidth, i * watermarkHeight));
|
||||
contentStream.showTextWithPositioning(new Object[] { watermarkText });
|
||||
}
|
||||
}
|
||||
|
||||
contentStream.endText();
|
||||
|
||||
// Close the content stream
|
||||
contentStream.close();
|
||||
}
|
||||
return PdfUtils.pdfDocToWebResponse(document, pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_watermarked.pdf");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
package stirling.software.SPDF.controller.converters;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.pdfbox.rendering.ImageType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.core.io.ByteArrayResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import stirling.software.SPDF.utils.PdfUtils;
|
||||
|
||||
@Controller
|
||||
public class ConvertImgPDFController {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(ConvertImgPDFController.class);
|
||||
|
||||
@GetMapping("/img-to-pdf")
|
||||
public String convertToPdfForm(Model model) {
|
||||
model.addAttribute("currentPage", "img-to-pdf");
|
||||
return "convert/img-to-pdf";
|
||||
}
|
||||
|
||||
@GetMapping("/pdf-to-img")
|
||||
public String pdfToimgForm(Model model) {
|
||||
model.addAttribute("currentPage", "pdf-to-img");
|
||||
return "convert/pdf-to-img";
|
||||
}
|
||||
|
||||
@PostMapping("/img-to-pdf")
|
||||
public ResponseEntity<byte[]> convertToPdf(@RequestParam("fileInput") MultipartFile file) throws IOException {
|
||||
// Convert the file to PDF and get the resulting bytes
|
||||
byte[] bytes = PdfUtils.convertToPdf(file.getInputStream());
|
||||
logger.info("File {} successfully converted to pdf", file.getOriginalFilename());
|
||||
|
||||
return PdfUtils.bytesToWebResponse(bytes, file.getOriginalFilename().replaceFirst("[.][^.]+$", "")+ "_coverted.pdf");
|
||||
}
|
||||
|
||||
@PostMapping("/pdf-to-img")
|
||||
public ResponseEntity<Resource> convertToImage(@RequestParam("fileInput") MultipartFile file, @RequestParam("imageFormat") String imageFormat,
|
||||
@RequestParam("singleOrMultiple") String singleOrMultiple, @RequestParam("colorType") String colorType, @RequestParam("dpi") String dpi) throws IOException {
|
||||
|
||||
byte[] pdfBytes = file.getBytes();
|
||||
ImageType colorTypeResult = ImageType.RGB;
|
||||
if ("greyscale".equals(colorType)) {
|
||||
colorTypeResult = ImageType.GRAY;
|
||||
} else if ("blackwhite".equals(colorType)) {
|
||||
colorTypeResult = ImageType.BINARY;
|
||||
}
|
||||
// returns bytes for image
|
||||
boolean singleImage = singleOrMultiple.equals("single");
|
||||
byte[] result = null;
|
||||
try {
|
||||
result = PdfUtils.convertFromPdf(pdfBytes, imageFormat.toUpperCase(), colorTypeResult, singleImage, Integer.valueOf(dpi));
|
||||
} catch (IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
} catch (Exception e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (singleImage) {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.parseMediaType(getMediaType(imageFormat)));
|
||||
headers.setCacheControl("must-revalidate, post-check=0, pre-check=0");
|
||||
ResponseEntity<Resource> response = new ResponseEntity<>(new ByteArrayResource(result), headers, HttpStatus.OK);
|
||||
return response;
|
||||
} else {
|
||||
ByteArrayResource resource = new ByteArrayResource(result);
|
||||
// return the Resource in the response
|
||||
return ResponseEntity.ok().header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename="+ file.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_convertedToImages.zip").contentType(MediaType.APPLICATION_OCTET_STREAM)
|
||||
.contentLength(resource.contentLength()).body(resource);
|
||||
}
|
||||
}
|
||||
|
||||
private String getMediaType(String imageFormat) {
|
||||
if (imageFormat.equalsIgnoreCase("PNG"))
|
||||
return "image/png";
|
||||
else if (imageFormat.equalsIgnoreCase("JPEG") || imageFormat.equalsIgnoreCase("JPG"))
|
||||
return "image/jpeg";
|
||||
else if (imageFormat.equalsIgnoreCase("GIF"))
|
||||
return "image/gif";
|
||||
else
|
||||
return "application/octet-stream";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
package stirling.software.SPDF.controller.converters;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
import stirling.software.SPDF.LibreOfficeListener;
|
||||
import stirling.software.SPDF.utils.PdfUtils;
|
||||
import stirling.software.SPDF.utils.ProcessExecutor;
|
||||
@Controller
|
||||
public class ConvertOfficeController {
|
||||
|
||||
|
||||
@GetMapping("/file-to-pdf")
|
||||
public String convertToPdfForm(Model model) {
|
||||
model.addAttribute("currentPage", "file-to-pdf");
|
||||
return "convert/file-to-pdf";
|
||||
}
|
||||
|
||||
@PostMapping("/file-to-pdf")
|
||||
public ResponseEntity<byte[]> processPdfWithOCR(@RequestParam("fileInput") MultipartFile inputFile) throws IOException, InterruptedException {
|
||||
|
||||
//unused but can start server instance if startup time is to long
|
||||
//LibreOfficeListener.getInstance().start();
|
||||
|
||||
byte[] pdfByteArray = convertToPdf(inputFile);
|
||||
return PdfUtils.bytesToWebResponse(pdfByteArray, inputFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_convertedToPDF.pdf");
|
||||
}
|
||||
|
||||
|
||||
public byte[] convertToPdf(MultipartFile inputFile) throws IOException, InterruptedException {
|
||||
// Save the uploaded file to a temporary location
|
||||
Path tempInputFile = Files.createTempFile("input_", "." + getFileExtension(inputFile.getOriginalFilename()));
|
||||
inputFile.transferTo(tempInputFile.toFile());
|
||||
|
||||
// Prepare the output file path
|
||||
Path tempOutputFile = Files.createTempFile("output_", ".pdf");
|
||||
|
||||
// Run the LibreOffice command
|
||||
List<String> command = new ArrayList<>(Arrays.asList("unoconv", "-vvv",
|
||||
"-f",
|
||||
"pdf",
|
||||
"-o",
|
||||
tempOutputFile.toString(),
|
||||
tempInputFile.toString()));
|
||||
int returnCode = ProcessExecutor.runCommandWithOutputHandling(command);
|
||||
|
||||
// Read the converted PDF file
|
||||
byte[] pdfBytes = Files.readAllBytes(tempOutputFile);
|
||||
|
||||
// Clean up the temporary files
|
||||
Files.delete(tempInputFile);
|
||||
Files.delete(tempOutputFile);
|
||||
|
||||
return pdfBytes;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private String getFileExtension(String fileName) {
|
||||
int dotIndex = fileName.lastIndexOf('.');
|
||||
if (dotIndex == -1) {
|
||||
return "";
|
||||
}
|
||||
return fileName.substring(dotIndex + 1);
|
||||
}
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
package stirling.software.SPDF.controller.security;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||
import org.apache.pdfbox.pdmodel.encryption.AccessPermission;
|
||||
import org.apache.pdfbox.pdmodel.encryption.StandardProtectionPolicy;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import stirling.software.SPDF.utils.PdfUtils;
|
||||
|
||||
@Controller
|
||||
public class PasswordController {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(PasswordController.class);
|
||||
|
||||
@GetMapping("/add-password")
|
||||
public String addPasswordForm(Model model) {
|
||||
model.addAttribute("currentPage", "add-password");
|
||||
return "security/add-password";
|
||||
}
|
||||
|
||||
@GetMapping("/remove-password")
|
||||
public String removePasswordForm(Model model) {
|
||||
model.addAttribute("currentPage", "remove-password");
|
||||
return "security/remove-password";
|
||||
}
|
||||
|
||||
@GetMapping("/change-permissions")
|
||||
public String permissionsForm(Model model) {
|
||||
model.addAttribute("currentPage", "change-permissions");
|
||||
return "security/change-permissions";
|
||||
}
|
||||
|
||||
@PostMapping("/remove-password")
|
||||
public ResponseEntity<byte[]> compressPDF(@RequestParam("fileInput") MultipartFile fileInput, @RequestParam(name = "password") String password) throws IOException {
|
||||
PDDocument document = PDDocument.load(fileInput.getBytes(), password);
|
||||
document.setAllSecurityToBeRemoved(true);
|
||||
return PdfUtils.pdfDocToWebResponse(document, fileInput.getOriginalFilename().replaceFirst("[.][^.]+$", "")+ "_password_removed.pdf");
|
||||
}
|
||||
|
||||
@PostMapping("/add-password")
|
||||
public ResponseEntity<byte[]> compressPDF(@RequestParam("fileInput") MultipartFile fileInput, @RequestParam(defaultValue = "", name = "password") String password,
|
||||
@RequestParam(defaultValue = "128", name = "keyLength") int keyLength, @RequestParam(defaultValue = "false", name = "canAssembleDocument") boolean canAssembleDocument,
|
||||
@RequestParam(defaultValue = "false", name = "canExtractContent") boolean canExtractContent,
|
||||
@RequestParam(defaultValue = "false", name = "canExtractForAccessibility") boolean canExtractForAccessibility,
|
||||
@RequestParam(defaultValue = "false", name = "canFillInForm") boolean canFillInForm, @RequestParam(defaultValue = "false", name = "canModify") boolean canModify,
|
||||
@RequestParam(defaultValue = "false", name = "canModifyAnnotations") boolean canModifyAnnotations,
|
||||
@RequestParam(defaultValue = "false", name = "canPrint") boolean canPrint, @RequestParam(defaultValue = "false", name = "canPrintFaithful") boolean canPrintFaithful)
|
||||
throws IOException {
|
||||
|
||||
PDDocument document = PDDocument.load(fileInput.getBytes());
|
||||
AccessPermission ap = new AccessPermission();
|
||||
|
||||
ap.setCanAssembleDocument(!canAssembleDocument);
|
||||
ap.setCanExtractContent(!canExtractContent);
|
||||
ap.setCanExtractForAccessibility(!canExtractForAccessibility);
|
||||
ap.setCanFillInForm(!canFillInForm);
|
||||
ap.setCanModify(!canModify);
|
||||
ap.setCanModifyAnnotations(!canModifyAnnotations);
|
||||
ap.setCanPrint(!canPrint);
|
||||
ap.setCanPrintFaithful(!canPrintFaithful);
|
||||
StandardProtectionPolicy spp = new StandardProtectionPolicy(password, password, ap);
|
||||
spp.setEncryptionKeyLength(keyLength);
|
||||
|
||||
spp.setPermissions(ap);
|
||||
|
||||
document.protect(spp);
|
||||
|
||||
return PdfUtils.pdfDocToWebResponse(document, fileInput.getOriginalFilename().replaceFirst("[.][^.]+$", "")+ "_passworded.pdf");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,157 +0,0 @@
|
||||
package stirling.software.SPDF.controller.security;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||
import org.apache.pdfbox.pdmodel.PDDocumentCatalog;
|
||||
import org.apache.pdfbox.pdmodel.PDPage;
|
||||
import org.apache.pdfbox.pdmodel.PDPageContentStream;
|
||||
import org.apache.pdfbox.pdmodel.font.PDFont;
|
||||
import org.apache.pdfbox.pdmodel.font.PDType1Font;
|
||||
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotation;
|
||||
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationMarkup;
|
||||
import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm;
|
||||
import org.apache.pdfbox.pdmodel.interactive.form.PDField;
|
||||
import org.apache.pdfbox.util.Matrix;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import stirling.software.SPDF.utils.PdfUtils;
|
||||
import stirling.software.SPDF.utils.WatermarkRemover;
|
||||
import org.apache.pdfbox.pdmodel.graphics.state.PDExtendedGraphicsState;
|
||||
|
||||
@Controller
|
||||
public class WatermarkController {
|
||||
|
||||
@GetMapping("/add-watermark")
|
||||
public String addWatermarkForm(Model model) {
|
||||
model.addAttribute("currentPage", "add-watermark");
|
||||
return "security/add-watermark";
|
||||
}
|
||||
|
||||
@GetMapping("/remove-watermark")
|
||||
public String removeWatermarkForm(Model model) {
|
||||
model.addAttribute("currentPage", "remove-watermark");
|
||||
return "security/remove-watermark";
|
||||
}
|
||||
|
||||
@PostMapping("/add-watermark")
|
||||
public ResponseEntity<byte[]> addWatermark(@RequestParam("fileInput") MultipartFile pdfFile, @RequestParam("watermarkText") String watermarkText,
|
||||
@RequestParam(defaultValue = "30", name = "fontSize") float fontSize, @RequestParam(defaultValue = "0", name = "rotation") float rotation,
|
||||
@RequestParam(defaultValue = "0.5", name = "opacity") float opacity,
|
||||
@RequestParam(defaultValue = "50", name = "widthSpacer") int widthSpacer, @RequestParam(defaultValue = "50", name = "heightSpacer") int heightSpacer)
|
||||
throws IOException {
|
||||
|
||||
// Load the input PDF
|
||||
PDDocument document = PDDocument.load(pdfFile.getInputStream());
|
||||
|
||||
// Create a page in the document
|
||||
for (PDPage page : document.getPages()) {
|
||||
|
||||
|
||||
|
||||
|
||||
// Get the page's content stream
|
||||
PDPageContentStream contentStream = new PDPageContentStream(document, page, PDPageContentStream.AppendMode.APPEND, true);
|
||||
|
||||
// Set transparency
|
||||
PDExtendedGraphicsState graphicsState = new PDExtendedGraphicsState();
|
||||
graphicsState.setNonStrokingAlphaConstant(opacity);
|
||||
contentStream.setGraphicsStateParameters(graphicsState);
|
||||
|
||||
// Set font of watermark
|
||||
PDFont font = PDType1Font.HELVETICA_BOLD;
|
||||
contentStream.beginText();
|
||||
contentStream.setFont(font, fontSize);
|
||||
contentStream.setNonStrokingColor(Color.LIGHT_GRAY);
|
||||
|
||||
// Set size and location of watermark
|
||||
float pageWidth = page.getMediaBox().getWidth();
|
||||
float pageHeight = page.getMediaBox().getHeight();
|
||||
float watermarkWidth = widthSpacer + font.getStringWidth(watermarkText) * fontSize / 1000;
|
||||
float watermarkHeight = heightSpacer + fontSize;
|
||||
int watermarkRows = (int) (pageHeight / watermarkHeight + 1);
|
||||
int watermarkCols = (int) (pageWidth / watermarkWidth + 1);
|
||||
|
||||
// Add the watermark text
|
||||
for (int i = 0; i < watermarkRows; i++) {
|
||||
for (int j = 0; j < watermarkCols; j++) {
|
||||
contentStream.setTextMatrix(Matrix.getRotateInstance((float) Math.toRadians(rotation), j * watermarkWidth, i * watermarkHeight));
|
||||
contentStream.showTextWithPositioning(new Object[] { watermarkText });
|
||||
}
|
||||
}
|
||||
|
||||
contentStream.endText();
|
||||
|
||||
// Close the content stream
|
||||
contentStream.close();
|
||||
}
|
||||
return PdfUtils.pdfDocToWebResponse(document, pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_watermarked.pdf");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@PostMapping("/remove-watermark")
|
||||
public ResponseEntity<byte[]> removeWatermark(@RequestParam("fileInput") MultipartFile pdfFile, @RequestParam("watermarkText") String watermarkText) throws Exception {
|
||||
|
||||
// Load the input PDF
|
||||
PDDocument document = PDDocument.load(pdfFile.getInputStream());
|
||||
|
||||
// Create a new PDF document for the output
|
||||
PDDocument outputDocument = new PDDocument();
|
||||
|
||||
// Loop through the pages
|
||||
int numPages = document.getNumberOfPages();
|
||||
for (int i = 0; i < numPages; i++) {
|
||||
PDPage page = document.getPage(i);
|
||||
|
||||
// Process the content stream to remove the watermark text
|
||||
WatermarkRemover editor = new WatermarkRemover(watermarkText) {};
|
||||
editor.processPage(page);
|
||||
editor.processPage(page);
|
||||
// Add the page to the output document
|
||||
outputDocument.addPage(page);
|
||||
}
|
||||
|
||||
for (PDPage page : outputDocument.getPages()) {
|
||||
List<PDAnnotation> annotations = page.getAnnotations();
|
||||
List<PDAnnotation> annotationsToRemove = new ArrayList<>();
|
||||
|
||||
for (PDAnnotation annotation : annotations) {
|
||||
if (annotation instanceof PDAnnotationMarkup) {
|
||||
PDAnnotationMarkup markup = (PDAnnotationMarkup) annotation;
|
||||
String contents = markup.getContents();
|
||||
if (contents != null && contents.contains(watermarkText)) {
|
||||
annotationsToRemove.add(markup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
annotations.removeAll(annotationsToRemove);
|
||||
}
|
||||
PDDocumentCatalog catalog = outputDocument.getDocumentCatalog();
|
||||
PDAcroForm acroForm = catalog.getAcroForm();
|
||||
if (acroForm != null) {
|
||||
List<PDField> fields = acroForm.getFields();
|
||||
for (PDField field : fields) {
|
||||
String fieldValue = field.getValueAsString();
|
||||
if (fieldValue.contains(watermarkText)) {
|
||||
field.setValue(fieldValue.replace(watermarkText, ""));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return PdfUtils.pdfDocToWebResponse(outputDocument, "removed.pdf");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
package stirling.software.SPDF.controller.web;
|
||||
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Hidden;
|
||||
|
||||
@Controller
|
||||
public class ConverterWebController {
|
||||
|
||||
@GetMapping("/img-to-pdf")
|
||||
@Hidden
|
||||
public String convertImgToPdfForm(Model model) {
|
||||
model.addAttribute("currentPage", "img-to-pdf");
|
||||
return "convert/img-to-pdf";
|
||||
}
|
||||
|
||||
|
||||
@GetMapping("/pdf-to-img")
|
||||
@Hidden
|
||||
public String pdfToimgForm(Model model) {
|
||||
model.addAttribute("currentPage", "pdf-to-img");
|
||||
return "convert/pdf-to-img";
|
||||
}
|
||||
|
||||
@GetMapping("/file-to-pdf")
|
||||
@Hidden
|
||||
public String convertToPdfForm(Model model) {
|
||||
model.addAttribute("currentPage", "file-to-pdf");
|
||||
return "convert/file-to-pdf";
|
||||
}
|
||||
|
||||
|
||||
|
||||
//PDF TO......
|
||||
|
||||
@GetMapping("/pdf-to-html")
|
||||
@Hidden
|
||||
public ModelAndView pdfToHTML() {
|
||||
ModelAndView modelAndView = new ModelAndView("convert/pdf-to-html");
|
||||
modelAndView.addObject("currentPage", "pdf-to-html");
|
||||
return modelAndView;
|
||||
}
|
||||
|
||||
@GetMapping("/pdf-to-presentation")
|
||||
@Hidden
|
||||
public ModelAndView pdfToPresentation() {
|
||||
ModelAndView modelAndView = new ModelAndView("convert/pdf-to-presentation");
|
||||
modelAndView.addObject("currentPage", "pdf-to-presentation");
|
||||
return modelAndView;
|
||||
}
|
||||
|
||||
@GetMapping("/pdf-to-text")
|
||||
@Hidden
|
||||
public ModelAndView pdfToText() {
|
||||
ModelAndView modelAndView = new ModelAndView("convert/pdf-to-text");
|
||||
modelAndView.addObject("currentPage", "pdf-to-text");
|
||||
return modelAndView;
|
||||
}
|
||||
|
||||
@GetMapping("/pdf-to-word")
|
||||
@Hidden
|
||||
public ModelAndView pdfToWord() {
|
||||
ModelAndView modelAndView = new ModelAndView("convert/pdf-to-word");
|
||||
modelAndView.addObject("currentPage", "pdf-to-word");
|
||||
return modelAndView;
|
||||
}
|
||||
|
||||
@GetMapping("/pdf-to-xml")
|
||||
@Hidden
|
||||
public ModelAndView pdfToXML() {
|
||||
ModelAndView modelAndView = new ModelAndView("convert/pdf-to-xml");
|
||||
modelAndView.addObject("currentPage", "pdf-to-xml");
|
||||
return modelAndView;
|
||||
}
|
||||
|
||||
|
||||
@GetMapping("/pdf-to-pdfa")
|
||||
@Hidden
|
||||
public String pdfToPdfAForm(Model model) {
|
||||
model.addAttribute("currentPage", "pdf-to-pdfa");
|
||||
return "convert/pdf-to-pdfa";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
package stirling.software.SPDF.controller.web;
|
||||
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Hidden;
|
||||
|
||||
@Controller
|
||||
public class GeneralWebController {
|
||||
@GetMapping("/merge-pdfs")
|
||||
@Hidden
|
||||
public String mergePdfForm(Model model) {
|
||||
model.addAttribute("currentPage", "merge-pdfs");
|
||||
return "merge-pdfs";
|
||||
}
|
||||
@GetMapping("/about")
|
||||
@Hidden
|
||||
public String gameForm(Model model) {
|
||||
model.addAttribute("currentPage", "about");
|
||||
return "about";
|
||||
}
|
||||
|
||||
@GetMapping("/multi-tool")
|
||||
@Hidden
|
||||
public String multiToolForm(Model model) {
|
||||
model.addAttribute("currentPage", "multi-tool");
|
||||
return "multi-tool";
|
||||
}
|
||||
|
||||
@GetMapping("/")
|
||||
public String home(Model model) {
|
||||
model.addAttribute("currentPage", "home");
|
||||
return "home";
|
||||
}
|
||||
|
||||
@GetMapping("/home")
|
||||
public String root(Model model) {
|
||||
return "redirect:/";
|
||||
}
|
||||
|
||||
@GetMapping("/remove-pages")
|
||||
@Hidden
|
||||
public String pageDeleter(Model model) {
|
||||
model.addAttribute("currentPage", "remove-pages");
|
||||
return "remove-pages";
|
||||
}
|
||||
|
||||
@GetMapping("/pdf-organizer")
|
||||
@Hidden
|
||||
public String pageOrganizer(Model model) {
|
||||
model.addAttribute("currentPage", "pdf-organizer");
|
||||
return "pdf-organizer";
|
||||
}
|
||||
|
||||
@GetMapping("/rotate-pdf")
|
||||
@Hidden
|
||||
public String rotatePdfForm(Model model) {
|
||||
model.addAttribute("currentPage", "rotate-pdf");
|
||||
return "rotate-pdf";
|
||||
}
|
||||
|
||||
@GetMapping("/split-pdfs")
|
||||
@Hidden
|
||||
public String splitPdfForm(Model model) {
|
||||
model.addAttribute("currentPage", "split-pdfs");
|
||||
return "split-pdfs";
|
||||
}
|
||||
|
||||
@GetMapping("/sign")
|
||||
@Hidden
|
||||
public String signForm(Model model) {
|
||||
model.addAttribute("currentPage", "sign");
|
||||
return "sign";
|
||||
}
|
||||
|
||||
@GetMapping(value = "/robots.txt", produces = MediaType.TEXT_PLAIN_VALUE)
|
||||
@ResponseBody
|
||||
@Hidden
|
||||
public String getRobotsTxt() {
|
||||
String allowGoogleVisibility = System.getProperty("ALLOW_GOOGLE_VISABILITY");
|
||||
if (allowGoogleVisibility == null)
|
||||
allowGoogleVisibility = System.getenv("ALLOW_GOOGLE_VISABILITY");
|
||||
if (allowGoogleVisibility == null)
|
||||
allowGoogleVisibility = "false";
|
||||
if (Boolean.parseBoolean(allowGoogleVisibility)) {
|
||||
return "User-agent: Googlebot\nAllow: /\n\nUser-agent: *\nAllow: /";
|
||||
} else {
|
||||
return "User-agent: Googlebot\nDisallow: /\n\nUser-agent: *\nDisallow: /";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
package stirling.software.SPDF.controller.web;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Hidden;
|
||||
|
||||
@Controller
|
||||
public class OtherWebController {
|
||||
@GetMapping("/compress-pdf")
|
||||
@Hidden
|
||||
public String compressPdfForm(Model model) {
|
||||
model.addAttribute("currentPage", "compress-pdf");
|
||||
return "other/compress-pdf";
|
||||
}
|
||||
|
||||
@GetMapping("/extract-image-scans")
|
||||
@Hidden
|
||||
public ModelAndView extractImageScansForm() {
|
||||
ModelAndView modelAndView = new ModelAndView("other/extract-image-scans");
|
||||
modelAndView.addObject("currentPage", "extract-image-scans");
|
||||
return modelAndView;
|
||||
}
|
||||
|
||||
@GetMapping("/extract-images")
|
||||
@Hidden
|
||||
public String extractImagesForm(Model model) {
|
||||
model.addAttribute("currentPage", "extract-images");
|
||||
return "other/extract-images";
|
||||
}
|
||||
|
||||
@GetMapping("/flatten")
|
||||
@Hidden
|
||||
public String flattenForm(Model model) {
|
||||
model.addAttribute("currentPage", "flatten");
|
||||
return "other/flatten";
|
||||
}
|
||||
|
||||
|
||||
|
||||
@GetMapping("/change-metadata")
|
||||
@Hidden
|
||||
public String addWatermarkForm(Model model) {
|
||||
model.addAttribute("currentPage", "change-metadata");
|
||||
return "other/change-metadata";
|
||||
}
|
||||
|
||||
@GetMapping("/compare")
|
||||
@Hidden
|
||||
public String compareForm(Model model) {
|
||||
model.addAttribute("currentPage", "compare");
|
||||
return "other/compare";
|
||||
}
|
||||
|
||||
public List<String> getAvailableTesseractLanguages() {
|
||||
String tessdataDir = "/usr/share/tesseract-ocr/4.00/tessdata";
|
||||
File[] files = new File(tessdataDir).listFiles();
|
||||
if (files == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return Arrays.stream(files).filter(file -> file.getName().endsWith(".traineddata")).map(file -> file.getName().replace(".traineddata", ""))
|
||||
.filter(lang -> !lang.equalsIgnoreCase("osd")).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@GetMapping("/ocr-pdf")
|
||||
@Hidden
|
||||
public ModelAndView ocrPdfPage() {
|
||||
ModelAndView modelAndView = new ModelAndView("other/ocr-pdf");
|
||||
modelAndView.addObject("languages", getAvailableTesseractLanguages());
|
||||
modelAndView.addObject("currentPage", "ocr-pdf");
|
||||
return modelAndView;
|
||||
}
|
||||
|
||||
|
||||
@GetMapping("/add-image")
|
||||
@Hidden
|
||||
public String overlayImage(Model model) {
|
||||
model.addAttribute("currentPage", "add-image");
|
||||
return "other/add-image";
|
||||
}
|
||||
|
||||
@GetMapping("/adjust-contrast")
|
||||
@Hidden
|
||||
public String contrast(Model model) {
|
||||
model.addAttribute("currentPage", "adjust-contrast");
|
||||
return "other/adjust-contrast";
|
||||
}
|
||||
|
||||
@GetMapping("/repair")
|
||||
@Hidden
|
||||
public String repairForm(Model model) {
|
||||
model.addAttribute("currentPage", "repair");
|
||||
return "other/repair";
|
||||
}
|
||||
|
||||
@GetMapping("/remove-blanks")
|
||||
@Hidden
|
||||
public String removeBlanksForm(Model model) {
|
||||
model.addAttribute("currentPage", "remove-blanks");
|
||||
return "other/remove-blanks";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package stirling.software.SPDF.controller.web;
|
||||
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Hidden;
|
||||
|
||||
@Controller
|
||||
public class SecurityWebController {
|
||||
@GetMapping("/add-password")
|
||||
@Hidden
|
||||
public String addPasswordForm(Model model) {
|
||||
model.addAttribute("currentPage", "add-password");
|
||||
return "security/add-password";
|
||||
}
|
||||
@GetMapping("/change-permissions")
|
||||
@Hidden
|
||||
public String permissionsForm(Model model) {
|
||||
model.addAttribute("currentPage", "change-permissions");
|
||||
return "security/change-permissions";
|
||||
}
|
||||
|
||||
@GetMapping("/remove-password")
|
||||
@Hidden
|
||||
public String removePasswordForm(Model model) {
|
||||
model.addAttribute("currentPage", "remove-password");
|
||||
return "security/remove-password";
|
||||
}
|
||||
|
||||
@GetMapping("/add-watermark")
|
||||
@Hidden
|
||||
public String addWatermarkForm(Model model) {
|
||||
model.addAttribute("currentPage", "add-watermark");
|
||||
return "security/add-watermark";
|
||||
}
|
||||
}
|
||||
@@ -28,5 +28,5 @@ public class ErrorUtils {
|
||||
modelAndView.addObject("stackTrace", stackTrace);
|
||||
return modelAndView;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
130
src/main/java/stirling/software/SPDF/utils/ImageFinder.java
Normal file
@@ -0,0 +1,130 @@
|
||||
package stirling.software.SPDF.utils;
|
||||
|
||||
import java.awt.geom.Point2D;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.pdfbox.contentstream.operator.Operator;
|
||||
import org.apache.pdfbox.contentstream.operator.OperatorName;
|
||||
import org.apache.pdfbox.cos.COSBase;
|
||||
import org.apache.pdfbox.cos.COSName;
|
||||
import org.apache.pdfbox.pdmodel.PDPage;
|
||||
import org.apache.pdfbox.pdmodel.graphics.PDXObject;
|
||||
import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject;
|
||||
import org.apache.pdfbox.pdmodel.graphics.image.PDImage;
|
||||
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
|
||||
|
||||
public class ImageFinder extends org.apache.pdfbox.contentstream.PDFGraphicsStreamEngine {
|
||||
private boolean hasImages = false;
|
||||
|
||||
public ImageFinder(PDPage page) {
|
||||
super(page);
|
||||
}
|
||||
|
||||
public boolean hasImages() {
|
||||
return hasImages;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processOperator(Operator operator, List<COSBase> operands) throws IOException {
|
||||
String operation = operator.getName();
|
||||
if (operation.equals(OperatorName.DRAW_OBJECT)) {
|
||||
COSBase base = operands.get(0);
|
||||
if (base instanceof COSName) {
|
||||
COSName objectName = (COSName) base;
|
||||
PDXObject xobject = getResources().getXObject(objectName);
|
||||
if (xobject instanceof PDImageXObject) {
|
||||
hasImages = true;
|
||||
} else if (xobject instanceof PDFormXObject) {
|
||||
PDFormXObject form = (PDFormXObject) xobject;
|
||||
ImageFinder innerFinder = new ImageFinder(getPage());
|
||||
innerFinder.processPage(getPage());
|
||||
if (innerFinder.hasImages()) {
|
||||
hasImages = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
super.processOperator(operator, operands);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendRectangle(Point2D p0, Point2D p1, Point2D p2, Point2D p3) throws IOException {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawImage(PDImage pdImage) throws IOException {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clip(int windingRule) throws IOException {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void moveTo(float x, float y) throws IOException {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void lineTo(float x, float y) throws IOException {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void curveTo(float x1, float y1, float x2, float y2, float x3, float y3) throws IOException {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Point2D getCurrentPoint() throws IOException {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closePath() throws IOException {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endPath() throws IOException {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void strokePath() throws IOException {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fillPath(int windingRule) throws IOException {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fillAndStrokePath(int windingRule) throws IOException {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shadingFill(COSName shadingName) throws IOException {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
// ... rest of the overridden methods
|
||||
}
|
||||
97
src/main/java/stirling/software/SPDF/utils/PDFToFile.java
Normal file
@@ -0,0 +1,97 @@
|
||||
package stirling.software.SPDF.utils;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
public class PDFToFile {
|
||||
public ResponseEntity<byte[]> processPdfToOfficeFormat(MultipartFile inputFile, String outputFormat, String libreOfficeFilter) throws IOException, InterruptedException {
|
||||
|
||||
if (!"application/pdf".equals(inputFile.getContentType())) {
|
||||
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
|
||||
// Get the original PDF file name without the extension
|
||||
String originalPdfFileName = inputFile.getOriginalFilename();
|
||||
String pdfBaseName = originalPdfFileName.substring(0, originalPdfFileName.lastIndexOf('.'));
|
||||
|
||||
// Validate output format
|
||||
List<String> allowedFormats = Arrays.asList("doc", "docx", "odt", "ppt", "pptx", "odp", "rtf", "html", "xml", "txt:Text");
|
||||
if (!allowedFormats.contains(outputFormat)) {
|
||||
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
|
||||
Path tempInputFile = null;
|
||||
Path tempOutputDir = null;
|
||||
byte[] fileBytes;
|
||||
String fileName = "temp.file";
|
||||
|
||||
try {
|
||||
// Save the uploaded file to a temporary location
|
||||
tempInputFile = Files.createTempFile("input_", ".pdf");
|
||||
Files.copy(inputFile.getInputStream(), tempInputFile, StandardCopyOption.REPLACE_EXISTING);
|
||||
|
||||
// Prepare the output directory
|
||||
tempOutputDir = Files.createTempDirectory("output_");
|
||||
|
||||
// Run the LibreOffice command
|
||||
List<String> command = new ArrayList<>(
|
||||
Arrays.asList("soffice", "--infilter=" + libreOfficeFilter, "--convert-to", outputFormat, "--outdir", tempOutputDir.toString(), tempInputFile.toString()));
|
||||
int returnCode = ProcessExecutor.getInstance(ProcessExecutor.Processes.LIBRE_OFFICE).runCommandWithOutputHandling(command);
|
||||
|
||||
// Get output files
|
||||
List<File> outputFiles = Arrays.asList(tempOutputDir.toFile().listFiles());
|
||||
|
||||
if (outputFiles.size() == 1) {
|
||||
// Return single output file
|
||||
File outputFile = outputFiles.get(0);
|
||||
if (outputFormat.equals("txt:Text")) {
|
||||
outputFormat = "txt";
|
||||
}
|
||||
fileName = pdfBaseName + "." + outputFormat;
|
||||
fileBytes = FileUtils.readFileToByteArray(outputFile);
|
||||
} else {
|
||||
// Return output files in a ZIP archive
|
||||
fileName = pdfBaseName + "To" + outputFormat + ".zip";
|
||||
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
||||
ZipOutputStream zipOutputStream = new ZipOutputStream(byteArrayOutputStream);
|
||||
|
||||
for (File outputFile : outputFiles) {
|
||||
ZipEntry entry = new ZipEntry(outputFile.getName());
|
||||
zipOutputStream.putNextEntry(entry);
|
||||
FileInputStream fis = new FileInputStream(outputFile);
|
||||
IOUtils.copy(fis, zipOutputStream);
|
||||
fis.close();
|
||||
zipOutputStream.closeEntry();
|
||||
}
|
||||
|
||||
zipOutputStream.close();
|
||||
fileBytes = byteArrayOutputStream.toByteArray();
|
||||
}
|
||||
|
||||
} finally {
|
||||
// Clean up the temporary files
|
||||
if (tempInputFile != null)
|
||||
Files.delete(tempInputFile);
|
||||
if (tempOutputDir != null)
|
||||
FileUtils.deleteDirectory(tempOutputDir.toFile());
|
||||
}
|
||||
return PdfUtils.bytesToWebResponse(fileBytes, fileName, MediaType.APPLICATION_OCTET_STREAM);
|
||||
}
|
||||
}
|
||||
@@ -2,12 +2,23 @@ package stirling.software.SPDF.utils;
|
||||
|
||||
import java.awt.Graphics;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.BufferedImageOp;
|
||||
import java.awt.image.ColorConvertOp;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyStore;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
@@ -15,10 +26,13 @@ import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.imageio.ImageReader;
|
||||
|
||||
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.LosslessFactory;
|
||||
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
|
||||
import org.apache.pdfbox.rendering.ImageType;
|
||||
import org.apache.pdfbox.rendering.PDFRenderer;
|
||||
@@ -28,60 +42,36 @@ import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
|
||||
import com.itextpdf.text.Document;
|
||||
import com.itextpdf.text.DocumentException;
|
||||
import com.itextpdf.text.pdf.PdfWriter;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
public class PdfUtils {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(PdfUtils.class);
|
||||
|
||||
public static byte[] convertToPdf(InputStream imageStream) throws IOException {
|
||||
|
||||
// Create a File object for the image
|
||||
File imageFile = new File("image.jpg");
|
||||
|
||||
try (FileOutputStream fos = new FileOutputStream(imageFile); InputStream input = imageStream) {
|
||||
byte[] buffer = new byte[1024];
|
||||
int len;
|
||||
// Read from the input stream and write to the file
|
||||
while ((len = input.read(buffer)) != -1) {
|
||||
fos.write(buffer, 0, len);
|
||||
}
|
||||
logger.info("Image successfully written to file: {}", imageFile.getAbsolutePath());
|
||||
} catch (IOException e) {
|
||||
logger.error("Error writing image to file: {}", imageFile.getAbsolutePath(), e);
|
||||
throw e;
|
||||
}
|
||||
|
||||
try (PDDocument doc = new PDDocument()) {
|
||||
// Create a new PDF page
|
||||
PDPage page = new PDPage();
|
||||
doc.addPage(page);
|
||||
|
||||
// Create an image object from the image file
|
||||
PDImageXObject image = PDImageXObject.createFromFileByContent(imageFile, doc);
|
||||
|
||||
try (PDPageContentStream contentStream = new PDPageContentStream(doc, page)) {
|
||||
// Draw the image onto the page
|
||||
contentStream.drawImage(image, 0, 0);
|
||||
logger.info("Image successfully added to PDF");
|
||||
} catch (IOException e) {
|
||||
logger.error("Error adding image to PDF", e);
|
||||
throw e;
|
||||
}
|
||||
|
||||
// Create a ByteArrayOutputStream to save the PDF to
|
||||
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
||||
doc.save(byteArrayOutputStream);
|
||||
logger.info("PDF successfully saved to byte array");
|
||||
return byteArrayOutputStream.toByteArray();
|
||||
}
|
||||
public static ResponseEntity<byte[]> boasToWebResponse(ByteArrayOutputStream baos, String docName) throws IOException {
|
||||
return PdfUtils.bytesToWebResponse(baos.toByteArray(), docName);
|
||||
}
|
||||
|
||||
public static byte[] convertFromPdf(byte[] inputStream, String imageType, ImageType colorType, boolean singleImage, int DPI)
|
||||
throws IOException, Exception {
|
||||
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))) {
|
||||
PDFRenderer pdfRenderer = new PDFRenderer(document);
|
||||
int pageCount = document.getNumberOfPages();
|
||||
@@ -91,7 +81,7 @@ public class PdfUtils {
|
||||
for (int i = 0; i < pageCount; i++) {
|
||||
images.add(pdfRenderer.renderImageWithDPI(i, 300, colorType));
|
||||
}
|
||||
|
||||
|
||||
if (singleImage) {
|
||||
// Combine all images into a single big image
|
||||
BufferedImage combined = new BufferedImage(images.get(0).getWidth(), images.get(0).getHeight() * pageCount, BufferedImage.TYPE_INT_RGB);
|
||||
@@ -134,44 +124,158 @@ public class PdfUtils {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
public static byte[] imageToPdf(MultipartFile[] files, boolean stretchToFit, boolean autoRotate, String colorType) throws IOException {
|
||||
try (PDDocument doc = new PDDocument()) {
|
||||
for (MultipartFile file : files) {
|
||||
String originalFilename = file.getOriginalFilename();
|
||||
if (originalFilename != null && (originalFilename.toLowerCase().endsWith(".tiff") || originalFilename.toLowerCase().endsWith(".tif")) ) {
|
||||
ImageReader reader = ImageIO.getImageReadersByFormatName("tiff").next();
|
||||
reader.setInput(ImageIO.createImageInputStream(file.getInputStream()));
|
||||
int numPages = reader.getNumImages(true);
|
||||
for (int i = 0; i < numPages; i++) {
|
||||
BufferedImage pageImage = reader.read(i);
|
||||
BufferedImage convertedImage = convertColorType(pageImage, colorType);
|
||||
PDImageXObject pdImage = LosslessFactory.createFromImage(doc, convertedImage);
|
||||
addImageToDocument(doc, pdImage, stretchToFit, autoRotate);
|
||||
}
|
||||
} else {
|
||||
File imageFile = Files.createTempFile("image", ".png").toFile();
|
||||
try (FileOutputStream fos = new FileOutputStream(imageFile); InputStream input = file.getInputStream()) {
|
||||
byte[] buffer = new byte[1024];
|
||||
int len;
|
||||
while ((len = input.read(buffer)) != -1) {
|
||||
fos.write(buffer, 0, len);
|
||||
}
|
||||
BufferedImage image = ImageIO.read(imageFile);
|
||||
BufferedImage convertedImage = convertColorType(image, colorType);
|
||||
PDImageXObject pdImage = LosslessFactory.createFromImage(doc, convertedImage);
|
||||
addImageToDocument(doc, pdImage, stretchToFit, autoRotate);
|
||||
} catch (IOException e) {
|
||||
logger.error("Error writing image to file: {}", imageFile.getAbsolutePath(), e);
|
||||
throw e;
|
||||
} finally {
|
||||
imageFile.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
||||
doc.save(byteArrayOutputStream);
|
||||
logger.info("PDF successfully saved to byte array");
|
||||
return byteArrayOutputStream.toByteArray();
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] overlayImage(byte[] pdfBytes, byte[] imageBytes, float x, float y) throws IOException {
|
||||
private static BufferedImage convertColorType(BufferedImage sourceImage, String colorType) {
|
||||
BufferedImage convertedImage;
|
||||
switch (colorType) {
|
||||
case "greyscale":
|
||||
convertedImage = new BufferedImage(sourceImage.getWidth(), sourceImage.getHeight(), BufferedImage.TYPE_BYTE_GRAY);
|
||||
convertedImage.getGraphics().drawImage(sourceImage, 0, 0, null);
|
||||
break;
|
||||
case "blackwhite":
|
||||
convertedImage = new BufferedImage(sourceImage.getWidth(), sourceImage.getHeight(), BufferedImage.TYPE_BYTE_BINARY);
|
||||
convertedImage.getGraphics().drawImage(sourceImage, 0, 0, null);
|
||||
break;
|
||||
default: // full color
|
||||
convertedImage = sourceImage;
|
||||
break;
|
||||
}
|
||||
return convertedImage;
|
||||
}
|
||||
|
||||
private static void addImageToDocument(PDDocument doc, PDImageXObject image, boolean stretchToFit, boolean autoRotate) throws IOException {
|
||||
boolean imageIsLandscape = image.getWidth() > image.getHeight();
|
||||
PDRectangle pageSize = PDRectangle.A4;
|
||||
if (autoRotate && imageIsLandscape) {
|
||||
pageSize = new PDRectangle(pageSize.getHeight(), pageSize.getWidth());
|
||||
}
|
||||
PDPage page = new PDPage(pageSize);
|
||||
doc.addPage(page);
|
||||
|
||||
try (PDDocument document = PDDocument.load(new ByteArrayInputStream(pdfBytes))) {
|
||||
// Get the first page of the PDF
|
||||
PDPage page = document.getPage(0);
|
||||
float pageWidth = page.getMediaBox().getWidth();
|
||||
float pageHeight = page.getMediaBox().getHeight();
|
||||
|
||||
try (PDPageContentStream contentStream = new PDPageContentStream(doc, page)) {
|
||||
if (stretchToFit) {
|
||||
contentStream.drawImage(image, 0, 0, pageWidth, pageHeight);
|
||||
} else {
|
||||
float imageAspectRatio = (float) image.getWidth() / (float) image.getHeight();
|
||||
float pageAspectRatio = pageWidth / pageHeight;
|
||||
|
||||
float scaleFactor = 1.0f;
|
||||
if (imageAspectRatio > pageAspectRatio) {
|
||||
scaleFactor = pageWidth / image.getWidth();
|
||||
} else {
|
||||
scaleFactor = pageHeight / image.getHeight();
|
||||
}
|
||||
|
||||
float xPos = (pageWidth - (image.getWidth() * scaleFactor)) / 2;
|
||||
float yPos = (pageHeight - (image.getHeight() * scaleFactor)) / 2;
|
||||
contentStream.drawImage(image, xPos, yPos, image.getWidth() * scaleFactor, image.getHeight() * scaleFactor);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
logger.error("Error adding image to PDF", e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
public static X509Certificate[] loadCertificateChainFromKeystore(InputStream keystoreInputStream, String keystorePassword) throws Exception {
|
||||
KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||
keystore.load(keystoreInputStream, keystorePassword.toCharArray());
|
||||
|
||||
String alias = keystore.aliases().nextElement();
|
||||
Certificate[] certChain = keystore.getCertificateChain(alias);
|
||||
X509Certificate[] x509CertChain = new X509Certificate[certChain.length];
|
||||
|
||||
for (int i = 0; i < certChain.length; i++) {
|
||||
x509CertChain[i] = (X509Certificate) certChain[i];
|
||||
}
|
||||
|
||||
return x509CertChain;
|
||||
}
|
||||
|
||||
public static KeyPair loadKeyPairFromKeystore(InputStream keystoreInputStream, String keystorePassword) throws Exception {
|
||||
KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||
keystore.load(keystoreInputStream, keystorePassword.toCharArray());
|
||||
|
||||
String alias = keystore.aliases().nextElement();
|
||||
PrivateKey privateKey = (PrivateKey) keystore.getKey(alias, keystorePassword.toCharArray());
|
||||
Certificate cert = keystore.getCertificate(alias);
|
||||
PublicKey publicKey = cert.getPublicKey();
|
||||
|
||||
return new KeyPair(publicKey, privateKey);
|
||||
}
|
||||
|
||||
public static byte[] overlayImage(byte[] pdfBytes, byte[] imageBytes, float x, float y, boolean everyPage) throws IOException {
|
||||
|
||||
PDDocument document = PDDocument.load(new ByteArrayInputStream(pdfBytes));
|
||||
|
||||
// Get the first page of the PDF
|
||||
int pages = document.getNumberOfPages();
|
||||
for (int i = 0; i < pages; i++) {
|
||||
PDPage page = document.getPage(i);
|
||||
try (PDPageContentStream contentStream = new PDPageContentStream(document, page, PDPageContentStream.AppendMode.APPEND, true)) {
|
||||
// Create an image object from the image bytes
|
||||
PDImageXObject image = PDImageXObject.createFromByteArray(document, imageBytes, "");
|
||||
// Draw the image onto the page at the specified x and y coordinates
|
||||
contentStream.drawImage(image, x, y);
|
||||
logger.info("Image successfully overlayed onto PDF");
|
||||
if (!everyPage && i == 0) {
|
||||
break;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// Log an error message if there is an issue overlaying the image onto the PDF
|
||||
logger.error("Error overlaying image onto PDF", e);
|
||||
throw e;
|
||||
}
|
||||
// Create a ByteArrayOutputStream to save the PDF to
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
document.save(baos);
|
||||
logger.info("PDF successfully saved to byte array");
|
||||
return baos.toByteArray();
|
||||
} catch (IOException e) {
|
||||
// Log an error message if there is an issue overlaying the image onto the PDF
|
||||
logger.error("Error overlaying image onto PDF", e);
|
||||
throw e;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public static ResponseEntity<byte[]> iTextDocToWebResponse(Document document, String docName) throws IOException, DocumentException {
|
||||
// Close the document
|
||||
document.close();
|
||||
|
||||
// Open Byte Array and save document to it
|
||||
// Create a ByteArrayOutputStream to save the PDF to
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
PdfWriter.getInstance(document, baos).close();
|
||||
|
||||
|
||||
return PdfUtils.boasToWebResponse(baos, docName);
|
||||
document.save(baos);
|
||||
logger.info("PDF successfully saved to byte array");
|
||||
return baos.toByteArray();
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static ResponseEntity<byte[]> pdfDocToWebResponse(PDDocument document, String docName) throws IOException {
|
||||
|
||||
@@ -183,19 +287,4 @@ public class PdfUtils {
|
||||
|
||||
return PdfUtils.boasToWebResponse(baos, docName);
|
||||
}
|
||||
|
||||
public static ResponseEntity<byte[]> boasToWebResponse(ByteArrayOutputStream baos, String docName) throws IOException {
|
||||
return PdfUtils.bytesToWebResponse(baos.toByteArray(), docName);
|
||||
|
||||
}
|
||||
|
||||
public static ResponseEntity<byte[]> bytesToWebResponse(byte[] bytes, String docName) throws IOException {
|
||||
|
||||
// Return the PDF as a response
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_PDF);
|
||||
headers.setContentLength(bytes.length);
|
||||
headers.setContentDispositionFormData("attachment", docName);
|
||||
return new ResponseEntity<>(bytes, headers, HttpStatus.OK);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,69 +1,102 @@
|
||||
package stirling.software.SPDF.utils;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.io.BufferedReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.Semaphore;
|
||||
|
||||
public class ProcessExecutor {
|
||||
public static int runCommandWithOutputHandling(List<String> command) throws IOException, InterruptedException {
|
||||
ProcessBuilder processBuilder = new ProcessBuilder(command);
|
||||
Process process = processBuilder.start();
|
||||
|
||||
// Read the error stream and standard output stream concurrently
|
||||
List<String> errorLines = new ArrayList<>();
|
||||
List<String> outputLines = new ArrayList<>();
|
||||
public enum Processes {
|
||||
LIBRE_OFFICE, OCR_MY_PDF, PYTHON_OPENCV, GHOSTSCRIPT
|
||||
}
|
||||
|
||||
Thread errorReaderThread = new Thread(() -> {
|
||||
try (BufferedReader errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream(), StandardCharsets.UTF_8))) {
|
||||
String line;
|
||||
while ((line = errorReader.readLine()) != null) {
|
||||
errorLines.add(line);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
private static final Map<Processes, ProcessExecutor> instances = new ConcurrentHashMap<>();
|
||||
|
||||
Thread outputReaderThread = new Thread(() -> {
|
||||
try (BufferedReader outputReader = new BufferedReader(new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8))) {
|
||||
String line;
|
||||
while ((line = outputReader.readLine()) != null) {
|
||||
outputLines.add(line);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
public static ProcessExecutor getInstance(Processes processType) {
|
||||
return instances.computeIfAbsent(processType, key -> {
|
||||
int semaphoreLimit = switch (key) {
|
||||
case LIBRE_OFFICE -> 1;
|
||||
case OCR_MY_PDF -> 2;
|
||||
case PYTHON_OPENCV -> 8;
|
||||
case GHOSTSCRIPT -> 16;
|
||||
};
|
||||
return new ProcessExecutor(semaphoreLimit);
|
||||
});
|
||||
}
|
||||
|
||||
errorReaderThread.start();
|
||||
outputReaderThread.start();
|
||||
private final Semaphore semaphore;
|
||||
|
||||
// Wait for the conversion process to complete
|
||||
int exitCode = process.waitFor();
|
||||
private ProcessExecutor(int semaphoreLimit) {
|
||||
this.semaphore = new Semaphore(semaphoreLimit);
|
||||
}
|
||||
|
||||
// Wait for the reader threads to finish
|
||||
errorReaderThread.join();
|
||||
outputReaderThread.join();
|
||||
public int runCommandWithOutputHandling(List<String> command) throws IOException, InterruptedException {
|
||||
int exitCode = 1;
|
||||
semaphore.acquire();
|
||||
try {
|
||||
|
||||
if (outputLines.size() > 0) {
|
||||
String outputMessage = String.join("\n", outputLines);
|
||||
System.out.println("Command output:\n" + outputMessage);
|
||||
}
|
||||
System.out.print("Running command: " + String.join(" ", command));
|
||||
ProcessBuilder processBuilder = new ProcessBuilder(command);
|
||||
Process process = processBuilder.start();
|
||||
|
||||
if (errorLines.size() > 0) {
|
||||
String errorMessage = String.join("\n", errorLines);
|
||||
System.out.println("Command error output:\n" + errorMessage);
|
||||
if (exitCode != 0) {
|
||||
throw new IOException("Command process failed with exit code " + exitCode + ". Error message: " + errorMessage);
|
||||
}
|
||||
}
|
||||
// Read the error stream and standard output stream concurrently
|
||||
List<String> errorLines = new ArrayList<>();
|
||||
List<String> outputLines = new ArrayList<>();
|
||||
|
||||
Thread errorReaderThread = new Thread(() -> {
|
||||
try (BufferedReader errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream(), StandardCharsets.UTF_8))) {
|
||||
String line;
|
||||
while ((line = errorReader.readLine()) != null) {
|
||||
errorLines.add(line);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
|
||||
Thread outputReaderThread = new Thread(() -> {
|
||||
try (BufferedReader outputReader = new BufferedReader(new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8))) {
|
||||
String line;
|
||||
while ((line = outputReader.readLine()) != null) {
|
||||
outputLines.add(line);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
|
||||
errorReaderThread.start();
|
||||
outputReaderThread.start();
|
||||
|
||||
// Wait for the conversion process to complete
|
||||
exitCode = process.waitFor();
|
||||
|
||||
// Wait for the reader threads to finish
|
||||
errorReaderThread.join();
|
||||
outputReaderThread.join();
|
||||
|
||||
if (outputLines.size() > 0) {
|
||||
String outputMessage = String.join("\n", outputLines);
|
||||
System.out.println("Command output:\n" + outputMessage);
|
||||
}
|
||||
|
||||
if (errorLines.size() > 0) {
|
||||
String errorMessage = String.join("\n", errorLines);
|
||||
System.out.println("Command error output:\n" + errorMessage);
|
||||
if (exitCode != 0) {
|
||||
throw new IOException("Command process failed with exit code " + exitCode + ". Error message: " + errorMessage);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
semaphore.release();
|
||||
}
|
||||
return exitCode;
|
||||
}
|
||||
|
||||
return exitCode;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
package stirling.software.SPDF.utils;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.apache.pdfbox.contentstream.PDFStreamEngine;
|
||||
import org.apache.pdfbox.contentstream.operator.Operator;
|
||||
import org.apache.pdfbox.cos.COSArray;
|
||||
import org.apache.pdfbox.cos.COSBase;
|
||||
import org.apache.pdfbox.cos.COSString;
|
||||
|
||||
public class WatermarkRemover extends PDFStreamEngine {
|
||||
|
||||
private final String watermarkText;
|
||||
private final Pattern pattern;
|
||||
|
||||
public WatermarkRemover(String watermarkText) {
|
||||
this.watermarkText = watermarkText;
|
||||
this.pattern = Pattern.compile(Pattern.quote(watermarkText));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processOperator(Operator operator, List<COSBase> operands) throws IOException {
|
||||
String operation = operator.getName();
|
||||
|
||||
boolean processText = false;
|
||||
if ("Tj".equals(operation) || "TJ".equals(operation) || "'".equals(operation) || "\"".equals(operation)) {
|
||||
processText = true;
|
||||
}
|
||||
|
||||
if (processText) {
|
||||
for(int j = 0 ; j < operands.size(); ++j) {
|
||||
COSBase operand = operands.get(j);
|
||||
if (operand instanceof COSString) {
|
||||
COSString cosString = (COSString) operand;
|
||||
String string = cosString.getString();
|
||||
Matcher matcher = pattern.matcher(string);
|
||||
if (matcher.find()) {
|
||||
string = matcher.replaceAll("");
|
||||
cosString.setValue(string.getBytes());
|
||||
}
|
||||
} else if (operand instanceof COSArray) {
|
||||
COSArray array = (COSArray) operand;
|
||||
for (int i = 0; i < array.size(); i++) {
|
||||
COSBase item = array.get(i);
|
||||
if (item instanceof COSString) {
|
||||
COSString cosString = (COSString) item;
|
||||
String string = cosString.getString();
|
||||
Matcher matcher = pattern.matcher(string);
|
||||
if (matcher.find()) {
|
||||
System.out.println("operation =" + operation);
|
||||
System.out.println("1 =" + string);
|
||||
string = matcher.replaceAll("");
|
||||
cosString.setValue(string.getBytes());
|
||||
array.set(i, cosString);
|
||||
operands.set(j, array);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
super.processOperator(operator, operands);
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
spring.http.multipart.max-file-size=1GB
|
||||
spring.http.multipart.max-request-size=1GB
|
||||
spring.http.multipart.max-file-size=2GB
|
||||
spring.http.multipart.max-request-size=2GB
|
||||
|
||||
multipart.enabled=true
|
||||
multipart.max-file-size=1000MB
|
||||
multipart.max-request-size=1000MB
|
||||
multipart.max-file-size=2000MB
|
||||
multipart.max-request-size=2000MB
|
||||
|
||||
spring.servlet.multipart.max-file-size=1000MB
|
||||
spring.servlet.multipart.max-request-size=1000MB
|
||||
spring.servlet.multipart.max-file-size=2000MB
|
||||
spring.servlet.multipart.max-request-size=2000MB
|
||||
|
||||
server.forward-headers-strategy=NATIVE
|
||||
|
||||
@@ -16,4 +16,10 @@ server.error.include-stacktrace=always
|
||||
server.error.include-exception=true
|
||||
server.error.include-message=always
|
||||
|
||||
server.servlet.session.tracking-modes=cookie
|
||||
server.servlet.session.tracking-modes=cookie
|
||||
server.servlet.context-path=${APP_ROOT_PATH:/}
|
||||
|
||||
spring.devtools.restart.enabled=true
|
||||
spring.devtools.livereload.enabled=true
|
||||
|
||||
spring.thymeleaf.encoding=UTF-8
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
###########
|
||||
# Generic #
|
||||
###########
|
||||
# the direction that the language is written (ltr = left to right, rtl = right to left)
|
||||
# the direction that the language is written (ltr=left to right, rtl=right to left)
|
||||
language.direction=rtl
|
||||
|
||||
pdfPrompt=اختر PDF
|
||||
@@ -21,6 +21,9 @@ false=\u062E\u0637\u0623
|
||||
unknown=\u063A\u064A\u0631 \u0645\u0639\u0631\u0648\u0641
|
||||
save=\u062D\u0641\u0638
|
||||
close=\u0625\u063A\u0644\u0627\u0642
|
||||
filesSelected = الملفات المحددة
|
||||
noFavourites = لم تتم إضافة أي مفضلات
|
||||
bored = الانتظار بالملل؟
|
||||
|
||||
#############
|
||||
# HOME-PAGE #
|
||||
@@ -31,14 +34,18 @@ navbar.convert=تحويل
|
||||
navbar.security=الأمان
|
||||
navbar.other=أخرى
|
||||
navbar.darkmode=الوضع الداكن
|
||||
navbar.pageOps=عمليات الصفحة
|
||||
|
||||
home.merge.title=دمج ملفات PDF
|
||||
home.multiTool.title=أداة متعددة PDF
|
||||
home.multiTool.desc=دمج الصفحات وتدويرها وإعادة ترتيبها وإزالتها
|
||||
|
||||
home.merge.title=دمج ملفات
|
||||
home.merge.desc=دمج ملفات PDF متعددة في ملف واحد بسهولة.
|
||||
|
||||
home.split.title=انقسام ملفات PDF
|
||||
home.split.title=انقسام ملفات
|
||||
home.split.desc=تقسيم ملفات PDF إلى مستندات متعددة
|
||||
|
||||
home.rotate.title=تدوير ملفات PDF
|
||||
home.rotate.title=تدوير ملفات
|
||||
home.rotate.desc=قم بتدوير ملفات PDF الخاصة بك بسهولة.
|
||||
|
||||
home.imageToPdf.title=صورة إلى PDF
|
||||
@@ -47,7 +54,7 @@ home.imageToPdf.desc=تحويل الصور (PNG ، JPEG ، GIF) إلى PDF.
|
||||
home.pdfToImage.title=تحويل PDF إلى صورة
|
||||
home.pdfToImage.desc=تحويل ملف PDF إلى صورة. (PNG ، JPEG ، GIF)
|
||||
|
||||
home.pdfOrganiser.title=منظم PDF
|
||||
home.pdfOrganiser.title=منظم
|
||||
home.pdfOrganiser.desc=إزالة / إعادة ترتيب الصفحات بأي ترتيب
|
||||
|
||||
home.addImage.title=إضافة صورة إلى ملف PDF
|
||||
@@ -56,8 +63,8 @@ home.addImage.desc=إضافة صورة إلى موقع معين في PDF (الع
|
||||
home.watermark.title=إضافة علامة مائية
|
||||
home.watermark.desc=أضف علامة مائية مخصصة إلى مستند PDF الخاص بك.
|
||||
|
||||
home.remove-watermark.title = \u0625\u0632\u0627\u0644\u0629 \u0627\u0644\u0639\u0644\u0627\u0645\u0629 \u0627\u0644\u0645\u0627\u0626\u064A\u0629
|
||||
home.remove-watermark.desc = \u0625\u0632\u0627\u0644\u0629 \u0627\u0644\u0639\u0644\u0627\u0645\u0627\u062A \u0627\u0644\u0645\u0627\u0626\u064A\u0629 \u0645\u0646 \u0645\u0633\u062A\u0646\u062F PDF \u0627\u0644\u062E\u0627\u0635 \u0628\u0643.
|
||||
home.remove-watermark.title=\u0625\u0632\u0627\u0644\u0629 \u0627\u0644\u0639\u0644\u0627\u0645\u0629 \u0627\u0644\u0645\u0627\u0626\u064A\u0629
|
||||
home.remove-watermark.desc=\u0625\u0632\u0627\u0644\u0629 \u0627\u0644\u0639\u0644\u0627\u0645\u0627\u062A \u0627\u0644\u0645\u0627\u0626\u064A\u0629 \u0645\u0646 \u0645\u0633\u062A\u0646\u062F PDF \u0627\u0644\u062E\u0627\u0635 \u0628\u0643.
|
||||
|
||||
home.permissions.title=تغيير الأذونات
|
||||
home.permissions.desc=قم بتغيير أذونات مستند PDF الخاص بك
|
||||
@@ -71,40 +78,135 @@ home.addPassword.desc=تشفير مستند PDF الخاص بك بكلمة مر
|
||||
home.removePassword.title=إزالة كلمة المرور
|
||||
home.removePassword.desc=إزالة الحماية بكلمة مرور من مستند PDF الخاص بك.
|
||||
|
||||
home.compressPdfs.title=ضغط ملفات PDF
|
||||
home.compressPdfs.title=ضغط ملفات
|
||||
home.compressPdfs.desc=ضغط ملفات PDF لتقليل حجم الملف.
|
||||
|
||||
home.changeMetadata.title = \u062A\u063A\u064A\u064A\u0631 \u0627\u0644\u0628\u064A\u0627\u0646\u0627\u062A \u0627\u0644\u0648\u0635\u0641\u064A\u0629
|
||||
home.changeMetadata.desc = \u062A\u063A\u064A\u064A\u0631 / \u0625\u0632\u0627\u0644\u0629 / \u0625\u0636\u0627\u0641\u0629 \u0628\u064A\u0627\u0646\u0627\u062A \u0623\u0648\u0644\u064A\u0629 \u0645\u0646 \u0645\u0633\u062A\u0646\u062F PDF
|
||||
home.changeMetadata.title=\u062A\u063A\u064A\u064A\u0631 \u0627\u0644\u0628\u064A\u0627\u0646\u0627\u062A \u0627\u0644\u0648\u0635\u0641\u064A\u0629
|
||||
home.changeMetadata.desc=\u062A\u063A\u064A\u064A\u0631 / \u0625\u0632\u0627\u0644\u0629 / \u0625\u0636\u0627\u0641\u0629 \u0628\u064A\u0627\u0646\u0627\u062A \u0623\u0648\u0644\u064A\u0629 \u0645\u0646 \u0645\u0633\u062A\u0646\u062F PDF
|
||||
|
||||
|
||||
home.fileToPDF.title=\u062A\u062D\u0648\u064A\u0644 \u0627\u0644\u0645\u0644\u0641 \u0625\u0644\u0649 PDF
|
||||
home.fileToPDF.desc=\u062A\u062D\u0648\u064A\u0644 \u0623\u064A \u0645\u0644\u0641 \u062A\u0642\u0631\u064A\u0628\u0627 \u0625\u0644\u0649 PDF (DOCX \u0648PNG \u0648XLS \u0648PPT \u0648TXT \u0648\u0627\u0644\u0645\u0632\u064A\u062F)
|
||||
|
||||
home.ocr.title=\u062A\u0634\u063A\u064A\u0644 OCR \u0639\u0644\u0649 PDF
|
||||
home.ocr.desc=\u0645\u0633\u062D \u0648\u0627\u0643\u062A\u0634\u0627\u0641 \u0627\u0644\u0646\u0635 \u0645\u0646 \u0627\u0644\u0635\u0648\u0631 \u062F\u0627\u062E\u0644 PDF \u0648\u0625\u0639\u0627\u062F\u0629 \u0625\u0636\u0627\u0641\u062A\u0647 \u0643\u0646\u0635.
|
||||
home.ocr.title=\u062A\u0634\u063A\u064A\u0644 OCR \u0639\u0644\u0649 PDF \u0648 / \u0623\u0648 \u0645\u0633\u062D \u0636\u0648\u0626\u064A
|
||||
home.ocr.desc=\u064A\u0642\u0648\u0645 \u0628\u0631\u0646\u0627\u0645\u062C \u0627\u0644\u062A\u0646\u0638\u064A\u0641 \u0628\u0645\u0633\u062D \u0648\u0627\u0643\u062A\u0634\u0627\u0641 \u0627\u0644\u0646\u0635 \u0645\u0646 \u0627\u0644\u0635\u0648\u0631 \u062F\u0627\u062E\u0644 \u0645\u0644\u0641 PDF \u0648\u064A\u0639\u064A\u062F \u0625\u0636\u0627\u0641\u062A\u0647 \u0643\u0646\u0635
|
||||
|
||||
home.extractImages.title=\u0627\u0633\u062A\u062E\u0631\u0627\u062C \u0627\u0644\u0635\u0648\u0631
|
||||
home.extractImages.desc=\u064A\u0633\u062A\u062E\u0631\u062C \u062C\u0645\u064A\u0639 \u0627\u0644\u0635\u0648\u0631 \u0645\u0646 \u0645\u0644\u0641 PDF \u0648\u064A\u062D\u0641\u0638\u0647\u0627 \u0641\u064A \u0627\u0644\u0631\u0645\u0632 \u0627\u0644\u0628\u0631\u064A\u062F\u064A
|
||||
|
||||
navbar.settings = \u0625\u0639\u062F\u0627\u062F\u0627\u062A
|
||||
home.pdfToPDFA.title=\u062A\u062D\u0648\u064A\u0644 \u0645\u0644\u0641\u0627\u062A PDF \u0625\u0644\u0649 PDF / A
|
||||
home.pdfToPDFA.desc=\u062A\u062D\u0648\u064A\u0644 PDF \u0625\u0644\u0649 PDF / A \u0644\u0644\u062A\u062E\u0632\u064A\u0646 \u0637\u0648\u064A\u0644 \u0627\u0644\u0645\u062F\u0649
|
||||
|
||||
|
||||
home.PDFToWord.title=تحويل PDF إلى Word
|
||||
home.PDFToWord.desc=تحويل PDF إلى تنسيقات Word (DOC و DOCX و ODT)
|
||||
|
||||
home.PDFToPresentation.title=PDF للعرض التقديمي
|
||||
home.PDFToPresentation.desc=تحويل PDF إلى تنسيقات عرض تقديمي (PPT و PPTX و ODP)
|
||||
|
||||
home.PDFToText.title=تحويل PDF إلى نص / RTF
|
||||
home.PDFToText.desc=تحويل PDF إلى تنسيق نص أو RTF
|
||||
|
||||
home.PDFToHTML.title=تحويل PDF إلى HTML
|
||||
home.PDFToHTML.desc=تحويل PDF إلى تنسيق HTML
|
||||
|
||||
home.PDFToXML.title=تحويل PDF إلى XML
|
||||
home.PDFToXML.desc=تحويل PDF إلى تنسيق XML
|
||||
|
||||
|
||||
home.ScannerImageSplit.title=كشف / انقسام الصور الممسوحة ضوئيًا
|
||||
home.ScannerImageSplit.desc=تقسيم عدة صور من داخل صورة / ملف PDF
|
||||
|
||||
home.sign.title = تسجيل الدخول
|
||||
home.sign.desc = إضافة التوقيع إلى PDF عن طريق الرسم أو النص أو الصورة
|
||||
|
||||
home.flatten.title = تسطيح
|
||||
home.flatten.desc = قم بإزالة كافة العناصر والنماذج التفاعلية من ملف PDF
|
||||
|
||||
home.repair.title = إصلاح
|
||||
home.repair.desc = يحاول إصلاح ملف PDF تالف / معطل
|
||||
|
||||
home.removeBlanks.title = إزالة الصفحات الفارغة
|
||||
home.removeBlanks.desc = يكتشف ويزيل الصفحات الفارغة من المستند
|
||||
|
||||
home.compare.title = قارن
|
||||
home.compare.desc = يقارن ويظهر الاختلافات بين 2 من مستندات PDF
|
||||
|
||||
downloadPdf = تنزيل PDF
|
||||
text=نص
|
||||
font=الخط
|
||||
|
||||
removeBlanks.title = إزالة الفراغات
|
||||
removeBlanks.header = إزالة الصفحات الفارغة
|
||||
removeBlanks.threshold = العتبة:
|
||||
removeBlanks.thresholdDesc = الحد الفاصل لتحديد مدى بياض البكسل الأبيض
|
||||
removeBlanks.whitePercent = نسبة الأبيض (٪):
|
||||
removeBlanks.whitePercentDesc = النسبة المئوية للصفحة التي يجب أن تكون بيضاء لتتم إزالتها
|
||||
removeBlanks.submit = إزالة الفراغات
|
||||
|
||||
compare.title=يقارن
|
||||
compare.header=قارن ملفات PDF
|
||||
compare.document.1=المستند 1
|
||||
compare.document.2=المستند 2
|
||||
compare.submit=يقارن
|
||||
|
||||
sign.title = تسجيل الدخول
|
||||
sign.header = توقيع ملفات PDF
|
||||
sign.upload = تحميل الصورة
|
||||
sign.draw = رسم التوقيع
|
||||
Sign.text = إدخال النص
|
||||
|
||||
sign.clear=واضح
|
||||
sign.add = إضافة
|
||||
|
||||
repair.title = إصلاح
|
||||
repair.header = إصلاح ملفات PDF
|
||||
repair.submit = الإصلاح
|
||||
|
||||
flatten.title = تسطيح
|
||||
flatten.header = تسوية ملفات PDF
|
||||
flatten.submit = تسطيح
|
||||
|
||||
ScannerImageSplit.selectText.1=عتبة الزاوية:
|
||||
ScannerImageSplit.selectText.2=تعيين الحد الأدنى للزاوية المطلقة المطلوبة لتدوير الصورة (افتراضي: 10).
|
||||
ScannerImageSplit.selectText.3=التسامح:
|
||||
ScannerImageSplit.selectText.4=يحدد نطاق تباين اللون حول لون الخلفية المقدر (الافتراضي: 30).
|
||||
ScannerImageSplit.selectText.5=أدنى مساحة:
|
||||
ScannerImageSplit.selectText.6=تعيين الحد الأدنى لمنطقة الصورة (الافتراضي: 10000).
|
||||
ScannerImageSplit.selectText.7=الحد الأدنى لمنطقة المحيط:
|
||||
ScannerImageSplit.selectText.8=تعيين الحد الأدنى لمنطقة المحيط للصورة
|
||||
ScannerImageSplit.selectText.9=حجم الحدود:
|
||||
ScannerImageSplit.selectText.10=يضبط حجم الحدود المضافة والمزالة لمنع الحدود البيضاء في الإخراج (الافتراضي: 1).
|
||||
|
||||
|
||||
navbar.settings=\u0625\u0639\u062F\u0627\u062F\u0627\u062A
|
||||
settings.title=\u0627\u0644\u0625\u0639\u062F\u0627\u062F\u0627\u062A
|
||||
settings.update = \u0627\u0644\u062A\u062D\u062F\u064A\u062B \u0645\u062A\u0627\u062D
|
||||
settings.appVersion = \u0625\u0635\u062F\u0627\u0631 \u0627\u0644\u062A\u0637\u0628\u064A\u0642:
|
||||
settings.update=\u0627\u0644\u062A\u062D\u062F\u064A\u062B \u0645\u062A\u0627\u062D
|
||||
settings.appVersion=\u0625\u0635\u062F\u0627\u0631 \u0627\u0644\u062A\u0637\u0628\u064A\u0642:
|
||||
settings.downloadOption.title=\u062A\u062D\u062F\u064A\u062F \u062E\u064A\u0627\u0631 \u0627\u0644\u062A\u0646\u0632\u064A\u0644 (\u0644\u0644\u062A\u0646\u0632\u064A\u0644\u0627\u062A \u0630\u0627\u062A \u0627\u0644\u0645\u0644\u0641 \u0627\u0644\u0648\u0627\u062D\u062F \u063A\u064A\u0631 \u0627\u0644\u0645\u0636\u063A\u0648\u0637):
|
||||
settings.downloadOption.1=\u0641\u062A\u062D \u0641\u064A \u0646\u0641\u0633 \u0627\u0644\u0646\u0627\u0641\u0630\u0629
|
||||
settings.downloadOption.2=\u0641\u062A\u062D \u0641\u064A \u0646\u0627\u0641\u0630\u0629 \u062C\u062F\u064A\u062F\u0629
|
||||
settings.downloadOption.3=\u062A\u0646\u0632\u064A\u0644 \u0627\u0644\u0645\u0644\u0641
|
||||
settings.zip=\u0645\u0644\u0641\u0627\u062A \u0627\u0644\u062A\u0646\u0632\u064A\u0644 \u0627\u0644\u0645\u062A\u0639\u062F\u062F \u0627\u0644\u0645\u0636\u063A\u0648\u0637\u0629
|
||||
settings.zipThreshold=\u0645\u0644\u0641\u0627\u062A \u0645\u0636\u063A\u0648\u0637\u0629 \u0639\u0646\u062F \u062A\u062C\u0627\u0648\u0632 \u0639\u062F\u062F \u0627\u0644\u0645\u0644\u0641\u0627\u062A \u0627\u0644\u062A\u064A \u062A\u0645 \u062A\u0646\u0632\u064A\u0644\u0647\u0627
|
||||
|
||||
#OCR
|
||||
OCR.title = OCR
|
||||
ocr.header=OCR (\u0627\u0644\u062A\u0639\u0631\u0641 \u0627\u0644\u0636\u0648\u0626\u064A \u0639\u0644\u0649 \u0627\u0644\u0623\u062D\u0631\u0641)
|
||||
ocr.selectText.1=\u062A\u062D\u062F\u064A\u062F \u0627\u0644\u0644\u063A\u0627\u062A \u0627\u0644\u062A\u064A \u0633\u064A\u062A\u0645 \u0627\u0643\u062A\u0634\u0627\u0641\u0647\u0627 \u062F\u0627\u062E\u0644 PDF (\u0627\u0644\u0644\u063A\u0627\u062A \u0627\u0644\u0645\u062F\u0631\u062C\u0629 \u0647\u064A \u062A\u0644\u0643 \u0627\u0644\u0645\u0643\u062A\u0634\u0641\u0629 \u062D\u0627\u0644\u064A\u0627):
|
||||
ocr.selectText.2=\u0625\u0646\u062A\u0627\u062C \u0645\u0644\u0641 \u0646\u0635\u064A \u064A\u062D\u062A\u0648\u064A \u0639\u0644\u0649 \u0646\u0635 OCR \u062C\u0646\u0628\u0627 \u0625\u0644\u0649 \u062C\u0646\u0628 \u0645\u0639 \u0645\u0644\u0641 PDF OCR'ed
|
||||
ocr.title=\u0627\u0644\u062A\u0639\u0631\u0641 \u0627\u0644\u0636\u0648\u0626\u064A \u0639\u0644\u0649 \u0627\u0644\u062D\u0631\u0648\u0641 / \u062A\u0646\u0638\u064A\u0641 \u0627\u0644\u0645\u0633\u062D \u0627\u0644\u0636\u0648\u0626\u064A
|
||||
ocr.header=\u0645\u0633\u062D \u0627\u0644\u0645\u0633\u062D \u0627\u0644\u0636\u0648\u0626\u064A / \u0627\u0644\u062A\u0639\u0631\u0641 \u0627\u0644\u0636\u0648\u0626\u064A \u0639\u0644\u0649 \u0627\u0644\u062D\u0631\u0648\u0641 (\u0627\u0644\u062A\u0639\u0631\u0641 \u0627\u0644\u0636\u0648\u0626\u064A \u0639\u0644\u0649 \u0627\u0644\u062D\u0631\u0648\u0641)
|
||||
ocr.selectText.1=\u062D\u062F\u062F \u0627\u0644\u0644\u063A\u0627\u062A \u0627\u0644\u062A\u064A \u0633\u064A\u062A\u0645 \u0627\u0643\u062A\u0634\u0627\u0641\u0647\u0627 \u062F\u0627\u062E\u0644 \u0645\u0644\u0641 PDF (\u0627\u0644\u0644\u063A\u0627\u062A \u0627\u0644\u0645\u062F\u0631\u062C\u0629 \u0647\u064A \u062A\u0644\u0643 \u0627\u0644\u062A\u064A \u062A\u0645 \u0627\u0643\u062A\u0634\u0627\u0641\u0647\u0627 \u062D\u0627\u0644\u064A\u064B\u0627):
|
||||
ocr.selectText.2=\u0625\u0646\u062A\u0627\u062C \u0645\u0644\u0641 \u0646\u0635\u064A \u064A\u062D\u062A\u0648\u064A \u0639\u0644\u0649 \u0646\u0635 OCR \u0628\u062C\u0627\u0646\u0628 \u0645\u0644\u0641 PDF \u0627\u0644\u0630\u064A \u062A\u0645 \u0625\u0639\u062F\u0627\u062F\u0647 \u0628\u0648\u0627\u0633\u0637\u0629 OCR
|
||||
ocr.selectText.3=\u062A\u0645 \u0645\u0633\u062D \u0627\u0644\u0635\u0641\u062D\u0627\u062A \u0627\u0644\u0635\u062D\u064A\u062D\u0629 \u0636\u0648\u0626\u064A\u064B\u0627 \u0628\u0632\u0627\u0648\u064A\u0629 \u0645\u0646\u062D\u0631\u0641\u0629 \u0639\u0646 \u0637\u0631\u064A\u0642 \u062A\u062F\u0648\u064A\u0631\u0647\u0627 \u0645\u0631\u0629 \u0623\u062E\u0631\u0649 \u0641\u064A \u0645\u0643\u0627\u0646\u0647\u0627
|
||||
ocr.selectText.4=\u0635\u0641\u062D\u0629 \u0646\u0638\u064A\u0641\u0629 \u0644\u0630\u0644\u0643 \u0645\u0646 \u063A\u064A\u0631 \u0627\u0644\u0645\u062D\u062A\u0645\u0644 \u0623\u0646 \u064A\u062C\u062F OCR \u0646\u0635\u064B\u0627 \u0641\u064A \u0636\u0648\u0636\u0627\u0621 \u0627\u0644\u062E\u0644\u0641\u064A\u0629. (\u0644\u0627 \u064A\u0648\u062C\u062F \u062A\u063A\u064A\u064A\u0631 \u0641\u064A \u0627\u0644\u0625\u062E\u0631\u0627\u062C)
|
||||
ocr.selectText.5=\u0635\u0641\u062D\u0629 \u0646\u0638\u064A\u0641\u0629 \u060C \u0644\u0630\u0644\u0643 \u0645\u0646 \u063A\u064A\u0631 \u0627\u0644\u0645\u062D\u062A\u0645\u0644 \u0623\u0646 \u064A\u062C\u062F OCR \u0646\u0635\u064B\u0627 \u0641\u064A \u0636\u0648\u0636\u0627\u0621 \u0627\u0644\u062E\u0644\u0641\u064A\u0629 \u060C \u0648\u064A\u062D\u0627\u0641\u0638 \u0639\u0644\u0649 \u0627\u0644\u062A\u0646\u0638\u064A\u0641 \u0641\u064A \u0627\u0644\u0625\u062E\u0631\u0627\u062C.
|
||||
ocr.selectText.6=\u064A\u062A\u062C\u0627\u0647\u0644 \u0627\u0644\u0635\u0641\u062D\u0627\u062A \u0627\u0644\u062A\u064A \u062A\u062D\u062A\u0648\u064A \u0639\u0644\u0649 \u0646\u0635 \u062A\u0641\u0627\u0639\u0644\u064A \u060C \u0641\u0642\u0637 \u0635\u0641\u062D\u0627\u062A OCRs \u0627\u0644\u062A\u064A \u0647\u064A \u0635\u0648\u0631
|
||||
ocr.selectText.7=\u0641\u0631\u0636 \u0627\u0644\u062A\u0639\u0631\u0641 \u0627\u0644\u0636\u0648\u0626\u064A \u0639\u0644\u0649 \u0627\u0644\u062D\u0631\u0648\u0641 \u060C \u0633\u064A\u0624\u062F\u064A \u0627\u0644\u062A\u0639\u0631\u0641 \u0627\u0644\u0636\u0648\u0626\u064A \u0639\u0644\u0649 \u0627\u0644\u062D\u0631\u0648\u0641 \u0639\u0644\u0649 \u0643\u0644 \u0635\u0641\u062D\u0629 \u0625\u0644\u0649 \u0625\u0632\u0627\u0644\u0629 \u062C\u0645\u064A\u0639 \u0639\u0646\u0627\u0635\u0631 \u0627\u0644\u0646\u0635 \u0627\u0644\u0623\u0635\u0644\u064A
|
||||
ocr.selectText.8=\u0639\u0627\u062F\u064A (\u062E\u0637\u0623 \u0625\u0630\u0627 \u0643\u0627\u0646 PDF \u064A\u062D\u062A\u0648\u064A \u0639\u0644\u0649 \u0646\u0635)
|
||||
ocr.selectText.9=\u0625\u0639\u062F\u0627\u062F\u0627\u062A \u0625\u0636\u0627\u0641\u064A\u0629
|
||||
ocr.selectText.10=\u0648\u0636\u0639 \u0627\u0644\u062A\u0639\u0631\u0641 \u0627\u0644\u0636\u0648\u0626\u064A \u0639\u0644\u0649 \u0627\u0644\u062D\u0631\u0648\u0641
|
||||
ocr.selectText.11 = إزالة الصور بعد التعرف الضوئي على الحروف (يزيل كل الصور ، يكون مفيدًا فقط إذا كان جزءًا من خطوة التحويل)
|
||||
ocr.selectText.12 = نوع العرض (متقدم)
|
||||
ocr.help=\u064A\u0631\u062C\u0649 \u0642\u0631\u0627\u0621\u0629 \u0647\u0630\u0647 \u0627\u0644\u0648\u062B\u0627\u0626\u0642 \u062D\u0648\u0644 \u0643\u064A\u0641\u064A\u0629 \u0627\u0633\u062A\u062E\u062F\u0627\u0645 \u0647\u0630\u0627 \u0644\u0644\u063A\u0627\u062A \u0623\u062E\u0631\u0649 \u0648 / \u0623\u0648 \u0627\u0644\u0627\u0633\u062A\u062E\u062F\u0627\u0645 \u0644\u064A\u0633 \u0641\u064A \u0639\u0627\u0645\u0644 \u0627\u0644\u0625\u0631\u0633\u0627\u0621
|
||||
ocr.credit=\u062A\u0633\u062A\u062E\u062F\u0645 \u0647\u0630\u0647 \u0627\u0644\u062E\u062F\u0645\u0629 OCRmyPDF \u0648 Tesseract \u0644 OCR.
|
||||
ocr.submit = \u0645\u0639\u0627\u0644\u062C\u0629 PDF \u0628\u0627\u0633\u062A\u062E\u062F\u0627\u0645 OCR
|
||||
ocr.submit=\u0645\u0639\u0627\u0644\u062C\u0629 PDF \u0628\u0627\u0633\u062A\u062E\u062F\u0627\u0645 OCR
|
||||
|
||||
|
||||
extractImages.title=\u0627\u0633\u062A\u062E\u0631\u0627\u062C \u0627\u0644\u0635\u0648\u0631
|
||||
@@ -122,14 +224,15 @@ fileToPDF.submit=\u062A\u062D\u0648\u064A\u0644 \u0625\u0644\u0649 PDF
|
||||
|
||||
#Add image
|
||||
addImage.title=إضافة صورة
|
||||
addImage.header=إضافة صورة إلى PDF (العمل قيد التقدم)
|
||||
addImage.header=إضافة صورة إلى PDF
|
||||
addImage.everyPage=كل صفحة؟
|
||||
addImage.submit=إضافة صورة
|
||||
|
||||
#compress
|
||||
compress.title=ضغط
|
||||
compress.header=\u0636\u063A\u0637 PDF
|
||||
compress.credit=\u062A\u0633\u062A\u062E\u062F\u0645 \u0647\u0630\u0647 \u0627\u0644\u062E\u062F\u0645\u0629 OCRmyPDF \u0644\u0636\u063A\u0637 / \u062A\u062D\u0633\u064A\u0646 PDF.
|
||||
compress.selectText.1 = \u0645\u0633\u062A\u0648\u0649 \u0627\u0644\u062A\u062D\u0633\u064A\u0646:
|
||||
compress.selectText.1=\u0645\u0633\u062A\u0648\u0649 \u0627\u0644\u062A\u062D\u0633\u064A\u0646:
|
||||
compress.selectText.2=0 (\u0628\u062F\u0648\u0646 \u062A\u062D\u0633\u064A\u0646)
|
||||
compress.selectText.3=1 (\u0627\u0641\u062A\u0631\u0627\u0636\u064A\u060C \u062A\u062D\u0633\u064A\u0646 \u0628\u062F\u0648\u0646 \u0641\u0642\u062F\u0627\u0646)
|
||||
compress.selectText.4=2 (\u062A\u062D\u0633\u064A\u0646 \u0636\u064A\u0627\u0639)
|
||||
@@ -149,6 +252,9 @@ pdfOrganiser.title=منظم الصفحة
|
||||
pdfOrganiser.header=منظم صفحات PDF
|
||||
pdfOrganiser.submit=إعادة ترتيب الصفحات
|
||||
|
||||
#multiTool
|
||||
multiTool.title=أداة متعددة PDF
|
||||
multiTool.header=أداة متعددة PDF
|
||||
|
||||
#pageRemover
|
||||
pageRemover.title=مزيل الصفحة
|
||||
@@ -184,18 +290,23 @@ split.submit=Split
|
||||
imageToPDF.title=صورة إلى PDF
|
||||
imageToPDF.header=صورة إلى PDF
|
||||
imageToPDF.submit=تحول
|
||||
imageToPDF.selectText.1=\u062A\u0645\u062F\u062F \u0644\u0644\u0645\u0644\u0627\u0621\u0645\u0629
|
||||
imageToPDF.selectText.2=\u062F\u0648\u0631\u0627\u0646 PDF \u062A\u0644\u0642\u0627\u0626\u064A\u064B\u0627
|
||||
imageToPDF.selectText.3=\u0627\u0644\u0645\u0646\u0637\u0642 \u0627\u0644\u0645\u062A\u0639\u062F\u062F \u0644\u0644\u0645\u0644\u0641\u0627\u062A (\u0645\u0641\u0639\u0651\u0644 \u0641\u0642\u0637 \u0625\u0630\u0627 \u0643\u0646\u062A \u062A\u0639\u0645\u0644 \u0645\u0639 \u0635\u0648\u0631 \u0645\u062A\u0639\u062F\u062F\u0629)
|
||||
imageToPDF.selectText.4=\u062F\u0645\u062C \u0641\u064A \u0645\u0644\u0641 PDF \u0648\u0627\u062D\u062F
|
||||
imageToPDF.selectText.5=\u062A\u062D\u0648\u064A\u0644 \u0625\u0644\u0649 \u0645\u0644\u0641\u0627\u062A PDF \u0645\u0646\u0641\u0635\u0644\u0629
|
||||
|
||||
#pdfToImage
|
||||
pdfToImage.title=تحويل PDF إلى صورة
|
||||
pdfToImage.header=تحويل PDF إلى صورة
|
||||
pdfToImage.selectText=تنسيق الصورة
|
||||
pdfToImage.singleOrMultiple = \u0646\u0648\u0639 \u0646\u062A\u064A\u062C\u0629 \u0627\u0644\u0635\u0648\u0631\u0629
|
||||
pdfToImage.single = \u0635\u0648\u0631\u0629 \u0648\u0627\u062D\u062F\u0629 \u0643\u0628\u064A\u0631\u0629
|
||||
pdfToImage.multi = \u0635\u0648\u0631 \u0645\u062A\u0639\u062F\u062F\u0629
|
||||
pdfToImage.colorType = \u0646\u0648\u0639 \u0627\u0644\u0644\u0648\u0646
|
||||
pdfToImage.color = \u0627\u0644\u0644\u0648\u0646
|
||||
pdfToImage.grey = \u062A\u062F\u0631\u062C \u0627\u0644\u0631\u0645\u0627\u062F\u064A
|
||||
pdfToImage.blackwhite = \u0623\u0628\u064A\u0636 \u0648\u0623\u0633\u0648\u062F (\u0642\u062F \u064A\u0641\u0642\u062F \u0627\u0644\u0628\u064A\u0627\u0646\u0627\u062A!)
|
||||
pdfToImage.singleOrMultiple=\u0646\u0648\u0639 \u0646\u062A\u064A\u062C\u0629 \u0627\u0644\u0635\u0648\u0631\u0629
|
||||
pdfToImage.single=\u0635\u0648\u0631\u0629 \u0648\u0627\u062D\u062F\u0629 \u0643\u0628\u064A\u0631\u0629
|
||||
pdfToImage.multi=\u0635\u0648\u0631 \u0645\u062A\u0639\u062F\u062F\u0629
|
||||
pdfToImage.colorType=\u0646\u0648\u0639 \u0627\u0644\u0644\u0648\u0646
|
||||
pdfToImage.color=\u0627\u0644\u0644\u0648\u0646
|
||||
pdfToImage.grey=\u062A\u062F\u0631\u062C \u0627\u0644\u0631\u0645\u0627\u062F\u064A
|
||||
pdfToImage.blackwhite=\u0623\u0628\u064A\u0636 \u0648\u0623\u0633\u0648\u062F (\u0642\u062F \u064A\u0641\u0642\u062F \u0627\u0644\u0628\u064A\u0627\u0646\u0627\u062A!)
|
||||
pdfToImage.submit=تحول
|
||||
|
||||
#addPassword
|
||||
@@ -229,11 +340,11 @@ watermark.selectText.7=\u0627\u0644\u062A\u0639\u062A\u064A\u0645 (0\u066A - 100
|
||||
watermark.submit=إضافة علامة مائية
|
||||
|
||||
#remove-watermark
|
||||
remove-watermark.title = \u0625\u0632\u0627\u0644\u0629 \u0627\u0644\u0639\u0644\u0627\u0645\u0629 \u0627\u0644\u0645\u0627\u0626\u064A\u0629
|
||||
remove-watermark.header = \u0625\u0632\u0627\u0644\u0629 \u0627\u0644\u0639\u0644\u0627\u0645\u0629 \u0627\u0644\u0645\u0627\u0626\u064A\u0629
|
||||
remove-watermark.selectText.1 = \u062D\u062F\u062F PDF \u0644\u0625\u0632\u0627\u0644\u0629 \u0627\u0644\u0639\u0644\u0627\u0645\u0629 \u0627\u0644\u0645\u0627\u0626\u064A\u0629 \u0645\u0646:
|
||||
remove-watermark.selectText.2 = \u0646\u0635 \u0627\u0644\u0639\u0644\u0627\u0645\u0629 \u0627\u0644\u0645\u0627\u0626\u064A\u0629:
|
||||
remove-watermark.submit = \u0625\u0632\u0627\u0644\u0629 \u0627\u0644\u0639\u0644\u0627\u0645\u0629 \u0627\u0644\u0645\u0627\u0626\u064A\u0629
|
||||
remove-watermark.title=\u0625\u0632\u0627\u0644\u0629 \u0627\u0644\u0639\u0644\u0627\u0645\u0629 \u0627\u0644\u0645\u0627\u0626\u064A\u0629
|
||||
remove-watermark.header=\u0625\u0632\u0627\u0644\u0629 \u0627\u0644\u0639\u0644\u0627\u0645\u0629 \u0627\u0644\u0645\u0627\u0626\u064A\u0629
|
||||
remove-watermark.selectText.1=\u062D\u062F\u062F PDF \u0644\u0625\u0632\u0627\u0644\u0629 \u0627\u0644\u0639\u0644\u0627\u0645\u0629 \u0627\u0644\u0645\u0627\u0626\u064A\u0629 \u0645\u0646:
|
||||
remove-watermark.selectText.2=\u0646\u0635 \u0627\u0644\u0639\u0644\u0627\u0645\u0629 \u0627\u0644\u0645\u0627\u0626\u064A\u0629:
|
||||
remove-watermark.submit=\u0625\u0632\u0627\u0644\u0629 \u0627\u0644\u0639\u0644\u0627\u0645\u0629 \u0627\u0644\u0645\u0627\u0626\u064A\u0629
|
||||
|
||||
#Change permissions
|
||||
permissions.title=تغيير الأذونات
|
||||
@@ -258,27 +369,63 @@ removePassword.selectText.1=حدد PDF لفك التشفير
|
||||
removePassword.selectText.2=كلمة المرور
|
||||
removePassword.submit=إزالة
|
||||
|
||||
changeMetadata.title = \u062A\u063A\u064A\u064A\u0631 \u0627\u0644\u0628\u064A\u0627\u0646\u0627\u062A \u0627\u0644\u0648\u0635\u0641\u064A\u0629
|
||||
changeMetadata.header = \u062A\u063A\u064A\u064A\u0631 \u0627\u0644\u0628\u064A\u0627\u0646\u0627\u062A \u0627\u0644\u0648\u0635\u0641\u064A\u0629
|
||||
changeMetadata.selectText.1 = \u064A\u0631\u062C\u0649 \u062A\u0639\u062F\u064A\u0644 \u0627\u0644\u0645\u062A\u063A\u064A\u0631\u0627\u062A \u0627\u0644\u062A\u064A \u062A\u0631\u063A\u0628 \u0641\u064A \u062A\u063A\u064A\u064A\u0631\u0647\u0627
|
||||
changeMetadata.selectText.2 = \u062D\u0630\u0641 \u0643\u0644 \u0627\u0644\u0628\u064A\u0627\u0646\u0627\u062A \u0627\u0644\u0623\u0648\u0644\u064A\u0629
|
||||
changeMetadata.selectText.3 = \u0625\u0638\u0647\u0627\u0631 \u0627\u0644\u0628\u064A\u0627\u0646\u0627\u062A \u0627\u0644\u0623\u0648\u0644\u064A\u0629 \u0627\u0644\u0645\u062E\u0635\u0635\u0629:
|
||||
changeMetadata.author = \u0627\u0644\u0645\u0624\u0644\u0641:
|
||||
changeMetadata.creationDate = \u062A\u0627\u0631\u064A\u062E \u0627\u0644\u0625\u0646\u0634\u0627\u0621 (yyyy / MM / dd HH: mm: ss):
|
||||
changeMetadata.creator = \u0627\u0644\u0645\u0646\u0634\u0626:
|
||||
changeMetadata.keywords = \u0627\u0644\u0643\u0644\u0645\u0627\u062A \u0627\u0644\u0631\u0626\u064A\u0633\u064A\u0629:
|
||||
changeMetadata.modDate = \u062A\u0627\u0631\u064A\u062E \u0627\u0644\u062A\u0639\u062F\u064A\u0644 (yyyy / MM / dd HH: mm: ss):
|
||||
changeMetadata.producer = \u0627\u0644\u0645\u0646\u062A\u062C:
|
||||
changeMetadata.subject = \u0627\u0644\u0645\u0648\u0636\u0648\u0639:
|
||||
changeMetadata.title = \u0627\u0644\u0639\u0646\u0648\u0627\u0646:
|
||||
changeMetadata.trapped = \u0645\u062D\u0627\u0635\u0631:
|
||||
changeMetadata.selectText.4 = \u0628\u064A\u0627\u0646\u0627\u062A \u0648\u0635\u0641\u064A\u0629 \u0623\u062E\u0631\u0649:
|
||||
changeMetadata.selectText.5 = \u0625\u0636\u0627\u0641\u0629 \u0625\u062F\u062E\u0627\u0644 \u0628\u064A\u0627\u0646\u0627\u062A \u0623\u0648\u0644\u064A\u0629 \u0645\u062E\u0635\u0635
|
||||
changeMetadata.submit = \u062A\u063A\u064A\u064A\u0631
|
||||
changeMetadata.title=\u062A\u063A\u064A\u064A\u0631 \u0627\u0644\u0628\u064A\u0627\u0646\u0627\u062A \u0627\u0644\u0648\u0635\u0641\u064A\u0629
|
||||
changeMetadata.header=\u062A\u063A\u064A\u064A\u0631 \u0627\u0644\u0628\u064A\u0627\u0646\u0627\u062A \u0627\u0644\u0648\u0635\u0641\u064A\u0629
|
||||
changeMetadata.selectText.1=\u064A\u0631\u062C\u0649 \u062A\u0639\u062F\u064A\u0644 \u0627\u0644\u0645\u062A\u063A\u064A\u0631\u0627\u062A \u0627\u0644\u062A\u064A \u062A\u0631\u063A\u0628 \u0641\u064A \u062A\u063A\u064A\u064A\u0631\u0647\u0627
|
||||
changeMetadata.selectText.2=\u062D\u0630\u0641 \u0643\u0644 \u0627\u0644\u0628\u064A\u0627\u0646\u0627\u062A \u0627\u0644\u0623\u0648\u0644\u064A\u0629
|
||||
changeMetadata.selectText.3=\u0625\u0638\u0647\u0627\u0631 \u0627\u0644\u0628\u064A\u0627\u0646\u0627\u062A \u0627\u0644\u0623\u0648\u0644\u064A\u0629 \u0627\u0644\u0645\u062E\u0635\u0635\u0629:
|
||||
changeMetadata.author=\u0627\u0644\u0645\u0624\u0644\u0641:
|
||||
changeMetadata.creationDate=\u062A\u0627\u0631\u064A\u062E \u0627\u0644\u0625\u0646\u0634\u0627\u0621 (yyyy / MM / dd HH: mm: ss):
|
||||
changeMetadata.creator=\u0627\u0644\u0645\u0646\u0634\u0626:
|
||||
changeMetadata.keywords=\u0627\u0644\u0643\u0644\u0645\u0627\u062A \u0627\u0644\u0631\u0626\u064A\u0633\u064A\u0629:
|
||||
changeMetadata.modDate=\u062A\u0627\u0631\u064A\u062E \u0627\u0644\u062A\u0639\u062F\u064A\u0644 (yyyy / MM / dd HH: mm: ss):
|
||||
changeMetadata.producer=\u0627\u0644\u0645\u0646\u062A\u062C:
|
||||
changeMetadata.subject=\u0627\u0644\u0645\u0648\u0636\u0648\u0639:
|
||||
changeMetadata.title=\u0627\u0644\u0639\u0646\u0648\u0627\u0646:
|
||||
changeMetadata.trapped=\u0645\u062D\u0627\u0635\u0631:
|
||||
changeMetadata.selectText.4=\u0628\u064A\u0627\u0646\u0627\u062A \u0648\u0635\u0641\u064A\u0629 \u0623\u062E\u0631\u0649:
|
||||
changeMetadata.selectText.5=\u0625\u0636\u0627\u0641\u0629 \u0625\u062F\u062E\u0627\u0644 \u0628\u064A\u0627\u0646\u0627\u062A \u0623\u0648\u0644\u064A\u0629 \u0645\u062E\u0635\u0635
|
||||
changeMetadata.submit=\u062A\u063A\u064A\u064A\u0631
|
||||
|
||||
|
||||
|
||||
xlsToPdf.title = \u062A\u062D\u0648\u064A\u0644 Excel \u0625\u0644\u0649 PDF
|
||||
xlsToPdf.header = \u062A\u062D\u0648\u064A\u0644 Excel \u0625\u0644\u0649 PDF
|
||||
xlsToPdf.selectText.1 = \u062D\u062F\u062F \u0648\u0631\u0642\u0629 \u0625\u0643\u0633\u0644 XLS \u0623\u0648 XLSX \u0644\u0644\u062A\u062D\u0648\u064A\u0644
|
||||
xlsToPdf.convert = \u062A\u062D\u0648\u064A\u0644
|
||||
xlsToPdf.title=\u062A\u062D\u0648\u064A\u0644 Excel \u0625\u0644\u0649 PDF
|
||||
xlsToPdf.header=\u062A\u062D\u0648\u064A\u0644 Excel \u0625\u0644\u0649 PDF
|
||||
xlsToPdf.selectText.1=\u062D\u062F\u062F \u0648\u0631\u0642\u0629 \u0625\u0643\u0633\u0644 XLS \u0623\u0648 XLSX \u0644\u0644\u062A\u062D\u0648\u064A\u0644
|
||||
xlsToPdf.convert=\u062A\u062D\u0648\u064A\u0644
|
||||
|
||||
pdfToPDFA.title=PDF \u0625\u0644\u0649 PDF / A
|
||||
pdfToPDFA.header=PDF \u0625\u0644\u0649 PDF / A
|
||||
pdfToPDFA.credit=\u062A\u0633\u062A\u062E\u062F\u0645 \u0647\u0630\u0647 \u0627\u0644\u062E\u062F\u0645\u0629 OCRmyPDF \u0644\u062A\u062D\u0648\u064A\u0644 PDF / A.
|
||||
pdfToPDFA.submit=\u062A\u062D\u0648\u064A\u0644
|
||||
|
||||
|
||||
PDFToWord.title=تحويل PDF إلى Word
|
||||
PDFToWord.header=تحويل PDF إلى Word
|
||||
PDFToWord.selectText.1=تنسيق ملف الإخراج
|
||||
PDFToWord.credit=تستخدم هذه الخدمة LibreOffice لتحويل الملفات.
|
||||
PDFToWord.submit=تحويل
|
||||
|
||||
PDFToPresentation.title=PDF للعرض التقديمي
|
||||
PDFToPresentation.header=PDF للعرض التقديمي
|
||||
PDFToPresentation.selectText.1=تنسيق ملف الإخراج
|
||||
PDFToPresentation.credit=تستخدم هذه الخدمة LibreOffice لتحويل الملف.
|
||||
PDFToPresentation.submit=تحويل
|
||||
|
||||
|
||||
PDFToText.title=تحويل PDF إلى نص / RTF
|
||||
PDFToText.header=تحويل PDF إلى نص / RTF
|
||||
PDFToText.selectText.1=تنسيق ملف الإخراج
|
||||
PDFToText.credit=تستخدم هذه الخدمة LibreOffice لتحويل الملفات.
|
||||
PDFToText.submit=تحويل
|
||||
|
||||
|
||||
PDFToHTML.title=PDF إلى HTML
|
||||
PDFToHTML.header=PDF إلى HTML
|
||||
PDFToHTML.credit=تستخدم هذه الخدمة LibreOffice لتحويل الملفات.
|
||||
PDFToHTML.submit=تحويل
|
||||
|
||||
PDFToXML.title=تحويل PDF إلى XML
|
||||
PDFToXML.header=تحويل PDF إلى XML
|
||||
PDFToXML.credit=تستخدم هذه الخدمة LibreOffice لتحويل الملفات.
|
||||
PDFToXML.submit=تحويل
|
||||
|
||||
444
src/main/resources/messages_ca_CA.properties
Normal file
@@ -0,0 +1,444 @@
|
||||
###########
|
||||
# Generic #
|
||||
###########
|
||||
# the direction that the language is written (ltr = left to right, rtl = right to left)
|
||||
language.direction=ltr
|
||||
|
||||
pdfPrompt=Selecciona PDF(s)
|
||||
multiPdfPrompt=Selecciona PDFs (2+)
|
||||
multiPdfDropPrompt=Selecciona (o arrossega) els documents PDF
|
||||
imgPrompt=Selecciona Imatge(s)
|
||||
genericSubmit=Envia
|
||||
processTimeWarning=Alerta: Aquest procés pot tardar 1 minut depenent de la mida de l'arxiu
|
||||
pageOrderPrompt=Ordre de Pàgines (Llista separada per comes) :
|
||||
goToPage=Anar
|
||||
true=Verdader
|
||||
false=Fals
|
||||
unknown=Desconegut
|
||||
save=Desa
|
||||
close=Tanca
|
||||
filesSelected=fitxers seleccionats
|
||||
noFavourites=No s'ha afegit cap favorit
|
||||
bored=Avorrit esperant?
|
||||
#############
|
||||
# HOME-PAGE #
|
||||
#############
|
||||
home.desc=L'eina allotjada localment per a necessitats PDF.
|
||||
|
||||
|
||||
navbar.convert=Converteix
|
||||
navbar.security=Seguretat
|
||||
navbar.other=Altres
|
||||
navbar.darkmode=Mode Fost
|
||||
navbar.pageOps=Operacions de Pàgina
|
||||
|
||||
home.multiTool.title=PDF Multi Tool
|
||||
home.multiTool.desc=Fusiona, Rota, Reorganitza, i Esborra pàgines
|
||||
|
||||
home.merge.title=Fusiona
|
||||
home.merge.desc=Fusiona fàcilment pàgines en una.
|
||||
|
||||
home.split.title=Divideix
|
||||
home.split.desc=Divideix PDFs en múltiples documents
|
||||
|
||||
home.rotate.title=Rota
|
||||
home.rotate.desc=Rota els PDFs.
|
||||
|
||||
home.imageToPdf.title=Imatge a PDF
|
||||
home.imageToPdf.desc=Converteix imatge (PNG, JPEG, GIF) a PDF.
|
||||
|
||||
home.pdfToImage.title=PDF a Imatge
|
||||
home.pdfToImage.desc=Converteix PDF a imatge. (PNG, JPEG, GIF)
|
||||
|
||||
home.pdfOrganiser.title=Organitza
|
||||
home.pdfOrganiser.desc=Elimina/Reorganitza pàgines en qualsevol ordre
|
||||
|
||||
home.addImage.title=Afegir imatge a PDF
|
||||
home.addImage.desc=Afegeix imatge en un PDF (En progrés)
|
||||
|
||||
home.watermark.title=Afegir Marca d'aigua
|
||||
home.watermark.desc=Afegir Marca d'aigua personalitzada en un PDF
|
||||
|
||||
home.remove-watermark.title=Treure Marca d'Aigua
|
||||
home.remove-watermark.desc=Treu Marca d'Aigua d'un PDF
|
||||
|
||||
home.permissions.title=Canvia permissos
|
||||
home.permissions.desc=Canvia permisos del document PDF
|
||||
|
||||
home.removePages.title=Elimina
|
||||
home.removePages.desc=Elimina pàgines del document PDF.
|
||||
|
||||
home.addPassword.title=Afegir Password
|
||||
home.addPassword.desc=Xifra document PDF amb password.
|
||||
|
||||
home.removePassword.title=Elimina Password
|
||||
home.removePassword.desc=Elimia Password de document PDF.
|
||||
|
||||
home.compressPdfs.title=Comprimeix
|
||||
home.compressPdfs.desc=Comprimeix PDFs per reduir la mida.
|
||||
|
||||
home.changeMetadata.title=Canvia Metadades
|
||||
home.changeMetadata.desc=Canvia/Treu/Afegeix matadades al document PDF.
|
||||
|
||||
home.fileToPDF.title=Converteix arxiu a PDF
|
||||
home.fileToPDF.desc=Converteix qualsevol arxiu a PDF (DOCX, PNG, XLS, PPT, TXT i més)
|
||||
|
||||
home.ocr.title=Executa exploracions OCR i/o neteja escanejos
|
||||
home.ocr.desc=Neteja escanejats i detecta text d'imatges dins d'un PDF i el torna a afegir com a text.
|
||||
|
||||
home.extractImages.title=Extreu Imatges
|
||||
home.extractImages.desc=Extreu les Imatges del PDF i les desa a zip
|
||||
|
||||
home.pdfToPDFA.title=PDF a PDF/A
|
||||
home.pdfToPDFA.desc=Converteix PDF a PDF/A per desar a llarg termini.
|
||||
|
||||
home.PDFToWord.title=PDF a Word
|
||||
home.PDFToWord.desc=Converteix PDF a formats de Word (DOC, DOCX and ODT)
|
||||
|
||||
home.PDFToPresentation.title=PDF a Presentació
|
||||
home.PDFToPresentation.desc=Convert PDF to Presentation formats (PPT, PPTX and ODP)
|
||||
|
||||
home.PDFToText.title=PDF a Text/RTF
|
||||
home.PDFToText.desc=Converteix PDF a Text o format RTF
|
||||
|
||||
home.PDFToHTML.title=PDF a HTML
|
||||
home.PDFToHTML.desc=Converteix PDF a format HTML
|
||||
|
||||
home.PDFToXML.title=PDF a XML
|
||||
home.PDFToXML.desc=Converteix PDF a format XML
|
||||
|
||||
home.ScannerImageSplit.title=Detecta/Divideix fotos escanejades
|
||||
home.ScannerImageSplit.desc=Divideix múltiples fotos dins del PDF/foto
|
||||
|
||||
home.sign.title=Sign
|
||||
home.sign.desc=Afegeix signatura al PDF mitjançant dibuix, text o imatge
|
||||
|
||||
home.flatten.title=Aplanar
|
||||
home.flatten.desc=Elimineu tots els elements i formularis interactius d'un PDF
|
||||
|
||||
home.repair.title=Reparar
|
||||
home.repair.desc=Intenta reparar un PDF danyat o trencat
|
||||
|
||||
home.removeBlanks.title=Elimina les pàgines en blanc
|
||||
home.removeBlanks.desc=Detecta i elimina les pàgines en blanc d'un document
|
||||
|
||||
home.compare.title=Compara
|
||||
home.compare.desc=Compara i mostra les diferències entre 2 documents PDF
|
||||
|
||||
downloadPdf=Descarregueu PDF
|
||||
text=Text
|
||||
font=Tipus de lletra
|
||||
|
||||
removeBlanks.title=Elimina els espais en blanc
|
||||
removeBlanks.header=Elimina les pàgines en blanc
|
||||
removeBlanks.threshold=Llindar:
|
||||
removeBlanks.thresholdDesc=Llindar per determinar el blanc que ha de ser un píxel blanc
|
||||
removeBlanks.whitePercent=Percentatge blanc (%):
|
||||
removeBlanks.whitePercentDesc=Percentatge de pàgina que ha de ser blanca per eliminar-la
|
||||
removeBlanks.submit=Elimina els espais en blanc
|
||||
|
||||
compare.title=Comparar
|
||||
compare.header=Compara PDF
|
||||
compare.document.1=Document 1
|
||||
compare.document.2=Document 2
|
||||
compare.submit=Comparar
|
||||
|
||||
sign.title=Sign
|
||||
sign.header=Firma els PDF
|
||||
sign.upload=Penja la imatge
|
||||
sign.draw=Dibuixa la signatura
|
||||
sign.text=Entrada de text
|
||||
sign.clear=Esborrar
|
||||
sign.add=Afegeix
|
||||
|
||||
repair.title=Reparar
|
||||
repair.header=Repara els PDF
|
||||
repair.submit=Reparar
|
||||
|
||||
flatten.title=Aplanar
|
||||
flatten.header=Aplana els PDF
|
||||
flatten.submit=Aplanar
|
||||
|
||||
ScannerImageSplit.selectText.1=Llindar d'angle:
|
||||
ScannerImageSplit.selectText.2=Estableix l'angle absolut mínim necessari perquè la imatge es giri (per defecte: 10).
|
||||
ScannerImageSplit.selectText.3=Tolerància:
|
||||
ScannerImageSplit.selectText.4=Determina l'interval de variació de color al voltant del color de fons estimat (per defecte: 30).
|
||||
ScannerImageSplit.selectText.5=Àrea Mínima:
|
||||
ScannerImageSplit.selectText.6=Estableix el llindar d'àrea mínima per a una foto (per defecte: 10000).
|
||||
ScannerImageSplit.selectText.7=Àrea de contorn mínima:
|
||||
ScannerImageSplit.selectText.8=Estableix el llindar mínim de l'àrea de contorn per a una foto
|
||||
ScannerImageSplit.selectText.9=Mida Vora:
|
||||
ScannerImageSplit.selectText.10=Estableix la mida de la vora afegida i eliminada per evitar vores blanques a la sortida (per defecte: 1).
|
||||
|
||||
navbar.settings=Opcions
|
||||
settings.title=Opcions
|
||||
settings.update=Actualització Disponible
|
||||
settings.appVersion=Versió App:
|
||||
settings.downloadOption.title=Trieu l'opció de descàrrega (per a descàrregues d'un sol fitxer no zip):
|
||||
settings.downloadOption.1=Obre mateixa finestra
|
||||
settings.downloadOption.2=Obre mateixa finestra
|
||||
settings.downloadOption.3=Descarrega Arxiu
|
||||
settings.zipThreshold=Comprimiu els fitxers quan el nombre de fitxers baixats superi
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#OCR
|
||||
ocr.title=OCR / Neteja escaneig
|
||||
ocr.header=Neteja Escanejos / OCR (Reconeixement òptic de caràcters)
|
||||
ocr.selectText.1=Selecciona els idiomes que s'han de detectar dins del PDF (els que s'indiquen són els detectats):
|
||||
ocr.selectText.2=Produeix un fitxer de text que contingui text OCR juntament amb el PDF editat per OCR
|
||||
ocr.selectText.3=Corregeix pàgines escanejades amb un angle esbiaixat girant-les de nou al seu lloc
|
||||
ocr.selectText.4=Neteja la pàgina, de manera que és menys probable que l'OCR trobi soroll de text de fons. (Sense canvis de sortida)
|
||||
ocr.selectText.5=Neteja la pàgina, de manera que és menys probable que l'OCR trobi text al soroll de fons, mantenint la neteja a la sortida.
|
||||
ocr.selectText.6=Ignora les pàgines que tenen text interactiu, només les pàgines OCR que són imatges
|
||||
ocr.selectText.7=Força OCR, l'OCR de cada pàgina elimina tots els elements de text originals
|
||||
ocr.selectText.8=Normal (error si el PDF conté text)
|
||||
ocr.selectText.9=Opcions Addicionals
|
||||
ocr.selectText.10=Mode OCR
|
||||
ocr.selectText.11=Elimia Imatges després de l'OCR (Alimina TOTES les imatges, útil si és part d'un procés de conversió)
|
||||
ocr.selectText.12=Tipus de Renderització (Avançat)
|
||||
ocr.help=Llegiu aquesta documentació sobre com utilitzar-la per a altres idiomes i/o no utilitzar-la a Docker
|
||||
ocr.credit=Aquest servei empra OCRmyPDF i Tesseract per OCR.
|
||||
ocr.submit=Processa PDF amb OCR
|
||||
|
||||
|
||||
|
||||
extractImages.title=Extreu Imatges
|
||||
extractImages.header=Extreu Imatges
|
||||
extractImages.selectText=Selecciona el format d'imatge al qual convertir les imatges extretes
|
||||
extractImages.submit=Extreu
|
||||
|
||||
|
||||
#File to PDF
|
||||
fileToPDF.title=Arxiu a PDF
|
||||
fileToPDF.header=Converteix arxiu a PDF
|
||||
fileToPDF.credit=Utilitza LibreOffice i Unoconv per a la conversió.
|
||||
fileToPDF.supportedFileTypes=Els tipus de fitxers admesos haurien d'incloure el següent, però per obtenir una llista completa actualitzada dels formats compatibles, consulteu la documentació de LibreOffice
|
||||
fileToPDF.submit=Converteix a PDF
|
||||
|
||||
|
||||
#compress
|
||||
compress.title=Comprimeix
|
||||
compress.header=Comprimeix PDF
|
||||
compress.credit=Utilitza OCRmyPDF per Compressió/Optimització de PDF.
|
||||
compress.selectText.1=Nivell Optimització:
|
||||
compress.selectText.2=0 (Sense Optimització)
|
||||
compress.selectText.3=1 (Defecte, sense pèrdua Optimització)
|
||||
compress.selectText.4=2 (Pèrdua Optimització)
|
||||
compress.selectText.5=3 (Pèrdua Optimització, més agressiu)
|
||||
compress.selectText.6=Activa la visualització web ràpida (linealitza PDF)
|
||||
compress.selectText.7=Activa pèrdua codificació JBIG2
|
||||
compress.submit=Comprimeix
|
||||
|
||||
|
||||
#Add image
|
||||
addImage.title=Afegir Imatge
|
||||
addImage.header=Afegir Imatge a PDF (en construcció)
|
||||
addImage.everyPage=Totes les pàgines?
|
||||
addImage.submit=Afegir Imatge
|
||||
|
||||
|
||||
#merge
|
||||
merge.title=Fusiona
|
||||
merge.header=Fusiona múltiples PDFs (2+)
|
||||
merge.submit=Fusiona
|
||||
|
||||
#pdfOrganiser
|
||||
pdfOrganiser.title=Organitzador de pàgines
|
||||
pdfOrganiser.header=Organitzador de pàgines PDF
|
||||
pdfOrganiser.submit=Reorganitza Pàgines
|
||||
|
||||
#multiTool
|
||||
multiTool.title=PDF Multi Tool
|
||||
multiTool.header=PDF Multi Tool
|
||||
|
||||
|
||||
#pageRemover
|
||||
pageRemover.title=Eliminació Pàgines
|
||||
pageRemover.header=Eliminació Pàgines PDF
|
||||
pageRemover.pagesToDelete=Pàgines a esborrar (Números de pàgina) :
|
||||
pageRemover.submit=Esborra Pàgines
|
||||
|
||||
#rotate
|
||||
rotate.title=Rota PDF
|
||||
rotate.header=Rota PDF
|
||||
rotate.selectAngle=Selecciona l'angle de gir (en múltiples de 90 graus):
|
||||
rotate.submit=Rota
|
||||
|
||||
|
||||
|
||||
|
||||
#merge
|
||||
split.title=Divideix PDF
|
||||
split.header=Divideix PDF
|
||||
split.desc.1=Els números seleccionats són el número de pàgina en què voleu fer la divisió
|
||||
split.desc.2=Per tant, seleccionant 1,3,7-8 dividiria un document de 10 pàgines en 6 PDFS separats amb:
|
||||
split.desc.3=Document #1: Pàgina 1
|
||||
split.desc.4=Document #2: Pàgina 2 i 3
|
||||
split.desc.5=Document #3: Pàgina 4, 5 i 6
|
||||
split.desc.6=Document #4: Pàgina 7
|
||||
split.desc.7=Document #5: Pàgina 8
|
||||
split.desc.8=Document #6: Pàgina 9 i 10
|
||||
split.splitPages=Introdueix pàgines per dividir-les:
|
||||
split.submit=Divideix
|
||||
|
||||
|
||||
#merge
|
||||
imageToPDF.title=Imatge a PDF
|
||||
imageToPDF.header=Imatge a PDF
|
||||
imageToPDF.submit=Converteix
|
||||
imageToPDF.selectText.1=Estirar per adaptar
|
||||
imageToPDF.selectText.2=Auto rota PDF
|
||||
imageToPDF.selectText.3=Lògica de diversos fitxers (només està activada si es treballa amb diverses imatges)
|
||||
imageToPDF.selectText.4=Combina en un únic PDF
|
||||
imageToPDF.selectText.5=Converteix per separar PDFs
|
||||
|
||||
#pdfToImage
|
||||
pdfToImage.title=PDF a Imatge
|
||||
pdfToImage.header=PDF a Imatge
|
||||
pdfToImage.selectText=Format Imatge
|
||||
pdfToImage.singleOrMultiple=Tipus Imatge Resultant
|
||||
pdfToImage.single=Única Imatge Gran
|
||||
pdfToImage.multi=Múltiples Imatges
|
||||
pdfToImage.colorType=Tipus Color
|
||||
pdfToImage.color=Color
|
||||
pdfToImage.grey=Escala de Grisos
|
||||
pdfToImage.blackwhite=Blanc i Negre (Pot perdre dades!)
|
||||
pdfToImage.submit=Converteix
|
||||
|
||||
#addPassword
|
||||
addPassword.title=Afegir Password
|
||||
addPassword.header=Afegir password (Encriptat)
|
||||
addPassword.selectText.1=PDF a encriptar
|
||||
addPassword.selectText.2=Password
|
||||
addPassword.selectText.3=Longitud clau de xifratge
|
||||
addPassword.selectText.4=Valors més alts són més forts, però els valors més baixos tenen una millor compatibilitat.
|
||||
addPassword.selectText.5=Permissos a Establir
|
||||
addPassword.selectText.6=Evita muntatge del document
|
||||
addPassword.selectText.7=Evita extracció de contingut
|
||||
addPassword.selectText.8=Evita extracció per accessibilitat
|
||||
addPassword.selectText.9=Evita emplenar formularis
|
||||
addPassword.selectText.10=Evita modificacions
|
||||
addPassword.selectText.11=Evita modificacions d'annotacions
|
||||
addPassword.selectText.12=Evita impressió
|
||||
addPassword.selectText.13=Evita impressió de diferents formats
|
||||
addPassword.submit=Encripta
|
||||
|
||||
#watermark
|
||||
watermark.title=Afegir Marca d'Aigua
|
||||
watermark.header=Afegir Marca d'Aigua
|
||||
watermark.selectText.1=Seleciona PDF per afegir Marca d'Aigua:
|
||||
watermark.selectText.2=Text de la Marca d'Aigua
|
||||
watermark.selectText.3=Mida de la Font:
|
||||
watermark.selectText.4=Rotació (0-360):
|
||||
watermark.selectText.5=separació d'amplada (Espai horitzontal entre cada Marca d'Aigua):
|
||||
watermark.selectText.6=separació d'alçada (Espai vertical entre cada Marca d'Aigua):
|
||||
watermark.selectText.7=Opacitat (0% - 100%):
|
||||
watermark.submit=Afegir Marca d'Aigua
|
||||
|
||||
#remove-watermark
|
||||
remove-watermark.title=Elimina Marca d'Aigua
|
||||
remove-watermark.header=Elimina Marca d'Aigua
|
||||
remove-watermark.selectText.1=Seleciona PDF per eliminar Marca d'Aigua:
|
||||
remove-watermark.selectText.2=Text de la Marca d'Aigua:
|
||||
remove-watermark.submit=Elimina Marca d'Aigua
|
||||
|
||||
#Change permissions
|
||||
permissions.title=Canviar Permissos
|
||||
permissions.header=Canviar Permissos
|
||||
permissions.warning=Advertència perquè aquests permisos siguin inalterables, es recomana establir-los amb una contrasenya a través de la pàgina d'afegir contrasenya
|
||||
permissions.selectText.1=Selecciona PDF per Canviar Permissos
|
||||
permissions.selectText.2=Permissos a canviar
|
||||
permissions.selectText.3=Evita muntatge del document
|
||||
permissions.selectText.4=Evita extracció de contingut
|
||||
permissions.selectText.5=evita extracció de contingut per accessibilitat
|
||||
permissions.selectText.6=Evita emplenar formularis
|
||||
permissions.selectText.7=Evita modificacions
|
||||
permissions.selectText.8=Evita modificacions d'annotacions
|
||||
permissions.selectText.9=Evita impressió
|
||||
permissions.selectText.10=Evita impressió de diferents formats
|
||||
permissions.submit=Canviar Permissos
|
||||
|
||||
#remove password
|
||||
removePassword.title=Treure Password
|
||||
removePassword.header=Treure Password (Decriptar)
|
||||
removePassword.selectText.1=Selecciona PDF a Decriptar
|
||||
removePassword.selectText.2=Password
|
||||
removePassword.submit=Treu Password
|
||||
|
||||
changeMetadata.title=Canvia Metadades
|
||||
changeMetadata.header=Canvia Metadades
|
||||
changeMetadata.selectText.1=Edit les variables a canviar
|
||||
changeMetadata.selectText.2=Neteja totes les matadades
|
||||
changeMetadata.selectText.3=Mostra Metadades Personalitzades:
|
||||
changeMetadata.author=Autor:
|
||||
changeMetadata.creationDate=Data Creació (yyyy/MM/dd HH:mm:ss):
|
||||
changeMetadata.creator=Creador:
|
||||
changeMetadata.keywords=Keywords:
|
||||
changeMetadata.modDate=Data Modificació (yyyy/MM/dd HH:mm:ss):
|
||||
changeMetadata.producer=Productor:
|
||||
changeMetadata.subject=Assumpte:
|
||||
changeMetadata.title=Títol:
|
||||
changeMetadata.trapped=Atrapat:
|
||||
changeMetadata.selectText.4=Altres Metadades:
|
||||
changeMetadata.selectText.5=Afegir entrada personalizada
|
||||
changeMetadata.submit=Canvia
|
||||
|
||||
xlsToPdf.title=Excel a PDF
|
||||
xlsToPdf.header=Excel a PDF
|
||||
xlsToPdf.selectText.1=Selecciona arxiu XLS o XLSX a convertir
|
||||
xlsToPdf.convert=Converteix
|
||||
|
||||
|
||||
|
||||
|
||||
pdfToPDFA.title=PDF a PDF/A
|
||||
pdfToPDFA.header=PDF a PDF/A
|
||||
pdfToPDFA.credit=Utilitza OCRmyPDF per la conversió a PDF/A
|
||||
pdfToPDFA.submit=Converteix
|
||||
|
||||
|
||||
|
||||
PDFToWord.title=PDF a Word
|
||||
PDFToWord.header=PDF a Word
|
||||
PDFToWord.selectText.1=Format d'Arxiu de Sortida
|
||||
PDFToWord.credit=Utilitza LibreOffice per a la conversió d'Arxius.
|
||||
PDFToWord.submit=Converteix
|
||||
|
||||
PDFToPresentation.title=PDF a Presentació
|
||||
PDFToPresentation.header=PDF a Presentació
|
||||
PDFToPresentation.selectText.1=Format d'Arxiu de Sortida
|
||||
PDFToPresentation.credit=Utilitza LibreOffice per a la conversió d'Arxius.
|
||||
PDFToPresentation.submit=Converteix
|
||||
|
||||
|
||||
PDFToText.title=PDF a Text/RTF
|
||||
PDFToText.header=PDF a Text/RTF
|
||||
PDFToText.selectText.1=Format d'Arxiu de Sortida
|
||||
PDFToText.credit=Utilitza LibreOffice per a la conversió d'Arxius.
|
||||
PDFToText.submit=Converteix
|
||||
|
||||
|
||||
PDFToHTML.title=PDF a HTML
|
||||
PDFToHTML.header=PDF a HTML
|
||||
PDFToHTML.credit=Utilitza LibreOffice per a la conversió d'Arxius.
|
||||
PDFToHTML.submit=Converteix
|
||||
|
||||
PDFToXML.title=PDF a XML
|
||||
PDFToXML.header=PDF a XML
|
||||
PDFToXML.credit=Utilitza LibreOffice per a la conversió d'Arxius.
|
||||
PDFToXML.submit=Converteix
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
###########
|
||||
# Generic #
|
||||
###########
|
||||
# the direction that the language is written (ltr = left to right, rtl = right to left)
|
||||
# the direction that the language is written (ltr=left to right, rtl=right to left)
|
||||
language.direction=ltr
|
||||
|
||||
pdfPrompt=PDF auswählen
|
||||
@@ -16,8 +16,10 @@ true=Wahr
|
||||
false=Falsch
|
||||
unknown=Unbekannt
|
||||
save=Speichern
|
||||
close=Schließen
|
||||
|
||||
close=Schließen
|
||||
filesSelected=Dateien ausgewählt
|
||||
noFavourites=Keine Favoriten hinzugefügt
|
||||
bored=Gelangweiltes Warten?
|
||||
#############
|
||||
# HOME-PAGE #
|
||||
#############
|
||||
@@ -27,14 +29,18 @@ navbar.convert=Konvertieren
|
||||
navbar.security=Sicherheit
|
||||
navbar.other=Anderes
|
||||
navbar.darkmode=Dark Mode
|
||||
navbar.pageOps=Seitenoperationen
|
||||
|
||||
home.merge.title=PDFs zusammenführen
|
||||
home.multiTool.title=PDF-Multitool
|
||||
home.multiTool.desc=Seiten zusammenführen, drehen, neu anordnen und entfernen
|
||||
|
||||
home.merge.title=Zusammenführen
|
||||
home.merge.desc=Mehrere PDF-Dateien zu einer einzigen zusammenführen.
|
||||
|
||||
home.split.title=PDFs aufteilen
|
||||
home.split.title=Aufteilen
|
||||
home.split.desc=PDFs in mehrere Dokumente aufteilen.
|
||||
|
||||
home.rotate.title=PDFs drehen
|
||||
home.rotate.title=Drehen
|
||||
home.rotate.desc=Drehen Sie Ihre PDFs ganz einfach.
|
||||
|
||||
home.imageToPdf.title=Bild zu PDF
|
||||
@@ -43,7 +49,7 @@ home.imageToPdf.desc=Konvertieren Sie ein Bild (PNG, JPEG, GIF) in ein PDF.
|
||||
home.pdfToImage.title=PDF zu Bild
|
||||
home.pdfToImage.desc=Konvertieren Sie ein PDF in ein Bild (PNG, JPEG, GIF).
|
||||
|
||||
home.pdfOrganiser.title=PDF organisieren
|
||||
home.pdfOrganiser.title=Organisieren
|
||||
home.pdfOrganiser.desc=Seiten entfernen und Seitenreihenfolge ändern.
|
||||
|
||||
home.addImage.title=Bild einfügen
|
||||
@@ -58,7 +64,7 @@ home.remove-watermark.desc=Wasserzeichen aus Ihrem PDF-Dokument entfernen.
|
||||
home.permissions.title=Berechtigungen ändern
|
||||
home.permissions.desc=Die Berechtigungen für Ihr PDF-Dokument verändern.
|
||||
|
||||
home.removePages.title=Seiten entfernen
|
||||
home.removePages.title=Entfernen
|
||||
home.removePages.desc=Ungewollte Seiten aus dem PDF entfernen.
|
||||
|
||||
home.addPassword.title=Passwort hinzufügen
|
||||
@@ -67,52 +73,144 @@ home.addPassword.desc=Das PDF mit einem Passwort verschlüsseln.
|
||||
home.removePassword.title=Passwort entfernen
|
||||
home.removePassword.desc=Den Passwortschutz eines PDFs entfernen.
|
||||
|
||||
home.compressPdfs.title=PDF komprimieren
|
||||
home.compressPdfs.title=Komprimieren
|
||||
home.compressPdfs.desc=PDF komprimieren um die Dateigröße zu reduzieren.
|
||||
|
||||
home.changeMetadata.title=Metadaten ändern
|
||||
home.changeMetadata.desc=Ändern/Entfernen/Hinzufügen von Metadaten aus einem PDF-Dokument
|
||||
home.changeMetadata.title=Metadaten ändern
|
||||
home.changeMetadata.desc=Ändern/Entfernen/Hinzufügen von Metadaten aus einem PDF-Dokument
|
||||
|
||||
home.fileToPDF.title=Datei in PDF konvertieren
|
||||
home.fileToPDF.desc=Konvertieren Sie nahezu jede Datei in PDF (DOCX, PNG, XLS, PPT, TXT und mehr)
|
||||
|
||||
home.ocr.title=OCR auf PDF ausführen
|
||||
home.ocr.desc=Scannt und erkennt Text aus Bildern in einer PDF-Datei und fügt ihn erneut als Text hinzu.
|
||||
home.ocr.title=Führe OCR auf PDF- und/oder Cleanup-Scans aus
|
||||
home.ocr.desc=Cleanup scannt und erkennt Text aus Bildern in einer PDF-Datei und fügt ihn erneut als Text hinzu.
|
||||
|
||||
home.extractImages.title=Bilder extrahieren
|
||||
home.extractImages.desc=Extrahiert alle Bilder aus einer PDF-Datei und speichert sie als Zip-Datei
|
||||
|
||||
home.pdfToPDFA.title=PDF zu PDF/A konvertieren
|
||||
home.pdfToPDFA.desc=PDF zu PDF/A für Langzeitarchivierung konvertieren
|
||||
|
||||
home.PDFToWord.title=PDF zu Word
|
||||
home.PDFToWord.desc=PDF in Word-Formate konvertieren (DOC, DOCX und ODT)
|
||||
|
||||
home.PDFToPresentation.title=PDF zu Präsentation
|
||||
home.PDFToPresentation.desc=PDF in Präsentationsformate konvertieren (PPT, PPTX und ODP)
|
||||
|
||||
home.PDFToText.title=PDF in Text/RTF
|
||||
home.PDFToText.desc=PDF in Text- oder RTF-Format konvertieren
|
||||
|
||||
home.PDFToHTML.title=PDF in HTML
|
||||
home.PDFToHTML.desc=PDF in HTML-Format konvertieren
|
||||
|
||||
home.PDFToXML.title=PDF in XML
|
||||
home.PDFToXML.desc=PDF in XML-Format konvertieren
|
||||
|
||||
home.ScannerImageSplit.title=Gescannte Fotos erkennen/aufteilen
|
||||
home.ScannerImageSplit.desc=Teilt mehrere Fotos innerhalb eines Fotos/PDF
|
||||
|
||||
home.sign.title=Signieren
|
||||
home.sign.desc=Fügt PDF-Signaturen durch Zeichnung, Text oder Bild hinzu
|
||||
|
||||
home.flatten.title=Abflachen
|
||||
home.flatten.desc=Alle interaktiven Elemente und Formulare aus einem PDF entfernen
|
||||
|
||||
home.repair.title=Reparatur
|
||||
home.repair.desc=Versucht, ein beschädigtes/kaputtes PDF zu reparieren
|
||||
|
||||
home.removeBlanks.title=Leere Seiten entfernen
|
||||
home.removeBlanks.desc=Erkennt und entfernt leere Seiten aus einem Dokument
|
||||
|
||||
home.compare.title=Vergleichen
|
||||
home.compare.desc=Vergleicht und zeigt die Unterschiede zwischen zwei PDF-Dokumenten an
|
||||
|
||||
downloadPdf=PDF herunterladen
|
||||
text=Text
|
||||
font=Schriftart
|
||||
|
||||
removeBlanks.title=Leerzeichen entfernen
|
||||
removeBlanks.header=Leere Seiten entfernen
|
||||
removeBlanks.threshold=Schwellenwert:
|
||||
removeBlanks.thresholdDesc=Schwellenwert zur Bestimmung, wie weiß ein weißer Pixel sein muss
|
||||
removeBlanks.whitePercent=Weißprozentsatz (%):
|
||||
removeBlanks.whitePercentDesc=Prozentsatz der Seite, die weiß sein muss, um entfernt zu werden
|
||||
removeBlanks.submit=Leerzeichen entfernen
|
||||
|
||||
compare.title=Vergleichen
|
||||
compare.header=PDFs vergleichen
|
||||
compare.document.1=Dokument 1
|
||||
compare.document.2=Dokument 2
|
||||
compare.submit=Vergleichen
|
||||
|
||||
sign.title=Signieren
|
||||
sign.header=PDFs signieren
|
||||
sign.upload=Bild hochladen
|
||||
sign.draw=Signatur zeichnen
|
||||
sign.text=Texteingabe
|
||||
sign.clear=Klar
|
||||
sign.add=Hinzufügen
|
||||
|
||||
repair.title=Reparieren
|
||||
Repair.header=PDFs reparieren
|
||||
repair.submit=Reparieren
|
||||
|
||||
flatten.title=Abflachen
|
||||
flatten.header=PDFs reduzieren
|
||||
flatten.submit=Abflachen
|
||||
|
||||
ScannerImageSplit.selectText.1=Winkelschwelle:
|
||||
ScannerImageSplit.selectText.2=Legt den minimalen absoluten Winkel fest, der erforderlich ist, damit das Bild gedreht werden kann (Standard: 10).
|
||||
ScannerImageSplit.selectText.3=Toleranz:
|
||||
ScannerImageSplit.selectText.4=Bestimmt den Bereich der Farbvariation um die geschätzte Hintergrundfarbe herum (Standard: 30).
|
||||
ScannerImageSplit.selectText.5=Mindestbereich:
|
||||
ScannerImageSplit.selectText.6=Legt den minimalen Bereichsschwellenwert für ein Foto fest (Standard: 10000).
|
||||
ScannerImageSplit.selectText.7=Minimaler Konturbereich:
|
||||
ScannerImageSplit.selectText.8=Legt den minimalen Konturbereichsschwellenwert für ein Foto fest
|
||||
ScannerImageSplit.selectText.9=Randgröße:
|
||||
ScannerImageSplit.selectText.10=Legt die Größe des hinzugefügten und entfernten Randes fest, um weiße Ränder in der Ausgabe zu verhindern (Standard: 1).
|
||||
|
||||
|
||||
navbar.settings=Einstellungen
|
||||
settings.title=Einstellungen
|
||||
settings.update=Update verfügbar
|
||||
settings.update=Update verfügbar
|
||||
settings.appVersion=App-Version:
|
||||
settings.downloadOption.title=Download-Option wählen (für einzelne Dateien, die keine Zip-Downloads sind):
|
||||
settings.downloadOption.1=Im selben Fenster öffnen
|
||||
settings.downloadOption.2=In neuem Fenster öffnen
|
||||
settings.downloadOption.title=Download-Option wählen (für einzelne Dateien, die keine Zip-Downloads sind):
|
||||
settings.downloadOption.1=Im selben Fenster öffnen
|
||||
settings.downloadOption.2=In neuem Fenster öffnen
|
||||
settings.downloadOption.3=Datei herunterladen
|
||||
settings.zip=Dateien mit mehrfachem Download zippen
|
||||
settings.zipThreshold=Dateien komprimieren, wenn die Anzahl der heruntergeladenen Dateien überschritten wird
|
||||
|
||||
#OCR
|
||||
ocr.title=OCR
|
||||
ocr.header=OCR (Optische Zeichenerkennung)
|
||||
ocr.selectText.1=Wählen Sie die Sprachen aus, die in der PDF-Datei erkannt werden sollen (die aufgelisteten sind die aktuell erkannten):
|
||||
ocr.selectText.2=Textdatei mit OCR-Text neben der OCR-PDF-Datei erstellen
|
||||
ocr.help=Bitte lesen Sie diese Dokumentation, um zu erfahren, wie Sie dies für andere Sprachen verwenden und/oder nicht in Docker verwenden können
|
||||
ocr.credit=Dieser Dienst verwendet OCRmyPDF und Tesseract für OCR.
|
||||
ocr.title=OCR / Scan-Bereinigung
|
||||
ocr.header=Scans bereinigen / OCR (Optical Character Recognition)
|
||||
ocr.selectText.1=Sprachen auswählen, die im PDF erkannt werden sollen (die aufgelisteten sind die aktuell erkannten):
|
||||
ocr.selectText.2=Textdatei erzeugen, die OCR-Text neben dem OCR-bearbeiteten PDF enthält
|
||||
ocr.selectText.3=Korrekte Seiten wurden in einem schiefen Winkel gescannt, indem sie wieder an ihren Platz gedreht wurden
|
||||
ocr.selectText.4=Seite säubern, daher ist es weniger wahrscheinlich, dass OCR Text im Hintergrundrauschen findet. (Keine Ausgangsänderung)
|
||||
ocr.selectText.5=Seite säubern, sodass es weniger wahrscheinlich ist, dass OCR Text im Hintergrundrauschen findet, Bereinigung der Ausgabe wird beibehalten.
|
||||
ocr.selectText.6=Ignoriert Seiten mit interaktivem Text, nur OCR-Seiten, die Bilder sind
|
||||
ocr.selectText.7=OCR erzwingen, OCR wird jede Seite entfernen und alle ursprünglichen Textelemente entfernen
|
||||
ocr.selectText.8=Normal (Fehler, wenn PDF Text enthält)
|
||||
ocr.selectText.9=Zusätzliche Einstellungen
|
||||
ocr.selectText.10=OCR-Modus
|
||||
ocr.selectText.11=Bilder nach OCR entfernen (Entfernt ALLE Bilder, nur sinnvoll, wenn Teil des Konvertierungsschritts)
|
||||
ocr.selectText.12=Rendertyp (Erweitert)
|
||||
ocr.help=Bitte lesen Sie diese Dokumentation, um zu erfahren, wie Sie dies für andere Sprachen verwenden und/oder nicht in Docker verwenden können
|
||||
ocr.credit=Dieser Dienst verwendet OCRmyPDF und Tesseract für OCR.
|
||||
ocr.submit=PDF mit OCR verarbeiten
|
||||
|
||||
|
||||
extractImages.title=Bilder extrahieren
|
||||
extractImages.header=Bilder extrahieren
|
||||
extractImages.selectText=Wählen Sie das Bildformat aus, in das extrahierte Bilder konvertiert werden sollen
|
||||
extractImages.selectText=Wählen Sie das Bildformat aus, in das extrahierte Bilder konvertiert werden sollen
|
||||
extractImages.submit=Extrahieren
|
||||
|
||||
|
||||
#File to PDF
|
||||
fileToPDF.title=Datei in PDF
|
||||
fileToPDF.header=Beliebige Dateien in PDF konvertieren
|
||||
fileToPDF.credit=Dieser Dienst verwendet LibreOffice und Unoconv für die Dateikonvertierung.
|
||||
fileToPDF.supportedFileTypes=Unterstützte Dateitypen sollten die folgenden enthalten, eine vollständige aktualisierte Liste der unterstützten Formate finden Sie jedoch in der LibreOffice-Dokumentation
|
||||
fileToPDF.credit=Dieser Dienst verwendet LibreOffice und Unoconv für die Dateikonvertierung.
|
||||
fileToPDF.supportedFileTypes=Unterstützte Dateitypen sollten die folgenden enthalten, eine vollständige aktualisierte Liste der unterstützten Formate finden Sie jedoch in der LibreOffice-Dokumentation
|
||||
fileToPDF.submit=In PDF konvertieren
|
||||
|
||||
|
||||
@@ -121,13 +219,14 @@ fileToPDF.submit=In PDF konvertieren
|
||||
|
||||
#Add image
|
||||
addImage.title=Bild hinzufügen
|
||||
addImage.header=Ein Bild einfügen (Work in progress)
|
||||
addImage.header=Ein Bild einfügen
|
||||
addImage.everyPage=Jede Seite?
|
||||
addImage.submit=Bild hinzufügen
|
||||
|
||||
#compress
|
||||
compress.title=Komprimieren
|
||||
compress.header=PDF komprimieren
|
||||
compress.credit=Dieser Dienst verwendet OCRmyPDF für die PDF-Komprimierung/-Optimierung.
|
||||
compress.credit=Dieser Dienst verwendet OCRmyPDF für die PDF-Komprimierung/-Optimierung.
|
||||
compress.selectText.1=Optimierungsstufe:
|
||||
compress.selectText.2=0 (Keine Optimierung)
|
||||
compress.selectText.3=1 (Standard, verlustfreie Optimierung)
|
||||
@@ -148,6 +247,9 @@ pdfOrganiser.title=Seiten anordnen
|
||||
pdfOrganiser.header=PDF Seitenorganisation
|
||||
pdfOrganiser.submit=Seiten anordnen
|
||||
|
||||
#Mehrfachwerkzeug
|
||||
multiTool.title=PDF-Multitool
|
||||
multiTool.header=PDF-Multitool
|
||||
|
||||
#pageRemover
|
||||
pageRemover.title=Seiten entfernen
|
||||
@@ -183,18 +285,23 @@ split.submit=Aufteilen
|
||||
imageToPDF.title=Bild zu PDF
|
||||
imageToPDF.header=Bild zu PDF
|
||||
imageToPDF.submit=Umwandeln
|
||||
imageToPDF.selectText.1=Auf Seite strecken
|
||||
imageToPDF.selectText.2=PDF automatisch drehen
|
||||
imageToPDF.selectText.3=Mehrere Dateien verarbeiten (nur aktiv, wenn Sie mit mehreren Bildern arbeiten)
|
||||
imageToPDF.selectText.4=In ein einziges PDF zusammenführen
|
||||
imageToPDF.selectText.5=In separate PDFs konvertieren
|
||||
|
||||
#pdfToImage
|
||||
pdfToImage.title=PDF zu Bild
|
||||
pdfToImage.header=PDF zu Bild
|
||||
pdfToImage.selectText=Bildformat
|
||||
pdfToImage.singleOrMultiple=Bildergebnistyp
|
||||
pdfToImage.single=Einzelnes großes Bild
|
||||
pdfToImage.single=Einzelnes großes Bild
|
||||
pdfToImage.multi=Mehrere Bilder
|
||||
pdfToImage.colorType=Farbtyp
|
||||
pdfToImage.color=Farbe
|
||||
pdfToImage.grey=Graustufen
|
||||
pdfToImage.blackwhite=Schwarzweiß (Datenverlust möglich!)
|
||||
pdfToImage.blackwhite=Schwarzweiß (Datenverlust möglich!)
|
||||
pdfToImage.submit=Umwandeln
|
||||
|
||||
#addPassword
|
||||
@@ -230,7 +337,7 @@ watermark.submit=Wasserzeichen hinzufügen
|
||||
#remove-watermark
|
||||
remove-watermark.title=Wasserzeichen entfernen
|
||||
remove-watermark.header=Wasserzeichen entfernen
|
||||
remove-watermark.selectText.1=PDF auswählen, um Wasserzeichen zu entfernen von:
|
||||
remove-watermark.selectText.1=PDF auswählen, um Wasserzeichen zu entfernen von:
|
||||
remove-watermark.selectText.2=Wasserzeichentext:
|
||||
remove-watermark.submit=Wasserzeichen entfernen
|
||||
|
||||
@@ -258,39 +365,70 @@ removePassword.selectText.2=Passwort
|
||||
removePassword.submit=Entfernen
|
||||
|
||||
|
||||
changeMetadata.title=Metadaten ändern
|
||||
changeMetadata.header=Metadaten ändern
|
||||
changeMetadata.selectText.1=Bitte bearbeiten Sie die Variablen, die Sie ändern möchten
|
||||
changeMetadata.selectText.2=Alle Metadaten löschen
|
||||
changeMetadata.title=Metadaten ändern
|
||||
changeMetadata.header=Metadaten ändern
|
||||
changeMetadata.selectText.1=Bitte bearbeiten Sie die Variablen, die Sie ändern möchten
|
||||
changeMetadata.selectText.2=Alle Metadaten löschen
|
||||
changeMetadata.selectText.3=Benutzerdefinierte Metadaten anzeigen:
|
||||
changeMetadata.author=Autor:
|
||||
changeMetadata.creationDate=Erstellungsdatum (jjjj/MM/tt HH:mm:ss):
|
||||
changeMetadata.creator=Ersteller:
|
||||
changeMetadata.keywords=Schlüsselwörter:
|
||||
changeMetadata.modDate=Änderungsdatum (JJJJ/MM/TT HH:mm:ss):
|
||||
changeMetadata.keywords=Schlüsselwörter:
|
||||
changeMetadata.modDate=Änderungsdatum (JJJJ/MM/TT HH:mm:ss):
|
||||
changeMetadata.producer=Produzent:
|
||||
changeMetadata.subject=Betreff:
|
||||
changeMetadata.title=Titel:
|
||||
changeMetadata.trapped=Gefangen:
|
||||
changeMetadata.selectText.4=Andere Metadaten:
|
||||
changeMetadata.selectText.5=Benutzerdefinierten Metadateneintrag hinzufügen
|
||||
changeMetadata.submit=Ändern
|
||||
changeMetadata.selectText.5=Benutzerdefinierten Metadateneintrag hinzufügen
|
||||
changeMetadata.submit=Ändern
|
||||
|
||||
|
||||
|
||||
xlsToPdf.title=Excel in PDF
|
||||
xlsToPdf.header=Excel in PDF
|
||||
xlsToPdf.selectText.1=XLS- oder XLSX-Excel-Tabelle zum Konvertieren auswählen
|
||||
xlsToPdf.selectText.1=XLS- oder XLSX-Excel-Tabelle zum Konvertieren auswählen
|
||||
xlsToPdf.convert=konvertieren
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
pdfToPDFA.title=PDF zu PDF/A
|
||||
pdfToPDFA.header=PDF zu PDF/A
|
||||
pdfToPDFA.credit=Dieser Dienst verwendet OCRmyPDF für die PDF/A-Konvertierung
|
||||
pdfToPDFA.submit=Konvertieren
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
PDFToWord.title=PDF zu Word
|
||||
PDFToWord.header=PDF zu Word
|
||||
PDFToWord.selectText.1=Ausgabedateiformat
|
||||
PDFToWord.credit=Dieser Dienst verwendet LibreOffice für die Dateikonvertierung.
|
||||
PDFToWord.submit=Konvertieren
|
||||
|
||||
PDFToPresentation.title=PDF zu Präsentation
|
||||
PDFToPresentation.header=PDF zu Präsentation
|
||||
PDFToPresentation.selectText.1=Ausgabedateiformat
|
||||
PDFToPresentation.credit=Dieser Dienst verwendet LibreOffice für die Dateikonvertierung.
|
||||
PDFToPresentation.submit=Konvertieren
|
||||
|
||||
|
||||
PDFToText.title=PDF in Text/RTF
|
||||
PDFToText.header=PDF in Text/RTF
|
||||
PDFToText.selectText.1=Ausgabedateiformat
|
||||
PDFToText.credit=Dieser Dienst verwendet LibreOffice für die Dateikonvertierung.
|
||||
PDFToText.submit=Konvertieren
|
||||
|
||||
|
||||
PDFToHTML.title=PDF in HTML
|
||||
PDFToHTML.header=PDF in HTML
|
||||
PDFToHTML.credit=Dieser Dienst verwendet LibreOffice für die Dateikonvertierung.
|
||||
PDFToHTML.submit=Konvertieren
|
||||
|
||||
PDFToXML.title=PDF in XML
|
||||
PDFToXML.header=PDF in XML
|
||||
PDFToXML.credit=Dieser Dienst verwendet LibreOffice für die Dateikonvertierung.
|
||||
PDFToXML.submit=Konvertieren
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -4,10 +4,10 @@
|
||||
# the direction that the language is written (ltr = left to right, rtl = right to left)
|
||||
language.direction=ltr
|
||||
|
||||
pdfPrompt=Choose PDF
|
||||
multiPdfPrompt=Choose PDFs (2+)
|
||||
pdfPrompt=Select PDF(s)
|
||||
multiPdfPrompt=Select PDFs (2+)
|
||||
multiPdfDropPrompt=Select (or drag & drop) all PDFs you require
|
||||
imgPrompt=Choose Image
|
||||
imgPrompt=Select Image(s)
|
||||
genericSubmit=Submit
|
||||
processTimeWarning=Warning: This process can take up to a minute depending on file-size
|
||||
pageOrderPrompt=Page Order (Enter a comma-separated list of page numbers) :
|
||||
@@ -17,24 +17,31 @@ false=False
|
||||
unknown=Unknown
|
||||
save=Save
|
||||
close=Close
|
||||
|
||||
filesSelected=files selected
|
||||
noFavourites=No favourites added
|
||||
bored=Bored Waiting?
|
||||
#############
|
||||
# HOME-PAGE #
|
||||
#############
|
||||
home.desc=Your locally hosted one-stop-shop for all your PDF needs.
|
||||
|
||||
|
||||
navbar.convert=Convert
|
||||
navbar.security=Security
|
||||
navbar.other=Other
|
||||
navbar.darkmode=Dark Mode
|
||||
navbar.pageOps=Page Operations
|
||||
|
||||
home.merge.title=Merge PDFs
|
||||
home.multiTool.title=PDF Multi Tool
|
||||
home.multiTool.desc=Merge, Rotate, Rearrange, and Remove pages
|
||||
|
||||
home.merge.title=Merge
|
||||
home.merge.desc=Easily merge multiple PDFs into one.
|
||||
|
||||
home.split.title=Split PDFs
|
||||
home.split.title=Split
|
||||
home.split.desc=Split PDFs into multiple documents
|
||||
|
||||
home.rotate.title=Rotate PDFs
|
||||
home.rotate.title=Rotate
|
||||
home.rotate.desc=Easily rotate your PDFs.
|
||||
|
||||
home.imageToPdf.title=Image to PDF
|
||||
@@ -43,10 +50,10 @@ home.imageToPdf.desc=Convert a image (PNG, JPEG, GIF) to PDF.
|
||||
home.pdfToImage.title=PDF to Image
|
||||
home.pdfToImage.desc=Convert a PDF to a image. (PNG, JPEG, GIF)
|
||||
|
||||
home.pdfOrganiser.title=PDF Organiser
|
||||
home.pdfOrganiser.title=Organise
|
||||
home.pdfOrganiser.desc=Remove/Rearrange pages in any order
|
||||
|
||||
home.addImage.title=Add image onto PDF
|
||||
home.addImage.title=Add image
|
||||
home.addImage.desc=Adds a image onto a set location on the PDF (Work in progress)
|
||||
|
||||
home.watermark.title=Add Watermark
|
||||
@@ -58,7 +65,7 @@ home.remove-watermark.desc=Remove watermarks from your PDF document.
|
||||
home.permissions.title=Change Permissions
|
||||
home.permissions.desc=Change the permissions of your PDF document
|
||||
|
||||
home.removePages.title=Remove Pages
|
||||
home.removePages.title=Remove
|
||||
home.removePages.desc=Delete unwanted pages from your PDF document.
|
||||
|
||||
home.addPassword.title=Add Password
|
||||
@@ -67,7 +74,7 @@ home.addPassword.desc=Encrypt your PDF document with a password.
|
||||
home.removePassword.title=Remove Password
|
||||
home.removePassword.desc=Remove password protection from your PDF document.
|
||||
|
||||
home.compressPdfs.title=Compress PDFs
|
||||
home.compressPdfs.title=Compress
|
||||
home.compressPdfs.desc=Compress PDFs to reduce their file size.
|
||||
|
||||
home.changeMetadata.title=Change Metadata
|
||||
@@ -76,12 +83,92 @@ home.changeMetadata.desc=Change/Remove/Add metadata from a PDF document
|
||||
home.fileToPDF.title=Convert file to PDF
|
||||
home.fileToPDF.desc=Convert nearly any file to PDF (DOCX, PNG, XLS, PPT, TXT and more)
|
||||
|
||||
home.ocr.title=Run OCR on PDF
|
||||
home.ocr.desc=Scans and detects text from images within a PDF and re-adds it as text.
|
||||
home.ocr.title=OCR / Cleanup scans
|
||||
home.ocr.desc=Cleanup scans and detects text from images within a PDF and re-adds it as text.
|
||||
|
||||
home.extractImages.title=Extract Images
|
||||
home.extractImages.desc=Extracts all images from a PDF and saves them to zip
|
||||
|
||||
home.pdfToPDFA.title=PDF to PDF/A
|
||||
home.pdfToPDFA.desc=Convert PDF to PDF/A for long-term storage
|
||||
|
||||
home.PDFToWord.title=PDF to Word
|
||||
home.PDFToWord.desc=Convert PDF to Word formats (DOC, DOCX and ODT)
|
||||
|
||||
home.PDFToPresentation.title=PDF to Presentation
|
||||
home.PDFToPresentation.desc=Convert PDF to Presentation formats (PPT, PPTX and ODP)
|
||||
|
||||
home.PDFToText.title=PDF to Text/RTF
|
||||
home.PDFToText.desc=Convert PDF to Text or RTF format
|
||||
|
||||
home.PDFToHTML.title=PDF to HTML
|
||||
home.PDFToHTML.desc=Convert PDF to HTML format
|
||||
|
||||
home.PDFToXML.title=PDF to XML
|
||||
home.PDFToXML.desc=Convert PDF to XML format
|
||||
|
||||
home.ScannerImageSplit.title=Detect/Split Scanned photos
|
||||
home.ScannerImageSplit.desc=Splits multiple photos from within a photo/PDF
|
||||
|
||||
home.sign.title=Sign
|
||||
home.sign.desc=Adds signature to PDF by drawing, text or image
|
||||
|
||||
home.flatten.title=Flatten
|
||||
home.flatten.desc=Remove all interactive elements and forms from a PDF
|
||||
|
||||
home.repair.title=Repair
|
||||
home.repair.desc=Tries to repair a corrupt/broken PDF
|
||||
|
||||
home.removeBlanks.title=Remove Blank pages
|
||||
home.removeBlanks.desc=Detects and removes blank pages from a document
|
||||
|
||||
home.compare.title=Compare
|
||||
home.compare.desc=Compares and shows the differences between 2 PDF Documents
|
||||
|
||||
downloadPdf=Download PDF
|
||||
text=Text
|
||||
font=Font
|
||||
|
||||
removeBlanks.title=Remove Blanks
|
||||
removeBlanks.header=Remove Blank Pages
|
||||
removeBlanks.threshold=Threshold:
|
||||
removeBlanks.thresholdDesc=Threshold for determining how white a white pixel must be
|
||||
removeBlanks.whitePercent=White Percent (%):
|
||||
removeBlanks.whitePercentDesc=Percent of page that must be white to be removed
|
||||
removeBlanks.submit=Remove Blanks
|
||||
|
||||
compare.title=Compare
|
||||
compare.header=Compare PDFs
|
||||
compare.document.1=Document 1
|
||||
compare.document.2=Document 2
|
||||
compare.submit=Compare
|
||||
|
||||
sign.title=Sign
|
||||
sign.header=Sign PDFs
|
||||
sign.upload=Upload Image
|
||||
sign.draw=Draw Signature
|
||||
sign.text=Text Input
|
||||
sign.clear=Clear
|
||||
sign.add=Add
|
||||
|
||||
repair.title=Repair
|
||||
repair.header=Repair PDFs
|
||||
repair.submit=Repair
|
||||
|
||||
flatten.title=Flatten
|
||||
flatten.header=Flatten PDFs
|
||||
flatten.submit=Flatten
|
||||
|
||||
ScannerImageSplit.selectText.1=Angle Threshold:
|
||||
ScannerImageSplit.selectText.2=Sets the minimum absolute angle required for the image to be rotated (default: 10).
|
||||
ScannerImageSplit.selectText.3=Tolerance:
|
||||
ScannerImageSplit.selectText.4=Determines the range of color variation around the estimated background color (default: 30).
|
||||
ScannerImageSplit.selectText.5=Minimum Area:
|
||||
ScannerImageSplit.selectText.6=Sets the minimum area threshold for a photo (default: 10000).
|
||||
ScannerImageSplit.selectText.7=Minimum Contour Area:
|
||||
ScannerImageSplit.selectText.8=Sets the minimum contour area threshold for a photo
|
||||
ScannerImageSplit.selectText.9=Border Size:
|
||||
ScannerImageSplit.selectText.10=Sets the size of the border added and removed to prevent white borders in the output (default: 1).
|
||||
|
||||
navbar.settings=Settings
|
||||
settings.title=Settings
|
||||
@@ -91,17 +178,33 @@ settings.downloadOption.title=Choose download option (For single file non zip do
|
||||
settings.downloadOption.1=Open in same window
|
||||
settings.downloadOption.2=Open in new window
|
||||
settings.downloadOption.3=Download file
|
||||
settings.zip=Zip multi-download files
|
||||
settings.zipThreshold=Zip files when the number of downloaded files exceeds
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#OCR
|
||||
ocr.title=OCR
|
||||
ocr.header=OCR (Optical Character Recognition)
|
||||
ocr.title=OCR / Scan Cleanup
|
||||
ocr.header=Cleanup Scans / OCR (Optical Character Recognition)
|
||||
ocr.selectText.1=Select languages that are to be detected within the PDF (Ones listed are the ones currently detected):
|
||||
ocr.selectText.2=Produce text file containing OCR text alongside the OCR'ed PDF
|
||||
ocr.selectText.3=Correct pages were scanned at a skewed angle by rotating them back into place
|
||||
ocr.selectText.4=Clean page so its less likely that OCR will find text in background noise. (No output change)
|
||||
ocr.selectText.5=Clean page so its less likely that OCR will find text in background noise, maintains cleanup in output.
|
||||
ocr.selectText.6=Ignores pages that have interactive text on them, only OCRs pages that are images
|
||||
ocr.selectText.7=Force OCR, will OCR Every page removing all original text elements
|
||||
ocr.selectText.8=Normal (Will error if PDF contains text)
|
||||
ocr.selectText.9=Additional Settings
|
||||
ocr.selectText.10=OCR Mode
|
||||
ocr.selectText.11=Remove images after OCR (Removes ALL images, only useful if part of conversion step)
|
||||
ocr.selectText.12=Render Type (Advanced)
|
||||
ocr.help=Please read this documentation on how to use this for other languages and/or use not in docker
|
||||
ocr.credit=This service uses OCRmyPDF and Tesseract for OCR.
|
||||
ocr.submit=Process PDF with OCR
|
||||
|
||||
|
||||
|
||||
extractImages.title=Extract Images
|
||||
extractImages.header=Extract Images
|
||||
extractImages.selectText=Select image format to convert extracted images to
|
||||
@@ -132,7 +235,8 @@ compress.submit=Compress
|
||||
|
||||
#Add image
|
||||
addImage.title=Add Image
|
||||
addImage.header=Add image to PDF (Work in progress)
|
||||
addImage.header=Add image to PDF
|
||||
addImage.everyPage=Every Page?
|
||||
addImage.submit=Add image
|
||||
|
||||
|
||||
@@ -146,6 +250,10 @@ pdfOrganiser.title=Page Organiser
|
||||
pdfOrganiser.header=PDF Page Organiser
|
||||
pdfOrganiser.submit=Rearrange Pages
|
||||
|
||||
#multiTool
|
||||
multiTool.title=PDF Multi Tool
|
||||
multiTool.header=PDF Multi Tool
|
||||
|
||||
|
||||
#pageRemover
|
||||
pageRemover.title=Page Remover
|
||||
@@ -181,7 +289,12 @@ split.submit=Split
|
||||
imageToPDF.title=Image to PDF
|
||||
imageToPDF.header=Image to PDF
|
||||
imageToPDF.submit=Convert
|
||||
|
||||
imageToPDF.selectText.1=Stretch to fit
|
||||
imageToPDF.selectText.2=Auto rotate PDF
|
||||
imageToPDF.selectText.3=Multi file logic (Only enabled if working with multiple images)
|
||||
imageToPDF.selectText.4=Merge into single PDF
|
||||
imageToPDF.selectText.5=Convert to separate PDFs
|
||||
|
||||
#pdfToImage
|
||||
pdfToImage.title=PDF to Image
|
||||
pdfToImage.header=PDF to Image
|
||||
@@ -281,15 +394,42 @@ xlsToPdf.convert=convert
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
pdfToPDFA.title=PDF To PDF/A
|
||||
pdfToPDFA.header=PDF To PDF/A
|
||||
pdfToPDFA.credit=This service uses OCRmyPDF for PDF/A conversion
|
||||
pdfToPDFA.submit=Convert
|
||||
|
||||
|
||||
|
||||
PDFToWord.title=PDF to Word
|
||||
PDFToWord.header=PDF to Word
|
||||
PDFToWord.selectText.1=Output file format
|
||||
PDFToWord.credit=This service uses LibreOffice for file conversion.
|
||||
PDFToWord.submit=Convert
|
||||
|
||||
PDFToPresentation.title=PDF to Presentation
|
||||
PDFToPresentation.header=PDF to Presentation
|
||||
PDFToPresentation.selectText.1=Output file format
|
||||
PDFToPresentation.credit=This service uses LibreOffice for file conversion.
|
||||
PDFToPresentation.submit=Convert
|
||||
|
||||
|
||||
PDFToText.title=PDF to Text/RTF
|
||||
PDFToText.header=PDF to Text/RTF
|
||||
PDFToText.selectText.1=Output file format
|
||||
PDFToText.credit=This service uses LibreOffice for file conversion.
|
||||
PDFToText.submit=Convert
|
||||
|
||||
|
||||
PDFToHTML.title=PDF to HTML
|
||||
PDFToHTML.header=PDF to HTML
|
||||
PDFToHTML.credit=This service uses LibreOffice for file conversion.
|
||||
PDFToHTML.submit=Convert
|
||||
|
||||
PDFToXML.title=PDF to XML
|
||||
PDFToXML.header=PDF to XML
|
||||
PDFToXML.credit=This service uses LibreOffice for file conversion.
|
||||
PDFToXML.submit=Convert
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,293 +0,0 @@
|
||||
###########
|
||||
# Generic #
|
||||
###########
|
||||
# the direction that the language is written (ltr = left to right, rtl = right to left)
|
||||
language.direction=ltr
|
||||
|
||||
pdfPrompt=Choose PDF
|
||||
multiPdfPrompt=Choose PDFs (2+)
|
||||
multiPdfDropPrompt=Select (or drag & drop) all PDFs you require
|
||||
imgPrompt=Choose Image
|
||||
genericSubmit=Submit
|
||||
processTimeWarning=Warning: This process can take up to a minute depending on file-size
|
||||
pageOrderPrompt=Page Order (Enter a comma-separated list of page numbers) :
|
||||
goToPage=Go
|
||||
true=True
|
||||
false=False
|
||||
unknown=Unknown
|
||||
save=Save
|
||||
close=Close
|
||||
|
||||
#############
|
||||
# HOME-PAGE #
|
||||
#############
|
||||
home.desc=Your locally hosted one-stop-shop for all your PDF needs.
|
||||
|
||||
navbar.convert=Convert
|
||||
navbar.security=Security
|
||||
navbar.other=Other
|
||||
navbar.darkmode=Dark Mode
|
||||
|
||||
home.merge.title=Merge PDFs
|
||||
home.merge.desc=Easily merge multiple PDFs into one.
|
||||
|
||||
home.split.title=Split PDFs
|
||||
home.split.desc=Split PDFs into multiple documents
|
||||
|
||||
home.rotate.title=Rotate PDFs
|
||||
home.rotate.desc=Easily rotate your PDFs.
|
||||
|
||||
home.imageToPdf.title=Image to PDF
|
||||
home.imageToPdf.desc=Convert a images (PNG, JPEG, GIF) to PDF.
|
||||
|
||||
home.pdfToImage.title=PDF to Image
|
||||
home.pdfToImage.desc=Convert a PDF to a image. (PNG, JPEG, GIF)
|
||||
|
||||
home.pdfOrganiser.title=PDF Organizer
|
||||
home.pdfOrganiser.desc=Remove/Rearrange pages in any order
|
||||
|
||||
home.addImage.title=Add image onto PDF
|
||||
home.addImage.desc=Adds a image onto a set location on the PDF (Work in progress)
|
||||
|
||||
home.watermark.title=Add Watermark
|
||||
home.watermark.desc=Add a custom watermark to your PDF document.
|
||||
|
||||
home.remove-watermark.title=Remove Watermark
|
||||
home.remove-watermark.desc=Remove watermarks from your PDF document.
|
||||
|
||||
home.permissions.title=Change Permissions
|
||||
home.permissions.desc=Change the permissions of your PDF document
|
||||
|
||||
home.removePages.title=Remove Pages
|
||||
home.removePages.desc=Delete unwanted pages from your PDF document.
|
||||
|
||||
home.addPassword.title=Add Password
|
||||
home.addPassword.desc=Encrypt your PDF document with a password.
|
||||
|
||||
home.removePassword.title=Remove Password
|
||||
home.removePassword.desc=Remove password protection from your PDF document.
|
||||
|
||||
home.compressPdfs.title=Compress PDFs
|
||||
home.compressPdfs.desc=Compress PDFs to reduce their file size.
|
||||
|
||||
home.changeMetadata.title=Change Metadata
|
||||
home.changeMetadata.desc=Change/Remove/Add metadata from a PDF document
|
||||
|
||||
home.fileToPDF.title=Convert file to PDF
|
||||
home.fileToPDF.desc=Convert nearly any file to PDF (DOCX, PNG, XLS, PPT, TXT and more)
|
||||
|
||||
home.ocr.title=Run OCR on PDF
|
||||
home.ocr.desc=Scans and detects text from images within a PDF and re-adds it as text.
|
||||
|
||||
home.extractImages.title=Extract Images
|
||||
home.extractImages.desc=Extracts all images from a PDF and saves them to zip
|
||||
|
||||
|
||||
navbar.settings=Settings
|
||||
settings.title=Settings
|
||||
settings.update=Update available
|
||||
settings.appVersion=App Version:
|
||||
settings.downloadOption.title=Choose download option (For single file non zip downloads):
|
||||
settings.downloadOption.1=Open in same window
|
||||
settings.downloadOption.2=Open in new window
|
||||
settings.downloadOption.3=Download file
|
||||
settings.zip=Zip multi-download files
|
||||
|
||||
#OCR
|
||||
ocr.title=OCR
|
||||
ocr.header=OCR (Optical Character Recognition)
|
||||
ocr.selectText.1=Select languages that are to be detected within the PDF (Ones listed are the ones currently detected):
|
||||
ocr.selectText.2=Produce text file containing OCR text alongside the OCR'ed PDF
|
||||
ocr.help=Please read this documentation on how to use this for other languages and/or use not in docker
|
||||
ocr.credit=This service uses OCRmyPDF and Tesseract for OCR.
|
||||
ocr.submit=Process PDF with OCR
|
||||
|
||||
extractImages.title=Extract Images
|
||||
extractImages.header=Extract Images
|
||||
extractImages.selectText=Select image format to convert extracted images to
|
||||
extractImages.submit=Extract
|
||||
|
||||
|
||||
#File to PDF
|
||||
fileToPDF.title=File to PDF
|
||||
fileToPDF.header=Convert any file to PDF
|
||||
fileToPDF.credit=This service uses LibreOffice and Unoconv for file conversion.
|
||||
fileToPDF.supportedFileTypes=Supported file types should include the below however for a full updated list of supported formats, please refer to the LibreOffice documentation
|
||||
fileToPDF.submit=Convert to PDF
|
||||
|
||||
|
||||
#Add image
|
||||
addImage.title=Add Image
|
||||
addImage.header=Add image to PDF (Work in progress)
|
||||
addImage.submit=Add image
|
||||
|
||||
#compress
|
||||
compress.title=Compress
|
||||
compress.header=Compress PDF
|
||||
compress.credit=This service uses OCRmyPDF for PDF Compress/Optimisation.
|
||||
compress.selectText.1=Optimization level:
|
||||
compress.selectText.2=0 (No optimization)
|
||||
compress.selectText.3=1 (Default, lossless optimization)
|
||||
compress.selectText.4=2 (Lossy optimization)
|
||||
compress.selectText.5=3 (Lossy optimization, more aggressive)
|
||||
compress.selectText.6=Enable fast web view (linearize PDF)
|
||||
compress.selectText.7=Enable lossy JBIG2 encoding
|
||||
compress.submit=Compress
|
||||
|
||||
|
||||
#merge
|
||||
merge.title=Merge
|
||||
merge.header=Merge multiple PDFs (2+)
|
||||
merge.submit=Merge
|
||||
|
||||
#pdfOrganiser
|
||||
pdfOrganiser.title=Page Organizer
|
||||
pdfOrganiser.header=PDF Page Organizer
|
||||
pdfOrganiser.submit=Rearrange Pages
|
||||
|
||||
|
||||
#pageRemover
|
||||
pageRemover.title=Page Remover
|
||||
pageRemover.header=PDF Page remover
|
||||
pageRemover.pagesToDelete=Pages to delete (Enter a comma-separated list of page numbers) :
|
||||
pageRemover.submit=Delete Pages
|
||||
|
||||
#rotate
|
||||
rotate.title=Rotate PDF
|
||||
rotate.header=Rotate PDF
|
||||
rotate.selectAngle=Select rotation angle (in multiples of 90 degrees):
|
||||
rotate.submit=Rotate
|
||||
|
||||
|
||||
|
||||
|
||||
#merge
|
||||
split.title=Split PDF
|
||||
split.header=Split PDF
|
||||
split.desc.1=The numbers you select are the page number you wish to do a split on
|
||||
split.desc.2=As such selecting 1,3,7-8 would split a 10 page document into 6 separate PDFS with:
|
||||
split.desc.3=Document #1: Page 1
|
||||
split.desc.4=Document #2: Page 2 and 3
|
||||
split.desc.5=Document #3: Page 4, 5 and 6
|
||||
split.desc.6=Document #4: Page 7
|
||||
split.desc.7=Document #5: Page 8
|
||||
split.desc.8=Document #6: Page 9 and 10
|
||||
split.splitPages=Enter pages to split on:
|
||||
split.submit=Split
|
||||
|
||||
|
||||
#merge
|
||||
imageToPDF.title=Image to PDF
|
||||
imageToPDF.header=Image to PDF
|
||||
imageToPDF.submit=Convert
|
||||
|
||||
#pdfToImage
|
||||
pdfToImage.title=PDF to Image
|
||||
pdfToImage.header=PDF to Image
|
||||
pdfToImage.selectText=Image Format
|
||||
pdfToImage.singleOrMultiple=Image result type
|
||||
pdfToImage.single=Single Big Image
|
||||
pdfToImage.multi=Multiple Images
|
||||
pdfToImage.colorType=Color type
|
||||
pdfToImage.color=Color
|
||||
pdfToImage.grey=Grayscale
|
||||
pdfToImage.blackwhite=Black and White (May lose data!)
|
||||
pdfToImage.submit=Convert
|
||||
|
||||
|
||||
#addPassword
|
||||
addPassword.title=Add Password
|
||||
addPassword.header=Add password (Encrypt)
|
||||
addPassword.selectText.1=Select PDF to encrypt
|
||||
addPassword.selectText.2=Password
|
||||
addPassword.selectText.3=Encryption Key Length
|
||||
addPassword.selectText.4=Higher values are stronger, but lower values have better compatibility.
|
||||
addPassword.selectText.5=Permissions to set
|
||||
addPassword.selectText.6=Prevent assembly of document
|
||||
addPassword.selectText.7=Prevent content extraction
|
||||
addPassword.selectText.8=Prevent extraction for accessibility
|
||||
addPassword.selectText.9=Prevent filling in form
|
||||
addPassword.selectText.10=Prevent modification
|
||||
addPassword.selectText.11=Prevent annotation modification
|
||||
addPassword.selectText.12=Prevent printing
|
||||
addPassword.selectText.13=Prevent printing different formats
|
||||
addPassword.submit=Encrypt
|
||||
|
||||
#watermark
|
||||
watermark.title=Add Watermark
|
||||
watermark.header=Add Watermark
|
||||
watermark.selectText.1=Select PDF to add watermark to:
|
||||
watermark.selectText.2=Watermark Text:
|
||||
watermark.selectText.3=Font Size:
|
||||
watermark.selectText.4=Rotation (0-360):
|
||||
watermark.selectText.5=widthSpacer (Space between each watermark horizontally):
|
||||
watermark.selectText.6=heightSpacer (Space between each watermark vertically):
|
||||
watermark.selectText.7=Opacity (0% - 100%):
|
||||
watermark.submit=Add Watermark
|
||||
|
||||
#remove-watermark
|
||||
remove-watermark.title=Remove Watermark
|
||||
remove-watermark.header=Remove Watermark
|
||||
remove-watermark.selectText.1=Select PDF to remove watermark from:
|
||||
remove-watermark.selectText.2=Watermark Text:
|
||||
remove-watermark.submit=Remove Watermark
|
||||
|
||||
#Change permissions
|
||||
permissions.title=Change Permissions
|
||||
permissions.header=Change Permissions
|
||||
permissions.warning=Warning to have these permissions be unchangeable it is recommended to set them with a password via the add-password page
|
||||
permissions.selectText.1=Select PDF to change permissions
|
||||
permissions.selectText.2=Permissions to set
|
||||
permissions.selectText.3=Prevent assembly of document
|
||||
permissions.selectText.4=Prevent content extraction
|
||||
permissions.selectText.5=Prevent extraction for accessibility
|
||||
permissions.selectText.6=Prevent filling in form
|
||||
permissions.selectText.7=Prevent modification
|
||||
permissions.selectText.8=Prevent annotation modification
|
||||
permissions.selectText.9=Prevent printing
|
||||
permissions.selectText.10=Prevent printing different formats
|
||||
permissions.submit=Change
|
||||
|
||||
#remove password
|
||||
removePassword.title=Remove password
|
||||
removePassword.header=Remove password (Decrypt)
|
||||
removePassword.selectText.1=Select PDF to Decrypt
|
||||
removePassword.selectText.2=Password
|
||||
removePassword.submit=Remove
|
||||
|
||||
changeMetadata.title=Change Metadata
|
||||
changeMetadata.header=Change Metadata
|
||||
changeMetadata.selectText.1=Please edit the variables you wish to change
|
||||
changeMetadata.selectText.2=Delete all metadata
|
||||
changeMetadata.selectText.3=Show Custom Metadata:
|
||||
changeMetadata.author=Author:
|
||||
changeMetadata.creationDate=Creation Date (yyyy/MM/dd HH:mm:ss):
|
||||
changeMetadata.creator=Creator:
|
||||
changeMetadata.keywords=Keywords:
|
||||
changeMetadata.modDate=Modification Date (yyyy/MM/dd HH:mm:ss):
|
||||
changeMetadata.producer=Producer:
|
||||
changeMetadata.subject=Subject:
|
||||
changeMetadata.title=Title:
|
||||
changeMetadata.trapped=Trapped:
|
||||
changeMetadata.selectText.4=Other Metadata:
|
||||
changeMetadata.selectText.5=Add Custom Metadata Entry
|
||||
changeMetadata.submit=Change
|
||||
|
||||
|
||||
fileToPDF.credit=This service uses LibreOffice and Unoconv for file conversion.
|
||||
fileToPDF.supportedFileTypes=Supported file types should include the below however for a full updated list of supported formats, please refer to the LibreOffice documentation
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
429
src/main/resources/messages_es_ES.properties
Normal file
@@ -0,0 +1,429 @@
|
||||
###########
|
||||
# Generic #
|
||||
###########
|
||||
# the direction that the language is written (ltr = left to right, rtl = right to left)
|
||||
language.direction=ltr
|
||||
|
||||
pdfPrompt=Selecciona PDF(s)
|
||||
multiPdfPrompt=Selecciona PDFs (2+)
|
||||
multiPdfDropPrompt=Selecciona (o arrastra y suelta) todos los PDFs que quieras
|
||||
imgPrompt=Selecciona Imagen(es)
|
||||
genericSubmit=Enviar
|
||||
processTimeWarning=Advertencia: este proceso puede tardar hasta un minuto dependiendo del tamaño del archivo
|
||||
pageOrderPrompt=Orden de páginas (Introduzca una lista de números de página separados por coma):
|
||||
goToPage=Ir
|
||||
true=Verdadero
|
||||
false=Falso
|
||||
unknown=Desconocido
|
||||
save=Guardar
|
||||
close=Cerrar
|
||||
filesSelected=archivos seleccionados
|
||||
noFavourites=No se agregaron favoritos
|
||||
bored=¿Aburrido de esperar?
|
||||
#############
|
||||
# HOME-PAGE #
|
||||
#############
|
||||
home.desc=Tu autohospedada ventanilla única para todas tus necesidades PDF.
|
||||
|
||||
navbar.convert=Convertir
|
||||
navbar.security=Seguridad
|
||||
navbar.other=Otro
|
||||
navbar.darkmode=Modo oscuro
|
||||
navbar.pageOps=Operaciones de página
|
||||
|
||||
home.multiTool.title=Multiherramienta PDF
|
||||
home.multiTool.desc=Combinar, rotar, reorganizar y eliminar páginas
|
||||
|
||||
home.merge.title=Unir
|
||||
home.merge.desc=Unir fácilmente múltiples PDFs en uno.
|
||||
|
||||
home.split.title=Divide
|
||||
home.split.desc=Divide PDFs en múltiples documentos
|
||||
|
||||
home.rotate.title=Rota
|
||||
home.rotate.desc=Rota fácilmente tus PDFs.
|
||||
|
||||
home.imageToPdf.title=Imagen a PDF
|
||||
home.imageToPdf.desc=Convierte una imagen (PNG, JPEG, GIF) a PDF.
|
||||
|
||||
home.pdfToImage.title=PDF a Imagen
|
||||
home.pdfToImage.desc=Convierte un PDF a una imagen. (PNG, JPEG, GIF)
|
||||
|
||||
home.pdfOrganiser.title=Organizador
|
||||
home.pdfOrganiser.desc=Elimina/Reorganiza páginas en cualquier orden
|
||||
|
||||
home.addImage.title=Agregar imagen al PDF
|
||||
home.addImage.desc=Agrega una imagen en una ubicación establecida en el PDF (trabajo en progreso)
|
||||
|
||||
home.watermark.title=Añade marca de agua
|
||||
home.watermark.desc=Añade una marca de agua predefinida a tu documento PDF.
|
||||
|
||||
home.remove-watermark.title=Elimina marca de agua
|
||||
home.remove-watermark.desc=Elimina marcas de agua de tu documento PDF.
|
||||
|
||||
home.permissions.title=Cambia Permisos
|
||||
home.permissions.desc=Cambia los permisos de tu documento PDF
|
||||
|
||||
home.removePages.title=Elimina
|
||||
home.removePages.desc=Elimina páginas no deseadas de tu documento PDF.
|
||||
|
||||
home.addPassword.title=Añade Contraseña
|
||||
home.addPassword.desc=Encripta tu documento PDF con una contraseña.
|
||||
|
||||
home.removePassword.title=Elimina Contraseña
|
||||
home.removePassword.desc=Elimina la contraseña de tu documento PDF.
|
||||
|
||||
home.compressPdfs.title=Comprime
|
||||
home.compressPdfs.desc=Comprime PDFs para reducir el tamaño del fichero.
|
||||
|
||||
home.changeMetadata.title=Cambia Metadatos
|
||||
home.changeMetadata.desc=Cambia/Elimina/Añade metadatos a tu documento PDF.
|
||||
|
||||
home.fileToPDF.title=Convierte fichero a PDF
|
||||
home.fileToPDF.desc=Convierte casi cualquier archivo a PDF (DOCX, PNG, XLS, PPT, TXT y más)
|
||||
|
||||
home.ocr.title=Ejecute 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.extractImages.title=Extraer imágenes
|
||||
home.extractImages.desc=Extrae todas las imágenes de un PDF y las guarda en zip
|
||||
|
||||
home.pdfToPDFA.title=Convierte PDF to PDF/A
|
||||
home.pdfToPDFA.desc=Convierte PDF to PDF/A para almacenamiento a largo plazo
|
||||
|
||||
home.PDFToWord.title=PDF a Word
|
||||
home.PDFToWord.desc=Convertir formatos PDF a Word (DOC, DOCX y ODT)
|
||||
|
||||
home.PDFToPresentation.title=PDF a presentación
|
||||
home.PDFToPresentation.desc=Convertir PDF a formatos de presentación (PPT, PPTX y ODP)
|
||||
|
||||
home.PDFToText.title=PDF a texto/RTF
|
||||
home.PDFToText.desc=Convertir PDF a texto o formato RTF
|
||||
|
||||
home.PDFToHTML.title=PDF a HTML
|
||||
home.PDFToHTML.desc=Convertir PDF a formato HTML
|
||||
|
||||
home.PDFToXML.title=PDF a XML
|
||||
home.PDFToXML.desc=Convertir PDF a formato XML
|
||||
|
||||
home.ScannerImageSplit.title=Detectar/Dividir fotos escaneadas
|
||||
home.ScannerImageSplit.desc=Dividir varias fotos dentro de una foto/PDF
|
||||
|
||||
home.sign.title=Firmar
|
||||
home.sign.desc=Añade firma a PDF mediante dibujo, texto o imagen
|
||||
|
||||
home.flatten.title=Aplanar
|
||||
home.flatten.desc=Eliminar todos los elementos y formularios interactivos de un PDF
|
||||
|
||||
home.repair.title=Reparar
|
||||
home.repair.desc=Intenta reparar un PDF corrupto/roto
|
||||
|
||||
home.removeBlanks.title=Eliminar páginas en blanco
|
||||
home.removeBlanks.desc=Detecta y elimina páginas en blanco de un documento
|
||||
|
||||
home.compare.title=Comparar
|
||||
home.compare.desc=Compara y muestra las diferencias entre 2 documentos PDF
|
||||
|
||||
downloadPdf=Descargar PDF
|
||||
text=Texto
|
||||
font=Fuente
|
||||
|
||||
removeBlanks.title=Eliminar espacios en blanco
|
||||
removeBlanks.header=Eliminar páginas en blanco
|
||||
removeBlanks.threshold=Umbral:
|
||||
removeBlanks.thresholdDesc=Umbral para determinar qué tan blanco debe ser un píxel blanco
|
||||
removeBlanks.whitePercent=Porcentaje de blanco (%):
|
||||
removeBlanks.whitePercentDesc=Porcentaje de página que debe ser blanca para ser eliminada
|
||||
removeBlanks.submit=Eliminar espacios en blanco
|
||||
|
||||
compare.title=Comparar
|
||||
compare.header=Comparar archivos PDF
|
||||
compare.document.1=Documento 1
|
||||
compare.document.2=Documento 2
|
||||
compare.submit=Comparar
|
||||
|
||||
sign.title=Firmar
|
||||
sign.header=Firmar archivos PDF
|
||||
sign.upload=Subir imagen
|
||||
sign.draw=Dibujar firma
|
||||
sign.text=Entrada de texto
|
||||
sign.clear=Borrar
|
||||
sign.add=Agregar
|
||||
|
||||
repair.title=Reparar
|
||||
repair.header=Reparar archivos PDF
|
||||
repair.submit=Reparar
|
||||
|
||||
flatten.title=Aplanar
|
||||
flatten.header=Acoplar archivos PDF
|
||||
flatten.submit=Aplanar
|
||||
|
||||
ScannerImageSplit.selectText.1=Umbral de ángulo:
|
||||
ScannerImageSplit.selectText.2=Establece el ángulo absoluto mínimo requerido para rotar la imagen (predeterminado: 10).
|
||||
ScannerImageSplit.selectText.3=Tolerancia:
|
||||
ScannerImageSplit.selectText.4=Determina el rango de variación de color alrededor del color de fondo estimado (predeterminado: 30).
|
||||
ScannerImageSplit.selectText.5=Área mínima:
|
||||
ScannerImageSplit.selectText.6=Establece el umbral mínimo de área para una foto (predeterminado: 10000).
|
||||
ScannerImageSplit.selectText.7=Área de contorno mínima:
|
||||
ScannerImageSplit.selectText.8=Establece el umbral mínimo del área de contorno para una foto
|
||||
ScannerImageSplit.selectText.9=Tamaño del borde:
|
||||
ScannerImageSplit.selectText.10=Establece el tamaño del borde agregado y eliminado para evitar bordes blancos en la salida (predeterminado: 1).
|
||||
|
||||
navbar.settings=Ajustes
|
||||
settings.title=Ajustes
|
||||
settings.update=Actualización disponible
|
||||
settings.appVersion=Version de la aplicacion:
|
||||
settings.downloadOption.title=Elija la opción de descarga (para descargas de un solo archivo sin zip):
|
||||
settings.downloadOption.1=Abre en la misma ventana
|
||||
settings.downloadOption.2=Abre en una nueva ventana
|
||||
settings.downloadOption.3=Descarga el fichero
|
||||
settings.zipThreshold=Ficheros Zip cuando excede el número de ficheros descargados
|
||||
|
||||
|
||||
|
||||
|
||||
#OCR
|
||||
ocr.title=OCR / Escaneo de limpieza
|
||||
ocr.header=Escaneos de limpieza / OCR (Reconocimiento óptico de caracteres)
|
||||
ocr.selectText.1=Selecciona los idiomas que se detectarán en el PDF (Los enumerados son los detectados actualmente):
|
||||
ocr.selectText.2=Produzca un archivo de texto que contenga texto OCR junto con el PDF editado con OCR
|
||||
ocr.selectText.3=Corrija las páginas que se escanearon en un ángulo torcido girándolas nuevamente a su lugar
|
||||
ocr.selectText.4=Limpie la página para que sea menos probable que el OCR encuentre texto en el ruido de fondo. (Sin cambio de salida)
|
||||
ocr.selectText.5=Limpie la página para que sea menos probable que el OCR encuentre texto en el ruido de fondo, mantiene la limpieza en la salida.
|
||||
ocr.selectText.6=Ignora las páginas que tienen texto interactivo, solo las páginas OCR que son imágenes
|
||||
ocr.selectText.7=Fuerza OCR, OCR eliminará en cada página todo el texto original
|
||||
ocr.selectText.8=Normal (Se producirá un error si el PDF contiene texto)
|
||||
ocr.selectText.9=Ajustes Adicionales
|
||||
ocr.selectText.10=Modo OCR
|
||||
ocr.selectText.11=Eliminar imágenes después de OCR (Elimina TODAS las imágenes, solo es útil si es parte del paso de conversión)
|
||||
ocr.selectText.12=Tipo de procesamiento (avanzado)
|
||||
ocr.help=Lea esta documentación sobre cómo usar esto para otros idiomas y/o no usarlo en docker
|
||||
ocr.credit=Este servicio utiliza OCRmyPDF y Tesseract para OCR.
|
||||
ocr.submit=Procesa PDF con OCR
|
||||
|
||||
|
||||
|
||||
extractImages.title=Extraer imágenes
|
||||
extractImages.header=Extraer imágenes
|
||||
extractImages.selectText=Selecciona el formato de imagen para convertir las imágenes extraídas
|
||||
extractImages.submit=Extraer
|
||||
|
||||
|
||||
#File to PDF
|
||||
fileToPDF.title=Fichero a PDF
|
||||
fileToPDF.header=Convierte cualquier fichero a PDF
|
||||
fileToPDF.credit=Este servicio usa LibreOffice y Unoconv para la conversión de ficheros.
|
||||
fileToPDF.supportedFileTypes=Los tipos de ficheros soportados deben incluir los de abajo sin embargo para una completa y acutualizada lista de formatos soportados, por favor consulte la documentación de LibreOffice
|
||||
fileToPDF.submit=Convertir a PDF
|
||||
|
||||
|
||||
#compress
|
||||
compress.title=Comprimir
|
||||
compress.header=Comprimir PDF
|
||||
compress.credit=Este servicio usa OCRmyPDF para la Compresión/Optimizatión del PDF.
|
||||
compress.selectText.1=Nivel de Optimización:
|
||||
compress.selectText.2=0 (Sin optimización)
|
||||
compress.selectText.3=1 (Por defecto, optimización sin pérdidas)
|
||||
compress.selectText.4=2 (Optimización con pérdida)
|
||||
compress.selectText.5=3 (Optimización con pérdida, más agresiva)
|
||||
compress.selectText.6=Habilita la vista web rápida (linealizar PDF)
|
||||
compress.selectText.7=Habilita la codificación JBIG2 con pérdida
|
||||
compress.submit=Comprimir
|
||||
|
||||
|
||||
#Add image
|
||||
addImage.title=Añade Imagen
|
||||
addImage.header=Añade image de PDF
|
||||
addImage.everyPage=¿Todas las páginas?
|
||||
addImage.submit=Añade imagen
|
||||
|
||||
|
||||
#merge
|
||||
merge.title=Une
|
||||
merge.header=Une múltiples PDFs (2+)
|
||||
merge.submit=Une
|
||||
|
||||
#pdfOrganiser
|
||||
pdfOrganiser.title=Organizador de páginas
|
||||
pdfOrganiser.header=Organizador de páginas PDF
|
||||
pdfOrganiser.submit=Organiza páginas
|
||||
|
||||
#herramienta multiple
|
||||
multiTool.title=Multiherramienta PDF
|
||||
multiTool.header=Multiherramienta PDF
|
||||
|
||||
#pageRemover
|
||||
pageRemover.title=Eliminador de páginas
|
||||
pageRemover.header=Eliminador de páginas PDF
|
||||
pageRemover.pagesToDelete=Páginas a eliminar (Introduzca una lista de números de página separados por coma):
|
||||
pageRemover.submit=Elimina Páginas
|
||||
|
||||
#rotate
|
||||
rotate.title=Rotar PDF
|
||||
rotate.header=Rotar PDF
|
||||
rotate.SeleccionaAngle=Selecciona ángulo de rotación (múltiple de 90 grados):
|
||||
rotate.submit=Rotar
|
||||
|
||||
|
||||
|
||||
|
||||
#merge
|
||||
split.title=Dividir PDF
|
||||
split.header=Dividir PDF
|
||||
split.desc.1=Los números que selecciona son el número de página en el que desea hacer una división
|
||||
split.desc.2=Como tal, seleccionar 1,3,7-8 dividiría un documento de 10 páginas en 6 archivos PDF separados con:
|
||||
split.desc.3=Documento #1: Página 1
|
||||
split.desc.4=Documento #2: Páginas 2 y 3
|
||||
split.desc.5=Documento #3: Páginas 4, 5 y 6
|
||||
split.desc.6=Documento #4: Página 7
|
||||
split.desc.7=Documento #5: Página 8
|
||||
split.desc.8=Documento #6: Páginas 9 y 10
|
||||
split.splitPages=Introduzca las páginas para dividir:
|
||||
split.submit=Dividir
|
||||
|
||||
|
||||
#merge
|
||||
imageToPDF.title=Imagen a PDF
|
||||
imageToPDF.header=Imagen a PDF
|
||||
imageToPDF.submit=Convertir
|
||||
imageToPDF.selectText.1=Estirar para ajustar
|
||||
imageToPDF.selectText.2=Auto rotación PDF
|
||||
imageToPDF.selectText.3=Lógica de archivos múltiples (Únicamente activado si funciona con multiples imágenes)
|
||||
imageToPDF.selectText.4=Une en un único PDF
|
||||
imageToPDF.selectText.5=Convertir a PDFs separados
|
||||
|
||||
#pdfToImage
|
||||
pdfToImage.title=PDF a Imagen
|
||||
pdfToImage.header=PDF a Imagen
|
||||
pdfToImage.selectText=Formato de Imagen
|
||||
pdfToImage.singleOrMultiple=Tipo resultante de imagen
|
||||
pdfToImage.single=Imagen Grande Única
|
||||
pdfToImage.multi=Múltiples Imágenes
|
||||
pdfToImage.colorType=Tipo de color
|
||||
pdfToImage.color=Color
|
||||
pdfToImage.grey=Escala de Grises
|
||||
pdfToImage.blackwhite=Blanco y Negro (¡Puedes perder datos!)
|
||||
pdfToImage.submit=Convertir
|
||||
|
||||
#addPassword
|
||||
addPassword.title=Añade Contraseña
|
||||
addPassword.header=Añade contraseña (Encripta)
|
||||
addPassword.selectText.1=Selecciona PDF para encriptar
|
||||
addPassword.selectText.2=Contraseña
|
||||
addPassword.selectText.3=Longitud de la clave de cifrado
|
||||
addPassword.selectText.4=Valores altos son más fuertes, pero valores bajos tienen mejor compatibilidad.
|
||||
addPassword.selectText.5=Permisos para establecer
|
||||
addPassword.selectText.6=Impedir el ensamblaje del documento
|
||||
addPassword.selectText.7=Impedir la extracción de contenido
|
||||
addPassword.selectText.8=Impedir la extracción para la accesibilidad
|
||||
addPassword.selectText.9=Impedir rellenar formulario
|
||||
addPassword.selectText.10=Impedir modificación
|
||||
addPassword.selectText.11=Impedir modificación de anotaciones
|
||||
addPassword.selectText.12=Impedir imprimir
|
||||
addPassword.selectText.13=Impedir imprimir diferentes formatos
|
||||
addPassword.submit=Encripta
|
||||
|
||||
#watermark
|
||||
watermark.title=Añade marca de agua
|
||||
watermark.header=Añade marca de agua
|
||||
watermark.selectText.1=Selecciona PDF para añadir marca de agua:
|
||||
watermark.selectText.2=Texto de la marca de agua:
|
||||
watermark.selectText.3=Tamaño de la Fuente:
|
||||
watermark.selectText.4=Rotación (0-360):
|
||||
watermark.selectText.5=Ancho (Espacio entre cada marca de agua horizontalmente):
|
||||
watermark.selectText.6=Alto (Espacio entre cada marca de agua verticalmente):
|
||||
watermark.selectText.7=Opacidad (0% - 100%):
|
||||
watermark.submit=Añade marca de agua
|
||||
|
||||
#remove-watermark
|
||||
remove-watermark.title=Elimina marca de agua
|
||||
remove-watermark.header=Elimina marca de agua
|
||||
remove-watermark.selectText.1=Selecciona PDF para eliminar la marca de agua:
|
||||
remove-watermark.selectText.2=Texto de la marca de agua:
|
||||
remove-watermark.submit=Elimina marca de agua
|
||||
|
||||
#Change permissions
|
||||
permissions.title=Cambiar Permisos
|
||||
permissions.header=Cambiar Permisos
|
||||
permissions.warning=Advertencia para que estos permisos no se puedan cambiar, se recomienda configurarlos con una contraseña a través de la página de cambio de contraseña
|
||||
permissions.selectText.1=Selecciona PDF para cambiar los permisos
|
||||
permissions.selectText.2=Permisos a establecer
|
||||
permissions.selectText.3=Impedir el ensamblaje del documento
|
||||
permissions.selectText.4=Impedir la extracción de contenido
|
||||
permissions.selectText.5=Impedir la extracción para la accesibilidad
|
||||
permissions.selectText.6=Impedir rellenar formulario
|
||||
permissions.selectText.7=Impedir modificación
|
||||
permissions.selectText.8=Impedir modificación de anotaciones
|
||||
permissions.selectText.9=Impedir imprimir
|
||||
permissions.selectText.10=Impedir imprimir diferentes formatos
|
||||
permissions.submit=Cambiar
|
||||
|
||||
#remove password
|
||||
removePassword.title=Elimina contraseña
|
||||
removePassword.header=Elimina contraseña (Desencripta)
|
||||
removePassword.selectText.1=Selecciona PDF para Desencriptar
|
||||
removePassword.selectText.2=Contraseña
|
||||
removePassword.submit=Elimina
|
||||
|
||||
changeMetadata.title=Cambia Metadatos
|
||||
changeMetadata.header=Cambia Metadatos
|
||||
changeMetadata.selectText.1=Edite las variables que desea cambiar
|
||||
changeMetadata.selectText.2=Elimina todos los metadatos
|
||||
changeMetadata.selectText.3=Mostrar metadatos personalizados:
|
||||
changeMetadata.author=Autor:
|
||||
changeMetadata.creationDate=Fecha de Creación (yyyy/MM/dd HH:mm:ss):
|
||||
changeMetadata.creator=Creador:
|
||||
changeMetadata.keywords=Palabras clave:
|
||||
changeMetadata.modDate=Fecha de Modificación (yyyy/MM/dd HH:mm:ss):
|
||||
changeMetadata.producer=Productor:
|
||||
changeMetadata.subject=Asunto:
|
||||
changeMetadata.title=Título:
|
||||
changeMetadata.trapped=Trapped:
|
||||
changeMetadata.selectText.4=Otros Metadatos:
|
||||
changeMetadata.selectText.5=Agregar entrada de metadatos personalizados
|
||||
changeMetadata.submit=Cambia
|
||||
|
||||
xlsToPdf.title=Excel a PDF
|
||||
xlsToPdf.header=Excel a PDF
|
||||
xlsToPdf.selectText.1=Selecciona hoja de cálculo de Excel XLS o XLSX para convertir
|
||||
xlsToPdf.convert=convertir
|
||||
|
||||
|
||||
|
||||
|
||||
pdfToPDFA.title=PDF a PDF/A
|
||||
pdfToPDFA.header=PDF a PDF/A
|
||||
pdfToPDFA.credit=Este servicio usa OCRmyPDF para la conversión a PDF/A
|
||||
pdfToPDFA.submit=Convertir
|
||||
|
||||
|
||||
|
||||
PDFToWord.title=PDF a Word
|
||||
PDFToWord.header=PDF a Word
|
||||
PDFToWord.selectText.1=Formato de archivo de salida
|
||||
PDFToWord.credit=Este servicio utiliza LibreOffice para la conversión de archivos.
|
||||
PDFToWord.submit=Convertir
|
||||
|
||||
PDFToPresentation.title=PDF a presentación
|
||||
PDFToPresentation.header=PDF a presentación
|
||||
PDFToPresentation.selectText.1=Formato de archivo de salida
|
||||
PDFToPresentation.credit=Este servicio utiliza LibreOffice para la conversión de archivos.
|
||||
PDFToPresentation.submit=Convertir
|
||||
|
||||
|
||||
PDFToText.title=PDF a texto/RTF
|
||||
PDFToText.header=PDF a texto/RTF
|
||||
PDFToText.selectText.1=Formato de archivo de salida
|
||||
PDFToText.credit=Este servicio utiliza LibreOffice para la conversión de archivos.
|
||||
PDFToText.submit=Convertir
|
||||
|
||||
|
||||
PDFToHTML.title=PDF a HTML
|
||||
PDFToHTML.header=PDF a HTML
|
||||
PDFToHTML.credit=Este servicio utiliza LibreOffice para la conversión de archivos.
|
||||
PDFToHTML.submit=Convertir
|
||||
|
||||
PDFToXML.title=PDF a XML
|
||||
PDFToXML.header=PDF a XML
|
||||
PDFToXML.credit=Este servicio utiliza LibreOffice para la conversión de archivos.
|
||||
PDFToXML.submit=Convertir
|
||||
@@ -5,7 +5,7 @@
|
||||
###########
|
||||
# Generic #
|
||||
###########
|
||||
# the direction that the language is written (ltr = left to right, rtl = right to left)
|
||||
# the direction that the language is written (ltr=left to right, rtl=right to left)
|
||||
language.direction=ltr
|
||||
|
||||
pdfPrompt=Choisir PDF
|
||||
@@ -13,15 +13,17 @@ multiPdfPrompt=Choisir des PDF (2+)
|
||||
multiPdfDropPrompt=Sélectionnez (ou glissez-déposez) tous les PDF dont vous avez besoin
|
||||
imgPrompt=Choisir une image
|
||||
genericSubmit=Soumettre
|
||||
processTimeWarning=AttentionÂ: ce processus peut prendre jusqu'Ã une minute en fonction de la taille du fichier
|
||||
pageOrderPrompt=Ordre des pages (Entrez une liste de numéros de page séparés par des virgules)Â:
|
||||
processTimeWarning=Attention<EFBFBD>: ce processus peut prendre jusqu'à une minute en fonction de la taille du fichier
|
||||
pageOrderPrompt=Ordre des pages (Entrez une liste de numéros de page séparés par des virgules):
|
||||
goToPage=Aller
|
||||
true=Vrai
|
||||
false=Faux
|
||||
unknown=Inconnu
|
||||
save=Enregistrer
|
||||
close=Fermer
|
||||
|
||||
filesSelected=fichiers sélectionnés
|
||||
noFavourites=Aucun favori ajouté
|
||||
bored=Ennuyé d'attendre ?
|
||||
#############
|
||||
# HOME-PAGE #
|
||||
#############
|
||||
@@ -31,14 +33,18 @@ navbar.convert=Convertir
|
||||
navbar.security=Sécurité
|
||||
navbar.other=Autre
|
||||
navbar.darkmode=Mode sombre
|
||||
navbar.pageOps=Opérations de page
|
||||
|
||||
home.merge.title=Fusionner des PDF
|
||||
home.multiTool.title=Multi-outil PDF
|
||||
home.multiTool.desc=Fusionner, faire pivoter, réorganiser et supprimer des pages
|
||||
|
||||
home.merge.title=Fusionnez
|
||||
home.merge.desc=Fusionnez facilement plusieurs PDF en un seul.
|
||||
|
||||
home.split.title=Fractionner les PDF
|
||||
home.split.title=Fractionner
|
||||
home.split.desc=Diviser les PDF en plusieurs documents
|
||||
|
||||
home.rotate.title=Faire pivoter les PDF
|
||||
home.rotate.title=Tourner
|
||||
home.rotate.desc=Faites pivoter facilement vos PDF.
|
||||
|
||||
home.imageToPdf.title=Image au format PDF
|
||||
@@ -47,7 +53,7 @@ home.imageToPdf.desc=Convertir une image (PNG, JPEG, GIF) en PDF.
|
||||
home.pdfToImage.title=PDF vers image
|
||||
home.pdfToImage.desc=Convertir un PDF en image. (PNG, JPEG, GIF)
|
||||
|
||||
home.pdfOrganiser.title=Organisateur PDF
|
||||
home.pdfOrganiser.title=Organisateur
|
||||
home.pdfOrganiser.desc=Supprimer/Réorganiser les pages dans n'importe quel ordre
|
||||
|
||||
home.addImage.title=Ajouter une image au PDF
|
||||
@@ -62,7 +68,7 @@ home.remove-watermark.desc=Supprimez les filigranes de votre document PDF.
|
||||
home.permissions.title=Modifier les autorisations
|
||||
home.permissions.desc=Modifier les permissions de votre document PDF
|
||||
|
||||
home.removePages.title=Supprimer des pages
|
||||
home.removePages.title=Supprimer
|
||||
home.removePages.desc=Supprimez les pages inutiles de votre document PDF.
|
||||
|
||||
home.addPassword.title=Ajouter un mot de passe
|
||||
@@ -71,76 +77,166 @@ home.addPassword.desc=Cryptez votre document PDF avec un mot de passe.
|
||||
home.removePassword.title=Supprimer le mot de passe
|
||||
home.removePassword.desc=Supprimez la protection par mot de passe de votre document PDF.
|
||||
|
||||
home.compressPdfs.title=Compresser les PDF
|
||||
home.compressPdfs.title=Compresser
|
||||
home.compressPdfs.desc=Compressez les PDF pour réduire leur taille de fichier.
|
||||
|
||||
home.changeMetadata.title=Modifier les métadonnées
|
||||
home.changeMetadata.desc=Modifier/Supprimer/Ajouter des métadonnées d'un document PDF
|
||||
home.changeMetadata.title=Modifier les métadonnées
|
||||
home.changeMetadata.desc=Modifier/Supprimer/Ajouter des métadonnées d'un document PDF
|
||||
|
||||
|
||||
home.fileToPDF.title=Convertir un fichier en PDF
|
||||
home.fileToPDF.desc=Convertissez presque n\u2019importe quel fichier en PDF (DOCX, PNG, XLS, PPT, TXT et plus)
|
||||
|
||||
home.ocr.title=Exécuter OCR sur PDF
|
||||
home.ocr.desc=Analyse et détecte le texte des images d\u2019un fichier PDF et le rajoute en tant que texte.
|
||||
home.ocr.title=Exécuter l'OCR sur les scans PDF et/ou de nettoyage
|
||||
home.ocr.desc=Le nettoyage analyse et détecte le texte des images dans un PDF et le rajoute en tant que texte.
|
||||
|
||||
home.extractImages.title=Extraire les images
|
||||
home.extractImages.desc=Extrait toutes les images d\u2019un PDF et les enregistre au format zip
|
||||
|
||||
|
||||
navbar.settings=Paramètres
|
||||
settings.title=Paramètres
|
||||
settings.update=Mise à jour disponible
|
||||
settings.appVersion=Version de l\u2019application :
|
||||
settings.downloadOption.title=Choisissez l\u2019option de téléchargement (pour les téléchargements sans fichier unique) :
|
||||
settings.downloadOption.1=Ouvrir dans la même fenêtre
|
||||
settings.downloadOption.2=Ouvrir dans une nouvelle fenêtre
|
||||
settings.downloadOption.3=Fichier téléchargé
|
||||
settings.zip=Fichiers multi-téléchargements Zip
|
||||
home.pdfToPDFA.title=Convertir PDF en PDF/A
|
||||
home.pdfToPDFA.desc=Convertir un PDF en PDF/A pour un stockage à long terme
|
||||
|
||||
home.PDFToWord.title=PDF vers Word
|
||||
home.PDFToWord.desc=Convertir les formats PDF en Word (DOC, DOCX et ODT)
|
||||
|
||||
home.PDFToPresentation.title=PDF vers présentation
|
||||
home.PDFToPresentation.desc=Convertir des PDF en formats de présentation (PPT, PPTX et ODP)
|
||||
|
||||
home.PDFToText.title=PDF vers texte/RTF
|
||||
home.PDFToText.desc=Convertir un PDF au format Texte ou RTF
|
||||
|
||||
home.PDFToHTML.title=PDF vers HTML
|
||||
home.PDFToHTML.desc=Convertir le PDF au format HTML
|
||||
|
||||
home.PDFToXML.title=PDF vers XML
|
||||
home.PDFToXML.desc=Convertir le PDF au format XML
|
||||
|
||||
home.ScannerImageSplit.title=Détecter/diviser les photos numérisées
|
||||
home.ScannerImageSplit.desc=Divise plusieurs photos à partir d'une photo/PDF
|
||||
|
||||
home.sign.title=Signe
|
||||
home.sign.desc=Ajoute une signature au PDF par dessin, texte ou image
|
||||
|
||||
home.flatten.title=Aplatir
|
||||
home.flatten.desc=Supprimer tous les éléments et formulaires interactifs d'un PDF
|
||||
|
||||
home.repair.title=Réparer
|
||||
home.repair.desc=Essaye de réparer un PDF corrompu/cassé
|
||||
|
||||
home.removeBlanks.title=Supprimer les pages vierges
|
||||
home.removeBlanks.desc=Détecte et supprime les pages vierges d'un document
|
||||
|
||||
home.compare.title=Comparer
|
||||
home.compare.desc=Compare et affiche les différences entre 2 documents PDF
|
||||
|
||||
downloadPdf=Télécharger le PDF
|
||||
text=Texte
|
||||
font=Police
|
||||
|
||||
removeBlanks.title=Supprimer les blancs
|
||||
removeBlanks.header=Supprimer les pages vierges
|
||||
removeBlanks.threshold=Seuil :
|
||||
removeBlanks.thresholdDesc=Seuil pour déterminer à quel point un pixel blanc doit être blanc
|
||||
removeBlanks.whitePercent=Pourcentage blanc (%) :
|
||||
removeBlanks.whitePercentDesc=Pourcentage de page qui doit être blanche pour être supprimée
|
||||
removeBlanks.submit=Supprimer les blancs
|
||||
|
||||
compare.title=Comparer
|
||||
compare.header=Comparer des PDF
|
||||
compare.document.1=Document 1
|
||||
compare.document.2=Document 2
|
||||
compare.submit=Comparer
|
||||
|
||||
sign.title=Signe
|
||||
sign.header=Signer des PDF
|
||||
sign.upload=Télécharger l'image
|
||||
sign.draw=Dessiner une signature
|
||||
sign.text=Saisie de texte
|
||||
sign.clear=Effacer
|
||||
sign.add=Ajouter
|
||||
|
||||
repair.title=Réparer
|
||||
repair.header=Réparer les PDF
|
||||
repair.submit=Réparer
|
||||
|
||||
flatten.title=Aplatir
|
||||
flatten.header=Aplatir les PDF
|
||||
flatten.submit=Aplatir
|
||||
|
||||
ScannerImageSplit.selectText.1=Seuil d'angle :
|
||||
ScannerImageSplit.selectText.2=Définit l'angle absolu minimum requis pour la rotation de l'image (par défaut : 10).
|
||||
ScannerImageSplit.selectText.3=Tolérance :
|
||||
ScannerImageSplit.selectText.4=Détermine la plage de variation de couleur autour de la couleur d'arrière-plan estimée (par défaut : 30).
|
||||
ScannerImageSplit.selectText.5=Zone minimale :
|
||||
ScannerImageSplit.selectText.6=Définit le seuil de zone minimum pour une photo (par défaut : 10000).
|
||||
ScannerImageSplit.selectText.7=Zone de contour minimale :
|
||||
ScannerImageSplit.selectText.8=Définit le seuil de zone de contour minimum pour une photo
|
||||
ScannerImageSplit.selectText.9=Taille de la bordure :
|
||||
ScannerImageSplit.selectText.10=Définit la taille de la bordure ajoutée et supprimée pour éviter les bordures blanches dans la sortie (par défaut : 1).
|
||||
|
||||
navbar.settings=Paramètres
|
||||
settings.title=Paramètres
|
||||
settings.update=Mise à jour disponible
|
||||
settings.appVersion=Version de l'application :
|
||||
settings.downloadOption.title=Choisissez l'option de téléchargement (pour les téléchargements sans fichier unique) :
|
||||
settings.downloadOption.1=Ouvrir dans la même fenêtre
|
||||
settings.downloadOption.2=Ouvrir dans une nouvelle fenêtre
|
||||
settings.downloadOption.3=Fichier téléchargé
|
||||
settings.zipThreshold=Zip les fichiers lorsque le nombre de fichiers téléchargés dépasse
|
||||
|
||||
|
||||
#OCR
|
||||
ocr.title=OCR
|
||||
ocr.header=OCR (reconnaissance optique de caractères)
|
||||
ocr.selectText.1=Sélectionnez les langues à détecter dans le fichier PDF (celles répertoriées sont celles actuellement détectées) :
|
||||
ocr.selectText.2=Produire un fichier texte contenant du texte OCR à côté du PDF OCR
|
||||
ocr.help=Veuillez lire cette documentation pour savoir comment l\u2019utiliser pour d\u2019autres langues et/ou une utilisation non dans docker
|
||||
ocr.credit=Ce service utilise OCRmyPDF et Tesseract pour l\u2019OCR.
|
||||
ocr.title=OCR / Nettoyage de numérisation
|
||||
ocr.header=Nettoyage des scans / OCR (reconnaissance optique des caractères)
|
||||
ocr.selectText.1=Sélectionnez les langues à détecter dans le PDF (celles répertoriées sont celles actuellement détectées) :
|
||||
ocr.selectText.2=Produire un fichier texte contenant du texte OCR avec le PDF OCR
|
||||
ocr.selectText.3=Les pages correctes ont été numérisées à un angle oblique en les remettant en place
|
||||
ocr.selectText.4=Nettoyer la page pour qu'il soit moins probable que l'OCR trouve du texte dans le bruit de fond. (Pas de changement de sortie)
|
||||
ocr.selectText.5=Nettoyer la page afin qu'il soit moins probable que l'OCR trouve du texte dans le bruit de fond, maintient le nettoyage dans la sortie.
|
||||
ocr.selectText.6=Ignore les pages contenant du texte interactif, seulement les pages OCR qui sont des images
|
||||
ocr.selectText.7=Forcer l'OCR, OCR chaque page supprimera tous les éléments de texte d'origine
|
||||
ocr.selectText.8=Normal (Erreur si le PDF contient du texte)
|
||||
ocr.selectText.9=Paramètres supplémentaires
|
||||
ocr.selectText.10=Mode ROC
|
||||
ocr.selectText.11=Supprimer les images après l'OCR (Supprime TOUTES les images, utile uniquement si elles font partie de l'étape de conversion)
|
||||
ocr.selectText.12=Type de rendu (avancé)
|
||||
ocr.help=Veuillez lire cette documentation pour savoir comment l'utiliser pour d'autres langues et/ou une utilisation non dans docker
|
||||
ocr.credit=Ce service utilise OCRmyPDF et Tesseract pour l'OCR.
|
||||
ocr.submit=Traiter PDF avec OCR
|
||||
|
||||
|
||||
extractImages.title=Extraire les images
|
||||
extractImages.header=Extraire les images
|
||||
extractImages.selectText=Sélectionner le format d\u2019image pour convertir les images extraites en
|
||||
extractImages.submit=Extrait
|
||||
extractImages.selectText=Sélectionner le format d'image pour convertir les images extraites en
|
||||
extractImages.submit=Extraire
|
||||
|
||||
|
||||
#File au format PDF
|
||||
fileToPDF.title=Fichier au PDF
|
||||
fileToPDF.header=Convertir n\u2019importe quel fichier au format PDF
|
||||
#File to PDF
|
||||
fileToPDF.title=Fichier au format PDF
|
||||
fileToPDF.header=Convertir n'importe quel fichier au format PDF
|
||||
fileToPDF.credit=Ce service utilise LibreOffice et Unoconv pour la conversion de fichiers.
|
||||
fileToPDF.supportedFileTypes=Les types de fichiers pris en charge doivent inclure les éléments ci-dessous, mais pour une liste complète et mise à jour des formats pris en charge, veuillez vous référer à la documentation de LibreOffice
|
||||
fileToPDF.supportedFileTypes=Les types de fichiers pris en charge doivent inclure les éléments ci-dessous, mais pour une liste complète et mise à jour des formats pris en charge, veuillez vous référer à la documentation de LibreOffice.
|
||||
fileToPDF.submit=Convertir en PDF
|
||||
|
||||
|
||||
|
||||
|
||||
#Add image
|
||||
addImage.title=Ajouter une image
|
||||
addImage.header=Ajouter une image au PDF (Travail en cours)
|
||||
addImage.header=Ajouter une image au PDF
|
||||
addImage.everyPage=Chaque page?
|
||||
addImage.submit=Ajouter une image
|
||||
|
||||
#compress
|
||||
compress.title=Compresser
|
||||
compress.header=Compresser le PDF
|
||||
compress.credit=Ce service utilise OCRmyPDF pour la compression/optimisation PDF.
|
||||
compress.selectText.1=Niveau d\u2019optimisation :
|
||||
compress.selectText.2=0 (pas d\u2019optimisation)
|
||||
compress.selectText.3=1 (par défaut, optimisation sans perte)
|
||||
compress.selectText.1=Niveau d'optimisation :
|
||||
compress.selectText.2=0 (pas d'optimisation)
|
||||
compress.selectText.3=1 (par défaut, optimisation sans perte)
|
||||
compress.selectText.4=2 (optimisation avec perte)
|
||||
compress.selectText.5=3 (optimisation avec perte, plus agressive)
|
||||
compress.selectText.6=Activer l\u2019affichage Web rapide (linéariser PDF)
|
||||
compress.selectText.7=Activer l\u2019encodage JBIG2 avec perte
|
||||
compress.selectText.6=Activer l'affichage Web rapide (linéariser PDF)
|
||||
compress.selectText.7=Activer l'encodage JBIG2 avec perte
|
||||
compress.submit=Compresser
|
||||
|
||||
|
||||
@@ -154,53 +250,58 @@ pdfOrganiser.title=Organisateur de pages
|
||||
pdfOrganiser.header=Organisateur de pages PDF
|
||||
pdfOrganiser.submit=Réorganiser les pages
|
||||
|
||||
#Outil Multi-fonction
|
||||
multiTool.title=Multi-outil PDF
|
||||
multiTool.header=Outil multiple PDF
|
||||
|
||||
|
||||
#pageRemover
|
||||
pageRemover.title=Suppresseur de pages
|
||||
pageRemover.header=Outil de suppression de pages PDF
|
||||
pageRemover.pagesToDelete=Pages à supprimer (Entrez une liste de numéros de page séparés par des virgules)Â:
|
||||
pageRemover.pagesToDelete=Pages à supprimer (Entrez une liste de numéros de page séparés par des virgules):
|
||||
pageRemover.submit=Supprimer des pages
|
||||
|
||||
#rotate
|
||||
rotate.title=Faire pivoter le PDF
|
||||
rotate.header=Faire pivoter le PDF
|
||||
rotate.selectAngle=S\u00e9lectionner l'angle de rotation (en multiples de 90 degr\u00e9s):
|
||||
rotate.selectAngle=Sélectionner l'angle de rotation (en multiples de 90 degrés) :
|
||||
rotate.submit=Rotation
|
||||
|
||||
|
||||
|
||||
|
||||
#merge
|
||||
#Split PDF
|
||||
split.title=Fractionner le PDF
|
||||
split.header=Diviser le PDF
|
||||
split.desc.1=Les numéros que vous sélectionnez sont le numéro de page sur lequel vous souhaitez faire un fractionnement
|
||||
split.desc.2=Ainsi, la sélection de 1,3,7-8 diviserait un document de 10 pages en 6 PDF distincts avecÂ:
|
||||
split.desc.3=Document #1Â: Page 1
|
||||
split.desc.4=Document #2Â: Pages 2 et 3
|
||||
split.desc.5=Document #3Â: Pages 4, 5 et 6
|
||||
split.desc.6=Document #4Â: Page 7
|
||||
split.desc.7=Document #5Â: Page 8
|
||||
split.desc.8=Document #6Â: Pages 9 et 10
|
||||
split.splitPages=Entrez les pages sur lesquelles fractionnerÂ:
|
||||
split.desc.1=Les numéros que vous sélectionnez sont le numéro de page sur lequel vous souhaitez faire un fractionnement.
|
||||
split.desc.2=Ainsi, la sélection de 1,3,7-8 diviserait un document de 10 pages en 6 PDF distincts avec :
|
||||
split.desc.3=Document #1 : Page 1
|
||||
split.desc.4=Document #2 : Pages 2 et 3
|
||||
split.desc.5=Document #3 : Pages 4, 5 et 6
|
||||
split.desc.6=Document #4 : Page 7
|
||||
split.desc.7=Document #5 : Page 8
|
||||
split.desc.8=Document #6 : Pages 9 et 10
|
||||
split.splitPages=Entrez les pages sur lesquelles fractionner :
|
||||
split.submit=Diviser
|
||||
|
||||
|
||||
#merge
|
||||
#imageToPDF
|
||||
imageToPDF.title=Image au format PDF
|
||||
imageToPDF.header=Image au format PDF
|
||||
imageToPDF.submit=Convertir
|
||||
imageToPDF.selectText.1=Étirer pour s'adapter
|
||||
imageToPDF.selectText.2=Rotation automatique du PDF
|
||||
imageToPDF.selectText.3=Logique de fichiers multiples (activé uniquement si vous travaillez avec plusieurs images)
|
||||
imageToPDF.selectText.4=Fusionner en un seul PDF
|
||||
imageToPDF.selectText.5=Convertir en PDFs distincts
|
||||
|
||||
#pdfToImage
|
||||
#PDF to Image
|
||||
pdfToImage.title=PDF vers image
|
||||
pdfToImage.header=PDF vers image
|
||||
pdfToImage.selectText=Format d'image
|
||||
pdfToImage.singleOrMultiple=Type de résultat d'image
|
||||
pdfToImage.singleOrMultiple=Type de résultat d'image
|
||||
pdfToImage.single=Une seule grande image
|
||||
pdfToImage.multi=Plusieurs images
|
||||
pdfToImage.colorType=Type de couleur
|
||||
pdfToImage.color=Couleur
|
||||
pdfToImage.grey=Niveaux de gris
|
||||
pdfToImage.blackwhite=Noir et Blanc (Peut perdre des données !)
|
||||
pdfToImage.blackwhite=Noir et Blanc (Peut perdre des données !)
|
||||
pdfToImage.submit=Convertir
|
||||
|
||||
#addPassword
|
||||
@@ -224,36 +325,36 @@ addPassword.submit=Crypter
|
||||
#watermark
|
||||
watermark.title=Ajouter un filigrane
|
||||
watermark.header=Ajouter un filigrane
|
||||
watermark.selectText.1=Sélectionnez le PDF auquel ajouter un filigraneÂ:
|
||||
watermark.selectText.2=Texte du filigraneÂ:
|
||||
watermark.selectText.3=Taille de la policeÂ:
|
||||
watermark.selectText.4=Rotation (0-360)Â:
|
||||
watermark.selectText.5=widthSpacer (Espace entre chaque filigrane horizontalement)Â:
|
||||
watermark.selectText.6=heightSpacer (Espace entre chaque filigrane verticalement)Â:
|
||||
watermark.selectText.7=Opacité (0 % - 100 %):
|
||||
watermark.selectText.1=Sélectionnez le PDF auquel ajouter un filigrane :
|
||||
watermark.selectText.2=Texte du filigrane :
|
||||
watermark.selectText.3=Taille de la police :
|
||||
watermark.selectText.4=Rotation (0-360) :
|
||||
watermark.selectText.5=widthSpacer (Espace entre chaque filigrane horizontalement) :
|
||||
watermark.selectText.6=heightSpacer (Espace entre chaque filigrane verticalement) :
|
||||
watermark.selectText.7=Opacité (0 % - 100 %) :
|
||||
watermark.submit=Ajouter un filigrane
|
||||
|
||||
#remove-watermark
|
||||
remove-watermark.title=Supprimer le filigrane
|
||||
remove-watermark.header=Supprimer le filigrane
|
||||
remove-watermark.selectText.1=Sélectionnez le PDF pour supprimer le filigrane:
|
||||
remove-watermark.selectText.2=Texte du filigrane:
|
||||
remove-watermark.selectText.1=Sélectionnez le PDF pour supprimer le filigrane :
|
||||
remove-watermark.selectText.2=Texte du filigrane :
|
||||
remove-watermark.submit=Supprimer le filigrane
|
||||
|
||||
#Change permissions
|
||||
#Change Permissions
|
||||
permissions.title=Modifier les autorisations
|
||||
permissions.header=Modifier les autorisations
|
||||
permissions.warning=Attention pour que ces permissions soient immuables il est recommandé de les définir avec un mot de passe via la page add-password
|
||||
permissions.selectText.1=Sélectionnez PDF pour modifier les autorisations
|
||||
permissions.selectText.2=Autorisations à définir
|
||||
permissions.warning=Attention pour que ces permissions soient immuables il est recommandé de les définir avec un mot de passe via la page add-password.
|
||||
permissions.selectText.1=Sélectionnez le PDF pour modifier les autorisations :
|
||||
permissions.selectText.2=Autorisations à définir :
|
||||
permissions.selectText.3=Employer l'assemblage du document
|
||||
permissions.selectText.4=Employer l'extraction de contenu
|
||||
permissions.selectText.5=Employer l'extraction pour l'accessibilité
|
||||
permissions.selectText.6=Employer de remplir le formulaire
|
||||
permissions.selectText.7=Employer la modification
|
||||
permissions.selectText.8=Employer la modification des annotations
|
||||
permissions.selectText.9=Employer l'impression
|
||||
permissions.selectText.10=Emp�cher l'impression de diff�rents formats
|
||||
permissions.selectText.6=Employer pour remplir le formulaire
|
||||
permissions.selectText.7=Employer pour la modification
|
||||
permissions.selectText.8=Employer pour la modification des annotations
|
||||
permissions.selectText.9=Employer pour l'impression
|
||||
permissions.selectText.10=Empêcher l'impression de différents formats
|
||||
permissions.submit=Modificateur
|
||||
|
||||
#supprimer le mot de passe
|
||||
@@ -263,27 +364,67 @@ removePassword.selectText.1=Sélectionnez le PDF à déchiffrer
|
||||
removePassword.selectText.2=Mot de passe
|
||||
removePassword.submit=Supprimer
|
||||
|
||||
changeMetadata.title=Modifier les métadonnées
|
||||
changeMetadata.header=Modifier les métadonnées
|
||||
changeMetadata.selectText.1=Veuillez modifier les variables que vous souhaitez modifier
|
||||
changeMetadata.selectText.2=Supprimer toutes les métadonnées
|
||||
changeMetadata.selectText.3=Afficher les métadonnées personnalisées:
|
||||
changeMetadata.author=Auteur:
|
||||
changeMetadata.creationDate=Date de création (aaaa/MM/jj HH:mm:ss):
|
||||
changeMetadata.creator=Créateur:
|
||||
changeMetadata.keywords=Mots clés:
|
||||
changeMetadata.modDate=Date de modification (aaaa/MM/jj HH:mm:ss):
|
||||
changeMetadata.producer=Producteur:
|
||||
changeMetadata.subject=Objet:
|
||||
changeMetadata.title=Titre:
|
||||
changeMetadata.trapped=Piégé:
|
||||
changeMetadata.selectText.4=Autres métadonnées:
|
||||
changeMetadata.selectText.5=Ajouter une entrée de métadonnées personnalisées
|
||||
#Change Metadata
|
||||
changeMetadata.title=Modifier les métadonnées
|
||||
changeMetadata.header=Modifier les métadonnées
|
||||
changeMetadata.selectText.1=Veuillez modifier les variables que vous souhaitez modifier.
|
||||
changeMetadata.selectText.2=Supprimer toutes les métadonnées.
|
||||
changeMetadata.selectText.3=Afficher les métadonnées personnalisées :
|
||||
changeMetadata.author=Auteur :
|
||||
changeMetadata.creationDate=Date de création (aaaa/MM/jj HH:mm:ss) :
|
||||
changeMetadata.creator=Créateur :
|
||||
changeMetadata.keywords=Mots clés :
|
||||
changeMetadata.modDate=Date de modification (aaaa/MM/jj HH:mm:ss) :
|
||||
changeMetadata.producer=Producteur :
|
||||
changeMetadata.subject=Objet :
|
||||
changeMetadata.title=Titre :
|
||||
changeMetadata.trapped=Piégé :
|
||||
changeMetadata.selectText.4=Autres métadonnées :
|
||||
changeMetadata.selectText.5=Ajouter une entrée de métadonnées personnalisées
|
||||
changeMetadata.submit=Modifier
|
||||
|
||||
|
||||
#XLS to PDF
|
||||
xlsToPdf.title=Excel vers PDF
|
||||
xlsToPdf.header=Excel en PDF
|
||||
xlsToPdf.selectText.1=Sélectionnez une feuille Excel XLS ou XLSX à convertir
|
||||
xlsToPdf.convert=convertir
|
||||
xlsToPdf.selectText.1=Sélectionnez une feuille Excel XLS ou XLSX à convertir.
|
||||
xlsToPdf.convert=Convertir
|
||||
|
||||
pdfToPDFA.title=PDF vers PDF/A
|
||||
pdfToPDFA.header=PDF vers PDF/A
|
||||
pdfToPDFA.credit=Ce service utilise OCRmyPDF pour la conversion PDF/A
|
||||
pdfToPDFA.submit=Convertir
|
||||
|
||||
|
||||
|
||||
|
||||
PDFToWord.title=PDF vers Word
|
||||
PDFToWord.header=PDF vers Word
|
||||
PDFToWord.selectText.1=Format du fichier de sortie
|
||||
PDFToWord.credit=Ce service utilise LibreOffice pour la conversion de fichiers.
|
||||
PDFToWord.submit=Convertir
|
||||
|
||||
PDFToPresentation.title=PDF vers présentation
|
||||
PDFToPresentation.header=PDF vers présentation
|
||||
PDFToPresentation.selectText.1=Format du fichier de sortie
|
||||
PDFToPresentation.credit=Ce service utilise LibreOffice pour la conversion de fichiers.
|
||||
PDFToPresentation.submit=Convertir
|
||||
|
||||
|
||||
PDFToText.title=PDF vers Texte/RTF
|
||||
PDFToText.header=PDF vers texte/RTF
|
||||
PDFToText.selectText.1=Format du fichier de sortie
|
||||
PDFToText.credit=Ce service utilise LibreOffice pour la conversion de fichiers.
|
||||
PDFToText.submit=Convertir
|
||||
|
||||
|
||||
PDFToHTML.title=PDF vers HTML
|
||||
PDFToHTML.header=PDF vers HTML
|
||||
PDFToHTML.credit=Ce service utilise LibreOffice pour la conversion de fichiers.
|
||||
PDFToHTML.submit=Convertir
|
||||
|
||||
PDFToXML.title=PDF vers XML
|
||||
PDFToXML.header=PDF vers XML
|
||||
PDFToXML.credit=Ce service utilise LibreOffice pour la conversion de fichiers.
|
||||
PDFToXML.submit=Convertir
|
||||
|
||||
|
||||
|
||||
431
src/main/resources/messages_zh_CN.properties
Normal file
@@ -0,0 +1,431 @@
|
||||
###########
|
||||
# Generic #
|
||||
###########
|
||||
# the direction that the language is written (ltr = left to right, rtl = right to left)
|
||||
language.direction=ltr
|
||||
|
||||
pdfPrompt=选择PDF
|
||||
multiPdfPrompt=选择多个PDF(2个或更多)
|
||||
multiPdfDropPrompt=选择(或拖拽)所需的PDF
|
||||
imgPrompt=选择图像
|
||||
genericSubmit=提交
|
||||
processTimeWarning=警告:此过程可能需要多达一分钟,具体时间取决于文件大小
|
||||
pageOrderPrompt=页面顺序(输入逗号分隔的页码列表):
|
||||
goToPage=到
|
||||
true=True
|
||||
false=False
|
||||
unknown=未知
|
||||
save=保存
|
||||
close=关闭
|
||||
filesSelected=\u9009\u62E9\u7684\u6587\u4EF6
|
||||
noFavourites=\u6CA1\u6709\u6DFB\u52A0\u6536\u85CF\u5939
|
||||
bored=\u65E0\u804A\u7B49\u5F85\uFF1F
|
||||
#############
|
||||
# HOME-PAGE #
|
||||
#############
|
||||
home.desc=您的本地托管一站式服务,满足您的所有PDF需求。
|
||||
|
||||
|
||||
navbar.convert=转换
|
||||
navbar.security=安全
|
||||
navbar.other=其他
|
||||
navbar.darkmode=暗模式
|
||||
navbar.pageOps=页面操作
|
||||
|
||||
home.multiTool.title=PDF多功能工具
|
||||
home.multiTool.desc=合并、旋转、重新排列和删除PDF页面
|
||||
|
||||
home.merge.title=合并
|
||||
home.merge.desc=轻松合并多个PDF为一个。
|
||||
|
||||
home.split.title=拆分
|
||||
home.split.desc=将 PDF 拆分为多个文档。
|
||||
|
||||
home.rotate.title=旋转
|
||||
home.rotate.desc=旋转PDF。
|
||||
|
||||
home.imageToPdf.title=转换图像到PDF
|
||||
home.imageToPdf.desc=转换图像(PNG, JPEG, GIF)到 PDF。
|
||||
|
||||
home.pdfToImage.title=转换PDF到图像
|
||||
home.pdfToImage.desc=转换PDF到图像(PNG, JPEG, GIF)
|
||||
|
||||
home.pdfOrganiser.title=整理
|
||||
home.pdfOrganiser.desc=按任何顺序删除/重新排列页面。
|
||||
|
||||
home.addImage.title=在PDF中添加图片
|
||||
home.addImage.desc=将图像添加到PDF的设定位置上
|
||||
|
||||
home.watermark.title=添加水印
|
||||
home.watermark.desc=在PDF中添加一个自定义的水印。
|
||||
|
||||
home.remove-watermark.title=去除水印
|
||||
home.remove-watermark.desc=从你的PDF文档中去除水印。
|
||||
|
||||
home.permissions.title=更改权限
|
||||
home.permissions.desc=改变你的PDF文档的权限。
|
||||
|
||||
home.removePages.title=删除
|
||||
home.removePages.desc=从你的PDF文档中删除不需要的页面。
|
||||
|
||||
home.addPassword.title=添加密码
|
||||
home.addPassword.desc=用密码来加密你的PDF文档。
|
||||
|
||||
home.removePassword.title=删除密码
|
||||
home.removePassword.desc=从你的PDF文档中移除密码保护。
|
||||
|
||||
home.compressPdfs.title=压缩
|
||||
home.compressPdfs.desc=压缩PDF文件以减少其文件大小。
|
||||
|
||||
home.changeMetadata.title=更改元数据
|
||||
home.changeMetadata.desc=更改/删除/添加PDF文档的元数据。
|
||||
|
||||
home.fileToPDF.title=将文件转换为PDF文件
|
||||
home.fileToPDF.desc=将几乎所有文件转换为PDF(DOCX、PNG、XLS、PPT、TXT等)
|
||||
|
||||
home.ocr.title=运行OCR/清理扫描
|
||||
home.ocr.desc=清理和检测PDF中的文本图像,并将其重新添加为文本。
|
||||
|
||||
home.extractImages.title=提取图像
|
||||
home.extractImages.desc=从PDF中提取所有的图像并将其保存到压缩包中。
|
||||
|
||||
home.pdfToPDFA.title=PDF To PDF/A
|
||||
home.pdfToPDFA.desc=将PDF转换为PDF/A以便长期保存
|
||||
|
||||
home.PDFToWord.title=PDF to Word
|
||||
home.PDFToWord.desc=将PDF转换为Word格式(DOC、DOCX和ODT)。
|
||||
|
||||
home.PDFToPresentation.title=PDF To Presentation
|
||||
home.PDFToPresentation.desc=将PDF转换成演示文稿格式(PPT、PPTX和ODP)。
|
||||
|
||||
home.PDFToText.title=PDF To Text/RTF
|
||||
home.PDFToText.desc=将PDF转换为文本或RTF格式
|
||||
|
||||
home.PDFToHTML.title=PDF To HTML
|
||||
home.PDFToHTML.desc=将PDF转换为HTML格式
|
||||
|
||||
home.PDFToXML.title=PDF To XML
|
||||
home.PDFToXML.desc=将PDF转换为XML格式
|
||||
|
||||
home.ScannerImageSplit.title=检测/分割扫描的照片
|
||||
home.ScannerImageSplit.desc=从一张照片/PDF中分割出多张照片
|
||||
|
||||
home.sign.title=\u6807\u5FD7
|
||||
home.sign.desc=\u901A\u8FC7\u7ED8\u56FE\u3001\u6587\u672C\u6216\u56FE\u50CF\u5411 PDF \u6DFB\u52A0\u7B7E\u540D
|
||||
|
||||
home.flatten.title=\u5C55\u5E73
|
||||
home.flatten.desc=\u4ECE PDF \u4E2D\u5220\u9664\u6240\u6709\u4EA4\u4E92\u5143\u7D20\u548C\u8868\u5355
|
||||
|
||||
home.repair.title=\u4FEE\u590D
|
||||
home.repair.desc=\u5C1D\u8BD5\u4FEE\u590D\u635F\u574F/\u635F\u574F\u7684 PDF
|
||||
|
||||
home.removeBlanks.title=\u5220\u9664\u7A7A\u767D\u9875
|
||||
home.removeBlanks.desc=\u68C0\u6D4B\u5E76\u5220\u9664\u6587\u6863\u4E2D\u7684\u7A7A\u767D\u9875
|
||||
|
||||
home.compare.title=\u6BD4\u8F83
|
||||
home.compare.desc=\u6BD4\u8F83\u5E76\u663E\u793A 2 \u4E2A PDF \u6587\u6863\u4E4B\u95F4\u7684\u5DEE\u5F02
|
||||
|
||||
downloadPdf=\u4E0B\u8F7DPDF
|
||||
text=\u6587\u672C
|
||||
font=\u5B57\u4F53
|
||||
|
||||
removeBlanks.title=\u5220\u9664\u7A7A\u767D
|
||||
removeBlanks.header=\u5220\u9664\u7A7A\u767D\u9875
|
||||
removeBlanks.threshold=\u9608\u503C\uFF1A
|
||||
removeBlanks.thresholdDesc=\u786E\u5B9A\u767D\u8272\u50CF\u7D20\u5FC5\u987B\u6709\u591A\u767D\u7684\u9608\u503C
|
||||
removeBlanks.whitePercent=\u767D\u8272\u767E\u5206\u6BD4\uFF08%\uFF09\uFF1A
|
||||
removeBlanks.whitePercentDesc=\u5FC5\u987B\u4E3A\u767D\u8272\u624D\u80FD\u5220\u9664\u7684\u9875\u9762\u767E\u5206\u6BD4
|
||||
removeBlanks.submit=\u5220\u9664\u7A7A\u767D
|
||||
|
||||
compare.title=\u6BD4\u8F83
|
||||
compare.header=\u6BD4\u8F83 PDF
|
||||
compare.document.1=\u6587\u6863 1
|
||||
compare.document.2=\u6587\u6863 2
|
||||
compare.submit=\u6BD4\u8F83
|
||||
|
||||
sign.title=\u7B7E\u540D
|
||||
sign.header=\u7B7E\u7F72 PDF
|
||||
sign.upload=\u4E0A\u4F20\u56FE\u7247
|
||||
sign.draw=\u7ED8\u5236\u7B7E\u540D
|
||||
sign.text=\u6587\u672C\u8F93\u5165
|
||||
sign.clear=\u6E05\u9664
|
||||
sign.add=\u6DFB\u52A0
|
||||
|
||||
repair.title=\u4FEE\u590D
|
||||
repair.header=\u4FEE\u590D PDF
|
||||
repair.submit=\u4FEE\u590D
|
||||
|
||||
flatten.title=\u5C55\u5E73
|
||||
flatten.header=\u5C55\u5E73 PDF
|
||||
flatten.submit=\u5C55\u5E73
|
||||
|
||||
ScannerImageSplit.selectText.1=角度阈值:
|
||||
ScannerImageSplit.selectText.2=设置图像被旋转所需的最小绝对角度(默认:10)。
|
||||
ScannerImageSplit.selectText.3=公差:
|
||||
ScannerImageSplit.selectText.4=确定估计背景颜色周围的颜色变化范围(默认值:30)。
|
||||
ScannerImageSplit.selectText.5=最小面积:
|
||||
ScannerImageSplit.selectText.6=设置照片的最小面积阈值(默认:10000)。
|
||||
ScannerImageSplit.selectText.7=最小轮廓面积:
|
||||
ScannerImageSplit.selectText.8=设置照片的最小轮廓面积阈值。
|
||||
ScannerImageSplit.selectText.9=边框尺寸:
|
||||
ScannerImageSplit.selectText.10=设置添加和删除的边框大小,以防止输出中出现白边(默认值:1)。
|
||||
|
||||
navbar.settings=设置
|
||||
settings.title=设置
|
||||
settings.update=可更新
|
||||
settings.appVersion=应用程序版本:
|
||||
settings.downloadOption.title=选择下载选项(单个文件非压缩文件):
|
||||
settings.downloadOption.1=在同一窗口打开
|
||||
settings.downloadOption.2=在新窗口中打开
|
||||
settings.downloadOption.3=下载文件
|
||||
settings.zipThreshold=当下载的文件数量超过限制时,将文件压缩。
|
||||
|
||||
|
||||
|
||||
|
||||
#OCR
|
||||
ocr.title=OCR/扫描清理
|
||||
ocr.header=清理扫描件/OCR(光学字符识别)。
|
||||
ocr.selectText.1=选择要在PDF中检测的语言(列出的语言是目前检测到的):
|
||||
ocr.selectText.2=生成包含OCR文本的文本文件,与OCR编辑的PDF一起。
|
||||
ocr.selectText.3=通过将页面旋转回原位来纠正偏斜的扫描角度
|
||||
ocr.selectText.4=清理页面,降低OCR在噪点中识别到文本的可能。(没有输出变化)
|
||||
ocr.selectText.5=清洁页面,降低OCR在噪点中识别到文本的可能,保持输出的清洁。
|
||||
ocr.selectText.6=忽略有交互式文本的页面,只对有图像的页面进行OCR。
|
||||
ocr.selectText.7=强制OCR,将OCR每个页面,删除所有的原始文本元素。
|
||||
ocr.selectText.8=Normal (如果PDF包含文本,将出现错误)
|
||||
ocr.selectText.9=额外设置
|
||||
ocr.selectText.10=OCR模式
|
||||
ocr.selectText.11=OCR后移除图像(移除所有图像,只有在转换步骤中才有用)。
|
||||
ocr.selectText.12=渲染类型(高级)
|
||||
ocr.help=请阅读此文档,了解如何将其用于其他语言和/或不在docker中使用。
|
||||
ocr.credit=此服务使用OCRmyPDF和Tesseract进行OCR。
|
||||
ocr.submit=用OCR处理PDF
|
||||
|
||||
|
||||
|
||||
extractImages.title=提取图像
|
||||
extractImages.header=提取图像
|
||||
extractImages.selectText=选择图像格式,将提取的图像转换为
|
||||
extractImages.submit=提取
|
||||
|
||||
|
||||
#File to PDF
|
||||
fileToPDF.title=文件转换为PDF
|
||||
fileToPDF.header=将任何文件转换为PDF。
|
||||
fileToPDF.credit=本服务使用LibreOffice和Unoconv进行文件转换。
|
||||
fileToPDF.supportedFileTypes=支持的文件类型应该包括以下几种,但是,对于支持的格式的完整更新列表,请参考LibreOffice文档。
|
||||
fileToPDF.submit=转换为 PDF
|
||||
|
||||
|
||||
#compress
|
||||
compress.title=压缩
|
||||
compress.header=压缩PDF
|
||||
compress.credit=此服务使用OCRmyPDF进行PDF压缩/优化。
|
||||
compress.selectText.1=优化水平:
|
||||
compress.selectText.2=0 (无优化)
|
||||
compress.selectText.3=1 (默认,无损优化)
|
||||
compress.selectText.4=2 (有损优化)
|
||||
compress.selectText.5=3 (有损优化,更激进)
|
||||
compress.selectText.6=启用快速网络视图(线性化PDF)。
|
||||
compress.selectText.7=启用有损的JBIG2编码
|
||||
compress.submit=压缩
|
||||
|
||||
|
||||
#Add image
|
||||
addImage.title=添加图像
|
||||
addImage.header=添加图片到PDF (Work in progress)
|
||||
addImage.everyPage=每一页?
|
||||
addImage.submit=添加图片
|
||||
|
||||
|
||||
#merge
|
||||
merge.title=合并
|
||||
merge.header=合并多个PDF(2个以上)。
|
||||
merge.submit=合并
|
||||
|
||||
#pdfOrganiser
|
||||
pdfOrganiser.title=页面排序
|
||||
pdfOrganiser.header=PDF页面排序
|
||||
pdfOrganiser.submit=重新排列页面
|
||||
|
||||
#multiTool
|
||||
multiTool.title=PDF多功能工具
|
||||
multiTool.header=PDF多功能工具
|
||||
|
||||
|
||||
#pageRemover
|
||||
pageRemover.title=删除页面
|
||||
pageRemover.header=PDF页面移除器
|
||||
pageRemover.pagesToDelete=要删除的页面(输入一个用逗号分隔的页码列表):
|
||||
pageRemover.submit=删除页面
|
||||
|
||||
#rotate
|
||||
rotate.title=旋转PDF
|
||||
rotate.header=旋转PDF
|
||||
rotate.selectAngle=选择旋转角度(以90度的倍数):
|
||||
rotate.submit=旋转
|
||||
|
||||
|
||||
|
||||
|
||||
#merge
|
||||
split.title=拆分PDF
|
||||
split.header=拆分PDF
|
||||
split.desc.1=选择希望进行分割的页数
|
||||
split.desc.2=如选择1,3,7-8将把一个10页的文件分割成6个独立的PDF:
|
||||
split.desc.3=Document #1: Page 1
|
||||
split.desc.4=Document #2: Page 2 and 3
|
||||
split.desc.5=Document #3: Page 4, 5 and 6
|
||||
split.desc.6=Document #4: Page 7
|
||||
split.desc.7=Document #5: Page 8
|
||||
split.desc.8=Document #6: Page 9 and 10
|
||||
split.splitPages=输入要分割的页面:
|
||||
split.submit=拆分
|
||||
|
||||
|
||||
#merge
|
||||
imageToPDF.title=图片转PDF
|
||||
imageToPDF.header=图像转为PDF
|
||||
imageToPDF.submit=转换
|
||||
imageToPDF.selectText.1=拉伸至适合的尺寸
|
||||
imageToPDF.selectText.2=自动旋转PDF
|
||||
imageToPDF.selectText.3=多文件逻辑(仅在处理多个图像时启用)
|
||||
imageToPDF.selectText.4=合并成一个PDF文件
|
||||
imageToPDF.selectText.5=转换为独立的PDF文件
|
||||
|
||||
#pdfToImage
|
||||
pdfToImage.title=PDF to Image
|
||||
pdfToImage.header=PDF转图片
|
||||
pdfToImage.selectText=图像格式
|
||||
pdfToImage.singleOrMultiple=图像结果类型
|
||||
pdfToImage.single=单张图片
|
||||
pdfToImage.multi=多张图片
|
||||
pdfToImage.colorType=颜色类型
|
||||
pdfToImage.color=颜色
|
||||
pdfToImage.grey=灰度
|
||||
pdfToImage.blackwhite=黑白(可能会丢失数据!)。
|
||||
pdfToImage.submit=转换
|
||||
|
||||
#addPassword
|
||||
addPassword.title=添加密码
|
||||
addPassword.header=添加密码(加密)。
|
||||
addPassword.selectText.1=选择要加密的PDF。
|
||||
addPassword.selectText.2=密码
|
||||
addPassword.selectText.3=加密密钥长度
|
||||
addPassword.selectText.4=值越高越强,但值越低兼容性越好。
|
||||
addPassword.selectText.5=要设置的权限
|
||||
addPassword.selectText.6=防止文件的拼接。
|
||||
addPassword.selectText.7=防止内容提取
|
||||
addPassword.selectText.8=防止为可访问性提取内容
|
||||
addPassword.selectText.9=防止填写表格
|
||||
addPassword.selectText.10=防止修改
|
||||
addPassword.selectText.11=防止修改注释
|
||||
addPassword.selectText.12=防止打印
|
||||
addPassword.selectText.13=防止打印不同的格式
|
||||
addPassword.submit=加密
|
||||
|
||||
#watermark
|
||||
watermark.title=添加水印
|
||||
watermark.header=添加水印
|
||||
watermark.selectText.1=选择要添加水印的PDF:
|
||||
watermark.selectText.2=水印文本:
|
||||
watermark.selectText.3=字体大小:
|
||||
watermark.selectText.4=旋转(0-360):
|
||||
watermark.selectText.5=widthSpacer(水平方向上每个水印之间的空间):
|
||||
watermark.selectText.6=heightSpacer(每个水印之间的垂直空间):
|
||||
watermark.selectText.7=透明度(0% - 100%):
|
||||
watermark.submit=添加水印
|
||||
|
||||
#remove-watermark
|
||||
remove-watermark.title=去除水印
|
||||
remove-watermark.header=去除水印
|
||||
remove-watermark.selectText.1=选择要去除水印的PDF:
|
||||
remove-watermark.selectText.2=水印文本:
|
||||
remove-watermark.submit=移除水印
|
||||
|
||||
#Change permissions
|
||||
permissions.title=更改权限
|
||||
permissions.header=改变权限
|
||||
permissions.warning=警告,为了使这些权限不能被改变,建议通过添加密码页面设置密码。
|
||||
permissions.selectText.1=选择PDF来改变权限
|
||||
permissions.selectText.2=要设置的权限
|
||||
permissions.selectText.3=防止文件的拼接
|
||||
permissions.selectText.4=防止内容提取
|
||||
permissions.selectText.5=防止提取内容的可访问性
|
||||
permissions.selectText.6=防止填写表格
|
||||
permissions.selectText.7=防止修改
|
||||
permissions.selectText.8=防止修改注释
|
||||
permissions.selectText.9=防止打印
|
||||
permissions.selectText.10=防止打印不同的格式
|
||||
permissions.submit=改变
|
||||
|
||||
#remove password
|
||||
removePassword.title=删除密码
|
||||
removePassword.header=移除密码(解密)。
|
||||
removePassword.selectText.1=选择要解密的PDF
|
||||
removePassword.selectText.2=密码
|
||||
removePassword.submit=删除
|
||||
|
||||
changeMetadata.title=更改元数据
|
||||
changeMetadata.header=更改元数据
|
||||
changeMetadata.selectText.1=请编辑你想要改变的变量。
|
||||
changeMetadata.selectText.2=删除所有元数据
|
||||
changeMetadata.selectText.3=显示自定义元数据:
|
||||
changeMetadata.author=作者:
|
||||
changeMetadata.creationDate=创建日期(yyyy/MM/dd HH:mm:ss):
|
||||
changeMetadata.creator=创建者:
|
||||
changeMetadata.keywords=关键词:
|
||||
changeMetadata.modDate=修改日期(yyyy/MM/dd HH:mm:ss):
|
||||
changeMetadata.producer=生产者:
|
||||
changeMetadata.subject=主题:
|
||||
changeMetadata.title=标题:
|
||||
changeMetadata.trapped=被困:
|
||||
changeMetadata.selectText.4=其他元数据:
|
||||
changeMetadata.selectText.5=添加自定义元数据条目
|
||||
changeMetadata.submit=更改
|
||||
|
||||
xlsToPdf.title=Excel转PDF
|
||||
xlsToPdf.header=Excel转PDF
|
||||
xlsToPdf.selectText.1=选择要转换的XLS或XLSX Excel表格
|
||||
xlsToPdf.convert=转换
|
||||
|
||||
|
||||
|
||||
|
||||
pdfToPDFA.title=PDF To PDF/A
|
||||
pdfToPDFA.header=PDF to PDF/A
|
||||
pdfToPDFA.credit=此服务使用OCRmyPDF进行PDF/A转换
|
||||
pdfToPDFA.submit=转换
|
||||
|
||||
|
||||
|
||||
PDFToWord.title=PDF to Word
|
||||
PDFToWord.header=将PDF转换成Word
|
||||
PDFToWord.selectText.1=输出文件格式
|
||||
PDFToWord.credit=此服务使用LibreOffice进行文件转换。
|
||||
PDFToWord.submit=转换
|
||||
|
||||
PDFToPresentation.title=PDF To Presentation
|
||||
PDFToPresentation.header=将PDF转为演示文稿
|
||||
PDFToPresentation.selectText.1=输出文件格式
|
||||
PDFToPresentation.credit=该服务使用LibreOffice进行文件转换。
|
||||
PDFToPresentation.submit=转换
|
||||
|
||||
|
||||
PDFToText.title=PDF To Text/RTF
|
||||
PDFToText.header=将PDF转换成文本/RTF
|
||||
PDFToText.selectText.1=输出文件格式
|
||||
PDFToText.credit=该服务使用LibreOffice进行文件转换。
|
||||
PDFToText.submit=转换
|
||||
|
||||
|
||||
PDFToHTML.title=PDF To HTML
|
||||
PDFToHTML.header=将PDF转换成HTML
|
||||
PDFToHTML.credit=此服务使用LibreOffice进行文件转换。
|
||||
PDFToHTML.submit=转换
|
||||
|
||||
PDFToXML.title=PDF To XML
|
||||
PDFToXML.header=将PDF转换为XML
|
||||
PDFToXML.credit=此服务使用LibreOffice进行文件转换。
|
||||
PDFToXML.submit=转换
|
||||
@@ -1,26 +1,39 @@
|
||||
/* Dark Mode Styles */
|
||||
body {
|
||||
background-color: #333 !important;
|
||||
color: #fff !important;
|
||||
--body-background-color: 51, 51, 51;
|
||||
--base-font-color: 255, 255, 255;
|
||||
background-color: rgb(var(--body-background-color)) !important;
|
||||
color: rgb(var(--base-font-color)) !important;
|
||||
}
|
||||
|
||||
.dark-card {
|
||||
background-color: #333 !important;
|
||||
color: white !important;
|
||||
background-color: rgb(var(--body-background-color)) !important;
|
||||
color: rgb(var(--base-font-color)) !important;
|
||||
}
|
||||
.jumbotron {
|
||||
background-color: #222; /* or any other dark color */
|
||||
color: #fff !important; /* or any other light color */
|
||||
color: rgb(var(--base-font-color)) !important; /* or any other light color */
|
||||
}
|
||||
|
||||
.list-group {
|
||||
background-color: #222 !important;
|
||||
color: fff !important;
|
||||
color: rgb(var(--base-font-color)) !important;
|
||||
}
|
||||
.list-group-item {
|
||||
background-color: #222 !important;
|
||||
color: fff !important;
|
||||
color: rgb(var(--base-font-color)) !important;
|
||||
}
|
||||
#support-section {
|
||||
background-color: #444 !important;
|
||||
}
|
||||
|
||||
#pages-container-wrapper {
|
||||
--background-color: rgba(255, 255, 255, 0.046) !important;
|
||||
--scroll-bar-color: #4c4c4c !important;
|
||||
--scroll-bar-thumb: #d3d3d3 !important;
|
||||
--scroll-bar-thumb-hover: rgb(var(--base-font-color)) !important;
|
||||
}
|
||||
|
||||
.favorite-icon img {
|
||||
filter: brightness(0) invert(1) !important;
|
||||
}
|
||||
78
src/main/resources/static/css/dragdrop.css
Normal file
@@ -0,0 +1,78 @@
|
||||
#drag-container {
|
||||
position: fixed;
|
||||
display:flex;
|
||||
inset: 0;
|
||||
pointer-events: none;
|
||||
z-index: 10000;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
#drag-container:not(:empty) {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
#drag-container .dragged-img {
|
||||
position: fixed;
|
||||
max-width: 200px;
|
||||
max-height: 200px;
|
||||
box-shadow: 0px 0px 12px rgba(0, 0, 0, 0.58);
|
||||
transform-origin: top left;
|
||||
}
|
||||
|
||||
.drag-manager_dragging {
|
||||
width: 0px;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.drag-manager_draghover {
|
||||
width: 375px !important;
|
||||
}
|
||||
|
||||
.drag-manager_draghover .insert-file-button-container {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.drag-manager_draghover .button-container {
|
||||
visibility: hidden !important;
|
||||
}
|
||||
|
||||
html[lang-direction=ltr] .drag-manager_draghover img {
|
||||
left: calc(50% + 62.5px) !important;
|
||||
}
|
||||
|
||||
html[lang-direction=rtl] .drag-manager_draghover img {
|
||||
left: 125px
|
||||
}
|
||||
|
||||
.drag-manager_dragging-container .hide-on-drag {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.drag-manager_endpoint {
|
||||
width: 80px;
|
||||
height: 100%;
|
||||
background-color: #FFFFFF10;
|
||||
transition: width 0.1s;
|
||||
animation: end-drop-expand .3s ease;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.drag-manager_endpoint svg {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
.drag-manager_endpoint.drag-manager_draghover {
|
||||
width: 150px !important;
|
||||
}
|
||||
|
||||
@keyframes end-drop-expand {
|
||||
from {
|
||||
width: 0;
|
||||
}
|
||||
to {
|
||||
width: 80px;
|
||||
}
|
||||
}
|
||||
@@ -16,8 +16,45 @@
|
||||
html[lang-direction=ltr] * {
|
||||
direction: ltr;
|
||||
}
|
||||
|
||||
html[lang-direction=rtl] * {
|
||||
direction: rtl;
|
||||
text-align: right;
|
||||
}
|
||||
.ignore-rtl {
|
||||
direction: ltr !important;
|
||||
text-align: left !important;
|
||||
}
|
||||
|
||||
.align-top {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
}
|
||||
.align-center-right {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 50%;
|
||||
}
|
||||
|
||||
.align-center-left {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 50%;
|
||||
}
|
||||
|
||||
.align-bottom {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.btn-group > label:first-of-type {
|
||||
border-top-left-radius: 0.25rem !important;
|
||||
border-bottom-left-radius: 0.25rem !important;
|
||||
}
|
||||
|
||||
.margin-auto-parent {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
}
|
||||
.margin-center {
|
||||
margin: 0 auto;
|
||||
}
|
||||
40
src/main/resources/static/css/imageHighlighter.css
Normal file
@@ -0,0 +1,40 @@
|
||||
|
||||
#image-highlighter {
|
||||
position: fixed;
|
||||
display:flex;
|
||||
inset: 0;
|
||||
z-index: 10000;
|
||||
background-color: rgba(0, 0, 0, 0);
|
||||
visibility: hidden;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: visbility 0.1s linear, background-color 0.1s linear;
|
||||
}
|
||||
|
||||
#image-highlighter > * {
|
||||
max-width: 80vw;
|
||||
max-height: 80vh;
|
||||
animation: image-highlight .1s linear;
|
||||
transition: transform .1s linear, opacity .1s linear;
|
||||
}
|
||||
|
||||
#image-highlighter > *.remove {
|
||||
transform: scale(0.8) !important;
|
||||
opacity: 0 !important;
|
||||
}
|
||||
|
||||
#image-highlighter:not(:empty) {
|
||||
background-color: rgba(0, 0, 0, 0.37);
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
@keyframes image-highlight {
|
||||
from {
|
||||
transform: scale(0.8);
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
transform: scale(1);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
5
src/main/resources/static/css/light-mode.css
Normal file
@@ -0,0 +1,5 @@
|
||||
/* Dark Mode Styles */
|
||||
body {
|
||||
--body-background-color: 255, 255, 255;
|
||||
--base-font-color: 33, 37, 41;
|
||||
}
|
||||
87
src/main/resources/static/css/pdfActions.css
Normal file
@@ -0,0 +1,87 @@
|
||||
|
||||
.pdf-actions_button-container {
|
||||
z-index: 2;
|
||||
display:flex;
|
||||
opacity: 0;
|
||||
transition: opacity 0.1s linear;
|
||||
}
|
||||
|
||||
.pdf-actions_container:hover .pdf-actions_button-container {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.pdf-actions_button-container > * {
|
||||
padding: 0.25rem 0.5rem;
|
||||
margin: 3px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.pdf-actions_container svg {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.pdf-actions_container:nth-child(1) .pdf-actions_move-left-button {
|
||||
display: none;
|
||||
}
|
||||
.pdf-actions_container:last-child .pdf-actions_move-right-button {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* "insert pdf" buttons that appear on the right when hover */
|
||||
.pdf-actions_insert-file-button-container {
|
||||
translate: 0 -50%;
|
||||
width: 80px;
|
||||
height: 100%;
|
||||
|
||||
z-index: 1;
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s;
|
||||
}
|
||||
|
||||
.pdf-actions_insert-file-button-container.left {
|
||||
left: -20px;
|
||||
}
|
||||
|
||||
.pdf-actions_insert-file-button-container.right {
|
||||
right: -20px;
|
||||
}
|
||||
|
||||
html[lang-direction=ltr] .pdf-actions_insert-file-button-container.right {
|
||||
display:none;
|
||||
}
|
||||
|
||||
html[lang-direction=rtl] .pdf-actions_insert-file-button-container.left {
|
||||
display:none;
|
||||
}
|
||||
|
||||
.pdf-actions_insert-file-button-container.left .pdf-actions_insert-file-button {
|
||||
left: 0;
|
||||
translate: 0 -50%;
|
||||
}
|
||||
|
||||
.pdf-actions_insert-file-button-container.right .pdf-actions_insert-file-button {
|
||||
right: 0;
|
||||
translate: 0 -50%;
|
||||
}
|
||||
|
||||
html[lang-direction=ltr] .pdf-actions_container:last-child > .pdf-actions_insert-file-button-container.right {
|
||||
display: block;
|
||||
}
|
||||
|
||||
|
||||
html[lang-direction=rtl] .pdf-actions_container:last-child > .pdf-actions_insert-file-button-container.left {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.pdf-actions_insert-file-button-container:hover {
|
||||
opacity: 1;
|
||||
transition: opacity 0.05s;
|
||||
}
|
||||
.pdf-actions_insert-file-button {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 50%;
|
||||
translate: 50% -50%;
|
||||
aspect-ratio: 1;
|
||||
border-radius: 100px;
|
||||
}
|
||||
37
src/main/resources/static/css/rainbow-mode.css
Normal file
@@ -0,0 +1,37 @@
|
||||
/* Rainbow Mode Styles */
|
||||
body {
|
||||
background: linear-gradient(90deg, rgba(255,0,0,1) 0%, rgba(255,154,0,1) 10%, rgba(208,222,33,1) 20%, rgba(79,220,74,1) 30%, rgba(63,218,216,1) 40%, rgba(47,201,226,1) 50%, rgba(28,127,238,1) 60%, rgba(95,21,242,1) 70%, rgba(186,12,248,1) 80%, rgba(251,7,217,1) 90%, rgba(255,0,0,1) 100%);
|
||||
color: #fff !important;
|
||||
--body-background-color: 255, 255, 255;
|
||||
--base-font-color: 33, 37, 41;
|
||||
}
|
||||
|
||||
.dark-card {
|
||||
background: linear-gradient(90deg, rgba(255,0,0,1) 0%, rgba(255,154,0,1) 10%, rgba(208,222,33,1) 20%, rgba(79,220,74,1) 30%, rgba(63,218,216,1) 40%, rgba(47,201,226,1) 50%, rgba(28,127,238,1) 60%, rgba(95,21,242,1) 70%, rgba(186,12,248,1) 80%, rgba(251,7,217,1) 90%, rgba(255,0,0,1) 100%) !important;
|
||||
color: white !important;
|
||||
}
|
||||
.jumbotron {
|
||||
background: linear-gradient(90deg, rgba(255,0,0,1) 0%, rgba(255,154,0,1) 10%, rgba(208,222,33,1) 20%, rgba(79,220,74,1) 30%, rgba(63,218,216,1) 40%, rgba(47,201,226,1) 50%, rgba(28,127,238,1) 60%, rgba(95,21,242,1) 70%, rgba(186,12,248,1) 80%, rgba(251,7,217,1) 90%, rgba(255,0,0,1) 100%);
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
.list-group {
|
||||
background: linear-gradient(90deg, rgba(255,0,0,1) 0%, rgba(255,154,0,1) 10%, rgba(208,222,33,1) 20%, rgba(79,220,74,1) 30%, rgba(63,218,216,1) 40%, rgba(47,201,226,1) 50%, rgba(28,127,238,1) 60%, rgba(95,21,242,1) 70%, rgba(186,12,248,1) 80%, rgba(251,7,217,1) 90%, rgba(255,0,0,1) 100%) !important;
|
||||
color: fff !important;
|
||||
}
|
||||
.list-group-item {
|
||||
background: linear-gradient(90deg, rgba(255,0,0,1) 0%, rgba(255,154,0,1) 10%, rgba(208,222,33,1) 20%, rgba(79,220,74,1) 30%, rgba(63,218,216,1) 40%, rgba(47,201,226,1) 50%, rgba(28,127,238,1) 60%, rgba(95,21,242,1) 70%, rgba(186,12,248,1) 80%, rgba(251,7,217,1) 90%, rgba(255,0,0,1) 100%) !important;
|
||||
color: fff !important;
|
||||
}
|
||||
#support-section {
|
||||
background: linear-gradient(90deg, rgba(255,0,0,1) 0%, rgba(255,154,0,1) 10%, rgba(208,222,33,1) 20%, rgba(79,220,74,1) 30%, rgba(63,218,216,1) 40%, rgba(47,201,226,1) 50%, rgba(28,127,238,1) 60%, rgba(95,21,242,1) 70%, rgba(186,12,248,1) 80%, rgba(251,7,217,1) 90%, rgba(255,0,0,1) 100%) !important;
|
||||
}
|
||||
|
||||
|
||||
#pages-container-wrapper {
|
||||
--background-color: rgba(255, 255, 255, 0.046) !important;
|
||||
--scroll-bar-color: #4c4c4c !important;
|
||||
--scroll-bar-thumb: #d3d3d3 !important;
|
||||
--scroll-bar-thumb-hover: #ffffff !important;
|
||||
}
|
||||
|
||||
26
src/main/resources/static/css/tab-container.css
Normal file
@@ -0,0 +1,26 @@
|
||||
|
||||
.tab-group {
|
||||
|
||||
}
|
||||
|
||||
.tab-container {
|
||||
display: none;
|
||||
}
|
||||
.tab-container.active {
|
||||
display: block;
|
||||
border: 1px solid rgba(var(--base-font-color), 0.25);
|
||||
padding: 15px;
|
||||
}
|
||||
.tab-buttons > button {
|
||||
margin-bottom: -1px;
|
||||
background: 0 0;
|
||||
border: 1px solid transparent;
|
||||
color: rgb(var(--base-font-color));
|
||||
|
||||
border-top-left-radius: 0.25rem;
|
||||
border-top-right-radius: 0.25rem;
|
||||
}
|
||||
.tab-buttons > button.active {
|
||||
background-color: rgb(var(--body-background-color));
|
||||
border-color: rgba(var(--base-font-color), 0.25) rgba(var(--base-font-color), 0.25) rgb(var(--body-background-color));
|
||||
}
|
||||
BIN
src/main/resources/static/fonts/Estonia.woff2
Normal file
BIN
src/main/resources/static/fonts/Tangerine.woff2
Normal file
10
src/main/resources/static/images/Files.svg
Normal file
@@ -0,0 +1,10 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_1_37)">
|
||||
<path d="M13 0H6C5.46957 0 4.96086 0.210714 4.58579 0.585786C4.21071 0.960859 4 1.46957 4 2C3.46957 2 2.96086 2.21071 2.58579 2.58579C2.21071 2.96086 2 3.46957 2 4V14C2 14.5304 2.21071 15.0391 2.58579 15.4142C2.96086 15.7893 3.46957 16 4 16H11C11.5304 16 12.0391 15.7893 12.4142 15.4142C12.7893 15.0391 13 14.5304 13 14C13.5304 14 14.0391 13.7893 14.4142 13.4142C14.7893 13.0391 15 12.5304 15 12V2C15 1.46957 14.7893 0.960859 14.4142 0.585786C14.0391 0.210714 13.5304 0 13 0ZM13 13V4C13 3.46957 12.7893 2.96086 12.4142 2.58579C12.0391 2.21071 11.5304 2 11 2H5C5 1.73478 5.10536 1.48043 5.29289 1.29289C5.48043 1.10536 5.73478 1 6 1H13C13.2652 1 13.5196 1.10536 13.7071 1.29289C13.8946 1.48043 14 1.73478 14 2V12C14 12.2652 13.8946 12.5196 13.7071 12.7071C13.5196 12.8946 13.2652 13 13 13ZM3 4C3 3.73478 3.10536 3.48043 3.29289 3.29289C3.48043 3.10536 3.73478 3 4 3H11C11.2652 3 11.5196 3.10536 11.7071 3.29289C11.8946 3.48043 12 3.73478 12 4V14C12 14.2652 11.8946 14.5196 11.7071 14.7071C11.5196 14.8946 11.2652 15 11 15H4C3.73478 15 3.48043 14.8946 3.29289 14.7071C3.10536 14.5196 3 14.2652 3 14V4Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_1_37">
|
||||
<rect width="16" height="16" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
4
src/main/resources/static/images/arrow-clockwise.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-clockwise" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd" d="M8 3a5 5 0 1 0 4.546 2.914.5.5 0 0 1 .908-.417A6 6 0 1 1 8 2v1z"/>
|
||||
<path d="M8 4.466V.534a.25.25 0 0 1 .41-.192l2.36 1.966c.12.1.12.284 0 .384L8.41 4.658A.25.25 0 0 1 8 4.466z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 352 B |
3
src/main/resources/static/images/arrow-left-right.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-left-right" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd" d="M1 11.5a.5.5 0 0 0 .5.5h11.793l-3.147 3.146a.5.5 0 0 0 .708.708l4-4a.5.5 0 0 0 0-.708l-4-4a.5.5 0 0 0-.708.708L13.293 11H1.5a.5.5 0 0 0-.5.5zm14-7a.5.5 0 0 1-.5.5H2.707l3.147 3.146a.5.5 0 1 1-.708.708l-4-4a.5.5 0 0 1 0-.708l4-4a.5.5 0 1 1 .708.708L2.707 4H14.5a.5.5 0 0 1 .5.5z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 453 B |
3
src/main/resources/static/images/arrow-right-short.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-right-short" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd" d="M4 8a.5.5 0 0 1 .5-.5h5.793L8.146 5.354a.5.5 0 1 1 .708-.708l3 3a.5.5 0 0 1 0 .708l-3 3a.5.5 0 0 1-.708-.708L10.293 8.5H4.5A.5.5 0 0 1 4 8z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 316 B |
3
src/main/resources/static/images/blank-file.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-file" viewBox="0 0 16 16">
|
||||
<path d="M4 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H4zm0 1h8a1 1 0 0 1 1 1v12a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 284 B |
4
src/main/resources/static/images/card-list.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-card-list" viewBox="0 0 16 16">
|
||||
<path d="M14.5 3a.5.5 0 0 1 .5.5v9a.5.5 0 0 1-.5.5h-13a.5.5 0 0 1-.5-.5v-9a.5.5 0 0 1 .5-.5h13zm-13-1A1.5 1.5 0 0 0 0 3.5v9A1.5 1.5 0 0 0 1.5 14h13a1.5 1.5 0 0 0 1.5-1.5v-9A1.5 1.5 0 0 0 14.5 2h-13z"/>
|
||||
<path d="M5 8a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7A.5.5 0 0 1 5 8zm0-2.5a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1-.5-.5zm0 5a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1-.5-.5zm-1-5a.5.5 0 1 1-1 0 .5.5 0 0 1 1 0zM4 8a.5.5 0 1 1-1 0 .5.5 0 0 1 1 0zm0 2.5a.5.5 0 1 1-1 0 .5.5 0 0 1 1 0z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 638 B |
5
src/main/resources/static/images/clipboard-data.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-clipboard-data" viewBox="0 0 16 16">
|
||||
<path d="M4 11a1 1 0 1 1 2 0v1a1 1 0 1 1-2 0v-1zm6-4a1 1 0 1 1 2 0v5a1 1 0 1 1-2 0V7zM7 9a1 1 0 0 1 2 0v3a1 1 0 1 1-2 0V9z"/>
|
||||
<path d="M4 1.5H3a2 2 0 0 0-2 2V14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V3.5a2 2 0 0 0-2-2h-1v1h1a1 1 0 0 1 1 1V14a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V3.5a1 1 0 0 1 1-1h1v-1z"/>
|
||||
<path d="M9.5 1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-3a.5.5 0 0 1-.5-.5v-1a.5.5 0 0 1 .5-.5h3zm-3-1A1.5 1.5 0 0 0 5 1.5v1A1.5 1.5 0 0 0 6.5 4h3A1.5 1.5 0 0 0 11 2.5v-1A1.5 1.5 0 0 0 9.5 0h-3z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 629 B |
4
src/main/resources/static/images/droplet.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-droplet" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd" d="M7.21.8C7.69.295 8 0 8 0c.109.363.234.708.371 1.038.812 1.946 2.073 3.35 3.197 4.6C12.878 7.096 14 8.345 14 10a6 6 0 0 1-12 0C2 6.668 5.58 2.517 7.21.8zm.413 1.021A31.25 31.25 0 0 0 5.794 3.99c-.726.95-1.436 2.008-1.96 3.07C3.304 8.133 3 9.138 3 10a5 5 0 0 0 10 0c0-1.201-.796-2.157-2.181-3.7l-.03-.032C9.75 5.11 8.5 3.72 7.623 1.82z"/>
|
||||
<path fill-rule="evenodd" d="M4.553 7.776c.82-1.641 1.717-2.753 2.093-3.13l.708.708c-.29.29-1.128 1.311-1.907 2.87l-.894-.448z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 632 B |
4
src/main/resources/static/images/file-earmark-pdf.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-file-earmark-pdf" viewBox="0 0 16 16">
|
||||
<path d="M14 14V4.5L9.5 0H4a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2zM9.5 3A1.5 1.5 0 0 0 11 4.5h2V14a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1h5.5v2z"/>
|
||||
<path d="M4.603 14.087a.81.81 0 0 1-.438-.42c-.195-.388-.13-.776.08-1.102.198-.307.526-.568.897-.787a7.68 7.68 0 0 1 1.482-.645 19.697 19.697 0 0 0 1.062-2.227 7.269 7.269 0 0 1-.43-1.295c-.086-.4-.119-.796-.046-1.136.075-.354.274-.672.65-.823.192-.077.4-.12.602-.077a.7.7 0 0 1 .477.365c.088.164.12.356.127.538.007.188-.012.396-.047.614-.084.51-.27 1.134-.52 1.794a10.954 10.954 0 0 0 .98 1.686 5.753 5.753 0 0 1 1.334.05c.364.066.734.195.96.465.12.144.193.32.2.518.007.192-.047.382-.138.563a1.04 1.04 0 0 1-.354.416.856.856 0 0 1-.51.138c-.331-.014-.654-.196-.933-.417a5.712 5.712 0 0 1-.911-.95 11.651 11.651 0 0 0-1.997.406 11.307 11.307 0 0 1-1.02 1.51c-.292.35-.609.656-.927.787a.793.793 0 0 1-.58.029zm1.379-1.901c-.166.076-.32.156-.459.238-.328.194-.541.383-.647.547-.094.145-.096.25-.04.361.01.022.02.036.026.044a.266.266 0 0 0 .035-.012c.137-.056.355-.235.635-.572a8.18 8.18 0 0 0 .45-.606zm1.64-1.33a12.71 12.71 0 0 1 1.01-.193 11.744 11.744 0 0 1-.51-.858 20.801 20.801 0 0 1-.5 1.05zm2.446.45c.15.163.296.3.435.41.24.19.407.253.498.256a.107.107 0 0 0 .07-.015.307.307 0 0 0 .094-.125.436.436 0 0 0 .059-.2.095.095 0 0 0-.026-.063c-.052-.062-.2-.152-.518-.209a3.876 3.876 0 0 0-.612-.053zM8.078 7.8a6.7 6.7 0 0 0 .2-.828c.031-.188.043-.343.038-.465a.613.613 0 0 0-.032-.198.517.517 0 0 0-.145.04c-.087.035-.158.106-.196.283-.04.192-.03.469.046.822.024.111.054.227.09.346z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
4
src/main/resources/static/images/file-earmark-ppt.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-file-earmark-ppt" viewBox="0 0 16 16">
|
||||
<path d="M7 5.5a1 1 0 0 0-1 1V13a.5.5 0 0 0 1 0v-2h1.188a2.75 2.75 0 0 0 0-5.5H7zM8.188 10H7V6.5h1.188a1.75 1.75 0 1 1 0 3.5z"/>
|
||||
<path d="M14 4.5V14a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2h5.5L14 4.5zm-3 0A1.5 1.5 0 0 1 9.5 3V1H4a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V4.5h-2z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 439 B |
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-file-earmark-richtext" viewBox="0 0 16 16">
|
||||
<path d="M14 4.5V14a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2h5.5L14 4.5zm-3 0A1.5 1.5 0 0 1 9.5 3V1H4a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V4.5h-2z"/>
|
||||
<path d="M4.5 12.5A.5.5 0 0 1 5 12h3a.5.5 0 0 1 0 1H5a.5.5 0 0 1-.5-.5zm0-2A.5.5 0 0 1 5 10h6a.5.5 0 0 1 0 1H5a.5.5 0 0 1-.5-.5zm1.639-3.708 1.33.886 1.854-1.855a.25.25 0 0 1 .289-.047l1.888.974V8.5a.5.5 0 0 1-.5.5H5a.5.5 0 0 1-.5-.5V8s1.54-1.274 1.639-1.208zM6.25 6a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 623 B |
4
src/main/resources/static/images/file-earmark-word.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-file-earmark-word" viewBox="0 0 16 16">
|
||||
<path d="M5.485 6.879a.5.5 0 1 0-.97.242l1.5 6a.5.5 0 0 0 .967.01L8 9.402l1.018 3.73a.5.5 0 0 0 .967-.01l1.5-6a.5.5 0 0 0-.97-.242l-1.036 4.144-.997-3.655a.5.5 0 0 0-.964 0l-.997 3.655L5.485 6.88z"/>
|
||||
<path d="M14 14V4.5L9.5 0H4a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2zM9.5 3A1.5 1.5 0 0 0 11 4.5h2V14a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1h5.5v2z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 510 B |
4
src/main/resources/static/images/file-earmark-x.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-file-earmark-x" viewBox="0 0 16 16">
|
||||
<path d="M6.854 7.146a.5.5 0 1 0-.708.708L7.293 9l-1.147 1.146a.5.5 0 0 0 .708.708L8 9.707l1.146 1.147a.5.5 0 0 0 .708-.708L8.707 9l1.147-1.146a.5.5 0 0 0-.708-.708L8 8.293 6.854 7.146z"/>
|
||||
<path d="M14 14V4.5L9.5 0H4a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2zM9.5 3A1.5 1.5 0 0 0 11 4.5h2V14a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1h5.5v2z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 496 B |
4
src/main/resources/static/images/file-pdf.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-file-pdf" viewBox="0 0 16 16">
|
||||
<path d="M4 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H4zm0 1h8a1 1 0 0 1 1 1v12a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1z"/>
|
||||
<path d="M4.603 12.087a.81.81 0 0 1-.438-.42c-.195-.388-.13-.776.08-1.102.198-.307.526-.568.897-.787a7.68 7.68 0 0 1 1.482-.645 19.701 19.701 0 0 0 1.062-2.227 7.269 7.269 0 0 1-.43-1.295c-.086-.4-.119-.796-.046-1.136.075-.354.274-.672.65-.823.192-.077.4-.12.602-.077a.7.7 0 0 1 .477.365c.088.164.12.356.127.538.007.187-.012.395-.047.614-.084.51-.27 1.134-.52 1.794a10.954 10.954 0 0 0 .98 1.686 5.753 5.753 0 0 1 1.334.05c.364.065.734.195.96.465.12.144.193.32.2.518.007.192-.047.382-.138.563a1.04 1.04 0 0 1-.354.416.856.856 0 0 1-.51.138c-.331-.014-.654-.196-.933-.417a5.716 5.716 0 0 1-.911-.95 11.642 11.642 0 0 0-1.997.406 11.311 11.311 0 0 1-1.021 1.51c-.29.35-.608.655-.926.787a.793.793 0 0 1-.58.029zm1.379-1.901c-.166.076-.32.156-.459.238-.328.194-.541.383-.647.547-.094.145-.096.25-.04.361.01.022.02.036.026.044a.27.27 0 0 0 .035-.012c.137-.056.355-.235.635-.572a8.18 8.18 0 0 0 .45-.606zm1.64-1.33a12.647 12.647 0 0 1 1.01-.193 11.666 11.666 0 0 1-.51-.858 20.741 20.741 0 0 1-.5 1.05zm2.446.45c.15.162.296.3.435.41.24.19.407.253.498.256a.107.107 0 0 0 .07-.015.307.307 0 0 0 .094-.125.436.436 0 0 0 .059-.2.095.095 0 0 0-.026-.063c-.052-.062-.2-.152-.518-.209a3.881 3.881 0 0 0-.612-.053zM8.078 5.8a6.7 6.7 0 0 0 .2-.828c.031-.188.043-.343.038-.465a.613.613 0 0 0-.032-.198.517.517 0 0 0-.145.04c-.087.035-.158.106-.196.283-.04.192-.03.469.046.822.024.111.054.227.09.346z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
4
src/main/resources/static/images/file-zip.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-file-zip" viewBox="0 0 16 16">
|
||||
<path d="M6.5 7.5a1 1 0 0 1 1-1h1a1 1 0 0 1 1 1v.938l.4 1.599a1 1 0 0 1-.416 1.074l-.93.62a1 1 0 0 1-1.109 0l-.93-.62a1 1 0 0 1-.415-1.074l.4-1.599V7.5zm2 0h-1v.938a1 1 0 0 1-.03.243l-.4 1.598.93.62.93-.62-.4-1.598a1 1 0 0 1-.03-.243V7.5z"/>
|
||||
<path d="M2 2a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V2zm5.5-1H4a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1H9v1H8v1h1v1H8v1h1v1H7.5V5h-1V4h1V3h-1V2h1V1z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 578 B |
3
src/main/resources/static/images/file.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-file-earmark" viewBox="0 0 16 16">
|
||||
<path d="M14 4.5V14a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2h5.5L14 4.5zm-3 0A1.5 1.5 0 0 1 9.5 3V1H4a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V4.5h-2z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 304 B |
3
src/main/resources/static/images/filetype-html.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-filetype-html" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd" d="M14 4.5V11h-1V4.5h-2A1.5 1.5 0 0 1 9.5 3V1H4a1 1 0 0 0-1 1v9H2V2a2 2 0 0 1 2-2h5.5L14 4.5Zm-9.736 7.35v3.999h-.791v-1.714H1.79v1.714H1V11.85h.791v1.626h1.682V11.85h.79Zm2.251.662v3.337h-.794v-3.337H4.588v-.662h3.064v.662H6.515Zm2.176 3.337v-2.66h.038l.952 2.159h.516l.946-2.16h.038v2.661h.715V11.85h-.8l-1.14 2.596H9.93L8.79 11.85h-.805v3.999h.706Zm4.71-.674h1.696v.674H12.61V11.85h.79v3.325Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 565 B |
3
src/main/resources/static/images/filetype-txt.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-filetype-txt" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd" d="M14 4.5V14a2 2 0 0 1-2 2h-2v-1h2a1 1 0 0 0 1-1V4.5h-2A1.5 1.5 0 0 1 9.5 3V1H4a1 1 0 0 0-1 1v9H2V2a2 2 0 0 1 2-2h5.5L14 4.5ZM1.928 15.849v-3.337h1.136v-.662H0v.662h1.134v3.337h.794Zm4.689-3.999h-.894L4.9 13.289h-.035l-.832-1.439h-.932l1.228 1.983-1.24 2.016h.862l.853-1.415h.035l.85 1.415h.907l-1.253-1.992 1.274-2.007Zm1.93.662v3.337h-.794v-3.337H6.619v-.662h3.064v.662H8.546Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 548 B |
3
src/main/resources/static/images/filetype-xml.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-filetype-xml" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd" d="M14 4.5V14a2 2 0 0 1-2 2v-1a1 1 0 0 0 1-1V4.5h-2A1.5 1.5 0 0 1 9.5 3V1H4a1 1 0 0 0-1 1v9H2V2a2 2 0 0 1 2-2h5.5L14 4.5ZM3.527 11.85h-.893l-.823 1.439h-.036L.943 11.85H.012l1.227 1.983L0 15.85h.861l.853-1.415h.035l.85 1.415h.908l-1.254-1.992 1.274-2.007Zm.954 3.999v-2.66h.038l.952 2.159h.516l.946-2.16h.038v2.661h.715V11.85h-.8l-1.14 2.596h-.025L4.58 11.85h-.806v3.999h.706Zm4.71-.674h1.696v.674H8.4V11.85h.791v3.325Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 588 B |
11
src/main/resources/static/images/flags/cn.svg
Normal file
@@ -0,0 +1,11 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="flag-icons-cn" viewBox="0 0 640 480">
|
||||
<defs>
|
||||
<path id="a" fill="#ff0" d="M-.6.8 0-1 .6.8-1-.3h2z"/>
|
||||
</defs>
|
||||
<path fill="#ee1c25" d="M0 0h640v480H0z"/>
|
||||
<use xlink:href="#a" width="30" height="20" transform="matrix(71.9991 0 0 72 120 120)"/>
|
||||
<use xlink:href="#a" width="30" height="20" transform="matrix(-12.33562 -20.5871 20.58684 -12.33577 240.3 48)"/>
|
||||
<use xlink:href="#a" width="30" height="20" transform="matrix(-3.38573 -23.75998 23.75968 -3.38578 288 95.8)"/>
|
||||
<use xlink:href="#a" width="30" height="20" transform="matrix(6.5991 -23.0749 23.0746 6.59919 288 168)"/>
|
||||
<use xlink:href="#a" width="30" height="20" transform="matrix(14.9991 -18.73557 18.73533 14.99929 240 216)"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 795 B |
5
src/main/resources/static/images/flags/de.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-de" viewBox="0 0 640 480">
|
||||
<path fill="#ffce00" d="M0 320h640v160H0z"/>
|
||||
<path d="M0 0h640v160H0z"/>
|
||||
<path fill="#d00" d="M0 160h640v160H0z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 210 B |