Compare commits
157 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
63eacf443e | ||
|
|
b32c28e9cb | ||
|
|
a5ee10e029 | ||
|
|
bb1d41d74a | ||
|
|
0698e2888d | ||
|
|
e1f0a6cb1d | ||
|
|
7fecae8b0d | ||
|
|
e6622dfdc4 | ||
|
|
34b4ae0e03 | ||
|
|
036fd711f9 | ||
|
|
80a59205fa | ||
|
|
cbe4bca716 | ||
|
|
232a556425 | ||
|
|
a497ad8c41 | ||
|
|
3041e80c37 | ||
|
|
1b2df20fdd | ||
|
|
0e69f7e0e8 | ||
|
|
94aba370e0 | ||
|
|
cd49d7ffa2 | ||
|
|
dc297644d1 | ||
|
|
a7b4e44e6d | ||
|
|
610ff22abe | ||
|
|
27e8335f79 | ||
|
|
168a0f001c | ||
|
|
5c6936b494 | ||
|
|
a715dbb25d | ||
|
|
a43e13cf94 | ||
|
|
7f805d16a1 | ||
|
|
6f3cbe0cae | ||
|
|
44e3556382 | ||
|
|
c5ba546a02 | ||
|
|
d63519bbf4 | ||
|
|
aeadc88f92 | ||
|
|
829e98c29b | ||
|
|
3e3f4a0188 | ||
|
|
e5bdd52b7c | ||
|
|
e653ef6522 | ||
|
|
5fcb4e893b | ||
|
|
c2b524e459 | ||
|
|
5a055bae5e | ||
|
|
c3f501d701 | ||
|
|
8acab77ae3 | ||
|
|
8bd2784f37 | ||
|
|
e2c5027311 | ||
|
|
d2b2adcbc1 | ||
|
|
48158379ee | ||
|
|
6ba84a190f | ||
|
|
d349aea1be | ||
|
|
79e2683cbe | ||
|
|
e4fb64ce16 | ||
|
|
d405b7a810 | ||
|
|
1d243a0ca5 | ||
|
|
1f10693eaf | ||
|
|
b7f62a635d | ||
|
|
4e991e7ec2 | ||
|
|
8fe7e57a6a | ||
|
|
5c79a5da29 | ||
|
|
d01473aceb | ||
|
|
3911be0177 | ||
|
|
78da44ad83 | ||
|
|
54859ac3ba | ||
|
|
9cb8c9f655 | ||
|
|
dfda474ba5 | ||
|
|
43f15b3e55 | ||
|
|
86c45f6f8f | ||
|
|
de83321c62 | ||
|
|
7b44cf77d6 | ||
|
|
c769a02982 | ||
|
|
aa671b8bd6 | ||
|
|
6e7c066e57 | ||
|
|
78ac9231c5 | ||
|
|
e9947da5b4 | ||
|
|
8df7dfc3be | ||
|
|
d79db6f3da | ||
|
|
84aebe3851 | ||
|
|
f5c285a70f | ||
|
|
2d6bf43bdb | ||
|
|
964f22e3e0 | ||
|
|
d325020e22 | ||
|
|
aec85ddd66 | ||
|
|
32b009b11f | ||
|
|
2e5b72e4fb | ||
|
|
1d3cf2bdc3 | ||
|
|
2a5fe2bd74 | ||
|
|
61ff0248da | ||
|
|
659af2089c | ||
|
|
8960313a2b | ||
|
|
6ee8e1e37f | ||
|
|
05977aa3a6 | ||
|
|
f7dbb8d0a6 | ||
|
|
eaf65d7981 | ||
|
|
a10e3a025b | ||
|
|
4d3e442ecc | ||
|
|
49576c0aa4 | ||
|
|
960af83f11 | ||
|
|
cf42ef7faa | ||
|
|
d894937c22 | ||
|
|
8938e86223 | ||
|
|
c1a39e53dc | ||
|
|
cf3693186a | ||
|
|
0fb0cb8bca | ||
|
|
fb18d0d04d | ||
|
|
779d9028fe | ||
|
|
f2b701e3e3 | ||
|
|
a138d5f5a9 | ||
|
|
6276f028ac | ||
|
|
b962e867d8 | ||
|
|
a286a92ede | ||
|
|
7fb8f5ed28 | ||
|
|
03d3235e1d | ||
|
|
d23551857c | ||
|
|
dd9dd72f35 | ||
|
|
9652f59ae9 | ||
|
|
3469beb5b3 | ||
|
|
690720f4e3 | ||
|
|
491be75e1f | ||
|
|
a868b2c649 | ||
|
|
0b49993d80 | ||
|
|
995a926e35 | ||
|
|
914dd0a21a | ||
|
|
d9b5d08b06 | ||
|
|
344d1163ff | ||
|
|
3f50979d3e | ||
|
|
c681f48459 | ||
|
|
2f5d7ed712 | ||
|
|
1efefcfcb8 | ||
|
|
909c9ed4d9 | ||
|
|
116b3535ee | ||
|
|
b7d6107a2d | ||
|
|
120b017b1a | ||
|
|
24568f4a42 | ||
|
|
03450454c5 | ||
|
|
7e982e125d | ||
|
|
e725451530 | ||
|
|
d7d6bc8108 | ||
|
|
6d66ac0a8b | ||
|
|
93f12d1313 | ||
|
|
eab9e3cffc | ||
|
|
d74c25e678 | ||
|
|
c729b7201f | ||
|
|
beab9932d7 | ||
|
|
65fcf29fd5 | ||
|
|
73007239ee | ||
|
|
816d874ac4 | ||
|
|
b66f86f7cc | ||
|
|
168ef747de | ||
|
|
ad047ab012 | ||
|
|
1ea3fb209b | ||
|
|
875d9da36b | ||
|
|
b21d2ecbd1 | ||
|
|
3bffc1da76 | ||
|
|
631d3948bd | ||
|
|
5774a22b64 | ||
|
|
39345bb6bb | ||
|
|
57b483047e | ||
|
|
0fb7633da8 | ||
|
|
dae2f33772 |
2
.github/CODEOWNERS
vendored
Normal file
2
.github/CODEOWNERS
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
# All PRs to V1 must be approved by Frooodle
|
||||
* @Frooodle
|
||||
2
.github/workflows/push-docker.yml
vendored
2
.github/workflows/push-docker.yml
vendored
@@ -139,6 +139,8 @@ jobs:
|
||||
cache-to: type=gha,mode=max
|
||||
tags: ${{ steps.meta3.outputs.tags }}
|
||||
labels: ${{ steps.meta3.outputs.labels }}
|
||||
build-args:
|
||||
VERSION_TAG=${{ steps.versionNumber.outputs.versionNumber }}
|
||||
platforms: linux/amd64,linux/arm64/v8
|
||||
- name: Build and Push Helm Chart
|
||||
run: |
|
||||
|
||||
10
.gitignore
vendored
10
.gitignore
vendored
@@ -15,8 +15,8 @@ local.properties
|
||||
.classpath
|
||||
.project
|
||||
version.properties
|
||||
pipeline/
|
||||
|
||||
pipeline/watchedFolders/
|
||||
pipeline/finishedFolders/
|
||||
#### Stirling-PDF Files ###
|
||||
customFiles/
|
||||
configs/
|
||||
@@ -120,4 +120,8 @@ watchedFolders/
|
||||
/build
|
||||
|
||||
/.vscode
|
||||
/.idea
|
||||
/.idea
|
||||
|
||||
# Ignore Mac DS_Store files
|
||||
.DS_Store
|
||||
**/.DS_Store
|
||||
10
Dockerfile
10
Dockerfile
@@ -6,7 +6,8 @@ ARG VERSION_TAG
|
||||
# Set Environment Variables
|
||||
ENV DOCKER_ENABLE_SECURITY=false \
|
||||
HOME=/home/stirlingpdfuser \
|
||||
VERSION_TAG=$VERSION_TAG
|
||||
VERSION_TAG=$VERSION_TAG \
|
||||
JAVA_TOOL_OPTIONS="$JAVA_TOOL_OPTIONS -XX:MaxRAMPercentage=75"
|
||||
# PUID=1000 \
|
||||
# PGID=1000 \
|
||||
# UMASK=022 \
|
||||
@@ -18,19 +19,20 @@ ENV DOCKER_ENABLE_SECURITY=false \
|
||||
## mkdir -p $HOME && chown stirlingpdfuser:stirlingpdfgroup $HOME
|
||||
|
||||
# Set up necessary directories and permissions
|
||||
RUN mkdir -p /scripts /usr/share/fonts/opentype/noto /usr/share/tesseract-ocr /configs /customFiles
|
||||
RUN mkdir -p /scripts /usr/share/fonts/opentype/noto /usr/share/tesseract-ocr /configs /logs /customFiles /pipeline /pipeline/defaultWebUIConfigs /pipeline/watchedFolders /pipeline/finishedFolders
|
||||
##&& \
|
||||
## chown -R stirlingpdfuser:stirlingpdfgroup /scripts /usr/share/fonts/opentype/noto /usr/share/tesseract-ocr /configs /customFiles && \
|
||||
## chown -R stirlingpdfuser:stirlingpdfgroup /usr/share/tesseract-ocr-original
|
||||
|
||||
# Copy necessary files
|
||||
COPY ./scripts/* /scripts/
|
||||
COPY ./pipeline/ /pipeline/
|
||||
COPY src/main/resources/static/fonts/*.ttf /usr/share/fonts/opentype/noto/
|
||||
COPY src/main/resources/static/fonts/*.otf /usr/share/fonts/opentype/noto/
|
||||
COPY build/libs/*.jar app.jar
|
||||
|
||||
# Set font cache and permissions
|
||||
RUN fc-cache -f -v && chmod +x /scripts/init.sh
|
||||
RUN fc-cache -f -v && chmod +x /scripts/*
|
||||
|
||||
##&& \
|
||||
## chown stirlingpdfuser:stirlingpdfgroup /app.jar && \
|
||||
@@ -42,4 +44,4 @@ EXPOSE 8080
|
||||
# Set user and run command
|
||||
##USER stirlingpdfuser
|
||||
ENTRYPOINT ["/scripts/init.sh"]
|
||||
CMD ["java", "-jar", "/app.jar"]
|
||||
CMD ["java", "-Dfile.encoding=UTF-8", "-jar", "/app.jar"]
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
# Build jbig2enc in a separate stage
|
||||
FROM bellsoft/liberica-openjdk-debian:17
|
||||
|
||||
ARG VERSION_TAG
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y --no-install-recommends \
|
||||
libreoffice-core \
|
||||
@@ -14,7 +17,8 @@ RUN apt-get update && \
|
||||
# Set Environment Variables
|
||||
ENV DOCKER_ENABLE_SECURITY=false \
|
||||
HOME=/home/stirlingpdfuser \
|
||||
VERSION_TAG=$VERSION_TAG
|
||||
VERSION_TAG=$VERSION_TAG \
|
||||
JAVA_TOOL_OPTIONS="$JAVA_TOOL_OPTIONS -XX:MaxRAMPercentage=75"
|
||||
# PUID=1000 \
|
||||
# PGID=1000 \
|
||||
# UMASK=022 \
|
||||
@@ -25,17 +29,24 @@ ENV DOCKER_ENABLE_SECURITY=false \
|
||||
# mkdir -p $HOME && chown stirlingpdfuser:stirlingpdfgroup $HOME
|
||||
|
||||
# Set up necessary directories and permissions
|
||||
RUN mkdir -p /scripts /usr/share/fonts/opentype/noto /configs /customFiles
|
||||
RUN mkdir -p /scripts /usr/share/fonts/opentype/noto /configs /customFiles /logs /pipeline /pipeline/defaultWebUIConfigs /pipeline/watchedFolders /pipeline/finishedFolders
|
||||
|
||||
# chown -R stirlingpdfuser:stirlingpdfgroup /usr/share/fonts/opentype/noto /configs /customFiles
|
||||
|
||||
# Copy necessary files
|
||||
COPY ./scripts/download-security-jar.sh /scripts/download-security-jar.sh
|
||||
COPY ./scripts/init-without-ocr.sh /scripts/init-without-ocr.sh
|
||||
COPY ./pipeline/ /pipeline/
|
||||
COPY src/main/resources/static/fonts/*.ttf /usr/share/fonts/opentype/noto/
|
||||
COPY src/main/resources/static/fonts/*.otf /usr/share/fonts/opentype/noto/
|
||||
COPY build/libs/*.jar app.jar
|
||||
|
||||
# Set font cache and permissions
|
||||
RUN fc-cache -f -v
|
||||
RUN fc-cache -f -v && \
|
||||
chmod +x /scripts/init-without-ocr.sh && \
|
||||
chmod +x /scripts/download-security-jar.sh
|
||||
|
||||
|
||||
# chown stirlingpdfuser:stirlingpdfgroup /app.jar
|
||||
|
||||
|
||||
@@ -50,5 +61,5 @@ ENV DOCKER_ENABLE_SECURITY=false
|
||||
|
||||
# Run the application
|
||||
#USER stirlingpdfuser
|
||||
ENTRYPOINT ["/scripts/init.sh"]
|
||||
CMD ["java", "-jar", "/app.jar"]
|
||||
ENTRYPOINT ["/scripts/init-without-ocr.sh"]
|
||||
CMD ["java", "-Dfile.encoding=UTF-8", "-jar", "/app.jar"]
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
# Build jbig2enc in a separate stage
|
||||
FROM bellsoft/liberica-openjdk-alpine:17
|
||||
|
||||
ARG VERSION_TAG
|
||||
|
||||
# Set Environment Variables
|
||||
ENV DOCKER_ENABLE_SECURITY=false \
|
||||
HOME=/home/stirlingpdfuser \
|
||||
VERSION_TAG=$VERSION_TAG
|
||||
VERSION_TAG=$VERSION_TAG \
|
||||
JAVA_TOOL_OPTIONS="$JAVA_TOOL_OPTIONS -XX:MaxRAMPercentage=75"
|
||||
# PUID=1000 \
|
||||
# PGID=1000 \
|
||||
# UMASK=022 \
|
||||
@@ -16,22 +19,28 @@ ENV DOCKER_ENABLE_SECURITY=false \
|
||||
|
||||
# Set up necessary directories and permissions
|
||||
#RUN mkdir -p /scripts /configs /customFiles && \
|
||||
# chown -R stirlingpdfuser:stirlingpdfgroup /scripts /configs /customFiles
|
||||
|
||||
# chown -R stirlingpdfuser:stirlingpdfgroup /scripts /configs /customFiles /logs /pipeline /pipeline/defaultWebUIConfigs /pipeline/watchedFolders /pipeline/finishedFolders
|
||||
|
||||
RUN mkdir -p /scripts /usr/share/fonts/opentype/noto /configs /customFiles
|
||||
|
||||
COPY ./scripts/download-security-jar.sh /scripts/download-security-jar.sh
|
||||
COPY ./scripts/init-without-ocr.sh /scripts/init-without-ocr.sh
|
||||
COPY ./pipeline/ /pipeline/
|
||||
COPY build/libs/*.jar app.jar
|
||||
|
||||
# Set font cache and permissions
|
||||
#RUN chown stirlingpdfuser:stirlingpdfgroup /app.jar
|
||||
|
||||
RUN chmod +x /scripts/init-without-ocr.sh && \
|
||||
chmod +x /scripts/download-security-jar.sh && \
|
||||
apk add --no-cache curl
|
||||
|
||||
# Expose the application port
|
||||
EXPOSE 8080
|
||||
|
||||
# Set environment variables
|
||||
ENV ENDPOINTS_GROUPS_TO_REMOVE=CLI
|
||||
|
||||
ENTRYPOINT ["/scripts/init.sh"]
|
||||
ENTRYPOINT ["/scripts/init-without-ocr.sh"]
|
||||
|
||||
# Run the application
|
||||
CMD ["java", "-jar", "/app.jar"]
|
||||
CMD ["java", "-Dfile.encoding=UTF-8", "-jar", "/app.jar"]
|
||||
|
||||
@@ -20,7 +20,7 @@ Install the following software, if not already installed:
|
||||
|
||||
- Git
|
||||
|
||||
- Python 3 (with pip)
|
||||
- Python 3.8 (with pip)
|
||||
|
||||
- Make
|
||||
|
||||
@@ -95,14 +95,14 @@ For Debian-based systems, you can use the following command:
|
||||
|
||||
```bash
|
||||
sudo apt-get install -y libreoffice-writer libreoffice-calc libreoffice-impress unpaper ocrmypdf
|
||||
pip3 install uno opencv-python-headless unoconv pngquant
|
||||
pip3 install uno opencv-python-headless unoconv pngquant WeasyPrint
|
||||
```
|
||||
|
||||
For Fedora:
|
||||
|
||||
```bash
|
||||
sudo dnf install -y libreoffice-writer libreoffice-calc libreoffice-impress unpaper ocrmypdf
|
||||
pip3 install uno opencv-python-headless unoconv pngquant
|
||||
pip3 install uno opencv-python-headless unoconv pngquant WeasyPrint
|
||||
```
|
||||
|
||||
### Step 4: Clone and Build Stirling-PDF
|
||||
@@ -176,7 +176,7 @@ rpm -qa | grep tesseract-langpack | sed 's/tesseract-langpack-//g'
|
||||
```bash
|
||||
./gradlew bootRun
|
||||
or
|
||||
java -jar build/libs/app.jar
|
||||
java -jar /opt/Stirling-PDF/Stirling-PDF-*.jar
|
||||
```
|
||||
|
||||
### Step 8: Adding a Desktop icon
|
||||
@@ -202,6 +202,64 @@ EOF
|
||||
|
||||
Note: Currently the app will run in the background until manually closed.
|
||||
|
||||
### Optional: Run Stirling-PDF as a service
|
||||
|
||||
First create a .env file, where you can store environment variables:
|
||||
```
|
||||
touch /opt/Stirling-PDF/.env
|
||||
```
|
||||
In this file you can add all variables, one variable per line, as stated in the main readme (for example SYSTEM_DEFAULTLOCALE="de-DE").
|
||||
|
||||
Create a new file where we store our service settings and open it with nano editor:
|
||||
```
|
||||
nano /etc/systemd/system/stirlingpdf.service
|
||||
```
|
||||
|
||||
Paste this content, make sure to update the filename of the jar-file. Press Ctrl+S and Ctrl+X to save and exit the nano editor:
|
||||
```
|
||||
[Unit]
|
||||
Description=Stirling-PDF service
|
||||
After=syslog.target network.target
|
||||
|
||||
[Service]
|
||||
SuccessExitStatus=143
|
||||
|
||||
User=root
|
||||
Group=root
|
||||
|
||||
Type=simple
|
||||
|
||||
EnvironmentFile=/opt/Stirling-PDF/.env
|
||||
WorkingDirectory=/opt/Stirling-PDF
|
||||
ExecStart=/usr/bin/java -jar Stirling-PDF-0.17.2.jar
|
||||
ExecStop=/bin/kill -15 $MAINPID
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
Notify systemd that it has to rebuild its internal service database (you have to run this command every time you make a change in the service file):
|
||||
```
|
||||
sudo systemctl daemon-reload
|
||||
```
|
||||
|
||||
Enable the service to tell the service to start it automatically:
|
||||
```
|
||||
sudo systemctl enable stirlingpdf.service
|
||||
```
|
||||
|
||||
See the status of the service:
|
||||
```
|
||||
sudo systemctl status stirlingpdf.service
|
||||
```
|
||||
|
||||
Manually start/stop/restart the service:
|
||||
```
|
||||
sudo systemctl start stirlingpdf.service
|
||||
sudo systemctl stop stirlingpdf.service
|
||||
sudo systemctl restart stirlingpdf.service
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
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.
|
||||
|
||||
11
README.md
11
README.md
@@ -14,10 +14,9 @@ This is a powerful locally hosted web based PDF manipulation tool using docker t
|
||||
|
||||
Stirling PDF makes no outbound calls for any record keeping or tracking.
|
||||
|
||||
All files and PDFs are either purely client side, in server memory only during the execution of the task or within a temporay file only for execution of the task.
|
||||
Any file which has been downloaded by the user will have already been deleted from the server by that time.
|
||||
All files and PDFs exist either exclusively on the client side, reside in server memory only during task execution, or temporarily reside in a file solely for the execution of the task. Any file downloaded by the user will have been deleted from the server by that point.
|
||||
|
||||
Feel free to request any features or bug fixes either in github issues or our [Discord](https://discord.gg/Cn8pWhQRxZ)
|
||||
Please feel free to submit feature requests or report bugs either through GitHub issues or on our [Discord](https://discord.gg/Cn8pWhQRxZ)
|
||||
|
||||

|
||||
|
||||
@@ -148,7 +147,7 @@ Note: Podman is CLI-compatible with Docker, so simply replace "docker" with "pod
|
||||
Please view https://github.com/Frooodle/Stirling-PDF/blob/main/HowToUseOCR.md
|
||||
|
||||
## Want to add your own language?
|
||||
Stirling PDF currently supports 20!
|
||||
Stirling PDF currently supports 21!
|
||||
- English (English) (en_GB)
|
||||
- English (US) (en_US)
|
||||
- Arabic (العربية) (ar_AR)
|
||||
@@ -169,6 +168,8 @@ Stirling PDF currently supports 20!
|
||||
- Dutch (Nederlands) (nl_NL)
|
||||
- Greek (el_GR)
|
||||
- Turkish (Türkçe) (tr_TR)
|
||||
- Indonesia (Bahasa Indonesia) (id_ID)
|
||||
- Hindi (हिंदी) (hi_IN)
|
||||
|
||||
If you want to add your own language to Stirling-PDF please refer
|
||||
https://github.com/Frooodle/Stirling-PDF/blob/main/HowToAddNewLanguage.md
|
||||
@@ -267,7 +268,7 @@ For API usage you must provide a header with 'X-API-Key' and the associated API
|
||||
- Fill forms mannual and automatic
|
||||
|
||||
### Q2: Why is my application downloading .htm files?
|
||||
This is a issue caused commonly by your NGINX congifuration. The default file upload size for NGINX is 1MB, you need to add the following in your Nginx sites-available file. ``client_max_body_size SIZE;`` Where "SIZE" is 50M for example for 50MB files.
|
||||
This is an issue caused commonly by your NGINX configuration. The default file upload size for NGINX is 1MB, you need to add the following in your Nginx sites-available file. ``client_max_body_size SIZE;`` Where "SIZE" is 50M for example for 50MB files.
|
||||
|
||||
### Q3: Why is my download timing out
|
||||
NGINX has timeout values by default so if you are running Stirling-PDF behind NGINX you may need to set a timeout value such as adding the config ``proxy_read_timeout 3600;``
|
||||
|
||||
@@ -19,6 +19,7 @@ add-image | ✔️ | ✔️ | ✔️
|
||||
add-watermark | ✔️ | ✔️ | ✔️
|
||||
adjust-contrast | ✔️ | ✔️ | ✔️
|
||||
auto-split-pdf | ✔️ | ✔️ | ✔️
|
||||
auto-redact | ✔️ | ✔️ | ✔️
|
||||
auto-rename | ✔️ | ✔️ | ✔️
|
||||
cert-sign | ✔️ | ✔️ | ✔️
|
||||
crop | ✔️ | ✔️ | ✔️
|
||||
@@ -33,7 +34,9 @@ img-to-pdf | ✔️ | ✔️ | ✔️
|
||||
markdown-to-pdf | ✔️ | ✔️ | ✔️
|
||||
merge-pdfs | ✔️ | ✔️ | ✔️
|
||||
multi-page-layout | ✔️ | ✔️ | ✔️
|
||||
overlay-pdf | ✔️ | ✔️ | ✔️
|
||||
pdf-organizer | ✔️ | ✔️ | ✔️
|
||||
pdf-to-csv | ✔️ | ✔️ | ✔️
|
||||
pdf-to-img | ✔️ | ✔️ | ✔️
|
||||
pdf-to-single-page | ✔️ | ✔️ | ✔️
|
||||
remove-pages | ✔️ | ✔️ | ✔️
|
||||
@@ -43,6 +46,8 @@ sanitize-pdf | ✔️ | ✔️ | ✔️
|
||||
scale-pages | ✔️ | ✔️ | ✔️
|
||||
sign | ✔️ | ✔️ | ✔️
|
||||
show-javascript | ✔️ | ✔️ | ✔️
|
||||
split-by-size-or-count | ✔️ | ✔️ | ✔️
|
||||
split-pdf-by-sections | ✔️ | ✔️ | ✔️
|
||||
split-pdfs | ✔️ | ✔️ | ✔️
|
||||
file-to-pdf | | ✔️ | ✔️
|
||||
pdf-to-html | | ✔️ | ✔️
|
||||
|
||||
59
build.gradle
59
build.gradle
@@ -2,13 +2,13 @@ plugins {
|
||||
id 'java'
|
||||
id 'org.springframework.boot' version '3.1.2'
|
||||
id 'io.spring.dependency-management' version '1.1.3'
|
||||
id 'org.springdoc.openapi-gradle-plugin' version '1.6.0'
|
||||
id "io.swagger.swaggerhub" version "1.2.0"
|
||||
id 'org.springdoc.openapi-gradle-plugin' version '1.8.0'
|
||||
id "io.swagger.swaggerhub" version "1.3.2"
|
||||
id 'edu.sc.seis.launch4j' version '3.0.5'
|
||||
}
|
||||
|
||||
group = 'stirling.software'
|
||||
version = '0.17.0'
|
||||
version = '0.18.1'
|
||||
sourceCompatibility = '17'
|
||||
|
||||
repositories {
|
||||
@@ -32,7 +32,6 @@ sourceSets {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
openApi {
|
||||
apiDocsUrl = "http://localhost:8080/v1/api-docs"
|
||||
outputDir = file("$projectDir")
|
||||
@@ -49,7 +48,7 @@ launch4j {
|
||||
|
||||
errTitle="Encountered error, Do you have Java 17?"
|
||||
downloadUrl="https://download.oracle.com/java/17/latest/jdk-17_windows-x64_bin.exe"
|
||||
variables=["BROWSER_OPEN=true"]
|
||||
variables=["BROWSER_OPEN=true", "ENDPOINTS_GROUPS_TO_REMOVE=CLI"]
|
||||
jreMinVersion="17"
|
||||
|
||||
mutexName="Stirling-PDF"
|
||||
@@ -69,33 +68,57 @@ dependencies {
|
||||
implementation 'org.springframework:spring-webmvc:6.0.15'
|
||||
|
||||
implementation 'org.yaml:snakeyaml:2.1'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-web:3.1.6'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf:3.1.6'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-web:3.2.1'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf:3.2.1'
|
||||
|
||||
if (System.getenv('DOCKER_ENABLE_SECURITY') != 'false') {
|
||||
implementation 'org.springframework.boot:spring-boot-starter-security:3.1.6'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-security:3.2.1'
|
||||
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5:3.1.2.RELEASE'
|
||||
implementation "org.springframework.boot:spring-boot-starter-data-jpa"
|
||||
implementation "com.h2database:h2"
|
||||
}
|
||||
|
||||
testImplementation 'org.springframework.boot:spring-boot-starter-test:3.1.6'
|
||||
testImplementation 'org.springframework.boot:spring-boot-starter-test:3.2.1'
|
||||
|
||||
// Batik
|
||||
implementation 'org.apache.xmlgraphics:batik-all:1.17'
|
||||
|
||||
// TwelveMonkeys
|
||||
implementation 'com.twelvemonkeys.imageio:imageio-batik:3.10.1'
|
||||
implementation 'com.twelvemonkeys.imageio:imageio-bmp:3.10.1'
|
||||
// implementation 'com.twelvemonkeys.imageio:imageio-hdr:3.10.1'
|
||||
// implementation 'com.twelvemonkeys.imageio:imageio-icns:3.10.1'
|
||||
// implementation 'com.twelvemonkeys.imageio:imageio-iff:3.10.1'
|
||||
implementation 'com.twelvemonkeys.imageio:imageio-jpeg:3.10.1'
|
||||
// implementation 'com.twelvemonkeys.imageio:imageio-pcx:3.10.1'
|
||||
// implementation 'com.twelvemonkeys.imageio:imageio-pict:3.10.1'
|
||||
// implementation 'com.twelvemonkeys.imageio:imageio-pnm:3.10.1'
|
||||
// implementation 'com.twelvemonkeys.imageio:imageio-psd:3.10.1'
|
||||
// implementation 'com.twelvemonkeys.imageio:imageio-sgi:3.10.1'
|
||||
// implementation 'com.twelvemonkeys.imageio:imageio-tga:3.10.1'
|
||||
// implementation 'com.twelvemonkeys.imageio:imageio-thumbsdb:3.10.1'
|
||||
implementation 'com.twelvemonkeys.imageio:imageio-tiff:3.10.1'
|
||||
implementation 'com.twelvemonkeys.imageio:imageio-webp:3.10.1'
|
||||
// implementation 'com.twelvemonkeys.imageio:imageio-xwd:3.10.1'
|
||||
|
||||
// https://mvnrepository.com/artifact/org.apache.pdfbox/jbig2-imageio
|
||||
implementation group: 'org.apache.pdfbox', name: 'jbig2-imageio', version: '3.0.4'
|
||||
implementation 'com.github.jai-imageio:jai-imageio-core:1.4.0'
|
||||
implementation 'com.github.jai-imageio:jai-imageio-jpeg2000:1.3.0'
|
||||
implementation 'commons-io:commons-io:2.13.0'
|
||||
implementation 'commons-io:commons-io:2.15.1'
|
||||
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.2.0'
|
||||
|
||||
//general PDF
|
||||
|
||||
// https://mvnrepository.com/artifact/com.opencsv/opencsv
|
||||
implementation group: 'com.opencsv', name: 'opencsv', version: '5.7.1'
|
||||
implementation 'org.apache.pdfbox:pdfbox:2.0.29'
|
||||
implementation 'org.apache.pdfbox:xmpbox:2.0.29'
|
||||
implementation ('com.opencsv:opencsv:5.7.1') {
|
||||
exclude group: 'commons-logging', module: 'commons-logging'
|
||||
}
|
||||
|
||||
implementation ('org.apache.pdfbox:pdfbox:2.0.29'){
|
||||
exclude group: 'commons-logging', module: 'commons-logging'
|
||||
}
|
||||
|
||||
implementation ('org.apache.pdfbox:xmpbox:2.0.29'){
|
||||
exclude group: 'commons-logging', module: 'commons-logging'
|
||||
}
|
||||
|
||||
implementation 'org.bouncycastle:bcprov-jdk18on:1.77'
|
||||
implementation 'org.bouncycastle:bcpkix-jdk18on:1.77'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-actuator'
|
||||
@@ -107,7 +130,7 @@ dependencies {
|
||||
implementation 'com.github.vladimir-bukhtoyarov:bucket4j-core:7.6.0'
|
||||
|
||||
developmentOnly("org.springframework.boot:spring-boot-devtools")
|
||||
compileOnly 'org.projectlombok:lombok:1.18.28'
|
||||
compileOnly 'org.projectlombok:lombok:1.18.30'
|
||||
annotationProcessor 'org.projectlombok:lombok:1.18.28'
|
||||
}
|
||||
|
||||
|
||||
39
pipeline/defaultWebUIConfigs/Prepare-pdfs-for-email.json
Normal file
39
pipeline/defaultWebUIConfigs/Prepare-pdfs-for-email.json
Normal file
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"name": "Prepare-pdfs-for-email",
|
||||
"pipeline": [
|
||||
{
|
||||
"operation": "/api/v1/misc/repair",
|
||||
"parameters": {}
|
||||
},
|
||||
{
|
||||
"operation": "/api/v1/security/sanitize-pdf",
|
||||
"parameters": {
|
||||
"removeJavaScript": true,
|
||||
"removeEmbeddedFiles": false,
|
||||
"removeMetadata": false,
|
||||
"removeLinks": false,
|
||||
"removeFonts": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"operation": "/api/v1/misc/compress-pdf",
|
||||
"parameters": {
|
||||
"optimizeLevel": 2,
|
||||
"expectedOutputSize": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"operation": "/api/v1/general/split-by-size-or-count",
|
||||
"parameters": {
|
||||
"splitType": 0,
|
||||
"splitValue": "15MB"
|
||||
}
|
||||
}
|
||||
],
|
||||
"_examples": {
|
||||
"outputDir": "{outputFolder}/{folderName}",
|
||||
"outputFileName": "{filename}-{pipelineName}-{date}-{time}"
|
||||
},
|
||||
"outputDir": "httpWebRequest",
|
||||
"outputFileName": "{filename}"
|
||||
}
|
||||
33
pipeline/defaultWebUIConfigs/split-rotate-auto-rename.json
Normal file
33
pipeline/defaultWebUIConfigs/split-rotate-auto-rename.json
Normal file
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"name": "split-rotate-auto-rename",
|
||||
"pipeline": [
|
||||
{
|
||||
"operation": "/api/v1/general/split-pdf-by-sections",
|
||||
"parameters": {
|
||||
"horizontalDivisions": 2,
|
||||
"verticalDivisions": 2,
|
||||
"fileInput": "automated"
|
||||
}
|
||||
},
|
||||
{
|
||||
"operation": "/api/v1/general/rotate-pdf",
|
||||
"parameters": {
|
||||
"angle": 90,
|
||||
"fileInput": "automated"
|
||||
}
|
||||
},
|
||||
{
|
||||
"operation": "/api/v1/misc/auto-rename",
|
||||
"parameters": {
|
||||
"useFirstTextAsFallback": false,
|
||||
"fileInput": "automated"
|
||||
}
|
||||
}
|
||||
],
|
||||
"_examples": {
|
||||
"outputDir": "{outputFolder}/{folderName}",
|
||||
"outputFileName": "{filename}-{pipelineName}-{date}-{time}"
|
||||
},
|
||||
"outputDir": "{outputFolder}",
|
||||
"outputFileName": "{filename}"
|
||||
}
|
||||
@@ -1,10 +1,11 @@
|
||||
import cv2
|
||||
import sys
|
||||
import argparse
|
||||
import numpy as np
|
||||
|
||||
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
|
||||
@@ -15,19 +16,11 @@ def is_blank_image(image_path, threshold=10, white_percent=99, white_value=255,
|
||||
_, 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 = 0
|
||||
total_pixels = thresholded_image.size
|
||||
for i in range(0, thresholded_image.shape[0], 2):
|
||||
for j in range(0, thresholded_image.shape[1], 2):
|
||||
if thresholded_image[i, j] == white_value:
|
||||
white_pixels += 1
|
||||
white_pixel_percentage = (white_pixels / (i * thresholded_image.shape[1] + j + 1)) * 100
|
||||
if white_pixel_percentage < white_percent:
|
||||
return False
|
||||
white_pixels = np.sum(thresholded_image == white_value)
|
||||
white_pixel_percentage = (white_pixels / thresholded_image.size) * 100
|
||||
|
||||
print(f"Page has white pixel percent of {white_pixel_percentage}")
|
||||
return True
|
||||
|
||||
return white_pixel_percentage >= white_percent
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
@@ -39,9 +32,6 @@ if __name__ == "__main__":
|
||||
|
||||
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)
|
||||
# Return code 1: The image is considered blank.
|
||||
# Return code 0: The image is not considered blank.
|
||||
sys.exit(int(blank))
|
||||
|
||||
19
scripts/download-security-jar.sh
Normal file
19
scripts/download-security-jar.sh
Normal file
@@ -0,0 +1,19 @@
|
||||
echo "Running Stirling PDF with DOCKER_ENABLE_SECURITY=${DOCKER_ENABLE_SECURITY} and VERSION_TAG=${VERSION_TAG}"
|
||||
# Check for DOCKER_ENABLE_SECURITY and download the appropriate JAR if required
|
||||
if [ "$DOCKER_ENABLE_SECURITY" = "true" ] && [ "$VERSION_TAG" != "alpha" ]; then
|
||||
if [ ! -f app-security.jar ]; then
|
||||
echo "Trying to download from: https://github.com/Frooodle/Stirling-PDF/releases/download/v$VERSION_TAG/Stirling-PDF-with-login.jar"
|
||||
curl -L -o app-security.jar https://github.com/Frooodle/Stirling-PDF/releases/download/v$VERSION_TAG/Stirling-PDF-with-login.jar
|
||||
|
||||
# If the first download attempt failed, try with the 'v' prefix
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Trying to download from: https://github.com/Frooodle/Stirling-PDF/releases/download/$VERSION_TAG/Stirling-PDF-with-login.jar"
|
||||
curl -L -o app-security.jar https://github.com/Frooodle/Stirling-PDF/releases/download/$VERSION_TAG/Stirling-PDF-with-login.jar
|
||||
fi
|
||||
|
||||
if [ $? -eq 0 ]; then # checks if curl was successful
|
||||
rm -f app.jar
|
||||
ln -s app-security.jar app.jar
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
6
scripts/init-without-ocr.sh
Normal file
6
scripts/init-without-ocr.sh
Normal file
@@ -0,0 +1,6 @@
|
||||
#!/bin/sh
|
||||
|
||||
/scripts/download-security-jar.sh
|
||||
|
||||
# Run the main command
|
||||
exec "$@"
|
||||
@@ -3,7 +3,7 @@
|
||||
# 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
|
||||
cp -rn /usr/share/tesseract-ocr-original/* /usr/share/tesseract-ocr
|
||||
|
||||
if [ -d /usr/share/tesseract-ocr/4.00/tessdata ]; then
|
||||
cp -r /usr/share/tesseract-ocr/4.00/tessdata/* /usr/share/tesseract-ocr/5/tessdata/ || true;
|
||||
@@ -20,26 +20,7 @@ if [[ -n "$TESSERACT_LANGS" ]]; then
|
||||
done
|
||||
fi
|
||||
|
||||
echo "Running Stirling PDF with DOCKER_ENABLE_SECURITY=${DOCKER_ENABLE_SECURITY} and VERSION_TAG=${VERSION_TAG}"
|
||||
# Check for DOCKER_ENABLE_SECURITY and download the appropriate JAR if required
|
||||
if [ "$DOCKER_ENABLE_SECURITY" = "true" ] && [ "$VERSION_TAG" != "alpha" ]; then
|
||||
if [ ! -f app-security.jar ]; then
|
||||
echo "Trying to download from: https://github.com/Frooodle/Stirling-PDF/releases/download/v$VERSION_TAG/Stirling-PDF-with-login.jar"
|
||||
curl -L -o app-security.jar https://github.com/Frooodle/Stirling-PDF/releases/download/v$VERSION_TAG/Stirling-PDF-with-login.jar
|
||||
|
||||
# If the first download attempt failed, try with the 'v' prefix
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Trying to download from: https://github.com/Frooodle/Stirling-PDF/releases/download/$VERSION_TAG/Stirling-PDF-with-login.jar"
|
||||
curl -L -o app-security.jar https://github.com/Frooodle/Stirling-PDF/releases/download/$VERSION_TAG/Stirling-PDF-with-login.jar
|
||||
fi
|
||||
|
||||
if [ $? -eq 0 ]; then # checks if curl was successful
|
||||
rm -f app.jar
|
||||
ln -s app-security.jar app.jar
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
/scripts/download-security-jar.sh
|
||||
|
||||
# Run the main command
|
||||
exec "$@"
|
||||
@@ -8,15 +8,16 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import stirling.software.SPDF.config.ConfigInitializer;
|
||||
import stirling.software.SPDF.utils.GeneralUtils;
|
||||
@SpringBootApplication
|
||||
|
||||
//@EnableScheduling
|
||||
@EnableScheduling
|
||||
public class SPdfApplication {
|
||||
|
||||
|
||||
@Autowired
|
||||
private Environment env;
|
||||
|
||||
@@ -28,11 +29,7 @@ public class SPdfApplication {
|
||||
|
||||
if (browserOpen) {
|
||||
try {
|
||||
String port = env.getProperty("local.server.port");
|
||||
if(port == null || port.length() == 0) {
|
||||
port="8080";
|
||||
}
|
||||
String url = "http://localhost:" + port;
|
||||
String url = "http://localhost:" + getPort();
|
||||
|
||||
String os = System.getProperty("os.name").toLowerCase();
|
||||
Runtime rt = Runtime.getRuntime();
|
||||
@@ -45,7 +42,7 @@ public class SPdfApplication {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication app = new SpringApplication(SPdfApplication.class);
|
||||
app.addInitializers(new ConfigInitializer());
|
||||
@@ -55,28 +52,28 @@ public class SPdfApplication {
|
||||
System.out.println("External configuration file 'configs/settings.yml' does not exist. Using default configuration and environment configuration instead.");
|
||||
}
|
||||
app.run(args);
|
||||
|
||||
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
|
||||
GeneralUtils.createDir("customFiles/static/");
|
||||
GeneralUtils.createDir("customFiles/templates/");
|
||||
|
||||
|
||||
|
||||
|
||||
System.out.println("Stirling-PDF Started.");
|
||||
|
||||
String port = System.getProperty("local.server.port");
|
||||
if(port == null || port.length() == 0) {
|
||||
port="8080";
|
||||
}
|
||||
String url = "http://localhost:" + port;
|
||||
|
||||
String url = "http://localhost:" + getPort();
|
||||
System.out.println("Navigate to " + url);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public static String getPort() {
|
||||
String port = System.getProperty("local.server.port");
|
||||
if (port == null || port.isEmpty()) {
|
||||
port = "8080";
|
||||
}
|
||||
return port;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,12 +41,16 @@ public class AppConfig {
|
||||
return (defaultNavBar != null) ? defaultNavBar : "Stirling PDF";
|
||||
}
|
||||
|
||||
@Bean(name = "enableAlphaFunctionality")
|
||||
public boolean enableAlphaFunctionality() {
|
||||
return applicationProperties.getSystem().getEnableAlphaFunctionality() != null ? applicationProperties.getSystem().getEnableAlphaFunctionality() : false;
|
||||
}
|
||||
|
||||
@Bean(name = "rateLimit")
|
||||
public boolean rateLimit() {
|
||||
String appName = System.getProperty("rateLimit");
|
||||
if (appName == null)
|
||||
appName = System.getenv("rateLimit");
|
||||
System.out.println("rateLimit=" + appName);
|
||||
return (appName != null) ? Boolean.valueOf(appName) : false;
|
||||
}
|
||||
|
||||
|
||||
@@ -81,6 +81,10 @@ public class EndpointConfiguration {
|
||||
addEndpointToGroup("PageOps", "auto-split-pdf");
|
||||
addEndpointToGroup("PageOps", "extract-page");
|
||||
addEndpointToGroup("PageOps", "pdf-to-single-page");
|
||||
addEndpointToGroup("PageOps", "split-by-size-or-count");
|
||||
addEndpointToGroup("PageOps", "overlay-pdf");
|
||||
addEndpointToGroup("PageOps", "split-pdf-by-sections");
|
||||
|
||||
|
||||
// Adding endpoints to "Convert" group
|
||||
addEndpointToGroup("Convert", "pdf-to-img");
|
||||
@@ -96,6 +100,8 @@ public class EndpointConfiguration {
|
||||
addEndpointToGroup("Convert", "html-to-pdf");
|
||||
addEndpointToGroup("Convert", "url-to-pdf");
|
||||
addEndpointToGroup("Convert", "markdown-to-pdf");
|
||||
addEndpointToGroup("Convert", "pdf-to-csv");
|
||||
|
||||
|
||||
// Adding endpoints to "Security" group
|
||||
addEndpointToGroup("Security", "add-password");
|
||||
@@ -104,6 +110,7 @@ public class EndpointConfiguration {
|
||||
addEndpointToGroup("Security", "add-watermark");
|
||||
addEndpointToGroup("Security", "cert-sign");
|
||||
addEndpointToGroup("Security", "sanitize-pdf");
|
||||
addEndpointToGroup("Security", "auto-redact");
|
||||
|
||||
|
||||
// Adding endpoints to "Other" group
|
||||
@@ -117,6 +124,7 @@ public class EndpointConfiguration {
|
||||
addEndpointToGroup("Other", "flatten");
|
||||
addEndpointToGroup("Other", "repair");
|
||||
addEndpointToGroup("Other", "remove-blanks");
|
||||
addEndpointToGroup("Other", "remove-annotations");
|
||||
addEndpointToGroup("Other", "compare");
|
||||
addEndpointToGroup("Other", "add-page-numbers");
|
||||
addEndpointToGroup("Other", "auto-rename");
|
||||
@@ -197,6 +205,11 @@ public class EndpointConfiguration {
|
||||
addEndpointToGroup("Java", "pdf-to-single-page");
|
||||
addEndpointToGroup("Java", "markdown-to-pdf");
|
||||
addEndpointToGroup("Java", "show-javascript");
|
||||
addEndpointToGroup("Java", "auto-redact");
|
||||
addEndpointToGroup("Java", "pdf-to-csv");
|
||||
addEndpointToGroup("Java", "split-by-size-or-count");
|
||||
addEndpointToGroup("Java", "overlay-pdf");
|
||||
addEndpointToGroup("Java", "split-pdf-by-sections");
|
||||
|
||||
//Javascript
|
||||
addEndpointToGroup("Javascript", "pdf-organizer");
|
||||
|
||||
@@ -16,33 +16,35 @@ import jakarta.servlet.http.HttpServletResponse;
|
||||
@Component
|
||||
public class MetricsFilter extends OncePerRequestFilter {
|
||||
|
||||
private final MeterRegistry meterRegistry;
|
||||
private final MeterRegistry meterRegistry;
|
||||
|
||||
@Autowired
|
||||
public MetricsFilter(MeterRegistry meterRegistry) {
|
||||
this.meterRegistry = meterRegistry;
|
||||
}
|
||||
@Autowired
|
||||
public MetricsFilter(MeterRegistry meterRegistry) {
|
||||
this.meterRegistry = meterRegistry;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
|
||||
throws ServletException, IOException {
|
||||
String uri = request.getRequestURI();
|
||||
|
||||
//System.out.println("uri="+uri + ", method=" + request.getMethod() );
|
||||
// Ignore static resources
|
||||
if (!(uri.startsWith("/js") || uri.startsWith("api-docs") || uri.endsWith("robots.txt") || uri.startsWith("/images") || uri.endsWith(".png") || uri.endsWith(".ico") || uri.endsWith(".css") || uri.endsWith(".svg")|| uri.endsWith(".js") || uri.contains("swagger") || uri.startsWith("/api"))) {
|
||||
Counter counter = Counter.builder("http.requests")
|
||||
.tag("uri", uri)
|
||||
.tag("method", request.getMethod())
|
||||
.register(meterRegistry);
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
|
||||
throws ServletException, IOException {
|
||||
String uri = request.getRequestURI();
|
||||
|
||||
counter.increment();
|
||||
//System.out.println("Counted");
|
||||
}
|
||||
// System.out.println("uri="+uri + ", method=" + request.getMethod() );
|
||||
// Ignore static resources
|
||||
if (!(uri.startsWith("/js") || uri.startsWith("/v1/api-docs") || uri.endsWith("robots.txt")
|
||||
|| uri.startsWith("/images") || uri.startsWith("/images")|| uri.endsWith(".png") || uri.endsWith(".ico") || uri.endsWith(".css") || uri.endsWith(".map")
|
||||
|| uri.endsWith(".svg") || uri.endsWith(".js") || uri.contains("swagger")
|
||||
|| uri.startsWith("/api/v1/info") || uri.startsWith("/site.webmanifest") || uri.startsWith("/fonts") || uri.startsWith("/pdfjs") )) {
|
||||
|
||||
|
||||
|
||||
Counter counter = Counter.builder("http.requests").tag("uri", uri).tag("method", request.getMethod())
|
||||
.register(meterRegistry);
|
||||
|
||||
filterChain.doFilter(request, response);
|
||||
}
|
||||
counter.increment();
|
||||
// System.out.println("Counted");
|
||||
}
|
||||
|
||||
|
||||
filterChain.doFilter(request, response);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,27 +1,42 @@
|
||||
package stirling.software.SPDF.config;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
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;
|
||||
import io.swagger.v3.oas.models.security.SecurityRequirement;
|
||||
import io.swagger.v3.oas.models.security.SecurityScheme;
|
||||
import stirling.software.SPDF.model.ApplicationProperties;
|
||||
|
||||
@Configuration
|
||||
public class OpenApiConfig {
|
||||
|
||||
@Autowired
|
||||
ApplicationProperties applicationProperties;
|
||||
|
||||
@Bean
|
||||
public OpenAPI customOpenAPI() {
|
||||
String version = getClass().getPackage().getImplementationVersion();
|
||||
if (version == null) {
|
||||
|
||||
version = "1.0.0"; // default version if all else fails
|
||||
|
||||
}
|
||||
String version = getClass().getPackage().getImplementationVersion();
|
||||
if (version == null) {
|
||||
version = "1.0.0"; // default version if all else fails
|
||||
}
|
||||
|
||||
SecurityScheme apiKeyScheme = new SecurityScheme().type(SecurityScheme.Type.APIKEY).in(SecurityScheme.In.HEADER)
|
||||
.name("X-API-KEY");
|
||||
if (!applicationProperties.getSecurity().getEnableLogin()) {
|
||||
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."));
|
||||
} else {
|
||||
return new OpenAPI().components(new Components().addSecuritySchemes("apiKey", apiKeyScheme))
|
||||
.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."))
|
||||
.addSecurityItem(new SecurityRequirement().addList("apiKey"));
|
||||
}
|
||||
|
||||
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."));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -2,27 +2,46 @@ package stirling.software.SPDF.config.security;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.authentication.BadCredentialsException;
|
||||
import org.springframework.security.authentication.LockedException;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
|
||||
@Component
|
||||
public class CustomAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
|
||||
|
||||
@Autowired
|
||||
private final LoginAttemptService loginAttemptService;
|
||||
|
||||
@Autowired
|
||||
public CustomAuthenticationFailureHandler(LoginAttemptService loginAttemptService) {
|
||||
this.loginAttemptService = loginAttemptService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception)
|
||||
throws IOException, ServletException {
|
||||
String ip = request.getRemoteAddr();
|
||||
logger.error("Failed login attempt from IP: " + ip);
|
||||
if (exception.getClass().isAssignableFrom(BadCredentialsException.class)) {
|
||||
setDefaultFailureUrl("/login?error=badcredentials");
|
||||
} else if (exception.getClass().isAssignableFrom(LockedException.class)) {
|
||||
|
||||
String username = request.getParameter("username");
|
||||
if(loginAttemptService.loginAttemptCheck(username)) {
|
||||
setDefaultFailureUrl("/login?error=locked");
|
||||
|
||||
} else {
|
||||
if (exception.getClass().isAssignableFrom(BadCredentialsException.class)) {
|
||||
setDefaultFailureUrl("/login?error=badcredentials");
|
||||
} else if (exception.getClass().isAssignableFrom(LockedException.class)) {
|
||||
setDefaultFailureUrl("/login?error=locked");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
super.onAuthenticationFailure(request, response, exception);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
package stirling.software.SPDF.config.security;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
|
||||
import org.springframework.security.web.savedrequest.SavedRequest;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.HttpSession;
|
||||
import stirling.software.SPDF.utils.RequestUriUtils;
|
||||
|
||||
@Component
|
||||
public class CustomAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
|
||||
|
||||
@Autowired
|
||||
private LoginAttemptService loginAttemptService;
|
||||
|
||||
@Override
|
||||
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws ServletException, IOException {
|
||||
String username = request.getParameter("username");
|
||||
loginAttemptService.loginSucceeded(username);
|
||||
|
||||
|
||||
// Get the saved request
|
||||
HttpSession session = request.getSession(false);
|
||||
SavedRequest savedRequest = session != null ? (SavedRequest) session.getAttribute("SPRING_SECURITY_SAVED_REQUEST") : null;
|
||||
if (savedRequest != null && !RequestUriUtils.isStaticResource(savedRequest.getRedirectUrl())) {
|
||||
// Redirect to the original destination
|
||||
super.onAuthenticationSuccess(request, response, authentication);
|
||||
} else {
|
||||
// Redirect to the root URL (considering context path)
|
||||
getRedirectStrategy().sendRedirect(request, response, "/");
|
||||
}
|
||||
|
||||
//super.onAuthenticationSuccess(request, response, authentication);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.authentication.LockedException;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
@@ -22,12 +23,18 @@ public class CustomUserDetailsService implements UserDetailsService {
|
||||
@Autowired
|
||||
private UserRepository userRepository;
|
||||
|
||||
|
||||
@Autowired
|
||||
private LoginAttemptService loginAttemptService;
|
||||
|
||||
@Override
|
||||
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
|
||||
User user = userRepository.findByUsername(username)
|
||||
.orElseThrow(() -> new UsernameNotFoundException("No user found with username: " + username));
|
||||
|
||||
if (loginAttemptService.isBlocked(username)) {
|
||||
throw new LockedException("Your account has been locked due to too many failed login attempts.");
|
||||
}
|
||||
|
||||
return new org.springframework.security.core.userdetails.User(
|
||||
user.getUsername(),
|
||||
user.getPassword(),
|
||||
|
||||
@@ -15,6 +15,7 @@ import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import stirling.software.SPDF.model.User;
|
||||
import stirling.software.SPDF.utils.RequestUriUtils;
|
||||
|
||||
@Component
|
||||
public class FirstLoginFilter extends OncePerRequestFilter {
|
||||
@@ -28,11 +29,7 @@ public class FirstLoginFilter extends OncePerRequestFilter {
|
||||
String method = request.getMethod();
|
||||
String requestURI = request.getRequestURI();
|
||||
// Check if the request is for static resources
|
||||
boolean isStaticResource = requestURI.startsWith("/css/")
|
||||
|| requestURI.startsWith("/js/")
|
||||
|| requestURI.startsWith("/images/")
|
||||
|| requestURI.startsWith("/public/")
|
||||
|| requestURI.endsWith(".svg");
|
||||
boolean isStaticResource = RequestUriUtils.isStaticResource(requestURI);
|
||||
|
||||
// If it's a static resource, just continue the filter chain and skip the logic below
|
||||
if (isStaticResource) {
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
package stirling.software.SPDF.config.security;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import jakarta.servlet.Filter;
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.ServletRequest;
|
||||
import jakarta.servlet.ServletResponse;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import stirling.software.SPDF.utils.RequestUriUtils;
|
||||
|
||||
public class IPRateLimitingFilter implements Filter {
|
||||
|
||||
private final ConcurrentHashMap<String, AtomicInteger> requestCounts = new ConcurrentHashMap<>();
|
||||
private final ConcurrentHashMap<String, AtomicInteger> getCounts = new ConcurrentHashMap<>();
|
||||
private final int maxRequests;
|
||||
private final int maxGetRequests;
|
||||
|
||||
public IPRateLimitingFilter(int maxRequests, int maxGetRequests) {
|
||||
this.maxRequests = maxRequests;
|
||||
this.maxGetRequests = maxGetRequests;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
|
||||
if (request instanceof HttpServletRequest) {
|
||||
HttpServletRequest httpRequest = (HttpServletRequest) request;
|
||||
String method = httpRequest.getMethod();
|
||||
String requestURI = httpRequest.getRequestURI();
|
||||
// Check if the request is for static resources
|
||||
boolean isStaticResource = RequestUriUtils.isStaticResource(requestURI);
|
||||
|
||||
// If it's a static resource, just continue the filter chain and skip the logic below
|
||||
if (isStaticResource) {
|
||||
chain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
|
||||
String clientIp = request.getRemoteAddr();
|
||||
requestCounts.computeIfAbsent(clientIp, k -> new AtomicInteger(0));
|
||||
if (!"GET".equalsIgnoreCase(method)) {
|
||||
|
||||
if (requestCounts.get(clientIp).incrementAndGet() > maxRequests) {
|
||||
// Handle limit exceeded (e.g., send error response)
|
||||
response.getWriter().write("Rate limit exceeded");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (requestCounts.get(clientIp).incrementAndGet() > maxGetRequests) {
|
||||
// Handle limit exceeded (e.g., send error response)
|
||||
response.getWriter().write("GET Rate limit exceeded");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
|
||||
public void resetRequestCounts() {
|
||||
requestCounts.clear();
|
||||
getCounts.clear();
|
||||
}
|
||||
}
|
||||
@@ -37,8 +37,10 @@ public class InitialSecuritySetup {
|
||||
initialPassword = "stirling";
|
||||
userService.saveUser(initialUsername, initialPassword, Role.ADMIN.getRoleId(), true);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
if(!userService.usernameExists(Role.INTERNAL_API_USER.getRoleId())) {
|
||||
userService.saveUser(Role.INTERNAL_API_USER.getRoleId(), UUID.randomUUID().toString(), Role.INTERNAL_API_USER.getRoleId());
|
||||
userService.addApiKeyToUser(Role.INTERNAL_API_USER.getRoleId());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
package stirling.software.SPDF.config.security;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import stirling.software.SPDF.model.ApplicationProperties;
|
||||
import stirling.software.SPDF.model.AttemptCounter;
|
||||
|
||||
@Service
|
||||
public class LoginAttemptService {
|
||||
|
||||
|
||||
@Autowired
|
||||
ApplicationProperties applicationProperties;
|
||||
|
||||
private int MAX_ATTEMPTS;
|
||||
private long ATTEMPT_INCREMENT_TIME;
|
||||
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
MAX_ATTEMPTS = applicationProperties.getSecurity().getLoginAttemptCount();
|
||||
ATTEMPT_INCREMENT_TIME = TimeUnit.MINUTES.toMillis(applicationProperties.getSecurity().getLoginResetTimeMinutes());
|
||||
}
|
||||
|
||||
private final ConcurrentHashMap<String, AttemptCounter> attemptsCache = new ConcurrentHashMap<>();
|
||||
|
||||
public void loginSucceeded(String key) {
|
||||
attemptsCache.remove(key);
|
||||
}
|
||||
|
||||
public boolean loginAttemptCheck(String key) {
|
||||
attemptsCache.compute(key, (k, attemptCounter) -> {
|
||||
if (attemptCounter == null || attemptCounter.shouldReset(ATTEMPT_INCREMENT_TIME)) {
|
||||
return new AttemptCounter();
|
||||
} else {
|
||||
attemptCounter.increment();
|
||||
return attemptCounter;
|
||||
}
|
||||
});
|
||||
return attemptsCache.get(key).getAttemptCount() >= MAX_ATTEMPTS;
|
||||
}
|
||||
|
||||
|
||||
public boolean isBlocked(String key) {
|
||||
AttemptCounter attemptCounter = attemptsCache.get(key);
|
||||
if (attemptCounter != null) {
|
||||
return attemptCounter.getAttemptCount() >= MAX_ATTEMPTS;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package stirling.software.SPDF.config.security;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class RateLimitResetScheduler {
|
||||
|
||||
private final IPRateLimitingFilter rateLimitingFilter;
|
||||
|
||||
public RateLimitResetScheduler(IPRateLimitingFilter rateLimitingFilter) {
|
||||
this.rateLimitingFilter = rateLimitingFilter;
|
||||
}
|
||||
|
||||
@Scheduled(cron = "0 0 0 * * MON") // At 00:00 every Monday TODO: configurable
|
||||
public void resetRateLimit() {
|
||||
rateLimitingFilter.resetRequestCounts();
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
|
||||
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
||||
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
@@ -15,12 +15,13 @@ import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
|
||||
import org.springframework.security.web.savedrequest.NullRequestCache;
|
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
|
||||
import stirling.software.SPDF.repository.JPATokenRepositoryImpl;
|
||||
@Configuration
|
||||
@EnableWebSecurity()
|
||||
@EnableGlobalMethodSecurity(prePostEnabled = true)
|
||||
@EnableMethodSecurity
|
||||
public class SecurityConfiguration {
|
||||
|
||||
@Autowired
|
||||
@@ -40,6 +41,11 @@ public class SecurityConfiguration {
|
||||
|
||||
@Autowired
|
||||
private UserAuthenticationFilter userAuthenticationFilter;
|
||||
|
||||
|
||||
|
||||
@Autowired
|
||||
private LoginAttemptService loginAttemptService;
|
||||
|
||||
@Autowired
|
||||
private FirstLoginFilter firstLoginFilter;
|
||||
@@ -51,14 +57,18 @@ public class SecurityConfiguration {
|
||||
if(loginEnabledValue) {
|
||||
|
||||
http.csrf(csrf -> csrf.disable());
|
||||
http.addFilterBefore(rateLimitingFilter(), UsernamePasswordAuthenticationFilter.class);
|
||||
http.addFilterAfter(firstLoginFilter, UsernamePasswordAuthenticationFilter.class);
|
||||
http
|
||||
.formLogin(formLogin -> formLogin
|
||||
.loginPage("/login")
|
||||
.successHandler(new CustomAuthenticationSuccessHandler())
|
||||
.defaultSuccessUrl("/")
|
||||
.failureHandler(new CustomAuthenticationFailureHandler())
|
||||
.failureHandler(new CustomAuthenticationFailureHandler(loginAttemptService))
|
||||
.permitAll()
|
||||
)
|
||||
).requestCache(requestCache -> requestCache
|
||||
.requestCache(new NullRequestCache())
|
||||
)
|
||||
.logout(logout -> logout
|
||||
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
|
||||
.logoutSuccessUrl("/login?logout=true")
|
||||
@@ -70,8 +80,19 @@ public class SecurityConfiguration {
|
||||
.tokenValiditySeconds(1209600) // 2 weeks
|
||||
)
|
||||
.authorizeHttpRequests(authz -> authz
|
||||
.requestMatchers(req -> req.getRequestURI().startsWith("/login") || req.getRequestURI().endsWith(".svg") || req.getRequestURI().startsWith("/register") || req.getRequestURI().startsWith("/error") || req.getRequestURI().startsWith("/images/") || req.getRequestURI().startsWith("/public/") || req.getRequestURI().startsWith("/css/") || req.getRequestURI().startsWith("/js/"))
|
||||
.permitAll()
|
||||
.requestMatchers(req -> {
|
||||
String uri = req.getRequestURI();
|
||||
String contextPath = req.getContextPath();
|
||||
|
||||
// Remove the context path from the URI
|
||||
String trimmedUri = uri.startsWith(contextPath) ? uri.substring(contextPath.length()) : uri;
|
||||
|
||||
return trimmedUri.startsWith("/login") || trimmedUri.endsWith(".svg") ||
|
||||
trimmedUri.startsWith("/register") || trimmedUri.startsWith("/error") ||
|
||||
trimmedUri.startsWith("/images/") || trimmedUri.startsWith("/public/") ||
|
||||
trimmedUri.startsWith("/css/") || trimmedUri.startsWith("/js/");
|
||||
}
|
||||
).permitAll()
|
||||
.anyRequest().authenticated()
|
||||
)
|
||||
.userDetailsService(userDetailsService)
|
||||
@@ -84,8 +105,17 @@ public class SecurityConfiguration {
|
||||
}
|
||||
return http.build();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Bean
|
||||
public IPRateLimitingFilter rateLimitingFilter() {
|
||||
int maxRequestsPerIp = 1000000; // Example limit TODO add config level
|
||||
return new IPRateLimitingFilter(maxRequestsPerIp, maxRequestsPerIp);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Bean
|
||||
public DaoAuthenticationProvider authenticationProvider() {
|
||||
|
||||
@@ -74,15 +74,17 @@ public class UserAuthenticationFilter extends OncePerRequestFilter {
|
||||
// If we still don't have any authentication, deny the request
|
||||
if (authentication == null || !authentication.isAuthenticated()) {
|
||||
String method = request.getMethod();
|
||||
if ("GET".equalsIgnoreCase(method) && !"/login".equals(requestURI)) {
|
||||
response.sendRedirect("/login"); // redirect to the login page
|
||||
String contextPath = request.getContextPath();
|
||||
|
||||
if ("GET".equalsIgnoreCase(method) && ! (contextPath + "/login").equals(requestURI)) {
|
||||
response.sendRedirect(contextPath + "/login"); // redirect to the login page
|
||||
return;
|
||||
} else {
|
||||
response.setStatus(HttpStatus.UNAUTHORIZED.value());
|
||||
response.getWriter().write("Authentication required. Please provide a X-API-KEY in request header.\nThis is found in Settings -> Account Settings -> API Key\nAlternativly you can disable authentication if this is unexpected");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
filterChain.doFilter(request, response);
|
||||
}
|
||||
@@ -90,15 +92,17 @@ public class UserAuthenticationFilter extends OncePerRequestFilter {
|
||||
@Override
|
||||
protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
|
||||
String uri = request.getRequestURI();
|
||||
|
||||
String contextPath = request.getContextPath();
|
||||
String[] permitAllPatterns = {
|
||||
"/login",
|
||||
"/register",
|
||||
"/error",
|
||||
"/images/",
|
||||
"/public/",
|
||||
"/css/",
|
||||
"/js/"
|
||||
contextPath + "/login",
|
||||
contextPath + "/register",
|
||||
contextPath + "/error",
|
||||
contextPath + "/images/",
|
||||
contextPath + "/public/",
|
||||
contextPath + "/css/",
|
||||
contextPath + "/js/",
|
||||
contextPath + "/pdfjs/",
|
||||
contextPath + "/site.webmanifest"
|
||||
};
|
||||
|
||||
for (String pattern : permitAllPatterns) {
|
||||
|
||||
@@ -16,11 +16,13 @@ import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import stirling.software.SPDF.controller.api.pipeline.UserServiceInterface;
|
||||
import stirling.software.SPDF.model.Authority;
|
||||
import stirling.software.SPDF.model.Role;
|
||||
import stirling.software.SPDF.model.User;
|
||||
import stirling.software.SPDF.repository.UserRepository;
|
||||
@Service
|
||||
public class UserService {
|
||||
public class UserService implements UserServiceInterface{
|
||||
|
||||
@Autowired
|
||||
private UserRepository userRepository;
|
||||
@@ -136,6 +138,11 @@ public class UserService {
|
||||
public void deleteUser(String username) {
|
||||
Optional<User> userOpt = userRepository.findByUsername(username);
|
||||
if (userOpt.isPresent()) {
|
||||
for (Authority authority : userOpt.get().getAuthorities()) {
|
||||
if (authority.getAuthority().equals(Role.INTERNAL_API_USER.getRoleId())) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
userRepository.delete(userOpt.get());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,9 @@
|
||||
package stirling.software.SPDF.controller.api;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.apache.pdfbox.io.MemoryUsageSetting;
|
||||
import org.apache.pdfbox.multipdf.PDFMergerUtility;
|
||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||
import org.apache.pdfbox.pdmodel.PDPage;
|
||||
import org.slf4j.Logger;
|
||||
@@ -20,12 +14,19 @@ import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
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.tags.Tag;
|
||||
import stirling.software.SPDF.model.api.general.MergePdfsRequest;
|
||||
import stirling.software.SPDF.utils.WebResponseUtils;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/general")
|
||||
@Tag(name = "General", description = "General APIs")
|
||||
@@ -34,82 +35,80 @@ public class MergeController {
|
||||
private static final Logger logger = LoggerFactory.getLogger(MergeController.class);
|
||||
|
||||
|
||||
private PDDocument mergeDocuments(List<PDDocument> documents) throws IOException {
|
||||
PDDocument mergedDoc = new PDDocument();
|
||||
for (PDDocument doc : documents) {
|
||||
for (PDPage page : doc.getPages()) {
|
||||
mergedDoc.addPage(page);
|
||||
}
|
||||
}
|
||||
return mergedDoc;
|
||||
}
|
||||
|
||||
private Comparator<MultipartFile> getSortComparator(String sortType) {
|
||||
switch (sortType) {
|
||||
case "byFileName":
|
||||
return Comparator.comparing(MultipartFile::getOriginalFilename);
|
||||
case "byDateModified":
|
||||
return (file1, file2) -> {
|
||||
try {
|
||||
BasicFileAttributes attr1 = Files.readAttributes(Paths.get(file1.getOriginalFilename()), BasicFileAttributes.class);
|
||||
BasicFileAttributes attr2 = Files.readAttributes(Paths.get(file2.getOriginalFilename()), BasicFileAttributes.class);
|
||||
return attr1.lastModifiedTime().compareTo(attr2.lastModifiedTime());
|
||||
} catch (IOException e) {
|
||||
return 0; // If there's an error, treat them as equal
|
||||
}
|
||||
};
|
||||
case "byDateCreated":
|
||||
return (file1, file2) -> {
|
||||
try {
|
||||
BasicFileAttributes attr1 = Files.readAttributes(Paths.get(file1.getOriginalFilename()), BasicFileAttributes.class);
|
||||
BasicFileAttributes attr2 = Files.readAttributes(Paths.get(file2.getOriginalFilename()), BasicFileAttributes.class);
|
||||
return attr1.creationTime().compareTo(attr2.creationTime());
|
||||
} catch (IOException e) {
|
||||
return 0; // If there's an error, treat them as equal
|
||||
}
|
||||
};
|
||||
case "byPDFTitle":
|
||||
return (file1, file2) -> {
|
||||
try (PDDocument doc1 = PDDocument.load(file1.getInputStream());
|
||||
PDDocument doc2 = PDDocument.load(file2.getInputStream())) {
|
||||
String title1 = doc1.getDocumentInformation().getTitle();
|
||||
String title2 = doc2.getDocumentInformation().getTitle();
|
||||
return title1.compareTo(title2);
|
||||
} catch (IOException e) {
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
case "orderProvided":
|
||||
default:
|
||||
return (file1, file2) -> 0; // Default is the order provided
|
||||
}
|
||||
}
|
||||
|
||||
@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. Input:PDF Output:PDF Type:MISO")
|
||||
public ResponseEntity<byte[]> mergePdfs(@ModelAttribute MergePdfsRequest form) throws IOException {
|
||||
|
||||
MultipartFile[] files = form.getFileInput();
|
||||
Arrays.sort(files, getSortComparator(form.getSortType()));
|
||||
|
||||
List<PDDocument> documents = new ArrayList<>();
|
||||
for (MultipartFile file : files) {
|
||||
try (InputStream is = file.getInputStream()) {
|
||||
documents.add(PDDocument.load(is));
|
||||
}
|
||||
}
|
||||
|
||||
try (PDDocument mergedDoc = mergeDocuments(documents)) {
|
||||
ResponseEntity<byte[]> response = WebResponseUtils.pdfDocToWebResponse(mergedDoc, files[0].getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_merged.pdf");
|
||||
return response;
|
||||
} finally {
|
||||
private PDDocument mergeDocuments(List<PDDocument> documents) throws IOException {
|
||||
PDDocument mergedDoc = new PDDocument();
|
||||
for (PDDocument doc : documents) {
|
||||
if (doc != null) {
|
||||
doc.close();
|
||||
for (PDPage page : doc.getPages()) {
|
||||
mergedDoc.addPage(page);
|
||||
}
|
||||
}
|
||||
return mergedDoc;
|
||||
}
|
||||
|
||||
private Comparator<MultipartFile> getSortComparator(String sortType) {
|
||||
switch (sortType) {
|
||||
case "byFileName":
|
||||
return Comparator.comparing(MultipartFile::getOriginalFilename);
|
||||
case "byDateModified":
|
||||
return (file1, file2) -> {
|
||||
try {
|
||||
BasicFileAttributes attr1 = Files.readAttributes(Paths.get(file1.getOriginalFilename()), BasicFileAttributes.class);
|
||||
BasicFileAttributes attr2 = Files.readAttributes(Paths.get(file2.getOriginalFilename()), BasicFileAttributes.class);
|
||||
return attr1.lastModifiedTime().compareTo(attr2.lastModifiedTime());
|
||||
} catch (IOException e) {
|
||||
return 0; // If there's an error, treat them as equal
|
||||
}
|
||||
};
|
||||
case "byDateCreated":
|
||||
return (file1, file2) -> {
|
||||
try {
|
||||
BasicFileAttributes attr1 = Files.readAttributes(Paths.get(file1.getOriginalFilename()), BasicFileAttributes.class);
|
||||
BasicFileAttributes attr2 = Files.readAttributes(Paths.get(file2.getOriginalFilename()), BasicFileAttributes.class);
|
||||
return attr1.creationTime().compareTo(attr2.creationTime());
|
||||
} catch (IOException e) {
|
||||
return 0; // If there's an error, treat them as equal
|
||||
}
|
||||
};
|
||||
case "byPDFTitle":
|
||||
return (file1, file2) -> {
|
||||
try (PDDocument doc1 = PDDocument.load(file1.getInputStream());
|
||||
PDDocument doc2 = PDDocument.load(file2.getInputStream())) {
|
||||
String title1 = doc1.getDocumentInformation().getTitle();
|
||||
String title2 = doc2.getDocumentInformation().getTitle();
|
||||
return title1.compareTo(title2);
|
||||
} catch (IOException e) {
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
case "orderProvided":
|
||||
default:
|
||||
return (file1, file2) -> 0; // Default is the order provided
|
||||
}
|
||||
}
|
||||
|
||||
@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. Input:PDF Output:PDF Type:MISO")
|
||||
public ResponseEntity<byte[]> mergePdfs(@ModelAttribute MergePdfsRequest form) throws IOException {
|
||||
try {
|
||||
MultipartFile[] files = form.getFileInput();
|
||||
Arrays.sort(files, getSortComparator(form.getSortType()));
|
||||
|
||||
PDFMergerUtility mergedDoc = new PDFMergerUtility();
|
||||
ByteArrayOutputStream docOutputstream = new ByteArrayOutputStream();
|
||||
|
||||
for (MultipartFile file : files) {
|
||||
mergedDoc.addSource(new ByteArrayInputStream(file.getBytes()));
|
||||
}
|
||||
|
||||
mergedDoc.setDestinationFileName(files[0].getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_merged.pdf");
|
||||
mergedDoc.setDestinationStream(docOutputstream);
|
||||
mergedDoc.mergeDocuments(MemoryUsageSetting.setupMainMemoryOnly());
|
||||
|
||||
return WebResponseUtils.bytesToWebResponse(docOutputstream.toByteArray(), mergedDoc.getDestinationFileName());
|
||||
} catch (Exception ex) {
|
||||
logger.error("Error in merge pdf process", ex);
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,8 +3,9 @@ import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import org.apache.pdfbox.multipdf.Overlay;
|
||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||
import org.springframework.http.MediaType;
|
||||
@@ -25,7 +26,7 @@ import stirling.software.SPDF.utils.WebResponseUtils;
|
||||
@Tag(name = "General", description = "General APIs")
|
||||
public class PdfOverlayController {
|
||||
|
||||
@PostMapping(value = "/overlay-pdfs", consumes = "multipart/form-data")
|
||||
@PostMapping(value = "/overlay-pdfs", consumes = "multipart/form-data")
|
||||
@Operation(summary = "Overlay PDF files in various modes", description = "Overlay PDF files onto a base PDF with different modes: Sequential, Interleaved, or Fixed Repeat. Input:PDF Output:PDF Type:MIMO")
|
||||
public ResponseEntity<byte[]> overlayPdfs(@ModelAttribute OverlayPdfsRequest request) throws IOException {
|
||||
MultipartFile baseFile = request.getFileInput();
|
||||
@@ -33,43 +34,53 @@ public class PdfOverlayController {
|
||||
|
||||
MultipartFile[] overlayFiles = request.getOverlayFiles();
|
||||
File[] overlayPdfFiles = new File[overlayFiles.length];
|
||||
try{
|
||||
for (int i = 0; i < overlayFiles.length; i++) {
|
||||
overlayPdfFiles[i] = GeneralUtils.multipartToFile(overlayFiles[i]);
|
||||
}
|
||||
|
||||
String mode = request.getOverlayMode(); // "SequentialOverlay", "InterleavedOverlay", "FixedRepeatOverlay"
|
||||
int[] counts = request.getCounts(); // Used for FixedRepeatOverlay mode
|
||||
|
||||
try (PDDocument basePdf = PDDocument.load(baseFile.getInputStream());
|
||||
Overlay overlay = new Overlay()) {
|
||||
Map<Integer, String> overlayGuide = prepareOverlayGuide(basePdf.getNumberOfPages(), overlayPdfFiles, mode, counts);
|
||||
|
||||
overlay.setInputPDF(basePdf);
|
||||
if(overlayPos == 0) {
|
||||
overlay.setOverlayPosition(Overlay.Position.FOREGROUND);
|
||||
} else {
|
||||
overlay.setOverlayPosition(Overlay.Position.BACKGROUND);
|
||||
}
|
||||
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
overlay.overlay(overlayGuide).save(outputStream);
|
||||
byte[] data = outputStream.toByteArray();
|
||||
|
||||
return WebResponseUtils.bytesToWebResponse(data, "overlaid.pdf", MediaType.APPLICATION_PDF);
|
||||
}
|
||||
List<File> tempFiles = new ArrayList<>(); // List to keep track of temporary files
|
||||
|
||||
try {
|
||||
for (int i = 0; i < overlayFiles.length; i++) {
|
||||
overlayPdfFiles[i] = GeneralUtils.multipartToFile(overlayFiles[i]);
|
||||
}
|
||||
|
||||
String mode = request.getOverlayMode(); // "SequentialOverlay", "InterleavedOverlay", "FixedRepeatOverlay"
|
||||
int[] counts = request.getCounts(); // Used for FixedRepeatOverlay mode
|
||||
|
||||
try (PDDocument basePdf = PDDocument.load(baseFile.getInputStream());
|
||||
Overlay overlay = new Overlay()) {
|
||||
Map<Integer, String> overlayGuide = prepareOverlayGuide(basePdf.getNumberOfPages(), overlayPdfFiles, mode, counts, tempFiles);
|
||||
|
||||
overlay.setInputPDF(basePdf);
|
||||
if (overlayPos == 0) {
|
||||
overlay.setOverlayPosition(Overlay.Position.FOREGROUND);
|
||||
} else {
|
||||
overlay.setOverlayPosition(Overlay.Position.BACKGROUND);
|
||||
}
|
||||
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
overlay.overlay(overlayGuide).save(outputStream);
|
||||
byte[] data = outputStream.toByteArray();
|
||||
String outputFilename = baseFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_overlayed.pdf"; // Remove file extension and append .pdf
|
||||
|
||||
return WebResponseUtils.bytesToWebResponse(data, outputFilename, MediaType.APPLICATION_PDF);
|
||||
}
|
||||
} finally {
|
||||
for (File overlayPdfFile : overlayPdfFiles) {
|
||||
if (overlayPdfFile != null) overlayPdfFile.delete();
|
||||
if (overlayPdfFile != null) {
|
||||
overlayPdfFile.delete();
|
||||
}
|
||||
}
|
||||
for (File tempFile : tempFiles) { // Delete temporary files
|
||||
if (tempFile != null) {
|
||||
tempFile.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Map<Integer, String> prepareOverlayGuide(int basePageCount, File[] overlayFiles, String mode, int[] counts) throws IOException {
|
||||
private Map<Integer, String> prepareOverlayGuide(int basePageCount, File[] overlayFiles, String mode, int[] counts, List<File> tempFiles) throws IOException {
|
||||
Map<Integer, String> overlayGuide = new HashMap<>();
|
||||
switch (mode) {
|
||||
case "SequentialOverlay":
|
||||
sequentialOverlay(overlayGuide, overlayFiles, basePageCount);
|
||||
sequentialOverlay(overlayGuide, overlayFiles, basePageCount, tempFiles);
|
||||
break;
|
||||
case "InterleavedOverlay":
|
||||
interleavedOverlay(overlayGuide, overlayFiles, basePageCount);
|
||||
@@ -83,24 +94,57 @@ public class PdfOverlayController {
|
||||
return overlayGuide;
|
||||
}
|
||||
|
||||
private void sequentialOverlay(Map<Integer, String> overlayGuide, File[] overlayFiles, int basePageCount) throws IOException {
|
||||
int currentPage = 1;
|
||||
for (File overlayFile : overlayFiles) {
|
||||
private void sequentialOverlay(Map<Integer, String> overlayGuide, File[] overlayFiles, int basePageCount, List<File> tempFiles) throws IOException {
|
||||
int overlayFileIndex = 0;
|
||||
int pageCountInCurrentOverlay = 0;
|
||||
|
||||
for (int basePageIndex = 1; basePageIndex <= basePageCount; basePageIndex++) {
|
||||
if (pageCountInCurrentOverlay == 0 || pageCountInCurrentOverlay >= getNumberOfPages(overlayFiles[overlayFileIndex])) {
|
||||
pageCountInCurrentOverlay = 0;
|
||||
overlayFileIndex = (overlayFileIndex + 1) % overlayFiles.length;
|
||||
}
|
||||
|
||||
try (PDDocument overlayPdf = PDDocument.load(overlayFiles[overlayFileIndex])) {
|
||||
PDDocument singlePageDocument = new PDDocument();
|
||||
singlePageDocument.addPage(overlayPdf.getPage(pageCountInCurrentOverlay));
|
||||
File tempFile = File.createTempFile("overlay-page-", ".pdf");
|
||||
singlePageDocument.save(tempFile);
|
||||
singlePageDocument.close();
|
||||
|
||||
overlayGuide.put(basePageIndex, tempFile.getAbsolutePath());
|
||||
tempFiles.add(tempFile); // Keep track of the temporary file for cleanup
|
||||
}
|
||||
|
||||
pageCountInCurrentOverlay++;
|
||||
}
|
||||
}
|
||||
|
||||
private int getNumberOfPages(File file) throws IOException {
|
||||
try (PDDocument doc = PDDocument.load(file)) {
|
||||
return doc.getNumberOfPages();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
private void interleavedOverlay(Map<Integer, String> overlayGuide, File[] overlayFiles, int basePageCount) throws IOException {
|
||||
for (int basePageIndex = 1; basePageIndex <= basePageCount; basePageIndex++) {
|
||||
File overlayFile = overlayFiles[(basePageIndex - 1) % overlayFiles.length];
|
||||
|
||||
// Load the overlay document to check its page count
|
||||
try (PDDocument overlayPdf = PDDocument.load(overlayFile)) {
|
||||
for (int i = 0; i < overlayPdf.getNumberOfPages(); i++) {
|
||||
if (currentPage > basePageCount) break;
|
||||
overlayGuide.put(currentPage++, overlayFile.getAbsolutePath());
|
||||
int overlayPageCount = overlayPdf.getNumberOfPages();
|
||||
if ((basePageIndex - 1) % overlayPageCount < overlayPageCount) {
|
||||
overlayGuide.put(basePageIndex, overlayFile.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void interleavedOverlay(Map<Integer, String> overlayGuide, File[] overlayFiles, int basePageCount) throws IOException {
|
||||
for (int i = 0; i < basePageCount; i++) {
|
||||
File overlayFile = overlayFiles[i % overlayFiles.length];
|
||||
overlayGuide.put(i + 1, overlayFile.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
|
||||
private void fixedRepeatOverlay(Map<Integer, String> overlayGuide, File[] overlayFiles, int[] counts, int basePageCount) throws IOException {
|
||||
if (overlayFiles.length != counts.length) {
|
||||
@@ -108,14 +152,22 @@ public class PdfOverlayController {
|
||||
}
|
||||
int currentPage = 1;
|
||||
for (int i = 0; i < overlayFiles.length; i++) {
|
||||
File overlayFile = overlayFiles[i];
|
||||
File overlayFile = overlayFiles[i];
|
||||
int repeatCount = counts[i];
|
||||
for (int j = 0; j < repeatCount; j++) {
|
||||
if (currentPage > basePageCount) break;
|
||||
overlayGuide.put(currentPage++, overlayFile.getAbsolutePath());
|
||||
|
||||
// Load the overlay document to check its page count
|
||||
try (PDDocument overlayPdf = PDDocument.load(overlayFile)) {
|
||||
int overlayPageCount = overlayPdf.getNumberOfPages();
|
||||
for (int j = 0; j < repeatCount; j++) {
|
||||
for (int page = 0; page < overlayPageCount; page++) {
|
||||
if (currentPage > basePageCount) break;
|
||||
overlayGuide.put(currentPage++, overlayFile.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Additional classes like OverlayPdfsRequest, WebResponseUtils, etc. are assumed to be defined elsewhere.
|
||||
|
||||
@@ -29,12 +29,12 @@ import stirling.software.SPDF.model.api.SplitPdfBySectionsRequest;
|
||||
import stirling.software.SPDF.utils.WebResponseUtils;
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/general")
|
||||
@Tag(name = "Misc", description = "Miscellaneous APIs")
|
||||
@Tag(name = "General", description = "General APIs")
|
||||
public class SplitPdfBySectionsController {
|
||||
|
||||
|
||||
@PostMapping(value = "/split-pdf-by-sections", consumes = "multipart/form-data")
|
||||
@Operation(summary = "Split PDF pages into smaller sections", description = "Split each page of a PDF into smaller sections based on the user's choice (halves, thirds, quarters, etc.), both vertically and horizontally. Input: PDF, Split Parameters. Output: ZIP containing split documents.")
|
||||
@Operation(summary = "Split PDF pages into smaller sections", description = "Split each page of a PDF into smaller sections based on the user's choice (halves, thirds, quarters, etc.), both vertically and horizontally. Input:PDF Output:ZIP-PDF Type:SISO")
|
||||
public ResponseEntity<byte[]> splitPdf(@ModelAttribute SplitPdfBySectionsRequest request) throws Exception {
|
||||
List<ByteArrayOutputStream> splitDocumentsBoas = new ArrayList<>();
|
||||
|
||||
@@ -42,10 +42,10 @@ public class SplitPdfBySectionsController {
|
||||
PDDocument sourceDocument = PDDocument.load(file.getInputStream());
|
||||
|
||||
// Process the PDF based on split parameters
|
||||
int horiz = request.getHorizontalDivisions();
|
||||
int verti = request.getVerticalDivisions();
|
||||
int horiz = request.getHorizontalDivisions() + 1;
|
||||
int verti = request.getVerticalDivisions() + 1;
|
||||
|
||||
List<PDDocument> splitDocuments = splitPdfPages(sourceDocument, horiz, verti);
|
||||
List<PDDocument> splitDocuments = splitPdfPages(sourceDocument, verti, horiz);
|
||||
for (PDDocument doc : splitDocuments) {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
doc.save(baos);
|
||||
@@ -108,7 +108,7 @@ public class SplitPdfBySectionsController {
|
||||
try (PDPageContentStream contentStream = new PDPageContentStream(subDoc, subPage)) {
|
||||
// Set clipping area and position
|
||||
float translateX = -subPageWidth * i;
|
||||
float translateY = height - subPageHeight * (j + 1) - subPageHeight;
|
||||
float translateY = height - subPageHeight * (verticalDivisions - j);
|
||||
|
||||
contentStream.saveGraphicsState();
|
||||
contentStream.addRect(0, 0, subPageWidth, subPageHeight);
|
||||
@@ -129,6 +129,7 @@ public class SplitPdfBySectionsController {
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -26,13 +26,13 @@ import stirling.software.SPDF.utils.WebResponseUtils;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/general")
|
||||
@Tag(name = "Misc", description = "Miscellaneous APIs")
|
||||
@Tag(name = "General", description = "General APIs")
|
||||
public class SplitPdfBySizeController {
|
||||
|
||||
|
||||
@PostMapping(value = "/split-by-size-or-count", consumes = "multipart/form-data")
|
||||
@Operation(summary = "Auto split PDF pages into separate documents based on size or count", description = "split PDF into multiple paged documents based on size/count, ie if 20 pages and split into 5, it does 5 documents each 4 pages\r\n"
|
||||
+ " if 10MB and each page is 1MB and you enter 2MB then 5 docs each 2MB (rounded so that it accepts 1.9MB but not 2.1MB) Input:PDF Output:ZIP Type:SIMO")
|
||||
+ " if 10MB and each page is 1MB and you enter 2MB then 5 docs each 2MB (rounded so that it accepts 1.9MB but not 2.1MB) Input:PDF Output:ZIP-PDF Type:SISO")
|
||||
public ResponseEntity<byte[]> autoSplitPdf(@ModelAttribute SplitPdfBySizeOrCountRequest request) throws Exception {
|
||||
List<ByteArrayOutputStream> splitDocumentsBoas = new ArrayList<ByteArrayOutputStream>();
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ import org.springframework.web.servlet.view.RedirectView;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import stirling.software.SPDF.config.security.UserService;
|
||||
import stirling.software.SPDF.model.Role;
|
||||
import stirling.software.SPDF.model.User;
|
||||
|
||||
@Controller
|
||||
@@ -32,6 +33,7 @@ public class UserController {
|
||||
@Autowired
|
||||
private UserService userService;
|
||||
|
||||
@PreAuthorize("!hasAuthority('ROLE_DEMO_USER')")
|
||||
@PostMapping("/register")
|
||||
public String register(@RequestParam String username, @RequestParam String password, Model model) {
|
||||
if(userService.usernameExists(username)) {
|
||||
@@ -43,6 +45,7 @@ public class UserController {
|
||||
return "redirect:/login?registered=true";
|
||||
}
|
||||
|
||||
@PreAuthorize("!hasAuthority('ROLE_DEMO_USER')")
|
||||
@PostMapping("/change-username-and-password")
|
||||
public RedirectView changeUsernameAndPassword(Principal principal,
|
||||
@RequestParam String currentPassword,
|
||||
@@ -85,7 +88,7 @@ public class UserController {
|
||||
}
|
||||
|
||||
|
||||
|
||||
@PreAuthorize("!hasAuthority('ROLE_DEMO_USER')")
|
||||
@PostMapping("/change-username")
|
||||
public RedirectView changeUsername(Principal principal,
|
||||
@RequestParam String currentPassword,
|
||||
@@ -122,7 +125,8 @@ public class UserController {
|
||||
|
||||
return new RedirectView("/login?messageType=credsUpdated");
|
||||
}
|
||||
|
||||
|
||||
@PreAuthorize("!hasAuthority('ROLE_DEMO_USER')")
|
||||
@PostMapping("/change-password")
|
||||
public RedirectView changePassword(Principal principal,
|
||||
@RequestParam String currentPassword,
|
||||
@@ -154,7 +158,7 @@ public class UserController {
|
||||
return new RedirectView("/login?messageType=credsUpdated");
|
||||
}
|
||||
|
||||
|
||||
@PreAuthorize("!hasAuthority('ROLE_DEMO_USER')")
|
||||
@PostMapping("/updateUserSettings")
|
||||
public String updateUserSettings(HttpServletRequest request, Principal principal) {
|
||||
Map<String, String[]> paramMap = request.getParameterMap();
|
||||
@@ -182,6 +186,18 @@ public class UserController {
|
||||
if(userService.usernameExists(username)) {
|
||||
return new RedirectView("/addUsers?messageType=usernameExists");
|
||||
}
|
||||
try {
|
||||
// Validate the role
|
||||
Role roleEnum = Role.fromString(role);
|
||||
if (roleEnum == Role.INTERNAL_API_USER) {
|
||||
// If the role is INTERNAL_API_USER, reject the request
|
||||
return new RedirectView("/addUsers?messageType=invalidRole");
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
// If the role ID is not valid, redirect with an error message
|
||||
return new RedirectView("/addUsers?messageType=invalidRole");
|
||||
}
|
||||
|
||||
userService.saveUser(username, password, role, forceChange);
|
||||
return new RedirectView("/addUsers"); // Redirect to account page after adding the user
|
||||
}
|
||||
@@ -203,6 +219,7 @@ public class UserController {
|
||||
return "redirect:/addUsers";
|
||||
}
|
||||
|
||||
@PreAuthorize("!hasAuthority('ROLE_DEMO_USER')")
|
||||
@PostMapping("/get-api-key")
|
||||
public ResponseEntity<String> getApiKey(Principal principal) {
|
||||
if (principal == null) {
|
||||
@@ -216,6 +233,7 @@ public class UserController {
|
||||
return ResponseEntity.ok(apiKey);
|
||||
}
|
||||
|
||||
@PreAuthorize("!hasAuthority('ROLE_DEMO_USER')")
|
||||
@PostMapping("/update-api-key")
|
||||
public ResponseEntity<String> updateApiKey(Principal principal) {
|
||||
if (principal == null) {
|
||||
|
||||
@@ -1,130 +0,0 @@
|
||||
package stirling.software.SPDF.controller.api.converters;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.xml.sax.InputSource;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Hidden;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import stirling.software.SPDF.model.api.GeneralFile;
|
||||
import stirling.software.SPDF.utils.FileToPdf;
|
||||
import stirling.software.SPDF.utils.WebResponseUtils;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/convert")
|
||||
@Tag(name = "Convert", description = "Convert APIs")
|
||||
public class ConvertEpubToPdf {
|
||||
//TODO
|
||||
@PostMapping(consumes = "multipart/form-data", value = "/epub-to-single-pdf")
|
||||
@Hidden
|
||||
@Operation(
|
||||
summary = "Convert an EPUB file to a single PDF",
|
||||
description = "This endpoint takes an EPUB file input and converts it to a single PDF."
|
||||
)
|
||||
public ResponseEntity<byte[]> epubToSinglePdf(
|
||||
@ModelAttribute GeneralFile request)
|
||||
throws Exception {
|
||||
MultipartFile fileInput = request.getFileInput();
|
||||
if (fileInput == null) {
|
||||
throw new IllegalArgumentException("Please provide an EPUB file for conversion.");
|
||||
}
|
||||
|
||||
String originalFilename = fileInput.getOriginalFilename();
|
||||
if (originalFilename == null || !originalFilename.endsWith(".epub")) {
|
||||
throw new IllegalArgumentException("File must be in .epub format.");
|
||||
}
|
||||
|
||||
Map<String, byte[]> epubContents = extractEpubContent(fileInput);
|
||||
List<String> htmlFilesOrder = getHtmlFilesOrderFromOpf(epubContents);
|
||||
|
||||
List<byte[]> individualPdfs = new ArrayList<>();
|
||||
|
||||
for (String htmlFile : htmlFilesOrder) {
|
||||
byte[] htmlContent = epubContents.get(htmlFile);
|
||||
byte[] pdfBytes = FileToPdf.convertHtmlToPdf(htmlContent, htmlFile.replace(".html", ".pdf"));
|
||||
individualPdfs.add(pdfBytes);
|
||||
}
|
||||
|
||||
// Pseudo-code to merge individual PDFs into one.
|
||||
byte[] mergedPdfBytes = mergeMultiplePdfsIntoOne(individualPdfs);
|
||||
|
||||
return WebResponseUtils.bytesToWebResponse(mergedPdfBytes, originalFilename.replace(".epub", ".pdf"));
|
||||
}
|
||||
|
||||
// Assuming a pseudo-code function that merges multiple PDFs into one.
|
||||
private byte[] mergeMultiplePdfsIntoOne(List<byte[]> individualPdfs) {
|
||||
// You can use a library such as PDFBox to perform the merging here.
|
||||
// Return the byte[] of the merged PDF.
|
||||
return null;
|
||||
}
|
||||
|
||||
private Map<String, byte[]> extractEpubContent(MultipartFile fileInput) throws IOException {
|
||||
Map<String, byte[]> contentMap = new HashMap<>();
|
||||
|
||||
try (ZipInputStream zis = new ZipInputStream(fileInput.getInputStream())) {
|
||||
ZipEntry zipEntry = zis.getNextEntry();
|
||||
while (zipEntry != null) {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
byte[] buffer = new byte[1024];
|
||||
int read = 0;
|
||||
while ((read = zis.read(buffer)) != -1) {
|
||||
baos.write(buffer, 0, read);
|
||||
}
|
||||
contentMap.put(zipEntry.getName(), baos.toByteArray());
|
||||
zipEntry = zis.getNextEntry();
|
||||
}
|
||||
}
|
||||
|
||||
return contentMap;
|
||||
}
|
||||
|
||||
private List<String> getHtmlFilesOrderFromOpf(Map<String, byte[]> epubContents) throws Exception {
|
||||
String opfContent = new String(epubContents.get("OEBPS/content.opf")); // Adjusting for given path
|
||||
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
|
||||
InputSource is = new InputSource(new StringReader(opfContent));
|
||||
Document doc = dBuilder.parse(is);
|
||||
|
||||
NodeList itemRefs = doc.getElementsByTagName("itemref");
|
||||
List<String> htmlFilesOrder = new ArrayList<>();
|
||||
|
||||
for (int i = 0; i < itemRefs.getLength(); i++) {
|
||||
Element itemRef = (Element) itemRefs.item(i);
|
||||
String idref = itemRef.getAttribute("idref");
|
||||
|
||||
NodeList items = doc.getElementsByTagName("item");
|
||||
for (int j = 0; j < items.getLength(); j++) {
|
||||
Element item = (Element) items.item(j);
|
||||
if (idref.equals(item.getAttribute("id"))) {
|
||||
htmlFilesOrder.add(item.getAttribute("href")); // Fetching the actual href
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return htmlFilesOrder;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package stirling.software.SPDF.controller.api.converters;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URLConnection;
|
||||
|
||||
import org.apache.pdfbox.rendering.ImageType;
|
||||
import org.slf4j.Logger;
|
||||
@@ -23,6 +24,7 @@ import stirling.software.SPDF.model.api.converters.ConvertToImageRequest;
|
||||
import stirling.software.SPDF.model.api.converters.ConvertToPdfRequest;
|
||||
import stirling.software.SPDF.utils.PdfUtils;
|
||||
import stirling.software.SPDF.utils.WebResponseUtils;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/convert")
|
||||
@Tag(name = "Convert", description = "Convert APIs")
|
||||
@@ -89,15 +91,7 @@ public class ConvertImgPDFController {
|
||||
}
|
||||
|
||||
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";
|
||||
String mimeType = URLConnection.guessContentTypeFromName("." + imageFormat);
|
||||
return mimeType.equals("null") ? "application/octet-stream" : mimeType;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ public class ConvertWebsiteToPDF {
|
||||
@PostMapping(consumes = "multipart/form-data", value = "/url/pdf")
|
||||
@Operation(
|
||||
summary = "Convert a URL to a PDF",
|
||||
description = "This endpoint fetches content from a URL and converts it to a PDF format."
|
||||
description = "This endpoint fetches content from a URL and converts it to a PDF format. Input:N/A Output:PDF Type:SISO"
|
||||
)
|
||||
public ResponseEntity<byte[]> urlToPdf(@ModelAttribute UrlToPdfRequest request) throws IOException, InterruptedException {
|
||||
String URL = request.getUrlInput();
|
||||
|
||||
@@ -28,12 +28,12 @@ import stirling.software.SPDF.model.api.extract.PDFFilePage;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/convert")
|
||||
@Tag(name = "General", description = "General APIs")
|
||||
@Tag(name = "Convert", description = "Convert APIs")
|
||||
public class ExtractController {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(CropController.class);
|
||||
|
||||
@PostMapping(value = "/pdf-to-csv", consumes = "multipart/form-data")
|
||||
@PostMapping(value = "/pdf/csv", consumes = "multipart/form-data")
|
||||
@Operation(summary = "Extracts a PDF document to csv", description = "This operation takes an input PDF file and returns CSV file of whole page. Input:PDF Output:CSV Type:SISO")
|
||||
public ResponseEntity<String> PdfToCsv(@ModelAttribute PDFFilePage form)
|
||||
throws Exception {
|
||||
|
||||
@@ -43,7 +43,7 @@ public class AutoSplitPdfController {
|
||||
private static final String QR_CONTENT = "https://github.com/Frooodle/Stirling-PDF";
|
||||
|
||||
@PostMapping(value = "/auto-split-pdf", consumes = "multipart/form-data")
|
||||
@Operation(summary = "Auto split PDF pages into separate documents", description = "This endpoint accepts a PDF file, scans each page for a specific QR code, and splits the document at the QR code boundaries. The output is a zip file containing each separate PDF document. Input:PDF Output:ZIP Type:SISO")
|
||||
@Operation(summary = "Auto split PDF pages into separate documents", description = "This endpoint accepts a PDF file, scans each page for a specific QR code, and splits the document at the QR code boundaries. The output is a zip file containing each separate PDF document. Input:PDF Output:ZIP-PDF Type:SISO")
|
||||
public ResponseEntity<byte[]> autoSplitPdf(@ModelAttribute AutoSplitPdfRequest request) throws IOException {
|
||||
MultipartFile file = request.getFileInput();
|
||||
boolean duplexMode = request.isDuplexMode();
|
||||
|
||||
@@ -15,6 +15,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
||||
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.tags.Tag;
|
||||
import stirling.software.SPDF.model.api.PDFFile;
|
||||
import stirling.software.SPDF.utils.WebResponseUtils;
|
||||
@@ -25,6 +26,7 @@ public class ShowJavascript {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(ShowJavascript.class);
|
||||
@PostMapping(consumes = "multipart/form-data", value = "/show-javascript")
|
||||
@Operation(summary = "Grabs all JS from a PDF and returns a single JS file with all code", description = "desc. Input:PDF Output:JS Type:SISO")
|
||||
public ResponseEntity<byte[]> extractHeader(@ModelAttribute PDFFile request) throws Exception {
|
||||
MultipartFile inputFile = request.getFileInput();
|
||||
String script = "";
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
package stirling.software.SPDF.controller.api.pipeline;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import jakarta.servlet.ServletContext;
|
||||
|
||||
import stirling.software.SPDF.SPdfApplication;
|
||||
import stirling.software.SPDF.model.ApiEndpoint;
|
||||
import stirling.software.SPDF.model.Role;
|
||||
import org.slf4j.Logger;
|
||||
@Service
|
||||
public class ApiDocService {
|
||||
|
||||
private final Map<String, ApiEndpoint> apiDocumentation = new HashMap<>();
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(ApiDocService.class);
|
||||
|
||||
@Autowired
|
||||
private ServletContext servletContext;
|
||||
|
||||
private String getApiDocsUrl() {
|
||||
String contextPath = servletContext.getContextPath();
|
||||
String port = SPdfApplication.getPort();
|
||||
|
||||
return "http://localhost:"+ port + contextPath + "/v1/api-docs";
|
||||
}
|
||||
|
||||
|
||||
@Autowired(required=false)
|
||||
private UserServiceInterface userService;
|
||||
|
||||
private String getApiKeyForUser() {
|
||||
if(userService == null)
|
||||
return "";
|
||||
return userService.getApiKeyForUser(Role.INTERNAL_API_USER.getRoleId());
|
||||
}
|
||||
|
||||
JsonNode apiDocsJsonRootNode;
|
||||
|
||||
|
||||
//@EventListener(ApplicationReadyEvent.class)
|
||||
private synchronized void loadApiDocumentation() {
|
||||
String apiDocsJson = "";
|
||||
try {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
String apiKey = getApiKeyForUser();
|
||||
if (!apiKey.isEmpty()) {
|
||||
headers.set("X-API-KEY", apiKey);
|
||||
}
|
||||
HttpEntity<String> entity = new HttpEntity<>(headers);
|
||||
|
||||
RestTemplate restTemplate = new RestTemplate();
|
||||
ResponseEntity<String> response = restTemplate.exchange(getApiDocsUrl(), HttpMethod.GET, entity, String.class);
|
||||
apiDocsJson = response.getBody();
|
||||
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
apiDocsJsonRootNode = mapper.readTree(apiDocsJson);
|
||||
|
||||
JsonNode paths = apiDocsJsonRootNode.path("paths");
|
||||
paths.fields().forEachRemaining(entry -> {
|
||||
String path = entry.getKey();
|
||||
JsonNode pathNode = entry.getValue();
|
||||
if (pathNode.has("post")) {
|
||||
JsonNode postNode = pathNode.get("post");
|
||||
ApiEndpoint endpoint = new ApiEndpoint(path, postNode);
|
||||
apiDocumentation.put(path, endpoint);
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
// Handle exceptions
|
||||
logger.error("Error grabbing swagger doc, body result {}", apiDocsJson);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isValidOperation(String operationName, Map<String, Object> parameters) {
|
||||
if(apiDocumentation.size() == 0) {
|
||||
loadApiDocumentation();
|
||||
}
|
||||
if (!apiDocumentation.containsKey(operationName)) {
|
||||
return false;
|
||||
}
|
||||
ApiEndpoint endpoint = apiDocumentation.get(operationName);
|
||||
return endpoint.areParametersValid(parameters);
|
||||
}
|
||||
|
||||
public boolean isMultiInput(String operationName) {
|
||||
if(apiDocsJsonRootNode == null || apiDocumentation.size() == 0) {
|
||||
loadApiDocumentation();
|
||||
}
|
||||
if (!apiDocumentation.containsKey(operationName)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ApiEndpoint endpoint = apiDocumentation.get(operationName);
|
||||
String description = endpoint.getDescription();
|
||||
|
||||
Pattern pattern = Pattern.compile("Type:(\\w+)");
|
||||
Matcher matcher = pattern.matcher(description);
|
||||
if (matcher.find()) {
|
||||
String type = matcher.group(1);
|
||||
return type.startsWith("MI");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Model class for API Endpoint
|
||||
|
||||
@@ -1,56 +1,31 @@
|
||||
package stirling.software.SPDF.controller.api.pipeline;
|
||||
|
||||
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.io.OutputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.io.ByteArrayResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.JsonMappingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import stirling.software.SPDF.model.ApplicationProperties;
|
||||
import stirling.software.SPDF.model.PipelineConfig;
|
||||
import stirling.software.SPDF.model.PipelineOperation;
|
||||
import stirling.software.SPDF.model.api.HandleDataRequest;
|
||||
import stirling.software.SPDF.utils.WebResponseUtils;
|
||||
|
||||
@@ -60,373 +35,39 @@ import stirling.software.SPDF.utils.WebResponseUtils;
|
||||
public class PipelineController {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(PipelineController.class);
|
||||
@Autowired
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
final String jsonFileName = "pipelineConfig.json";
|
||||
final String watchedFoldersDir = "./pipeline/watchedFolders/";
|
||||
final String finishedFoldersDir = "./pipeline/finishedFolders/";
|
||||
@Autowired
|
||||
PipelineProcessor processor;
|
||||
|
||||
@Scheduled(fixedRate = 25000)
|
||||
public void scanFolders() {
|
||||
logger.info("Scanning folders...");
|
||||
Path watchedFolderPath = Paths.get(watchedFoldersDir);
|
||||
if (!Files.exists(watchedFolderPath)) {
|
||||
try {
|
||||
Files.createDirectories(watchedFolderPath);
|
||||
logger.info("Created directory: {}", watchedFolderPath);
|
||||
} catch (IOException e) {
|
||||
logger.error("Error creating directory: {}", watchedFolderPath, e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
try (Stream<Path> paths = Files.walk(watchedFolderPath)) {
|
||||
paths.filter(Files::isDirectory).forEach(t -> {
|
||||
try {
|
||||
if (!t.equals(watchedFolderPath) && !t.endsWith("processing")) {
|
||||
handleDirectory(t);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("Error handling directory: {}", t, e);
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
logger.error("Error walking through directory: {}", watchedFolderPath, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Autowired
|
||||
ApplicationProperties applicationProperties;
|
||||
|
||||
|
||||
private void handleDirectory(Path dir) throws Exception {
|
||||
logger.info("Handling directory: {}", dir);
|
||||
Path jsonFile = dir.resolve(jsonFileName);
|
||||
Path processingDir = dir.resolve("processing"); // Directory to move files during processing
|
||||
if (!Files.exists(processingDir)) {
|
||||
Files.createDirectory(processingDir);
|
||||
logger.info("Created processing directory: {}", processingDir);
|
||||
}
|
||||
|
||||
if (Files.exists(jsonFile)) {
|
||||
// Read JSON file
|
||||
String jsonString;
|
||||
try {
|
||||
jsonString = new String(Files.readAllBytes(jsonFile));
|
||||
logger.info("Read JSON file: {}", jsonFile);
|
||||
} catch (IOException e) {
|
||||
logger.error("Error reading JSON file: {}", jsonFile, e);
|
||||
return;
|
||||
}
|
||||
|
||||
// Decode JSON to PipelineConfig
|
||||
PipelineConfig config;
|
||||
try {
|
||||
config = objectMapper.readValue(jsonString, PipelineConfig.class);
|
||||
// Assuming your PipelineConfig class has getters for all necessary fields, you
|
||||
// can perform checks here
|
||||
if (config.getOperations() == null || config.getOutputDir() == null || config.getName() == null) {
|
||||
throw new IOException("Invalid JSON format");
|
||||
}
|
||||
} catch (IOException e) {
|
||||
logger.error("Error parsing PipelineConfig: {}", jsonString, e);
|
||||
return;
|
||||
}
|
||||
|
||||
// For each operation in the pipeline
|
||||
for (PipelineOperation operation : config.getOperations()) {
|
||||
// Collect all files based on fileInput
|
||||
File[] files;
|
||||
String fileInput = (String) operation.getParameters().get("fileInput");
|
||||
if ("automated".equals(fileInput)) {
|
||||
// If fileInput is "automated", process all files in the directory
|
||||
try (Stream<Path> paths = Files.list(dir)) {
|
||||
files = paths
|
||||
.filter(path -> !Files.isDirectory(path)) // exclude directories
|
||||
.filter(path -> !path.equals(jsonFile)) // exclude jsonFile
|
||||
.map(Path::toFile)
|
||||
.toArray(File[]::new);
|
||||
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// If fileInput contains a path, process only this file
|
||||
files = new File[] { new File(fileInput) };
|
||||
}
|
||||
|
||||
// Prepare the files for processing
|
||||
List<File> filesToProcess = new ArrayList<>();
|
||||
for (File file : files) {
|
||||
logger.info(file.getName());
|
||||
logger.info("{} to {}",file.toPath(), processingDir.resolve(file.getName()));
|
||||
Files.move(file.toPath(), processingDir.resolve(file.getName()));
|
||||
filesToProcess.add(processingDir.resolve(file.getName()).toFile());
|
||||
}
|
||||
|
||||
// Process the files
|
||||
try {
|
||||
List<Resource> resources = handleFiles(filesToProcess.toArray(new File[0]), jsonString);
|
||||
|
||||
if(resources == null) {
|
||||
return;
|
||||
}
|
||||
// Move resultant files and rename them as per config in JSON file
|
||||
for (Resource resource : resources) {
|
||||
String resourceName = resource.getFilename();
|
||||
String baseName = resourceName.substring(0, resourceName.lastIndexOf("."));
|
||||
String extension = resourceName.substring(resourceName.lastIndexOf(".")+1);
|
||||
|
||||
String outputFileName = config.getOutputPattern().replace("{filename}", baseName);
|
||||
|
||||
outputFileName = outputFileName.replace("{pipelineName}", config.getName());
|
||||
DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyyMMdd");
|
||||
outputFileName = outputFileName.replace("{date}", LocalDate.now().format(dateFormatter));
|
||||
DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("HHmmss");
|
||||
outputFileName = outputFileName.replace("{time}", LocalTime.now().format(timeFormatter));
|
||||
|
||||
outputFileName += "." + extension;
|
||||
// {filename} {folder} {date} {tmime} {pipeline}
|
||||
String outputDir = config.getOutputDir();
|
||||
|
||||
String outputFolder = applicationProperties.getAutoPipeline().getOutputFolder();
|
||||
|
||||
if (outputFolder == null || outputFolder.isEmpty()) {
|
||||
// If the environment variable is not set, use the default value
|
||||
outputFolder = finishedFoldersDir;
|
||||
}
|
||||
logger.info("outputDir 0={}", outputDir);
|
||||
// Replace the placeholders in the outputDir string
|
||||
outputDir = outputDir.replace("{outputFolder}", outputFolder);
|
||||
outputDir = outputDir.replace("{folderName}", dir.toString());
|
||||
logger.info("outputDir 1={}", outputDir);
|
||||
outputDir = outputDir.replace("\\watchedFolders", "");
|
||||
outputDir = outputDir.replace("//watchedFolders", "");
|
||||
outputDir = outputDir.replace("\\\\watchedFolders", "");
|
||||
outputDir = outputDir.replace("/watchedFolders", "");
|
||||
|
||||
Path outputPath;
|
||||
logger.info("outputDir 2={}", outputDir);
|
||||
if (Paths.get(outputDir).isAbsolute()) {
|
||||
// If it's an absolute path, use it directly
|
||||
outputPath = Paths.get(outputDir);
|
||||
} else {
|
||||
// If it's a relative path, make it relative to the current working directory
|
||||
outputPath = Paths.get(".", outputDir);
|
||||
}
|
||||
|
||||
logger.info("outputPath={}", outputPath);
|
||||
|
||||
if (!Files.exists(outputPath)) {
|
||||
try {
|
||||
Files.createDirectories(outputPath);
|
||||
logger.info("Created directory: {}", outputPath);
|
||||
} catch (IOException e) {
|
||||
logger.error("Error creating directory: {}", outputPath, e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
logger.info("outputPath {}", outputPath);
|
||||
logger.info("outputPath.resolve(outputFileName).toString() {}", outputPath.resolve(outputFileName).toString());
|
||||
File newFile = new File(outputPath.resolve(outputFileName).toString());
|
||||
OutputStream os = new FileOutputStream(newFile);
|
||||
os.write(((ByteArrayResource)resource).getByteArray());
|
||||
os.close();
|
||||
logger.info("made {}", outputPath.resolve(outputFileName));
|
||||
}
|
||||
|
||||
// If successful, delete the original files
|
||||
for (File file : filesToProcess) {
|
||||
Files.deleteIfExists(processingDir.resolve(file.getName()));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// If an error occurs, move the original files back
|
||||
for (File file : filesToProcess) {
|
||||
Files.move(processingDir.resolve(file.getName()), file.toPath());
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<Resource> processFiles(List<Resource> outputFiles, String jsonString) throws Exception {
|
||||
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
JsonNode jsonNode = mapper.readTree(jsonString);
|
||||
|
||||
JsonNode pipelineNode = jsonNode.get("pipeline");
|
||||
logger.info("Running pipelineNode: {}", pipelineNode);
|
||||
ByteArrayOutputStream logStream = new ByteArrayOutputStream();
|
||||
PrintStream logPrintStream = new PrintStream(logStream);
|
||||
|
||||
boolean hasErrors = false;
|
||||
|
||||
for (JsonNode operationNode : pipelineNode) {
|
||||
String operation = operationNode.get("operation").asText();
|
||||
logger.info("Running operation: {}", operation);
|
||||
JsonNode parametersNode = operationNode.get("parameters");
|
||||
String inputFileExtension = "";
|
||||
if (operationNode.has("inputFileType")) {
|
||||
inputFileExtension = operationNode.get("inputFileType").asText();
|
||||
} else {
|
||||
inputFileExtension = ".pdf";
|
||||
}
|
||||
|
||||
List<Resource> newOutputFiles = new ArrayList<>();
|
||||
boolean hasInputFileType = false;
|
||||
|
||||
for (Resource file : outputFiles) {
|
||||
if (file.getFilename().endsWith(inputFileExtension)) {
|
||||
hasInputFileType = true;
|
||||
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
|
||||
body.add("fileInput", file);
|
||||
|
||||
Iterator<Map.Entry<String, JsonNode>> parameters = parametersNode.fields();
|
||||
while (parameters.hasNext()) {
|
||||
Map.Entry<String, JsonNode> parameter = parameters.next();
|
||||
body.add(parameter.getKey(), parameter.getValue().asText());
|
||||
}
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
|
||||
|
||||
HttpEntity<MultiValueMap<String, Object>> entity = new HttpEntity<>(body, headers);
|
||||
|
||||
RestTemplate restTemplate = new RestTemplate();
|
||||
String url = "http://localhost:8080/" + operation;
|
||||
|
||||
ResponseEntity<byte[]> response = restTemplate.exchange(url, HttpMethod.POST, entity, byte[].class);
|
||||
|
||||
// If the operation is filter and the response body is null or empty, skip this file
|
||||
if (operation.startsWith("filter-") && (response.getBody() == null || response.getBody().length == 0)) {
|
||||
logger.info("Skipping file due to failing {}", operation);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!response.getStatusCode().equals(HttpStatus.OK)) {
|
||||
logPrintStream.println("Error: " + response.getBody());
|
||||
hasErrors = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
// Define filename
|
||||
String filename;
|
||||
if ("auto-rename".equals(operation)) {
|
||||
// If the operation is "auto-rename", generate a new filename.
|
||||
// This is a simple example of generating a filename using current timestamp.
|
||||
// Modify as per your needs.
|
||||
filename = "file_" + System.currentTimeMillis();
|
||||
} else {
|
||||
// Otherwise, keep the original filename.
|
||||
filename = file.getFilename();
|
||||
}
|
||||
|
||||
// Check if the response body is a zip file
|
||||
if (isZip(response.getBody())) {
|
||||
// Unzip the file and add all the files to the new output files
|
||||
newOutputFiles.addAll(unzip(response.getBody()));
|
||||
} else {
|
||||
Resource outputResource = new ByteArrayResource(response.getBody()) {
|
||||
@Override
|
||||
public String getFilename() {
|
||||
return filename;
|
||||
}
|
||||
};
|
||||
newOutputFiles.add(outputResource);
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasInputFileType) {
|
||||
logPrintStream.println(
|
||||
"No files with extension " + inputFileExtension + " found for operation " + operation);
|
||||
hasErrors = true;
|
||||
}
|
||||
|
||||
outputFiles = newOutputFiles;
|
||||
}
|
||||
logPrintStream.close();
|
||||
|
||||
}
|
||||
if (hasErrors) {
|
||||
logger.error("Errors occurred during processing. Log: {}", logStream.toString());
|
||||
}
|
||||
return outputFiles;
|
||||
}
|
||||
|
||||
List<Resource> handleFiles(File[] files, String jsonString) throws Exception {
|
||||
if(files == null || files.length == 0) {
|
||||
logger.info("No files");
|
||||
return null;
|
||||
}
|
||||
|
||||
logger.info("Handling files: {} files, with JSON string of length: {}", files.length, jsonString.length());
|
||||
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
JsonNode jsonNode = mapper.readTree(jsonString);
|
||||
|
||||
JsonNode pipelineNode = jsonNode.get("pipeline");
|
||||
|
||||
boolean hasErrors = false;
|
||||
List<Resource> outputFiles = new ArrayList<>();
|
||||
|
||||
for (File file : files) {
|
||||
Path path = Paths.get(file.getAbsolutePath());
|
||||
System.out.println("Reading file: " + path); // debug statement
|
||||
|
||||
if (Files.exists(path)) {
|
||||
Resource fileResource = new ByteArrayResource(Files.readAllBytes(path)) {
|
||||
@Override
|
||||
public String getFilename() {
|
||||
return file.getName();
|
||||
}
|
||||
};
|
||||
outputFiles.add(fileResource);
|
||||
} else {
|
||||
System.out.println("File not found: " + path); // debug statement
|
||||
}
|
||||
}
|
||||
logger.info("Files successfully loaded. Starting processing...");
|
||||
return processFiles(outputFiles, jsonString);
|
||||
}
|
||||
|
||||
List<Resource> handleFiles(MultipartFile[] files, String jsonString) throws Exception {
|
||||
if(files == null || files.length == 0) {
|
||||
logger.info("No files");
|
||||
return null;
|
||||
}
|
||||
logger.info("Handling files: {} files, with JSON string of length: {}", files.length, jsonString.length());
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
JsonNode jsonNode = mapper.readTree(jsonString);
|
||||
|
||||
JsonNode pipelineNode = jsonNode.get("pipeline");
|
||||
|
||||
boolean hasErrors = false;
|
||||
List<Resource> outputFiles = new ArrayList<>();
|
||||
|
||||
for (MultipartFile file : files) {
|
||||
Resource fileResource = new ByteArrayResource(file.getBytes()) {
|
||||
@Override
|
||||
public String getFilename() {
|
||||
return file.getOriginalFilename();
|
||||
}
|
||||
};
|
||||
outputFiles.add(fileResource);
|
||||
}
|
||||
logger.info("Files successfully loaded. Starting processing...");
|
||||
return processFiles(outputFiles, jsonString);
|
||||
}
|
||||
@Autowired
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
|
||||
@PostMapping("/handleData")
|
||||
public ResponseEntity<byte[]> handleData(@ModelAttribute HandleDataRequest request) {
|
||||
MultipartFile[] files = request.getFileInputs();
|
||||
String jsonString = request.getJsonString();
|
||||
public ResponseEntity<byte[]> handleData(@ModelAttribute HandleDataRequest request) throws JsonMappingException, JsonProcessingException {
|
||||
if (!Boolean.TRUE.equals(applicationProperties.getSystem().getEnableAlphaFunctionality())) {
|
||||
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
|
||||
MultipartFile[] files = request.getFileInput();
|
||||
String jsonString = request.getJson();
|
||||
if (files == null) {
|
||||
return null;
|
||||
}
|
||||
PipelineConfig config = objectMapper.readValue(jsonString, PipelineConfig.class);
|
||||
logger.info("Received POST request to /handleData with {} files", files.length);
|
||||
try {
|
||||
List<Resource> outputFiles = handleFiles(files, jsonString);
|
||||
|
||||
List<Resource> inputFiles = processor.generateInputFiles(files);
|
||||
if(inputFiles == null || inputFiles.size() == 0) {
|
||||
return null;
|
||||
}
|
||||
List<Resource> outputFiles = processor.runPipelineAgainstFiles(inputFiles, config);
|
||||
if (outputFiles != null && outputFiles.size() == 1) {
|
||||
// If there is only one file, return it directly
|
||||
Resource singleFile = outputFiles.get(0);
|
||||
@@ -473,52 +114,4 @@ public class PipelineController {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isZip(byte[] data) {
|
||||
if (data == null || data.length < 4) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check the first four bytes of the data against the standard zip magic number
|
||||
return data[0] == 0x50 && data[1] == 0x4B && data[2] == 0x03 && data[3] == 0x04;
|
||||
}
|
||||
|
||||
private List<Resource> unzip(byte[] data) throws IOException {
|
||||
logger.info("Unzipping data of length: {}", data.length);
|
||||
List<Resource> unzippedFiles = new ArrayList<>();
|
||||
|
||||
try (ByteArrayInputStream bais = new ByteArrayInputStream(data);
|
||||
ZipInputStream zis = new ZipInputStream(bais)) {
|
||||
|
||||
ZipEntry entry;
|
||||
while ((entry = zis.getNextEntry()) != null) {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
byte[] buffer = new byte[1024];
|
||||
int count;
|
||||
|
||||
while ((count = zis.read(buffer)) != -1) {
|
||||
baos.write(buffer, 0, count);
|
||||
}
|
||||
|
||||
final String filename = entry.getName();
|
||||
Resource fileResource = new ByteArrayResource(baos.toByteArray()) {
|
||||
@Override
|
||||
public String getFilename() {
|
||||
return filename;
|
||||
}
|
||||
};
|
||||
|
||||
// If the unzipped file is a zip file, unzip it
|
||||
if (isZip(baos.toByteArray())) {
|
||||
logger.info("File {} is a zip file. Unzipping...", filename);
|
||||
unzippedFiles.addAll(unzip(baos.toByteArray()));
|
||||
} else {
|
||||
unzippedFiles.add(fileResource);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
logger.info("Unzipping completed. {} files were unzipped.", unzippedFiles.size());
|
||||
return unzippedFiles;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,258 @@
|
||||
package stirling.software.SPDF.controller.api.pipeline;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.io.ByteArrayResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import stirling.software.SPDF.model.ApplicationProperties;
|
||||
import stirling.software.SPDF.model.PipelineConfig;
|
||||
import stirling.software.SPDF.model.PipelineOperation;
|
||||
|
||||
@Service
|
||||
public class PipelineDirectoryProcessor {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(PipelineDirectoryProcessor.class);
|
||||
@Autowired
|
||||
private ObjectMapper objectMapper;
|
||||
@Autowired
|
||||
private ApiDocService apiDocService;
|
||||
@Autowired
|
||||
private ApplicationProperties applicationProperties;
|
||||
|
||||
final String watchedFoldersDir = "./pipeline/watchedFolders/";
|
||||
final String finishedFoldersDir = "./pipeline/finishedFolders/";
|
||||
|
||||
@Autowired
|
||||
PipelineProcessor processor;
|
||||
|
||||
@Scheduled(fixedRate = 60000)
|
||||
public void scanFolders() {
|
||||
if (!Boolean.TRUE.equals(applicationProperties.getSystem().getEnableAlphaFunctionality())) {
|
||||
return;
|
||||
}
|
||||
Path watchedFolderPath = Paths.get(watchedFoldersDir);
|
||||
if (!Files.exists(watchedFolderPath)) {
|
||||
try {
|
||||
Files.createDirectories(watchedFolderPath);
|
||||
logger.info("Created directory: {}", watchedFolderPath);
|
||||
} catch (IOException e) {
|
||||
logger.error("Error creating directory: {}", watchedFolderPath, e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
try (Stream<Path> paths = Files.walk(watchedFolderPath)) {
|
||||
paths.filter(Files::isDirectory).forEach(t -> {
|
||||
try {
|
||||
if (!t.equals(watchedFolderPath) && !t.endsWith("processing")) {
|
||||
handleDirectory(t);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("Error handling directory: {}", t, e);
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
logger.error("Error walking through directory: {}", watchedFolderPath, e);
|
||||
}
|
||||
}
|
||||
|
||||
public void handleDirectory(Path dir) throws IOException {
|
||||
logger.info("Handling directory: {}", dir);
|
||||
Path processingDir = createProcessingDirectory(dir);
|
||||
|
||||
Optional<Path> jsonFileOptional = findJsonFile(dir);
|
||||
if (!jsonFileOptional.isPresent()) {
|
||||
logger.warn("No .JSON settings file found. No processing will happen for dir {}.", dir);
|
||||
return;
|
||||
}
|
||||
|
||||
Path jsonFile = jsonFileOptional.get();
|
||||
PipelineConfig config = readAndParseJson(jsonFile);
|
||||
processPipelineOperations(dir, processingDir, jsonFile, config);
|
||||
}
|
||||
|
||||
private Path createProcessingDirectory(Path dir) throws IOException {
|
||||
Path processingDir = dir.resolve("processing");
|
||||
if (!Files.exists(processingDir)) {
|
||||
Files.createDirectory(processingDir);
|
||||
logger.info("Created processing directory: {}", processingDir);
|
||||
}
|
||||
return processingDir;
|
||||
}
|
||||
|
||||
private Optional<Path> findJsonFile(Path dir) throws IOException {
|
||||
try (Stream<Path> paths = Files.list(dir)) {
|
||||
return paths.filter(file -> file.toString().endsWith(".json")).findFirst();
|
||||
}
|
||||
}
|
||||
|
||||
private PipelineConfig readAndParseJson(Path jsonFile) throws IOException {
|
||||
String jsonString = new String(Files.readAllBytes(jsonFile), StandardCharsets.UTF_8);
|
||||
logger.debug("Reading JSON file: {}", jsonFile);
|
||||
return objectMapper.readValue(jsonString, PipelineConfig.class);
|
||||
}
|
||||
|
||||
private void processPipelineOperations(Path dir, Path processingDir, Path jsonFile, PipelineConfig config) throws IOException {
|
||||
for (PipelineOperation operation : config.getOperations()) {
|
||||
validateOperation(operation);
|
||||
File[] files = collectFilesForProcessing(dir, jsonFile, operation);
|
||||
if(files == null || files.length == 0) {
|
||||
logger.debug("No files detected for {} ", dir);
|
||||
return;
|
||||
}
|
||||
List<File> filesToProcess = prepareFilesForProcessing(files, processingDir);
|
||||
runPipelineAgainstFiles(filesToProcess, config, dir, processingDir);
|
||||
}
|
||||
}
|
||||
|
||||
private void validateOperation(PipelineOperation operation) throws IOException {
|
||||
if (!apiDocService.isValidOperation(operation.getOperation(), operation.getParameters())) {
|
||||
throw new IOException("Invalid operation: " + operation.getOperation());
|
||||
}
|
||||
}
|
||||
|
||||
private File[] collectFilesForProcessing(Path dir, Path jsonFile, PipelineOperation operation) throws IOException {
|
||||
try (Stream<Path> paths = Files.list(dir)) {
|
||||
if ("automated".equals(operation.getParameters().get("fileInput"))) {
|
||||
return paths.filter(path -> !Files.isDirectory(path) && !path.equals(jsonFile))
|
||||
.map(Path::toFile)
|
||||
.toArray(File[]::new);
|
||||
} else {
|
||||
String fileInput = (String) operation.getParameters().get("fileInput");
|
||||
return new File[]{new File(fileInput)};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<File> prepareFilesForProcessing(File[] files, Path processingDir) throws IOException {
|
||||
List<File> filesToProcess = new ArrayList<>();
|
||||
for (File file : files) {
|
||||
Path targetPath = resolveUniqueFilePath(processingDir, file.getName());
|
||||
Files.move(file.toPath(), targetPath);
|
||||
filesToProcess.add(targetPath.toFile());
|
||||
}
|
||||
return filesToProcess;
|
||||
}
|
||||
|
||||
private Path resolveUniqueFilePath(Path directory, String originalFileName) {
|
||||
Path filePath = directory.resolve(originalFileName);
|
||||
int counter = 1;
|
||||
|
||||
while (Files.exists(filePath)) {
|
||||
String newName = appendSuffixToFileName(originalFileName, "(" + counter + ")");
|
||||
filePath = directory.resolve(newName);
|
||||
counter++;
|
||||
}
|
||||
|
||||
return filePath;
|
||||
}
|
||||
|
||||
private String appendSuffixToFileName(String originalFileName, String suffix) {
|
||||
int dotIndex = originalFileName.lastIndexOf('.');
|
||||
if (dotIndex == -1) {
|
||||
return originalFileName + suffix;
|
||||
} else {
|
||||
return originalFileName.substring(0, dotIndex) + suffix + originalFileName.substring(dotIndex);
|
||||
}
|
||||
}
|
||||
|
||||
private void runPipelineAgainstFiles(List<File> filesToProcess, PipelineConfig config, Path dir, Path processingDir) throws IOException {
|
||||
try {
|
||||
List<Resource> inputFiles = processor.generateInputFiles(filesToProcess.toArray(new File[0]));
|
||||
if(inputFiles == null || inputFiles.size() == 0) {
|
||||
return;
|
||||
}
|
||||
List<Resource> outputFiles = processor.runPipelineAgainstFiles(inputFiles, config);
|
||||
if (outputFiles == null) return;
|
||||
moveAndRenameFiles(outputFiles, config, dir);
|
||||
deleteOriginalFiles(filesToProcess, processingDir);
|
||||
} catch (Exception e) {
|
||||
logger.error("error during processing", e);
|
||||
moveFilesBack(filesToProcess, processingDir);
|
||||
}
|
||||
}
|
||||
|
||||
private void moveAndRenameFiles(List<Resource> resources, PipelineConfig config, Path dir) throws IOException {
|
||||
for (Resource resource : resources) {
|
||||
String outputFileName = createOutputFileName(resource, config);
|
||||
Path outputPath = determineOutputPath(config, dir);
|
||||
|
||||
if (!Files.exists(outputPath)) {
|
||||
Files.createDirectories(outputPath);
|
||||
logger.info("Created directory: {}", outputPath);
|
||||
}
|
||||
|
||||
Path outputFile = outputPath.resolve(outputFileName);
|
||||
try (OutputStream os = new FileOutputStream(outputFile.toFile())) {
|
||||
os.write(((ByteArrayResource) resource).getByteArray());
|
||||
}
|
||||
|
||||
logger.info("File moved and renamed to {}", outputFile);
|
||||
}
|
||||
}
|
||||
|
||||
private String createOutputFileName(Resource resource, PipelineConfig config) {
|
||||
String resourceName = resource.getFilename();
|
||||
String baseName = resourceName.substring(0, resourceName.lastIndexOf('.'));
|
||||
String extension = resourceName.substring(resourceName.lastIndexOf('.') + 1);
|
||||
|
||||
String outputFileName = config.getOutputPattern()
|
||||
.replace("{filename}", baseName)
|
||||
.replace("{pipelineName}", config.getName())
|
||||
.replace("{date}", LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")))
|
||||
.replace("{time}", LocalTime.now().format(DateTimeFormatter.ofPattern("HHmmss")))
|
||||
+ "." + extension;
|
||||
|
||||
return outputFileName;
|
||||
}
|
||||
|
||||
private Path determineOutputPath(PipelineConfig config, Path dir) {
|
||||
String outputDir = config.getOutputDir()
|
||||
.replace("{outputFolder}", finishedFoldersDir)
|
||||
.replace("{folderName}", dir.toString())
|
||||
.replaceAll("\\\\?watchedFolders", "");
|
||||
|
||||
return Paths.get(outputDir).isAbsolute() ? Paths.get(outputDir) : Paths.get(".", outputDir);
|
||||
}
|
||||
|
||||
private void deleteOriginalFiles(List<File> filesToProcess, Path processingDir) throws IOException {
|
||||
for (File file : filesToProcess) {
|
||||
Files.deleteIfExists(processingDir.resolve(file.getName()));
|
||||
logger.info("Deleted original file: {}", file.getName());
|
||||
}
|
||||
}
|
||||
|
||||
private void moveFilesBack(List<File> filesToProcess, Path processingDir) {
|
||||
for (File file : filesToProcess) {
|
||||
try {
|
||||
Files.move(processingDir.resolve(file.getName()), file.toPath());
|
||||
logger.info("Moved file back to original location: {} , {}",file.toPath(), file.getName());
|
||||
} catch (IOException e) {
|
||||
logger.error("Error moving file back to original location: {}", file.getName(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,332 @@
|
||||
package stirling.software.SPDF.controller.api.pipeline;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.io.ByteArrayResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import jakarta.servlet.ServletContext;
|
||||
|
||||
import stirling.software.SPDF.SPdfApplication;
|
||||
import stirling.software.SPDF.model.PipelineConfig;
|
||||
import stirling.software.SPDF.model.PipelineOperation;
|
||||
import stirling.software.SPDF.model.Role;
|
||||
|
||||
@Service
|
||||
public class PipelineProcessor {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(PipelineProcessor.class);
|
||||
|
||||
|
||||
@Autowired
|
||||
private ApiDocService apiDocService;
|
||||
|
||||
@Autowired(required=false)
|
||||
private UserServiceInterface userService;
|
||||
|
||||
@Autowired
|
||||
private ServletContext servletContext;
|
||||
|
||||
|
||||
|
||||
|
||||
private String getApiKeyForUser() {
|
||||
if (userService == null)
|
||||
return "";
|
||||
return userService.getApiKeyForUser(Role.INTERNAL_API_USER.getRoleId());
|
||||
}
|
||||
|
||||
|
||||
private String getBaseUrl() {
|
||||
String contextPath = servletContext.getContextPath();
|
||||
String port = SPdfApplication.getPort();
|
||||
|
||||
return "http://localhost:" + port + contextPath + "/";
|
||||
}
|
||||
|
||||
|
||||
|
||||
List<Resource> runPipelineAgainstFiles(List<Resource> outputFiles, PipelineConfig config) throws Exception {
|
||||
|
||||
ByteArrayOutputStream logStream = new ByteArrayOutputStream();
|
||||
PrintStream logPrintStream = new PrintStream(logStream);
|
||||
|
||||
boolean hasErrors = false;
|
||||
|
||||
for (PipelineOperation pipelineOperation : config.getOperations()) {
|
||||
String operation = pipelineOperation.getOperation();
|
||||
boolean isMultiInputOperation = apiDocService.isMultiInput(operation);
|
||||
|
||||
logger.info("Running operation: {} isMultiInputOperation {}", operation, isMultiInputOperation);
|
||||
Map<String, Object> parameters = pipelineOperation.getParameters();
|
||||
String inputFileExtension = "";
|
||||
|
||||
//TODO
|
||||
//if (operationNode.has("inputFileType")) {
|
||||
// inputFileExtension = operationNode.get("inputFileType").asText();
|
||||
//} else {
|
||||
inputFileExtension = ".pdf";
|
||||
//}
|
||||
final String finalInputFileExtension = inputFileExtension;
|
||||
|
||||
String url = getBaseUrl() + operation;
|
||||
|
||||
List<Resource> newOutputFiles = new ArrayList<>();
|
||||
if (!isMultiInputOperation) {
|
||||
for (Resource file : outputFiles) {
|
||||
boolean hasInputFileType = false;
|
||||
if (file.getFilename().endsWith(inputFileExtension)) {
|
||||
hasInputFileType = true;
|
||||
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
|
||||
body.add("fileInput", file);
|
||||
|
||||
|
||||
for(Entry<String, Object> entry : parameters.entrySet()) {
|
||||
body.add(entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
ResponseEntity<byte[]> response = sendWebRequest(url, body);
|
||||
|
||||
// If the operation is filter and the response body is null or empty, skip this
|
||||
// file
|
||||
if (operation.startsWith("filter-")
|
||||
&& (response.getBody() == null || response.getBody().length == 0)) {
|
||||
logger.info("Skipping file due to failing {}", operation);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!response.getStatusCode().equals(HttpStatus.OK)) {
|
||||
logPrintStream.println("Error: " + response.getBody());
|
||||
hasErrors = true;
|
||||
continue;
|
||||
}
|
||||
processOutputFiles(operation, file.getFilename(), response, newOutputFiles);
|
||||
|
||||
}
|
||||
|
||||
if (!hasInputFileType) {
|
||||
logPrintStream.println(
|
||||
"No files with extension " + inputFileExtension + " found for operation " + operation);
|
||||
hasErrors = true;
|
||||
}
|
||||
|
||||
outputFiles = newOutputFiles;
|
||||
}
|
||||
|
||||
} else {
|
||||
// Filter and collect all files that match the inputFileExtension
|
||||
List<Resource> matchingFiles = outputFiles.stream()
|
||||
.filter(file -> file.getFilename().endsWith(finalInputFileExtension))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// Check if there are matching files
|
||||
if (!matchingFiles.isEmpty()) {
|
||||
// Create a new MultiValueMap for the request body
|
||||
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
|
||||
|
||||
// Add all matching files to the body
|
||||
for (Resource file : matchingFiles) {
|
||||
body.add("fileInput", file);
|
||||
}
|
||||
|
||||
for(Entry<String, Object> entry : parameters.entrySet()) {
|
||||
body.add(entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
ResponseEntity<byte[]> response = sendWebRequest(url, body);
|
||||
|
||||
// Handle the response
|
||||
if (response.getStatusCode().equals(HttpStatus.OK)) {
|
||||
processOutputFiles(operation, matchingFiles.get(0).getFilename(), response, newOutputFiles);
|
||||
} else {
|
||||
// Log error if the response status is not OK
|
||||
logPrintStream.println("Error in multi-input operation: " + response.getBody());
|
||||
hasErrors = true;
|
||||
}
|
||||
} else {
|
||||
logPrintStream.println("No files with extension " + inputFileExtension + " found for multi-input operation " + operation);
|
||||
hasErrors = true;
|
||||
}
|
||||
}
|
||||
logPrintStream.close();
|
||||
|
||||
}
|
||||
if (hasErrors) {
|
||||
logger.error("Errors occurred during processing. Log: {}", logStream.toString());
|
||||
}
|
||||
return outputFiles;
|
||||
}
|
||||
|
||||
private ResponseEntity<byte[]> sendWebRequest(String url, MultiValueMap<String, Object> body ){
|
||||
RestTemplate restTemplate = new RestTemplate();
|
||||
|
||||
// Set up headers, including API key
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
String apiKey = getApiKeyForUser();
|
||||
headers.add("X-API-Key", apiKey);
|
||||
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
|
||||
|
||||
// Create HttpEntity with the body and headers
|
||||
HttpEntity<MultiValueMap<String, Object>> entity = new HttpEntity<>(body, headers);
|
||||
|
||||
// Make the request to the REST endpoint
|
||||
return restTemplate.exchange(url, HttpMethod.POST, entity, byte[].class);
|
||||
}
|
||||
|
||||
private List<Resource> processOutputFiles(String operation, String fileName, ResponseEntity<byte[]> response, List<Resource> newOutputFiles) throws IOException{
|
||||
// Define filename
|
||||
String newFilename;
|
||||
if ("auto-rename".equals(operation)) {
|
||||
// If the operation is "auto-rename", generate a new filename.
|
||||
// This is a simple example of generating a filename using current timestamp.
|
||||
// Modify as per your needs.
|
||||
newFilename = "file_" + System.currentTimeMillis();
|
||||
} else {
|
||||
// Otherwise, keep the original filename.
|
||||
newFilename = fileName;
|
||||
}
|
||||
|
||||
// Check if the response body is a zip file
|
||||
if (isZip(response.getBody())) {
|
||||
// Unzip the file and add all the files to the new output files
|
||||
newOutputFiles.addAll(unzip(response.getBody()));
|
||||
} else {
|
||||
Resource outputResource = new ByteArrayResource(response.getBody()) {
|
||||
@Override
|
||||
public String getFilename() {
|
||||
return newFilename;
|
||||
}
|
||||
};
|
||||
newOutputFiles.add(outputResource);
|
||||
}
|
||||
|
||||
return newOutputFiles;
|
||||
|
||||
}
|
||||
List<Resource> generateInputFiles(File[] files) throws Exception {
|
||||
if (files == null || files.length == 0) {
|
||||
logger.info("No files");
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
List<Resource> outputFiles = new ArrayList<>();
|
||||
|
||||
for (File file : files) {
|
||||
Path path = Paths.get(file.getAbsolutePath());
|
||||
logger.info("Reading file: " + path); // debug statement
|
||||
|
||||
if (Files.exists(path)) {
|
||||
Resource fileResource = new ByteArrayResource(Files.readAllBytes(path)) {
|
||||
@Override
|
||||
public String getFilename() {
|
||||
return file.getName();
|
||||
}
|
||||
};
|
||||
outputFiles.add(fileResource);
|
||||
} else {
|
||||
logger.info("File not found: " + path);
|
||||
}
|
||||
}
|
||||
logger.info("Files successfully loaded. Starting processing...");
|
||||
return outputFiles;
|
||||
}
|
||||
|
||||
List<Resource> generateInputFiles(MultipartFile[] files) throws Exception {
|
||||
if (files == null || files.length == 0) {
|
||||
logger.info("No files");
|
||||
return null;
|
||||
}
|
||||
|
||||
List<Resource> outputFiles = new ArrayList<>();
|
||||
|
||||
for (MultipartFile file : files) {
|
||||
Resource fileResource = new ByteArrayResource(file.getBytes()) {
|
||||
@Override
|
||||
public String getFilename() {
|
||||
return file.getOriginalFilename();
|
||||
}
|
||||
};
|
||||
outputFiles.add(fileResource);
|
||||
}
|
||||
logger.info("Files successfully loaded. Starting processing...");
|
||||
return outputFiles;
|
||||
}
|
||||
|
||||
private boolean isZip(byte[] data) {
|
||||
if (data == null || data.length < 4) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check the first four bytes of the data against the standard zip magic number
|
||||
return data[0] == 0x50 && data[1] == 0x4B && data[2] == 0x03 && data[3] == 0x04;
|
||||
}
|
||||
|
||||
private List<Resource> unzip(byte[] data) throws IOException {
|
||||
logger.info("Unzipping data of length: {}", data.length);
|
||||
List<Resource> unzippedFiles = new ArrayList<>();
|
||||
|
||||
try (ByteArrayInputStream bais = new ByteArrayInputStream(data);
|
||||
ZipInputStream zis = new ZipInputStream(bais)) {
|
||||
|
||||
ZipEntry entry;
|
||||
while ((entry = zis.getNextEntry()) != null) {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
byte[] buffer = new byte[1024];
|
||||
int count;
|
||||
|
||||
while ((count = zis.read(buffer)) != -1) {
|
||||
baos.write(buffer, 0, count);
|
||||
}
|
||||
|
||||
final String filename = entry.getName();
|
||||
Resource fileResource = new ByteArrayResource(baos.toByteArray()) {
|
||||
@Override
|
||||
public String getFilename() {
|
||||
return filename;
|
||||
}
|
||||
};
|
||||
|
||||
// If the unzipped file is a zip file, unzip it
|
||||
if (isZip(baos.toByteArray())) {
|
||||
logger.info("File {} is a zip file. Unzipping...", filename);
|
||||
unzippedFiles.addAll(unzip(baos.toByteArray()));
|
||||
} else {
|
||||
unzippedFiles.add(fileResource);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
logger.info("Unzipping completed. {} files were unzipped.", unzippedFiles.size());
|
||||
return unzippedFiles;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package stirling.software.SPDF.controller.api.pipeline;
|
||||
public interface UserServiceInterface {
|
||||
String getApiKeyForUser(String username);
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
package stirling.software.SPDF.controller.web;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@@ -15,6 +16,8 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import stirling.software.SPDF.model.Authority;
|
||||
import stirling.software.SPDF.model.Role;
|
||||
import stirling.software.SPDF.model.User;
|
||||
import stirling.software.SPDF.repository.UserRepository;
|
||||
@Controller
|
||||
@@ -46,14 +49,28 @@ public class AccountWebController {
|
||||
@PreAuthorize("hasRole('ROLE_ADMIN')")
|
||||
@GetMapping("/addUsers")
|
||||
public String showAddUserForm(Model model, Authentication authentication) {
|
||||
List<User> allUsers = userRepository.findAll();
|
||||
List<User> allUsers = userRepository.findAll();
|
||||
Iterator<User> iterator = allUsers.iterator();
|
||||
|
||||
while(iterator.hasNext()) {
|
||||
User user = iterator.next();
|
||||
if(user != null) {
|
||||
for (Authority authority : user.getAuthorities()) {
|
||||
if (authority.getAuthority().equals(Role.INTERNAL_API_USER.getRoleId())) {
|
||||
iterator.remove();
|
||||
break; // Break out of the inner loop once the user is removed
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
model.addAttribute("users", allUsers);
|
||||
model.addAttribute("currentUsername", authentication.getName());
|
||||
return "addUsers";
|
||||
}
|
||||
|
||||
|
||||
|
||||
@PreAuthorize("!hasAuthority('ROLE_DEMO_USER')")
|
||||
@GetMapping("/account")
|
||||
public String account(HttpServletRequest request, Model model, Authentication authentication) {
|
||||
if (authentication == null || !authentication.isAuthenticated()) {
|
||||
@@ -100,7 +117,7 @@ public class AccountWebController {
|
||||
}
|
||||
|
||||
|
||||
|
||||
@PreAuthorize("!hasAuthority('ROLE_DEMO_USER')")
|
||||
@GetMapping("/change-creds")
|
||||
public String changeCreds(HttpServletRequest request, Model model, Authentication authentication) {
|
||||
if (authentication == null || !authentication.isAuthenticated()) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package stirling.software.SPDF.controller.web;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
@@ -41,31 +42,44 @@ public class GeneralWebController {
|
||||
model.addAttribute("currentPage", "pipeline");
|
||||
|
||||
List<String> pipelineConfigs = new ArrayList<>();
|
||||
try (Stream<Path> paths = Files.walk(Paths.get("./pipeline/defaultWebUIConfigs/"))) {
|
||||
List<Path> jsonFiles = paths
|
||||
.filter(Files::isRegularFile)
|
||||
.filter(p -> p.toString().endsWith(".json"))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
for (Path jsonFile : jsonFiles) {
|
||||
String content = Files.readString(jsonFile, StandardCharsets.UTF_8);
|
||||
pipelineConfigs.add(content);
|
||||
}
|
||||
List<Map<String, String>> pipelineConfigsWithNames = new ArrayList<>();
|
||||
for (String config : pipelineConfigs) {
|
||||
Map<String, Object> jsonContent = new ObjectMapper().readValue(config, new TypeReference<Map<String, Object>>(){});
|
||||
|
||||
String name = (String) jsonContent.get("name");
|
||||
Map<String, String> configWithName = new HashMap<>();
|
||||
configWithName.put("json", config);
|
||||
configWithName.put("name", name);
|
||||
List<Map<String, String>> pipelineConfigsWithNames = new ArrayList<>();
|
||||
|
||||
if(new File("./pipeline/defaultWebUIConfigs/").exists()) {
|
||||
try (Stream<Path> paths = Files.walk(Paths.get("./pipeline/defaultWebUIConfigs/"))) {
|
||||
List<Path> jsonFiles = paths
|
||||
.filter(Files::isRegularFile)
|
||||
.filter(p -> p.toString().endsWith(".json"))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
for (Path jsonFile : jsonFiles) {
|
||||
String content = Files.readString(jsonFile, StandardCharsets.UTF_8);
|
||||
pipelineConfigs.add(content);
|
||||
}
|
||||
|
||||
for (String config : pipelineConfigs) {
|
||||
Map<String, Object> jsonContent = new ObjectMapper().readValue(config, new TypeReference<Map<String, Object>>(){});
|
||||
|
||||
String name = (String) jsonContent.get("name");
|
||||
Map<String, String> configWithName = new HashMap<>();
|
||||
configWithName.put("json", config);
|
||||
configWithName.put("name", name);
|
||||
pipelineConfigsWithNames.add(configWithName);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
if(pipelineConfigsWithNames.size() == 0) {
|
||||
Map<String, String> configWithName = new HashMap<>();
|
||||
configWithName.put("json", "");
|
||||
configWithName.put("name", "No preloaded configs found");
|
||||
pipelineConfigsWithNames.add(configWithName);
|
||||
}
|
||||
model.addAttribute("pipelineConfigsWithNames", pipelineConfigsWithNames);
|
||||
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
model.addAttribute("pipelineConfigsWithNames", pipelineConfigsWithNames);
|
||||
|
||||
model.addAttribute("pipelineConfigs", pipelineConfigs);
|
||||
|
||||
|
||||
@@ -27,8 +27,8 @@ import stirling.software.SPDF.config.StartupApplicationListener;
|
||||
import stirling.software.SPDF.model.ApplicationProperties;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1")
|
||||
@Tag(name = "API", description = "Info APIs")
|
||||
@RequestMapping("/api/v1/info")
|
||||
@Tag(name = "Info", description = "Info APIs")
|
||||
public class MetricsController {
|
||||
|
||||
|
||||
|
||||
@@ -126,7 +126,13 @@ public class OtherWebController {
|
||||
model.addAttribute("currentPage", "remove-blanks");
|
||||
return "misc/remove-blanks";
|
||||
}
|
||||
|
||||
|
||||
@GetMapping("/remove-annotations")
|
||||
@Hidden
|
||||
public String removeAnnotationsForm(Model model) {
|
||||
model.addAttribute("currentPage", "remove-annotations");
|
||||
return "misc/remove-annotations";
|
||||
}
|
||||
|
||||
@GetMapping("/auto-crop")
|
||||
@Hidden
|
||||
|
||||
42
src/main/java/stirling/software/SPDF/model/ApiEndpoint.java
Normal file
42
src/main/java/stirling/software/SPDF/model/ApiEndpoint.java
Normal file
@@ -0,0 +1,42 @@
|
||||
package stirling.software.SPDF.model;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
|
||||
public class ApiEndpoint {
|
||||
private String name;
|
||||
private Map<String, JsonNode> parameters;
|
||||
private String description;
|
||||
|
||||
public ApiEndpoint(String name, JsonNode postNode) {
|
||||
this.name = name;
|
||||
this.parameters = new HashMap<>();
|
||||
postNode.path("parameters").forEach(paramNode -> {
|
||||
String paramName = paramNode.path("name").asText();
|
||||
parameters.put(paramName, paramNode);
|
||||
});
|
||||
this.description = postNode.path("description").asText();
|
||||
}
|
||||
|
||||
public boolean areParametersValid(Map<String, Object> providedParams) {
|
||||
for (String requiredParam : parameters.keySet()) {
|
||||
if (!providedParams.containsKey(requiredParam)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ApiEndpoint [name=" + name + ", parameters=" + parameters + "]";
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -106,6 +106,25 @@ public class ApplicationProperties {
|
||||
private Boolean enableLogin;
|
||||
private Boolean csrfDisabled;
|
||||
private InitialLogin initialLogin;
|
||||
private int loginAttemptCount;
|
||||
private long loginResetTimeMinutes;
|
||||
|
||||
|
||||
public int getLoginAttemptCount() {
|
||||
return loginAttemptCount;
|
||||
}
|
||||
|
||||
public void setLoginAttemptCount(int loginAttemptCount) {
|
||||
this.loginAttemptCount = loginAttemptCount;
|
||||
}
|
||||
|
||||
public long getLoginResetTimeMinutes() {
|
||||
return loginResetTimeMinutes;
|
||||
}
|
||||
|
||||
public void setLoginResetTimeMinutes(long loginResetTimeMinutes) {
|
||||
this.loginResetTimeMinutes = loginResetTimeMinutes;
|
||||
}
|
||||
|
||||
public InitialLogin getInitialLogin() {
|
||||
return initialLogin != null ? initialLogin : new InitialLogin();
|
||||
@@ -175,6 +194,19 @@ public class ApplicationProperties {
|
||||
private String rootURIPath;
|
||||
private String customStaticFilePath;
|
||||
private Integer maxFileSize;
|
||||
|
||||
private Boolean enableAlphaFunctionality;
|
||||
|
||||
|
||||
|
||||
|
||||
public Boolean getEnableAlphaFunctionality() {
|
||||
return enableAlphaFunctionality;
|
||||
}
|
||||
|
||||
public void setEnableAlphaFunctionality(Boolean enableAlphaFunctionality) {
|
||||
this.enableAlphaFunctionality = enableAlphaFunctionality;
|
||||
}
|
||||
|
||||
public String getDefaultLocale() {
|
||||
return defaultLocale;
|
||||
@@ -218,12 +250,13 @@ public class ApplicationProperties {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "System [defaultLocale=" + defaultLocale + ", googlevisibility=" + googlevisibility + ", rootURIPath="
|
||||
+ rootURIPath + ", customStaticFilePath=" + customStaticFilePath + ", maxFileSize=" + maxFileSize
|
||||
+ "]";
|
||||
return "System [defaultLocale=" + defaultLocale + ", googlevisibility=" + googlevisibility
|
||||
+ ", rootURIPath=" + rootURIPath + ", customStaticFilePath=" + customStaticFilePath
|
||||
+ ", maxFileSize=" + maxFileSize + ", enableAlphaFunctionality=" + enableAlphaFunctionality + "]";
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
public static class Ui {
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package stirling.software.SPDF.model;
|
||||
public class AttemptCounter {
|
||||
private int attemptCount;
|
||||
private long lastAttemptTime;
|
||||
|
||||
public AttemptCounter() {
|
||||
this.attemptCount = 1;
|
||||
this.lastAttemptTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public void increment() {
|
||||
this.attemptCount++;
|
||||
this.lastAttemptTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public int getAttemptCount() {
|
||||
return attemptCount;
|
||||
}
|
||||
|
||||
public long getlastAttemptTime() {
|
||||
return lastAttemptTime;
|
||||
}
|
||||
|
||||
public boolean shouldReset(long ATTEMPT_INCREMENT_TIME) {
|
||||
return System.currentTimeMillis() - lastAttemptTime > ATTEMPT_INCREMENT_TIME;
|
||||
}
|
||||
}
|
||||
@@ -14,8 +14,13 @@ public enum Role {
|
||||
EXTRA_LIMITED_API_USER("ROLE_EXTRA_LIMITED_API_USER", 20, 20),
|
||||
|
||||
// 0 API calls per day and 20 web calls
|
||||
WEB_ONLY_USER("ROLE_WEB_ONLY_USER", 0, 20);
|
||||
WEB_ONLY_USER("ROLE_WEB_ONLY_USER", 0, 20),
|
||||
|
||||
|
||||
INTERNAL_API_USER("STIRLING-PDF-BACKEND-API-USER", Integer.MAX_VALUE, Integer.MAX_VALUE),
|
||||
|
||||
DEMO_USER("ROLE_DEMO_USER", 100, 100);
|
||||
|
||||
private final String roleId;
|
||||
private final int apiCallsPerDay;
|
||||
private final int webCallsPerDay;
|
||||
|
||||
@@ -13,8 +13,8 @@ import lombok.NoArgsConstructor;
|
||||
public class HandleDataRequest {
|
||||
|
||||
@Schema(description = "The input files")
|
||||
private MultipartFile[] fileInputs;
|
||||
private MultipartFile[] fileInput;
|
||||
|
||||
@Schema(description = "JSON String")
|
||||
private String jsonString;
|
||||
private String json;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import java.util.List;
|
||||
|
||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Hidden;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
@@ -18,7 +19,7 @@ public class PDFWithPageNums extends PDFFile {
|
||||
@Schema(description = "The pages to select, Supports ranges (e.g., '1,3,5-9'), or 'all' or functions in the format 'an+b' where 'a' is the multiplier of the page number 'n', and 'b' is a constant (e.g., '2n+1', '3n', '6n-5')\"")
|
||||
private String pageNumbers;
|
||||
|
||||
|
||||
@Hidden
|
||||
public List<Integer> getPageNumbersList(){
|
||||
int pageCount = 0;
|
||||
try {
|
||||
@@ -30,6 +31,8 @@ public class PDFWithPageNums extends PDFFile {
|
||||
return GeneralUtils.parsePageString(pageNumbers, pageCount);
|
||||
|
||||
}
|
||||
|
||||
@Hidden
|
||||
public List<Integer> getPageNumbersList(PDDocument doc){
|
||||
int pageCount = 0;
|
||||
pageCount = doc.getNumberOfPages();
|
||||
|
||||
@@ -4,19 +4,18 @@ import java.awt.Graphics;
|
||||
import java.awt.image.BufferedImage;
|
||||
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.nio.file.Files;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
import javax.imageio.IIOImage;
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.imageio.ImageReader;
|
||||
import javax.imageio.ImageWriteParam;
|
||||
import javax.imageio.ImageWriter;
|
||||
import javax.imageio.stream.ImageOutputStream;
|
||||
|
||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||
import org.apache.pdfbox.pdmodel.PDPage;
|
||||
@@ -199,36 +198,56 @@ public class PdfUtils {
|
||||
try (PDDocument document = PDDocument.load(new ByteArrayInputStream(inputStream))) {
|
||||
PDFRenderer pdfRenderer = new PDFRenderer(document);
|
||||
int pageCount = document.getNumberOfPages();
|
||||
List<BufferedImage> images = new ArrayList<>();
|
||||
|
||||
// Create images of all pages
|
||||
for (int i = 0; i < pageCount; i++) {
|
||||
images.add(pdfRenderer.renderImageWithDPI(i, DPI, 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);
|
||||
Graphics g = combined.getGraphics();
|
||||
for (int i = 0; i < images.size(); i++) {
|
||||
g.drawImage(images.get(i), 0, i * images.get(0).getHeight(), null);
|
||||
}
|
||||
images = Arrays.asList(combined);
|
||||
}
|
||||
|
||||
// Create a ByteArrayOutputStream to save the image(s) to
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
|
||||
if (singleImage) {
|
||||
// Write the image to the output stream
|
||||
ImageIO.write(images.get(0), imageType, baos);
|
||||
if (imageType.toLowerCase().equals("tiff") || imageType.toLowerCase().equals("tif")) {
|
||||
// Write the images to the output stream as a TIFF with multiple frames
|
||||
ImageWriter writer = ImageIO.getImageWritersByFormatName("tiff").next();
|
||||
ImageWriteParam param = writer.getDefaultWriteParam();
|
||||
param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
|
||||
param.setCompressionType("ZLib");
|
||||
param.setCompressionQuality(1.0f);
|
||||
|
||||
try (ImageOutputStream ios = ImageIO.createImageOutputStream(baos)) {
|
||||
writer.setOutput(ios);
|
||||
writer.prepareWriteSequence(null);
|
||||
|
||||
for (int i = 0; i < pageCount; ++i) {
|
||||
BufferedImage image = pdfRenderer.renderImageWithDPI(i, DPI, colorType);
|
||||
writer.writeToSequence(new IIOImage(image, null, null), param);
|
||||
}
|
||||
|
||||
writer.endWriteSequence();
|
||||
}
|
||||
|
||||
writer.dispose();
|
||||
} else {
|
||||
// Combine all images into a single big image
|
||||
BufferedImage image = pdfRenderer.renderImageWithDPI(0, DPI, colorType);
|
||||
BufferedImage combined = new BufferedImage(image.getWidth(), image.getHeight() * pageCount, BufferedImage.TYPE_INT_RGB);
|
||||
Graphics g = combined.getGraphics();
|
||||
|
||||
for (int i = 0; i < pageCount; ++i) {
|
||||
if (i != 0) {
|
||||
image = pdfRenderer.renderImageWithDPI(i, DPI, colorType);
|
||||
}
|
||||
g.drawImage(image, 0, i * image.getHeight(), null);
|
||||
}
|
||||
|
||||
// Write the image to the output stream
|
||||
ImageIO.write(combined, imageType, baos);
|
||||
}
|
||||
|
||||
// Log that the image was successfully written to the byte array
|
||||
logger.info("Image successfully written to byte array");
|
||||
} else {
|
||||
// Zip the images and return as byte array
|
||||
try (ZipOutputStream zos = new ZipOutputStream(baos)) {
|
||||
for (int i = 0; i < images.size(); i++) {
|
||||
BufferedImage image = images.get(i);
|
||||
for (int i = 0; i < pageCount; ++i) {
|
||||
BufferedImage image = pdfRenderer.renderImageWithDPI(i, DPI, colorType);
|
||||
try (ByteArrayOutputStream baosImage = new ByteArrayOutputStream()) {
|
||||
ImageIO.write(image, imageType, baosImage);
|
||||
|
||||
@@ -264,28 +283,13 @@ public class PdfUtils {
|
||||
addImageToDocument(doc, pdImage, fitOption, 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 = ImageProcessingUtils.convertColorType(image, colorType);
|
||||
PDImageXObject pdImage;
|
||||
if (contentType != null && (contentType.equals("image/jpeg"))) {
|
||||
pdImage = JPEGFactory.createFromImage(doc, convertedImage);
|
||||
} else {
|
||||
pdImage = LosslessFactory.createFromImage(doc, convertedImage);
|
||||
}
|
||||
addImageToDocument(doc, pdImage, fitOption, autoRotate);
|
||||
} catch (IOException e) {
|
||||
logger.error("Error writing image to file: {}", imageFile.getAbsolutePath(), e);
|
||||
throw e;
|
||||
} finally {
|
||||
imageFile.delete();
|
||||
}
|
||||
BufferedImage image = ImageIO.read(file.getInputStream());
|
||||
BufferedImage convertedImage = ImageProcessingUtils.convertColorType(image, colorType);
|
||||
// Use JPEGFactory if it's JPEG since JPEG is lossy
|
||||
PDImageXObject pdImage = (contentType != null && contentType.equals("image/jpeg"))
|
||||
? JPEGFactory.createFromImage(doc, convertedImage)
|
||||
: LosslessFactory.createFromImage(doc, convertedImage);
|
||||
addImageToDocument(doc, pdImage, fitOption, autoRotate);
|
||||
}
|
||||
}
|
||||
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
package stirling.software.SPDF.utils;
|
||||
|
||||
public class RequestUriUtils {
|
||||
|
||||
public static boolean isStaticResource(String requestURI) {
|
||||
|
||||
return requestURI.startsWith("/css/")
|
||||
|| requestURI.startsWith("/js/")
|
||||
|| requestURI.startsWith("/images/")
|
||||
|| requestURI.startsWith("/public/")
|
||||
|| requestURI.startsWith("/pdfjs/")
|
||||
|| requestURI.endsWith(".svg");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
6
src/main/resources/banner.txt
Normal file
6
src/main/resources/banner.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
____ _____ ___ ____ _ ___ _ _ ____ ____ ____ _____
|
||||
/ ___|_ _|_ _| _ \| | |_ _| \ | |/ ___| | _ \| _ \| ___|
|
||||
\___ \ | | | || |_) | | | || \| | | _ _____| |_) | | | | |_
|
||||
___) || | | || _ <| |___ | || |\ | |_| |_____| __/| |_| | _|
|
||||
|____/ |_| |___|_| \_\_____|___|_| \_|\____| |_| |____/|_|
|
||||
Powered by Spring Boot ${spring-boot.version}
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<!-- daily rollover and keep 7 days' worth of history -->
|
||||
<fileNamePattern>auth-%d{MM-dd-yyyy}.log</fileNamePattern>
|
||||
<fileNamePattern>logs/auth-%d{yyyy-MM-dd}.log</fileNamePattern>
|
||||
<maxHistory>1</maxHistory>
|
||||
</rollingPolicy>
|
||||
</appender>
|
||||
@@ -30,7 +30,7 @@
|
||||
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<!-- daily rollover and keep 7 days' worth of history -->
|
||||
<fileNamePattern>info-%d{MM-dd-yyyy}.log</fileNamePattern>
|
||||
<fileNamePattern>logs/info-%d{yyyy-MM-dd}.log</fileNamePattern>
|
||||
<maxHistory>1</maxHistory>
|
||||
</rollingPolicy>
|
||||
</appender>
|
||||
|
||||
@@ -119,6 +119,7 @@ adminUserSettings.role=Role
|
||||
adminUserSettings.actions=Actions
|
||||
adminUserSettings.apiUser=Limited API User
|
||||
adminUserSettings.webOnlyUser=Web Only User
|
||||
adminUserSettings.demoUser=Demo User (No custom settings)
|
||||
adminUserSettings.forceChange=Force user to change username/password on login
|
||||
adminUserSettings.submit=Save User
|
||||
|
||||
@@ -255,6 +256,10 @@ home.removeBlanks.title=إزالة الصفحات الفارغة
|
||||
home.removeBlanks.desc=يكتشف ويزيل الصفحات الفارغة من المستند
|
||||
removeBlanks.tags=cleanup,streamline,non-content,organize
|
||||
|
||||
home.removeAnnotations.title=Remove Annotations
|
||||
home.removeAnnotations.desc=Removes all comments/annotations from a PDF
|
||||
removeAnnotations.tags=comments,highlight,notes,markup,remove
|
||||
|
||||
home.compare.title=قارن
|
||||
home.compare.desc=يقارن ويظهر الاختلافات بين 2 من مستندات PDF
|
||||
compare.tags=differentiate,contrast,changes,analysis
|
||||
@@ -539,6 +544,12 @@ removeBlanks.whitePercentDesc=النسبة المئوية للصفحة التي
|
||||
removeBlanks.submit=إزالة الفراغات
|
||||
|
||||
|
||||
#removeAnnotations
|
||||
removeAnnotations.title=Remove Annotations
|
||||
removeAnnotations.header=Remove Annotations
|
||||
removeAnnotations.submit=Remove
|
||||
|
||||
|
||||
#compare
|
||||
compare.title=يقارن
|
||||
compare.header=قارن ملفات PDF
|
||||
|
||||
@@ -119,6 +119,7 @@ adminUserSettings.role=Роля
|
||||
adminUserSettings.actions=Действия
|
||||
adminUserSettings.apiUser=Ограничен API потребител
|
||||
adminUserSettings.webOnlyUser=Само за уеб-потребител
|
||||
adminUserSettings.demoUser=Demo User (No custom settings)
|
||||
adminUserSettings.forceChange=Принудете потребителя да промени потребителското име/парола при влизане
|
||||
adminUserSettings.submit=Съхранете потребителя
|
||||
|
||||
@@ -255,6 +256,10 @@ home.removeBlanks.title=Премахване на празни страници
|
||||
home.removeBlanks.desc=Открива и премахва празни страници от документ
|
||||
removeBlanks.tags=почистване,рационализиране,без съдържание,организиране
|
||||
|
||||
home.removeAnnotations.title=Remove Annotations
|
||||
home.removeAnnotations.desc=Removes all comments/annotations from a PDF
|
||||
removeAnnotations.tags=comments,highlight,notes,markup,remove
|
||||
|
||||
home.compare.title=Сравнете
|
||||
home.compare.desc=Сравнява и показва разликите между 2 PDF документа
|
||||
compare.tags=разграничаване,контраст,промени,анализ
|
||||
@@ -539,6 +544,12 @@ removeBlanks.whitePercentDesc=Процент от страницата, коят
|
||||
removeBlanks.submit=Премахване на празни места
|
||||
|
||||
|
||||
#removeAnnotations
|
||||
removeAnnotations.title=Remove Annotations
|
||||
removeAnnotations.header=Remove Annotations
|
||||
removeAnnotations.submit=Remove
|
||||
|
||||
|
||||
#compare
|
||||
compare.title=Сравнявай
|
||||
compare.header=Сравнявай PDF-и
|
||||
|
||||
@@ -119,6 +119,7 @@ adminUserSettings.role=Rol
|
||||
adminUserSettings.actions=Accions
|
||||
adminUserSettings.apiUser=Usuari amb API limitada
|
||||
adminUserSettings.webOnlyUser=Usuari només WEB
|
||||
adminUserSettings.demoUser=Demo User (No custom settings)
|
||||
adminUserSettings.forceChange=Force user to change username/password on login
|
||||
adminUserSettings.submit=Desar Usuari
|
||||
|
||||
@@ -255,6 +256,10 @@ home.removeBlanks.title=Elimina les pàgines en blanc
|
||||
home.removeBlanks.desc=Detecta i elimina les pàgines en blanc d'un document
|
||||
removeBlanks.tags=cleanup,streamline,non-content,organize
|
||||
|
||||
home.removeAnnotations.title=Remove Annotations
|
||||
home.removeAnnotations.desc=Removes all comments/annotations from a PDF
|
||||
removeAnnotations.tags=comments,highlight,notes,markup,remove
|
||||
|
||||
home.compare.title=Compara
|
||||
home.compare.desc=Compara i mostra les diferències entre 2 documents PDF
|
||||
compare.tags=differentiate,contrast,changes,analysis
|
||||
@@ -539,6 +544,12 @@ removeBlanks.whitePercentDesc=Percentatge de pàgina que ha de ser blanca per el
|
||||
removeBlanks.submit=Elimina els espais en blanc
|
||||
|
||||
|
||||
#removeAnnotations
|
||||
removeAnnotations.title=Remove Annotations
|
||||
removeAnnotations.header=Remove Annotations
|
||||
removeAnnotations.submit=Remove
|
||||
|
||||
|
||||
#compare
|
||||
compare.title=Comparar
|
||||
compare.header=Compara PDF
|
||||
|
||||
@@ -119,6 +119,7 @@ adminUserSettings.role=Rolle
|
||||
adminUserSettings.actions=Aktion
|
||||
adminUserSettings.apiUser=Eingeschränkter API-Benutzer
|
||||
adminUserSettings.webOnlyUser=Nur Web-Benutzer
|
||||
adminUserSettings.demoUser=Demo User (No custom settings)
|
||||
adminUserSettings.forceChange=Benutzer dazu zwingen, Benutzernamen/Passwort bei der Anmeldung zu ändern
|
||||
adminUserSettings.submit=Benutzer speichern
|
||||
|
||||
@@ -255,6 +256,10 @@ home.removeBlanks.title=Leere Seiten entfernen
|
||||
home.removeBlanks.desc=Erkennt und entfernt leere Seiten aus einem Dokument
|
||||
removeBlanks.tags=cleanup,streamline,non-content,organize
|
||||
|
||||
home.removeAnnotations.title=Remove Annotations
|
||||
home.removeAnnotations.desc=Removes all comments/annotations from a PDF
|
||||
removeAnnotations.tags=comments,highlight,notes,markup,remove
|
||||
|
||||
home.compare.title=Vergleichen
|
||||
home.compare.desc=Vergleicht und zeigt die Unterschiede zwischen zwei PDF-Dokumenten an
|
||||
compare.tags=differentiate,contrast,changes,analysis
|
||||
@@ -539,6 +544,12 @@ removeBlanks.whitePercentDesc=Prozentsatz der Seite, die weiß sein muss, um ent
|
||||
removeBlanks.submit=Leere Seiten entfernen
|
||||
|
||||
|
||||
#removeAnnotations
|
||||
removeAnnotations.title=Remove Annotations
|
||||
removeAnnotations.header=Remove Annotations
|
||||
removeAnnotations.submit=Remove
|
||||
|
||||
|
||||
#compare
|
||||
compare.title=Vergleichen
|
||||
compare.header=PDFs vergleichen
|
||||
|
||||
@@ -119,6 +119,7 @@ adminUserSettings.role=\u03A1\u03CC\u03BB\u03BF\u03C2
|
||||
adminUserSettings.actions=\u0395\u03BD\u03AD\u03C1\u03B3\u03B5\u03B9\u03B5\u03C2
|
||||
adminUserSettings.apiUser=\u03A0\u03B5\u03C1\u03B9\u03BF\u03C1\u03B9\u03C3\u03BC\u03AD\u03BD\u03BF\u03C2 \u03A7\u03C1\u03AE\u03C3\u03C4\u03B7\u03C2 \u03B3\u03B9\u03B1 \u03B4\u03B9\u03B5\u03C0\u03B1\u03C6\u03AE \u03C0\u03C1\u03BF\u03B3\u03C1\u03B1\u03BC\u03BC\u03B1\u03C4\u03B9\u03C3\u03BC\u03BF\u03CD \u03B5\u03C6\u03B1\u03C1\u03BC\u03BF\u03B3\u03CE\u03BD (API User)
|
||||
adminUserSettings.webOnlyUser=\u03A7\u03C1\u03AE\u03C3\u03C4\u03B7\u03C2 \u03BC\u03CC\u03BD\u03BF \u0399\u03C3\u03C4\u03BF\u03CD
|
||||
adminUserSettings.demoUser=Demo User (No custom settings)
|
||||
adminUserSettings.forceChange=\u0391\u03BD\u03B1\u03B3\u03BA\u03AC\u03C3\u03C4\u03B5 \u03C4\u03BF\u03BD \u03C7\u03C1\u03AE\u03C3\u03C4\u03B7 \u03BD\u03B1 \u03B1\u03BB\u03BB\u03AC\u03BE\u03B5\u03B9 \u03C4\u03BF \u03CC\u03BD\u03BF\u03BC\u03B1 \u03C7\u03C1\u03AE\u03C3\u03C4\u03B7/\u03BA\u03C9\u03B4\u03B9\u03BA\u03CC \u03C0\u03C1\u03CC\u03C3\u03B2\u03B1\u03C3\u03B7\u03C2 \u03BA\u03B1\u03C4\u03AC \u03C4\u03B7 \u03C3\u03CD\u03BD\u03B4\u03B5\u03C3\u03B7
|
||||
adminUserSettings.submit=\u0391\u03C0\u03BF\u03B8\u03AE\u03BA\u03B5\u03C5\u03C3\u03B7 \u03A7\u03C1\u03AE\u03C3\u03C4\u03B7
|
||||
|
||||
@@ -255,6 +256,10 @@ home.removeBlanks.title=\u0391\u03C6\u03B1\u03AF\u03C1\u03B5\u03C3\u03B7 \u03BA\
|
||||
home.removeBlanks.desc=\u0391\u03BD\u03AF\u03C7\u03B5\u03C5\u03C3\u03B7 \u03BA\u03B1\u03B9 \u03B1\u03C6\u03B1\u03AF\u03C1\u03B5\u03C3\u03B7 \u03BA\u03B5\u03BD\u03CE\u03BD \u03C3\u03B5\u03BB\u03AF\u03B4\u03C9\u03BD \u03B1\u03C0\u03CC \u03AD\u03BD\u03B1 \u03AD\u03B3\u03B3\u03C1\u03B1\u03C6\u03BF
|
||||
removeBlanks.tags=cleanup,streamline,non-content,organize
|
||||
|
||||
home.removeAnnotations.title=Remove Annotations
|
||||
home.removeAnnotations.desc=Removes all comments/annotations from a PDF
|
||||
removeAnnotations.tags=comments,highlight,notes,markup,remove
|
||||
|
||||
home.compare.title=\u03A3\u03CD\u03B3\u03BA\u03C1\u03B9\u03C3\u03B7
|
||||
home.compare.desc=\u03A3\u03CD\u03B3\u03BA\u03C1\u03B9\u03C3\u03B7 \u03BA\u03B1\u03B9 \u03B5\u03BC\u03C6\u03AC\u03BD\u03B9\u03C3\u03B7 \u03C4\u03C9\u03BD \u03B4\u03B9\u03B1\u03C6\u03BF\u03C1\u03CE\u03BD \u03BC\u03B5\u03C4\u03B1\u03BE\u03CD \u03B4\u03CD\u03BF PDF \u03B1\u03C1\u03C7\u03B5\u03AF\u03C9\u03BD
|
||||
compare.tags=differentiate,contrast,changes,analysis
|
||||
@@ -539,6 +544,12 @@ removeBlanks.whitePercentDesc=\u03A4\u03BF \u03C0\u03BF\u03C3\u03BF\u03C3\u03C4\
|
||||
removeBlanks.submit=\u0391\u03C6\u03B1\u03AF\u03C1\u03B5\u03C3\u03B7 \u039A\u03B5\u03BD\u03CE\u03BD
|
||||
|
||||
|
||||
#removeAnnotations
|
||||
removeAnnotations.title=Remove Annotations
|
||||
removeAnnotations.header=Remove Annotations
|
||||
removeAnnotations.submit=Remove
|
||||
|
||||
|
||||
#compare
|
||||
compare.title=\u03A3\u03CD\u03B3\u03BA\u03C1\u03B9\u03C3\u03B7
|
||||
compare.header=\u03A3\u03CD\u03B3\u03BA\u03C1\u03B9\u03C3\u03B7 PDFs
|
||||
|
||||
@@ -119,6 +119,7 @@ adminUserSettings.role=Role
|
||||
adminUserSettings.actions=Actions
|
||||
adminUserSettings.apiUser=Limited API User
|
||||
adminUserSettings.webOnlyUser=Web Only User
|
||||
adminUserSettings.demoUser=Demo User (No custom settings)
|
||||
adminUserSettings.forceChange = Force user to change username/password on login
|
||||
adminUserSettings.submit=Save User
|
||||
|
||||
@@ -255,6 +256,10 @@ home.removeBlanks.title=Remove Blank pages
|
||||
home.removeBlanks.desc=Detects and removes blank pages from a document
|
||||
removeBlanks.tags=cleanup,streamline,non-content,organize
|
||||
|
||||
home.removeAnnotations.title=Remove Annotations
|
||||
home.removeAnnotations.desc=Removes all comments/annotations from a PDF
|
||||
removeAnnotations.tags=comments,highlight,notes,markup,remove
|
||||
|
||||
home.compare.title=Compare
|
||||
home.compare.desc=Compares and shows the differences between 2 PDF Documents
|
||||
compare.tags=differentiate,contrast,changes,analysis
|
||||
@@ -539,6 +544,12 @@ removeBlanks.whitePercentDesc=Percent of page that must be 'white' pixels to be
|
||||
removeBlanks.submit=Remove Blanks
|
||||
|
||||
|
||||
#removeAnnotations
|
||||
removeAnnotations.title=Remove Annotations
|
||||
removeAnnotations.header=Remove Annotations
|
||||
removeAnnotations.submit=Remove
|
||||
|
||||
|
||||
#compare
|
||||
compare.title=Compare
|
||||
compare.header=Compare PDFs
|
||||
|
||||
@@ -119,6 +119,7 @@ adminUserSettings.role=Role
|
||||
adminUserSettings.actions=Actions
|
||||
adminUserSettings.apiUser=Limited API User
|
||||
adminUserSettings.webOnlyUser=Web Only User
|
||||
adminUserSettings.demoUser=Demo User (No custom settings)
|
||||
adminUserSettings.forceChange=Force user to change username/password on login
|
||||
adminUserSettings.submit=Save User
|
||||
|
||||
@@ -255,6 +256,10 @@ home.removeBlanks.title=Remove Blank pages
|
||||
home.removeBlanks.desc=Detects and removes blank pages from a document
|
||||
removeBlanks.tags=cleanup,streamline,non-content,organize
|
||||
|
||||
home.removeAnnotations.title=Remove Annotations
|
||||
home.removeAnnotations.desc=Removes all comments/annotations from a PDF
|
||||
removeAnnotations.tags=comments,highlight,notes,markup,remove
|
||||
|
||||
home.compare.title=Compare
|
||||
home.compare.desc=Compares and shows the differences between 2 PDF Documents
|
||||
compare.tags=differentiate,contrast,changes,analysis
|
||||
@@ -539,6 +544,12 @@ removeBlanks.whitePercentDesc=Percent of page that must be 'white' pixels to be
|
||||
removeBlanks.submit=Remove Blanks
|
||||
|
||||
|
||||
#removeAnnotations
|
||||
removeAnnotations.title=Remove Annotations
|
||||
removeAnnotations.header=Remove Annotations
|
||||
removeAnnotations.submit=Remove
|
||||
|
||||
|
||||
#compare
|
||||
compare.title=Compare
|
||||
compare.header=Compare PDFs
|
||||
|
||||
@@ -119,6 +119,7 @@ adminUserSettings.role=Rol
|
||||
adminUserSettings.actions=Acciones
|
||||
adminUserSettings.apiUser=Usuario limitado de API
|
||||
adminUserSettings.webOnlyUser=Usuario solo web
|
||||
adminUserSettings.demoUser=Demo User (No custom settings)
|
||||
adminUserSettings.forceChange=Forzar usuario a cambiar usuario/contraseña en el acceso
|
||||
adminUserSettings.submit=Guardar Usuario
|
||||
|
||||
@@ -212,7 +213,7 @@ extractImages.tags=imagen,fotografía,guardar,archivo,zip,capturar,coger
|
||||
|
||||
home.pdfToPDFA.title=Convertir PDF a PDF/A
|
||||
home.pdfToPDFA.desc=Convertir PDF a PDF/A para almacenamiento a largo plazo
|
||||
pdfToPDFA.tags=archivo,largo plazo,estándar,conversión,almacewnamiento,conservación
|
||||
pdfToPDFA.tags=archivo,largo plazo,estándar,conversión,almacenamiento,conservación
|
||||
|
||||
home.PDFToWord.title=PDF a Word
|
||||
home.PDFToWord.desc=Convertir formatos PDF a Word (DOC, DOCX y ODT)
|
||||
@@ -255,6 +256,10 @@ home.removeBlanks.title=Eliminar páginas en blanco
|
||||
home.removeBlanks.desc=Detectar y eliminar páginas en blanco de un documento
|
||||
removeBlanks.tags=limpieza,dinámica,sin contenido,organizar
|
||||
|
||||
home.removeAnnotations.title=Remove Annotations
|
||||
home.removeAnnotations.desc=Removes all comments/annotations from a PDF
|
||||
removeAnnotations.tags=comments,highlight,notes,markup,remove
|
||||
|
||||
home.compare.title=Comparar
|
||||
home.compare.desc=Comparar y mostrar las diferencias entre 2 documentos PDF
|
||||
compare.tags=diferenciar,contrastar,cambios,análisis
|
||||
@@ -341,18 +346,18 @@ home.tableExtraxt.desc=Extraer Tablas de un PDF convirtiéndolas a CSV
|
||||
tableExtraxt.tags=CSV,Extraer tabla,extraer,convertir
|
||||
|
||||
|
||||
home.autoSizeSplitPDF.title=Auto Split by Size/Count
|
||||
home.autoSizeSplitPDF.desc=Split a single PDF into multiple documents based on size, page count, or document count
|
||||
autoSizeSplitPDF.tags=pdf,split,document,organization
|
||||
home.autoSizeSplitPDF.title=Auto dividir por tamaño/conteo
|
||||
home.autoSizeSplitPDF.desc=Divide un solo PDF en múltiples documentos según su tamaño, número de páginas, o número de documento
|
||||
autoSizeSplitPDF.tags=pdf,dividir,documento,organización
|
||||
|
||||
|
||||
home.overlay-pdfs.title=Overlay PDFs
|
||||
home.overlay-pdfs.desc=Overlays PDFs on-top of another PDF
|
||||
overlay-pdfs.tags=Overlay
|
||||
home.overlay-pdfs.title=Superponer PDFs
|
||||
home.overlay-pdfs.desc=Superponer PDFs encima de otro PDF
|
||||
overlay-pdfs.tags=Superponer
|
||||
|
||||
home.split-by-sections.title=Split PDF by Sections
|
||||
home.split-by-sections.desc=Divide each page of a PDF into smaller horizontal and vertical sections
|
||||
split-by-sections.tags=Section Split, Divide, Customize
|
||||
home.split-by-sections.title=Dividir PDF por Seccioned
|
||||
home.split-by-sections.desc=Dividir cada página de un PDF en secciones verticales y horizontales más pequeñas
|
||||
split-by-sections.tags=Dividir sección, Dividir, Personalizar
|
||||
|
||||
###########################
|
||||
# #
|
||||
@@ -539,6 +544,12 @@ removeBlanks.whitePercentDesc=Porcentaje de página que debe ser blanca para ser
|
||||
removeBlanks.submit=Eliminar espacios en blanco
|
||||
|
||||
|
||||
#removeAnnotations
|
||||
removeAnnotations.title=Remove Annotations
|
||||
removeAnnotations.header=Remove Annotations
|
||||
removeAnnotations.submit=Remove
|
||||
|
||||
|
||||
#compare
|
||||
compare.title=Comparar
|
||||
compare.header=Comparar archivos PDF
|
||||
@@ -849,37 +860,37 @@ PDFToCSV.prompt=Elija una página para extraer la tabla
|
||||
PDFToCSV.submit=Extraer
|
||||
|
||||
#split-by-size-or-count
|
||||
split-by-size-or-count.header=Split PDF by Size or Count
|
||||
split-by-size-or-count.type.label=Select Split Type
|
||||
split-by-size-or-count.type.size=By Size
|
||||
split-by-size-or-count.type.pageCount=By Page Count
|
||||
split-by-size-or-count.type.docCount=By Document Count
|
||||
split-by-size-or-count.value.label=Enter Value
|
||||
split-by-size-or-count.value.placeholder=Enter size (e.g., 2MB or 3KB) or count (e.g., 5)
|
||||
split-by-size-or-count.submit=Submit
|
||||
split-by-size-or-count.header=Dividir PDF por tamaño o número
|
||||
split-by-size-or-count.type.label=Seleccionar tipo de división
|
||||
split-by-size-or-count.type.size=Por tamaño
|
||||
split-by-size-or-count.type.pageCount=Por número de páginas
|
||||
split-by-size-or-count.type.docCount=por recuento de documentos
|
||||
split-by-size-or-count.value.label=Introduzca valor
|
||||
split-by-size-or-count.value.placeholder=Introduzca tamaño (p.ej., 2MB o 3KB) or recuento (p.ej., 5)
|
||||
split-by-size-or-count.submit=Enviar
|
||||
|
||||
|
||||
#overlay-pdfs
|
||||
overlay-pdfs.header=Overlay PDF Files
|
||||
overlay-pdfs.baseFile.label=Select Base PDF File
|
||||
overlay-pdfs.overlayFiles.label=Select Overlay PDF Files
|
||||
overlay-pdfs.mode.label=Select Overlay Mode
|
||||
overlay-pdfs.mode.sequential=Sequential Overlay
|
||||
overlay-pdfs.mode.interleaved=Interleaved Overlay
|
||||
overlay-pdfs.mode.fixedRepeat=Fixed Repeat Overlay
|
||||
overlay-pdfs.counts.label=Overlay Counts (for Fixed Repeat Mode)
|
||||
overlay-pdfs.counts.placeholder=Enter comma-separated counts (e.g., 2,3,1)
|
||||
overlay-pdfs.position.label=Select Overlay Position
|
||||
overlay-pdfs.position.foreground=Foreground
|
||||
overlay-pdfs.position.background=Background
|
||||
overlay-pdfs.submit=Submit
|
||||
overlay-pdfs.header=Superponer archivos PDF
|
||||
overlay-pdfs.baseFile.label=Selleccione archivo PDF de base
|
||||
overlay-pdfs.overlayFiles.label=Seleccione archivos PDF a superponer
|
||||
overlay-pdfs.mode.label=Seleccione modo de superposición
|
||||
overlay-pdfs.mode.sequential=Superposición Sequencial
|
||||
overlay-pdfs.mode.interleaved=Superposición Intercalada
|
||||
overlay-pdfs.mode.fixedRepeat=Superposición de repetición fija
|
||||
overlay-pdfs.counts.label=Recuento de superposición (para Modo de Repetición Fija)
|
||||
overlay-pdfs.counts.placeholder=Introduzca recuento separado por comas (p.ej., 2,3,1)
|
||||
overlay-pdfs.position.label=Seleccione Posición de Superposición
|
||||
overlay-pdfs.position.foreground=Arriba
|
||||
overlay-pdfs.position.background=Fondo
|
||||
overlay-pdfs.submit=Enviar
|
||||
|
||||
|
||||
#split-by-sections
|
||||
split-by-sections.title=Split PDF by Sections
|
||||
split-by-sections.header=Split PDF into Sections
|
||||
split-by-sections.horizontal.label=Horizontal Divisions
|
||||
split-by-sections.vertical.label=Vertical Divisions
|
||||
split-by-sections.horizontal.placeholder=Enter number of horizontal divisions
|
||||
split-by-sections.vertical.placeholder=Enter number of vertical divisions
|
||||
split-by-sections.submit=Split PDF
|
||||
split-by-sections.title=Dividir PDF por Secciones
|
||||
split-by-sections.header=Dividir PDF por Secciones
|
||||
split-by-sections.horizontal.label=Divisiones Horizontales
|
||||
split-by-sections.vertical.label=Divisiones Verticales
|
||||
split-by-sections.horizontal.placeholder=Introduzca el número de divisiones horizontales
|
||||
split-by-sections.vertical.placeholder=Introduzca el número de divisiones verticales
|
||||
split-by-sections.submit=Dividir PDF
|
||||
|
||||
@@ -119,6 +119,7 @@ adminUserSettings.role=Rol
|
||||
adminUserSettings.actions=Ekintzak
|
||||
adminUserSettings.apiUser=APIren erabiltzaile mugatua
|
||||
adminUserSettings.webOnlyUser=Web-erabiltzailea bakarrik
|
||||
adminUserSettings.demoUser=Demo User (No custom settings)
|
||||
adminUserSettings.forceChange=Force user to change username/password on login
|
||||
adminUserSettings.submit=Gorde Erabiltzailea
|
||||
|
||||
@@ -255,6 +256,10 @@ home.removeBlanks.title=Ezabatu orrialde zuriak
|
||||
home.removeBlanks.desc=Detektatu orrialde zuriak eta dokumentutik ezabatu
|
||||
removeBlanks.tags=cleanup,streamline,non-content,organize
|
||||
|
||||
home.removeAnnotations.title=Remove Annotations
|
||||
home.removeAnnotations.desc=Removes all comments/annotations from a PDF
|
||||
removeAnnotations.tags=comments,highlight,notes,markup,remove
|
||||
|
||||
home.compare.title=Konparatu
|
||||
home.compare.desc=Konparatu eta erakutsi 2 PDF dokumenturen aldeak
|
||||
compare.tags=differentiate,contrast,changes,analysis
|
||||
@@ -539,6 +544,12 @@ removeBlanks.whitePercentDesc=Zuria izan behar den orriaren ehunekoa ezabatua iz
|
||||
removeBlanks.submit=Ezabatu zuriuneak
|
||||
|
||||
|
||||
#removeAnnotations
|
||||
removeAnnotations.title=Remove Annotations
|
||||
removeAnnotations.header=Remove Annotations
|
||||
removeAnnotations.submit=Remove
|
||||
|
||||
|
||||
#compare
|
||||
compare.title=Konparatu
|
||||
compare.header=Konparatu PDF fitxategiak
|
||||
|
||||
@@ -119,6 +119,7 @@ adminUserSettings.role=Rôle
|
||||
adminUserSettings.actions=Actions
|
||||
adminUserSettings.apiUser=Utilisateur API limité
|
||||
adminUserSettings.webOnlyUser=Utilisateur Web uniquement
|
||||
adminUserSettings.demoUser=Demo User (No custom settings)
|
||||
adminUserSettings.forceChange=Forcer l\u2019utilisateur à changer son nom d\u2019utilisateur/mot de passe lors de la connexion
|
||||
adminUserSettings.submit=Ajouter
|
||||
|
||||
@@ -126,7 +127,7 @@ adminUserSettings.submit=Ajouter
|
||||
# HOME-PAGE #
|
||||
#############
|
||||
home.desc=Votre application Web hébergée localement pour répondre à tous vos besoins PDF.
|
||||
home.searchBar=Search for features...
|
||||
home.searchBar=Rechercher des fonctionnalités...
|
||||
|
||||
|
||||
home.viewPdf.title=Visionner le PDF
|
||||
@@ -255,6 +256,10 @@ home.removeBlanks.title=Supprimer les pages vierges
|
||||
home.removeBlanks.desc=Détectez et supprimez les pages vierges d\u2019un PDF.
|
||||
removeBlanks.tags=pages vierges,supprimer,nettoyer,cleanup,streamline,non-content,organize
|
||||
|
||||
home.removeAnnotations.title=Remove Annotations
|
||||
home.removeAnnotations.desc=Removes all comments/annotations from a PDF
|
||||
removeAnnotations.tags=comments,highlight,notes,markup,remove
|
||||
|
||||
home.compare.title=Comparer
|
||||
home.compare.desc=Comparez et visualisez les différences entre deux PDF.
|
||||
compare.tags=comparer,analyser,differentiate,contrast,changes,analysis
|
||||
@@ -336,18 +341,18 @@ home.autoRedact.title=Caviarder automatiquement
|
||||
home.autoRedact.desc=Caviardez automatiquement les informations sensibles d\u2019un PDF.
|
||||
showJS.tags=caviarder,redact,auto
|
||||
|
||||
home.tableExtraxt.title=PDF to CSV
|
||||
home.tableExtraxt.desc=Extracts Tables from a PDF converting it to CSV
|
||||
home.tableExtraxt.title=PDF en CSV
|
||||
home.tableExtraxt.desc=Extrait les tableaux d\u2019un PDF et les transforme en CSV
|
||||
tableExtraxt.tags=CSV,Table Extraction,extract,convert
|
||||
|
||||
|
||||
home.autoSizeSplitPDF.title=Auto Split by Size/Count
|
||||
home.autoSizeSplitPDF.desc=Split a single PDF into multiple documents based on size, page count, or document count
|
||||
home.autoSizeSplitPDF.title=Séparer automatiquement par taille/nombre
|
||||
home.autoSizeSplitPDF.desc=Séparer un PDF unique en plusieurs documents en fonction de la taille, du nombre de pages ou du nombre de documents.
|
||||
autoSizeSplitPDF.tags=pdf,split,document,organization
|
||||
|
||||
|
||||
home.overlay-pdfs.title=Overlay PDFs
|
||||
home.overlay-pdfs.desc=Overlays PDFs on-top of another PDF
|
||||
home.overlay-pdfs.title=Incrustation de PDF
|
||||
home.overlay-pdfs.desc=Incrustation d\u2019un PDF sur un autre PDF
|
||||
overlay-pdfs.tags=Overlay
|
||||
|
||||
home.split-by-sections.title=Split PDF by Sections
|
||||
@@ -453,7 +458,7 @@ addPageNumbers.selectText.5=Pages à numéroter
|
||||
addPageNumbers.selectText.6=Texte personnalisé
|
||||
addPageNumbers.customTextDesc=Texte personnalisé
|
||||
addPageNumbers.numberPagesDesc=Quelles pages numéroter, par défaut 'all' (toutes les pages), accepte également 1-5 ou 2,5,9, etc.
|
||||
addPageNumbers.customNumberDesc=La valeur par défaut est '{n}', accepte également 'Page {n} sur {total}', 'Texte-{n}', '{filename}-{n}
|
||||
addPageNumbers.customNumberDesc=La valeur par défaut est '{n}', accepte également 'Page {n} sur {total}', 'Texte-{n}', '{filename}-{n}'
|
||||
addPageNumbers.submit=Ajouter les numéros de page
|
||||
|
||||
|
||||
@@ -539,6 +544,12 @@ removeBlanks.whitePercentDesc=Pourcentage de la page qui doit contenir des pixel
|
||||
removeBlanks.submit=Supprimer les pages vierges
|
||||
|
||||
|
||||
#removeAnnotations
|
||||
removeAnnotations.title=Remove Annotations
|
||||
removeAnnotations.header=Remove Annotations
|
||||
removeAnnotations.submit=Remove
|
||||
|
||||
|
||||
#compare
|
||||
compare.title=Comparer
|
||||
compare.header=Comparer
|
||||
@@ -845,41 +856,41 @@ PDFToXML.submit=Convertir
|
||||
#PDFToCSV
|
||||
PDFToCSV.title=PDF en CSV
|
||||
PDFToCSV.header=PDF en CSV
|
||||
PDFToCSV.prompt=Choose page to extract table
|
||||
PDFToCSV.prompt=Choisir la page pour en extraire le tableau
|
||||
PDFToCSV.submit=Extrait
|
||||
|
||||
#split-by-size-or-count
|
||||
split-by-size-or-count.header=Split PDF by Size or Count
|
||||
split-by-size-or-count.type.label=Select Split Type
|
||||
split-by-size-or-count.type.size=By Size
|
||||
split-by-size-or-count.type.pageCount=By Page Count
|
||||
split-by-size-or-count.type.docCount=By Document Count
|
||||
split-by-size-or-count.value.label=Enter Value
|
||||
split-by-size-or-count.value.placeholder=Enter size (e.g., 2MB or 3KB) or count (e.g., 5)
|
||||
split-by-size-or-count.submit=Submit
|
||||
split-by-size-or-count.header=Séparer le PDF par taille ou par nombre
|
||||
split-by-size-or-count.type.label=Sélectionner le type de division
|
||||
split-by-size-or-count.type.size=Par taille
|
||||
split-by-size-or-count.type.pageCount=Par nombre de pages
|
||||
split-by-size-or-count.type.docCount=Par nombre de documents
|
||||
split-by-size-or-count.value.label=Entrer la valeur
|
||||
split-by-size-or-count.value.placeholder=Saisir la taille (par exemple, 2MB ou 3KB) ou le nombre (par exemple, 5)
|
||||
split-by-size-or-count.submit=Séparer
|
||||
|
||||
|
||||
#overlay-pdfs
|
||||
overlay-pdfs.header=Overlay PDF Files
|
||||
overlay-pdfs.baseFile.label=Select Base PDF File
|
||||
overlay-pdfs.overlayFiles.label=Select Overlay PDF Files
|
||||
overlay-pdfs.baseFile.label=Sélectionner le fichier PDF de base
|
||||
overlay-pdfs.overlayFiles.label=Sélectionner les fichiers PDF à superposer
|
||||
overlay-pdfs.mode.label=Select Overlay Mode
|
||||
overlay-pdfs.mode.sequential=Sequential Overlay
|
||||
overlay-pdfs.mode.interleaved=Interleaved Overlay
|
||||
overlay-pdfs.mode.fixedRepeat=Fixed Repeat Overlay
|
||||
overlay-pdfs.counts.label=Overlay Counts (for Fixed Repeat Mode)
|
||||
overlay-pdfs.mode.fixedRepeat=Superposition à répétition fixe
|
||||
overlay-pdfs.counts.label=Nombre de superpositions (pour le mode de répétition fixe)
|
||||
overlay-pdfs.counts.placeholder=Enter comma-separated counts (e.g., 2,3,1)
|
||||
overlay-pdfs.position.label=Select Overlay Position
|
||||
overlay-pdfs.position.foreground=Foreground
|
||||
overlay-pdfs.position.background=Background
|
||||
overlay-pdfs.position.foreground=Premier plan
|
||||
overlay-pdfs.position.background=Arrière-plan
|
||||
overlay-pdfs.submit=Submit
|
||||
|
||||
|
||||
#split-by-sections
|
||||
split-by-sections.title=Split PDF by Sections
|
||||
split-by-sections.header=Split PDF into Sections
|
||||
split-by-sections.horizontal.label=Horizontal Divisions
|
||||
split-by-sections.vertical.label=Vertical Divisions
|
||||
split-by-sections.horizontal.placeholder=Enter number of horizontal divisions
|
||||
split-by-sections.vertical.placeholder=Enter number of vertical divisions
|
||||
split-by-sections.submit=Split PDF
|
||||
split-by-sections.title=Diviser le PDF en sections
|
||||
split-by-sections.header=Diviser le PDF en sections
|
||||
split-by-sections.horizontal.label=Divisions horizontales
|
||||
split-by-sections.vertical.label=Divisions verticales
|
||||
split-by-sections.horizontal.placeholder=Saisir le nombre de divisions horizontales
|
||||
split-by-sections.vertical.placeholder=Entrer le nombre de divisions verticales
|
||||
split-by-sections.submit=Diviser le PDF
|
||||
|
||||
896
src/main/resources/messages_hi_IN.properties
Normal file
896
src/main/resources/messages_hi_IN.properties
Normal file
@@ -0,0 +1,896 @@
|
||||
###########
|
||||
# Generic #
|
||||
###########
|
||||
# the direction that the language is written (ltr=left to right, rtl = right to left)
|
||||
language.direction=ltr
|
||||
|
||||
pdfPrompt=पीडीएफ़(फ़ाइलें) चुनें
|
||||
multiPdfPrompt=पीडीएफ़(फ़ाइलें) चुनें (2+)
|
||||
multiPdfDropPrompt=सभी पीडीएफ़(फ़ाइलें) को चुनें (या खींचें और छोड़ें)
|
||||
imgPrompt=छवियों का चयन करें
|
||||
genericSubmit=प्रस्तुत करें
|
||||
processTimeWarning=चेतावनी: यह प्रक्रिया फ़ाइल के आकार पर निर्भर करती है और यह से एक मिनट तक लग सकती है
|
||||
pageOrderPrompt=कस्टम पेज क्रम (पेज नंबरों या 2n+1 जैसे कार्यों की एक कॉमा से अलग-अलग सूची दर्ज करें):
|
||||
goToPage=जाएँ
|
||||
true=सही
|
||||
false=गलत
|
||||
unknown=अज्ञात
|
||||
save=सहेजें
|
||||
close=बंद करें
|
||||
filesSelected=फ़ाइलें चयनित हैं
|
||||
noFavourites=कोई पसंदीदा जोड़ा नहीं गया है
|
||||
bored=बोर हो रहे हैं?
|
||||
alphabet=वर्णमाला
|
||||
downloadPdf=पीडीएफ़ डाउनलोड करें
|
||||
text=टेक्स्ट
|
||||
font=फ़ॉन्ट
|
||||
selectFillter=-- चुनें --
|
||||
pageNum=पृष्ठ संख्या
|
||||
sizes.small=छोटा
|
||||
sizes.medium=मध्यम
|
||||
sizes.large=बड़ा
|
||||
sizes.x-large=बहुत बड़ा
|
||||
error.pdfPassword=पीडीएफ़ दस्तावेज़ पासवर्ड से सुरक्षित है और या तो पासवर्ड नहीं दिया गया था या गलत था
|
||||
delete=हटाएँ
|
||||
username=उपयोगकर्ता नाम
|
||||
password=पासवर्ड
|
||||
welcome=स्वागत है
|
||||
property=संपत्ति
|
||||
black=काला
|
||||
white=सफ़ेद
|
||||
red=लाल
|
||||
green=हरा
|
||||
blue=नीला
|
||||
custom=कस्टम...
|
||||
|
||||
changedCredsMessage=क्रेडेंशियल्स बदल दी गईं!
|
||||
notAuthenticatedMessage=उपयोगकर्ता प्रमाणित नहीं है।
|
||||
userNotFoundMessage=उपयोगकर्ता नहीं मिला।
|
||||
incorrectPasswordMessage=वर्तमान पासवर्ड गलत है।
|
||||
usernameExistsMessage=नया उपयोगकर्ता नाम पहले से मौजूद है।
|
||||
|
||||
|
||||
|
||||
#############
|
||||
# NAVBAR #
|
||||
#############
|
||||
navbar.convert=कनवर्ट
|
||||
navbar.security=सुरक्षा
|
||||
navbar.other=विविध
|
||||
navbar.darkmode=डार्क मोड
|
||||
navbar.pageOps=पेज कार्य
|
||||
navbar.settings=सेटिंग्स
|
||||
|
||||
#############
|
||||
# SETTINGS #
|
||||
#############
|
||||
settings.title=सेटिंग्स
|
||||
settings.update=अपडेट उपलब्ध है
|
||||
settings.appVersion=ऐप संस्करण:
|
||||
settings.downloadOption.title=डाउनलोड विकल्प चुनें (एकल फ़ाइल गैर-ज़िप डाउनलोड के लिए):
|
||||
settings.downloadOption.1=एक ही विंडो में खोलें
|
||||
settings.downloadOption.2=नई विंडो में खोलें
|
||||
settings.downloadOption.3=फ़ाइल डाउनलोड करें
|
||||
settings.zipThreshold=जब डाउनलोड की गई फ़ाइलों की संख्या सीमा से अधिक हो
|
||||
settings.signOut=साइन आउट
|
||||
settings.accountSettings=खाता सेटिंग्स
|
||||
|
||||
|
||||
|
||||
changeCreds.title=क्रेडेंशियल बदलें
|
||||
changeCreds.header=अपना खाता विवरण अपडेट करें
|
||||
changeCreds.changeUserAndPassword=आप डिफ़ॉल्ट लॉगिन क्रेडेंशियल का उपयोग कर रहे हैं। कृपया एक नया पासवर्ड दर्ज करें (और यदि चाहें तो उपयोगकर्ता नाम)
|
||||
changeCreds.newUsername=नया उपयोगकर्ता नाम
|
||||
changeCreds.oldPassword=वर्तमान पासवर्ड
|
||||
changeCreds.newPassword=नया पासवर्ड
|
||||
changeCreds.confirmNewPassword=नए पासवर्ड की पुष्टि करें
|
||||
changeCreds.submit=परिवर्तन सबमिट करें
|
||||
|
||||
|
||||
|
||||
account.title=खाता सेटिंग्स
|
||||
account.accountSettings=खाता सेटिंग्स
|
||||
account.adminSettings=व्यवस्थापक सेटिंग्स - उपयोगकर्ताओं को देखें और जोड़ें
|
||||
account.userControlSettings=उपयोगकर्ता नियंत्रण सेटिंग्स
|
||||
account.changeUsername=उपयोगकर्ता नाम परिवर्तन करें
|
||||
account.changeUsername=उपयोगकर्ता नाम परिवर्तन करें
|
||||
account.password=पासवर्ड पुष्टि
|
||||
account.oldPassword=पुराना पासवर्ड
|
||||
account.newPassword=नया पासवर्ड
|
||||
account.changePassword=पासवर्ड बदलें
|
||||
account.confirmNewPassword=नए पासवर्ड की पुष्टि करें
|
||||
account.signOut=साइन आउट
|
||||
account.yourApiKey=आपकी API कुंजी
|
||||
account.syncTitle=अकाउंट से ब्राउज़र सेटिंग्स को सिंक करें
|
||||
account.settingsCompare=सेटिंग्स तुलना:
|
||||
account.property=संपत्ति
|
||||
account.webBrowserSettings=वेब ब्राउज़र सेटिंग्स
|
||||
account.syncToBrowser=सिंक अकाउंट -> ब्राउज़र
|
||||
account.syncToAccount=सिंक अकाउंट <- ब्राउज़र
|
||||
|
||||
|
||||
adminUserSettings.title=उपयोगकर्ता नियंत्रण सेटिंग्स
|
||||
adminUserSettings.header=व्यवस्थापक उपयोगकर्ता नियंत्रण सेटिंग्स
|
||||
adminUserSettings.admin=व्यवस्थापक
|
||||
adminUserSettings.user=उपयोगकर्ता
|
||||
adminUserSettings.addUser=नया उपयोगकर्ता जोड़ें
|
||||
adminUserSettings.roles=रोल्स
|
||||
adminUserSettings.role=रोल
|
||||
adminUserSettings.actions=क्रियाएँ
|
||||
adminUserSettings.apiUser=सीमित API उपयोगकर्ता
|
||||
adminUserSettings.webOnlyUser=केवल वेब उपयोगकर्ता
|
||||
adminUserSettings.demoUser=Demo User (No custom settings)
|
||||
adminUserSettings.forceChange=उपयोगकर्ता को लॉगिन पर उपयोगकर्ता नाम/पासवर्ड बदलने के लिए मजबूर करें
|
||||
adminUserSettings.submit=उपयोगकर्ता को सहेजें
|
||||
|
||||
#############
|
||||
# HOME-PAGE #
|
||||
#############
|
||||
home.desc=आपकी स्थानीय होस्ट की एक स्थानीय सेवा जहां आपकी सभी पीडीएफ़ आवश्यकताओं के लिए है।
|
||||
home.searchBar=विशेषताओं की खोज करें...
|
||||
|
||||
|
||||
home.viewPdf.title=पीडीएफ़ देखें
|
||||
home.viewPdf.desc=देखें, टिप्पणी करें, पाठ या छवियों को जोड़ें
|
||||
viewPdf.tags=देखें, पढ़ें, टिप्पणी, पाठ, छवि
|
||||
|
||||
home.multiTool.title=पीडीएफ़ मल्टी टूल
|
||||
home.multiTool.desc=पृष्ठ जोड़ें, घुमाएं, व्यवस्थित करें, और हटाएं
|
||||
multiTool.tags=मल्टी टूल, मल्टी ऑपरेशन, यूआई, क्लिक और खींचें, फ्रंट एंड, क्लाइंट साइड, इंटरैक्टिव, हिलनेवाला
|
||||
|
||||
home.merge.title=मर्ज
|
||||
home.merge.desc=आसानी से कई पीडीएफ़ को एक में मर्ज करें।
|
||||
merge.tags=मर्ज, पेज कार्य, बैक एंड, सर्वर साइड
|
||||
|
||||
home.split.title=विभाजन
|
||||
home.split.desc=पीडीएफ़ को कई दस्तावेज़ों में विभाजित करें
|
||||
split.tags=पेज कार्य, विभाजित करें, मल्टी पेज, कट, सर्वर साइड
|
||||
|
||||
home.rotate.title=रोटेट
|
||||
home.rotate.desc=आसानी से अपने पीडीएफ़ को घुमाएँ।
|
||||
rotate.tags=सर्वर साइड
|
||||
|
||||
|
||||
home.imageToPdf.title=छवि से पीडीएफ़ तक
|
||||
home.imageToPdf.desc=छवि (PNG, JPEG, GIF) को पीडीएफ़ में बदलें।
|
||||
imageToPdf.tags=परिवर्तन, छवि, jpg, चित्र, फोटो
|
||||
|
||||
home.pdfToImage.title=पीडीएफ़ से छवि तक
|
||||
home.pdfToImage.desc=पीडीएफ़ को छवि में बदलें। (PNG, JPEG, GIF)
|
||||
pdfToImage.tags=परिवर्तन, छवि, jpg, चित्र, फोटो
|
||||
|
||||
home.pdfOrganiser.title=संगठन करें
|
||||
home.pdfOrganiser.desc=किसी भी क्रम में पृष्ठ हटाएं/पुनःव्यवस्थित करें
|
||||
pdfOrganiser.tags=डुप्लेक्स, सम, विषम, क्रमबद्ध करें, विस्थापित
|
||||
|
||||
|
||||
home.addImage.title=छवि जोड़ें
|
||||
home.addImage.desc=पीडीएफ़ पर एक छवि को एक सेट स्थान पर जोड़ता है।
|
||||
addImage.tags=छवि, jpg, चित्र, फोटो
|
||||
|
||||
home.watermark.title=वॉटरमार्क जोड़ें
|
||||
home.watermark.desc=अपने पीडीएफ़ दस्तावेज़ में एक कस्टम वॉटरमार्क जोड़ें।
|
||||
watermark.tags=पाठ, बार-बार, लेबल, अपना, कॉपीराइट, ट्रेडमार्क, छवि, jpg, चित्र, फोटो
|
||||
|
||||
home.permissions.title=अनुमतियाँ बदलें
|
||||
home.permissions.desc=अपने पीडीएफ़ दस्तावेज़ की अनुमतियाँ बदलें
|
||||
permissions.tags=पढ़ें, लिखें, संपादित करें, प्रिंट
|
||||
|
||||
|
||||
home.removePages.title=हटाएं
|
||||
home.removePages.desc=अपने पीडीएफ़ दस्तावेज़ से अनचाहे पृष्ठों को हटाएं।
|
||||
removePages.tags=पृष्ठ हटाएं, पृष्ठ मिटाएं
|
||||
|
||||
home.addPassword.title=पासवर्ड जोड़ें
|
||||
home.addPassword.desc=अपने पीडीएफ़ दस्तावेज़ को एक पासवर्ड से एन्क्रिप्ट करें।
|
||||
addPassword.tags=सुरक्षित, सुरक्षा
|
||||
|
||||
home.removePassword.title=पासवर्ड हटाएं
|
||||
home.removePassword.desc=अपने पीडीएफ़ दस्तावेज़ से पासवर्ड सुरक्षा को हटाएं।
|
||||
removePassword.tags=सुरक्षित, डिक्रिप्ट, सुरक्षा, पासवर्ड हटाएं, पासवर्ड मिटाएं
|
||||
|
||||
home.compressPdfs.title=संकुचित करें (कम्प्रेस)
|
||||
home.compressPdfs.desc=फ़ाइल का आकार कम करने के लिए PDF को कम्प्रेस करें।
|
||||
compressPdfs.tags=स्क्विश, छोटा, छोटा
|
||||
|
||||
|
||||
home.changeMetadata.title=मेटाडेटा बदलें
|
||||
home.changeMetadata.desc=PDF दस्तावेज़ से मेटाडेटा बदलें/हटाएं/जोड़ें।
|
||||
changeMetadata.tags=शीर्षक, लेखक, तारीख, निर्माण, समय, प्रकाशक, उत्पादक, आँकड़े
|
||||
|
||||
home.fileToPDF.title=फ़ाइल को पीडीएफ़ में रूपांतरित करें
|
||||
home.fileToPDF.desc=लगभग किसी भी फ़ाइल को पीडीएफ़ में रूपांतरित करें (DOCX, PNG, XLS, PPT, TXT और अधिक)
|
||||
fileToPDF.tags=परिवर्तन, प्रारूप, दस्तावेज़, चित्र, स्लाइड, पाठ, परिवर्तन, ऑफिस, डॉक्स, वर्ड, एक्सेल, पावरपॉइंट
|
||||
|
||||
home.ocr.title=OCR / स्कैन को साफ करें
|
||||
home.ocr.desc=स्कैन को साफ करता है और पीडीएफ़ में छवियों से पाठ को पहचानता है और टेक्स्ट के रूप में फिर से जोड़ता है।
|
||||
ocr.tags=पहचान, टेक्स्ट, छवि, स्कैन, पढ़ें, पहचान, पता लगाना, संपादनीय
|
||||
|
||||
|
||||
home.extractImages.title=छवियां निकालें
|
||||
home.extractImages.desc=पीडीएफ़ से सभी छवियों को निकालता है और उन्हें ज़िप में सहेजता है
|
||||
extractImages.tags=चित्र, फोटो, सहेजें, संग्रह, ज़िप, कैप्चर, ग्रैब
|
||||
|
||||
home.pdfToPDFA.title=PDF से PDF/A में
|
||||
home.pdfToPDFA.desc=लंबे समय के लिए स्टोरेज के लिए पीडीएफ़ को पीडीएफ़/ए में रूपांतरित करें
|
||||
pdfToPDFA.tags=संग्रह, लंबे समय के लिए, मानक, परिवर्तन, स्टोरेज, संरक्षण
|
||||
|
||||
home.PDFToWord.title=PDF से वर्ड में
|
||||
home.PDFToWord.desc=PDF को वर्ड प्रारूपों में रूपांतरित करें (DOC, DOCX और ODT)
|
||||
PDFToWord.tags=doc,docx,odt,word,परिवर्तन,प्रारूप,रूपांतरण,ऑफिस,माइक्रोसॉफ्ट,डॉक फ़ाइल
|
||||
|
||||
home.PDFToPresentation.title=PDF से प्रस्तुति में
|
||||
home.PDFToPresentation.desc=PDF को प्रस्तुति प्रारूपों में रूपांतरित करें (PPT, PPTX और ODP)
|
||||
PDFToPresentation.tags=स्लाइड,दिखाना,ऑफिस,माइक्रोसॉफ्ट
|
||||
|
||||
home.PDFToText.title=PDF से RTF (पाठ) में
|
||||
home.PDFToText.desc=PDF को पाठ या RTF प्रारूप में रूपांतरित करें
|
||||
PDFToText.tags=रिचफॉर्मेट, रिचटेक्स्टफॉर्मेट, रिच टेक्स्ट फॉर्मेट
|
||||
|
||||
home.PDFToHTML.title=PDF से HTML में
|
||||
home.PDFToHTML.desc=PDF को HTML प्रारूप में रूपांतरित करें
|
||||
PDFToHTML.tags=वेब सामग्री, ब्राउज़र अनुकूल
|
||||
|
||||
|
||||
home.PDFToXML.title=PDF से XML में
|
||||
home.PDFToXML.desc=PDF को XML प्रारूप में रूपांतरित करें
|
||||
PDFToXML.tags=डेटा-निकालन, संरचित सामग्री, अंतरसंवाद, परिवर्तन, रूपांतरण
|
||||
|
||||
home.ScannerImageSplit.title=स्कैन की गई फोटो का पता लगाएं/विभाजित करें
|
||||
home.ScannerImageSplit.desc=एक फोटो/PDF के भीतर से कई फोटो को विभाजित करता है
|
||||
ScannerImageSplit.tags=अलग, ऑटो-डिटेक्ट, स्कैन, मल्टी-फोटो, संगठित
|
||||
|
||||
home.sign.title=हस्ताक्षर
|
||||
home.sign.desc=हस्ताक्षर को ड्राइंग, पाठ या छवि के रूप में पीडीएफ़ में जोड़ता है।
|
||||
sign.tags=अधिकृत करें, आदेश, ड्राइंग-हस्ताक्षर, पाठ-हस्ताक्षर, छवि-हस्ताक्षर
|
||||
|
||||
home.flatten.title=समतल करें
|
||||
home.flatten.desc=पीडीएफ़ से सभी इंटरैक्टिव तत्वों और फ़ॉर्म को हटाएं।
|
||||
flatten.tags=स्थैतिक, निष्क्रिय करें, गैर-इंटरैक्टिव, सरलीकृत
|
||||
|
||||
home.repair.title=मरम्मत
|
||||
home.repair.desc=किसी कोरप्ट/टूटे हुए पीडीएफ़ को मरम्मत करने का प्रयास करता है।
|
||||
repair.tags=ठीक करें, पुनर्स्थापित करें, सुधार, पुनर्प्राप्ति
|
||||
|
||||
home.removeBlanks.title=खाली पृष्ठ हटाएं
|
||||
home.removeBlanks.desc=दस्तावेज़ से खाली पृष्ठों का पता लगाता है और हटाता है
|
||||
removeBlanks.tags=सफ़ाई, सरलीकरण, गैर-सामग्री, संगठित
|
||||
|
||||
home.removeAnnotations.title=टिप्पणियाँ हटाएं
|
||||
home.removeAnnotations.desc=पीडीएफ़ से सभी टिप्पणियाँ/एनोटेशन्स को हटाता है
|
||||
removeAnnotations.tags=टिप्पणियाँ, हाइलाइट, नोट्स, मार्कअप, हटाएं
|
||||
|
||||
home.compare.title=तुलना करें
|
||||
home.compare.desc=2 पीडीएफ़ दस्तावेज़ों के बीच अंतर को तुलना करता है और दिखाता है
|
||||
compare.tags=भिन्नता, विविधता, परिवर्तन, विश्लेषण
|
||||
|
||||
home.certSign.title=प्रमाणपत्र के साथ हस्ताक्षर करें
|
||||
home.certSign.desc=पीडीएफ़ को प्रमाणपत्र/कुंजी (PEM/P12) के साथ हस्ताक्षरित करता है।
|
||||
certSign.tags=प्रमाणीकरण, PEM, P12, आधिकारिक, एन्क्रिप्ट
|
||||
|
||||
home.pageLayout.title=मल्टी-पेज लेआउट
|
||||
home.pageLayout.desc=पीडीएफ़ दस्तावेज़ के कई पेजों को एक ही पेज में मर्ज करता है।
|
||||
pageLayout.tags=मर्ज, संयोजित, एकल दृश्य, संगठित
|
||||
|
||||
home.scalePages.title=पेज का आकार/स्केल समायोजित करें
|
||||
home.scalePages.desc=पेज और/या उसकी सामग्री का आकार/स्केल बदलें।
|
||||
scalePages.tags=रीसाइज़, संशोधित, आयाम, अनुकूल
|
||||
|
||||
home.pipeline.title=पाइपलाइन (उन्नत)
|
||||
home.pipeline.desc=पाइपलाइन स्क्रिप्ट्स को परिभाषित करके पीडीएफ़ पर कई क्रियाएँ चलाएं
|
||||
pipeline.tags=स्वचालित करें, क्रम, स्क्रिप्टित, बैच-प्रक्रिया
|
||||
|
||||
home.add-page-numbers.title=पेज नंबर जोड़ें
|
||||
home.add-page-numbers.desc=एक सेट स्थान पर दस्तावेज़ के लिए पेज नंबर जोड़ें
|
||||
add-page-numbers.tags=पृष्ठांकन, लेबल, संगठन, सूचीकरण
|
||||
|
||||
home.auto-rename.title=ऑटो रिनेम पीडीएफ़ फ़ाइल
|
||||
home.auto-rename.desc=पाया गया है हैडर के आधार पर पीडीएफ़ फ़ाइल को ऑटोमैटिक रूप से नामांकित करता है
|
||||
auto-rename.tags=ऑटो-डिटेक्ट, हेडर-आधारित, संगठन, नया नाम देना
|
||||
|
||||
home.adjust-contrast.title=रंग/कंट्रास्ट समायोजित करें
|
||||
home.adjust-contrast.desc=पीडीएफ़ का कंट्रास्ट, सैट्युरेशन और ब्राइटनेस समायोजित करें
|
||||
adjust-contrast.tags=रंग सुधार, ट्यूनिंग, संशोधित करें, बेहतर
|
||||
|
||||
home.crop.title=क्रॉप पीडीएफ़
|
||||
home.crop.desc=पीडीएफ़ का साइज़ कम करने के लिए क्रॉप करें (पाठ को संरक्षित रखें!)
|
||||
crop.tags=ट्रिम, छोटा करें, संपादित करें, आकार
|
||||
|
||||
home.autoSplitPDF.title=ऑटो स्प्लिट पेज्स
|
||||
home.autoSplitPDF.desc=फिजिकल स्कैन किए गए पेज स्प्लिटर QR कोड के साथ ऑटो स्प्लिट स्कैन किया गया पीडीएफ़
|
||||
autoSplitPDF.tags=क्यूआर-आधारित, अलग करें, स्कैन-खंड, संगठित
|
||||
|
||||
home.sanitizePdf.title=सैनिटाइज़
|
||||
home.sanitizePdf.desc=पीडीएफ़ फ़ाइलों से स्क्रिप्ट और अन्य तत्वों को हटाता है
|
||||
sanitizePdf.tags=साफ, सुरक्षित, सुरक्षित, हटाएँ-खतरे
|
||||
|
||||
home.URLToPDF.title=URL/वेबसाइट से पीडीएफ़
|
||||
home.URLToPDF.desc=किसी भी http(s) URL को पीडीएफ़ में रूपांतरित करता है
|
||||
URLToPDF.tags=वेब-कैप्चर, पेज सहेजें, वेब-से-डॉक, संग्रह
|
||||
|
||||
home.HTMLToPDF.title=HTML से पीडीएफ़
|
||||
home.HTMLToPDF.desc=किसी भी HTML फ़ाइल या ज़िप को पीडीएफ़ में रूपांतरित करता है
|
||||
HTMLToPDF.tags=मार्कअप, वेब-सामग्री, परिवर्तन, रूपांतरण
|
||||
|
||||
|
||||
home.MarkdownToPDF.title=मार्कडाउन से पीडीएफ़
|
||||
home.MarkdownToPDF.desc=किसी भी मार्कडाउन फ़ाइल को पीडीएफ़ में रूपांतरित करता है
|
||||
MarkdownToPDF.tags=मार्कअप, वेब-सामग्री, परिवर्तन, रूपांतरण
|
||||
|
||||
|
||||
home.getPdfInfo.title=पीडीएफ़ पर सभी जानकारी प्राप्त करें
|
||||
home.getPdfInfo.desc=पीडीएफ़ पर संभावना से सभी सूचना को प्राप्त करता है
|
||||
getPdfInfo.tags=जानकारी, डेटा, स्टैट्स, सांख्यिकी
|
||||
|
||||
|
||||
home.extractPage.title=पृष्ठ(ओं) को निकालें
|
||||
home.extractPage.desc=पीडीएफ़ से चयनित पेजों को निकालता है
|
||||
extractPage.tags=निकालें
|
||||
|
||||
|
||||
home.PdfToSinglePage.title=पीडीएफ़ से एक बड़े पृष्ठ में
|
||||
home.PdfToSinglePage.desc=सभी पीडीएफ़ पेजों को एक बड़े एकल पृष्ठ में मर्ज करता है
|
||||
PdfToSinglePage.tags=एकल पृष्ठ
|
||||
|
||||
|
||||
home.showJS.title=जावास्क्रिप्ट दिखाएं
|
||||
home.showJS.desc=पीडीएफ़ में डाला गया कोई भी जावास्क्रिप्ट खोजता है और प्रदर्शित करता है
|
||||
showJS.tags=गोपनीयकरण, छिपाना, काला करना, काला, मार्कर, छिपा हुआ
|
||||
|
||||
home.autoRedact.title=स्वतः गोपनीयकरण
|
||||
home.autoRedact.desc=प्रविष्ट पाठ के आधार पर पीडीएफ़ में पाठ को स्वतः गोपनीयकरित(काला करें)
|
||||
showJS.tags=गोपनीयकरण, छिपाना, काला करना, काला, मार्कर, छिपा हुआ
|
||||
|
||||
home.tableExtraxt.title=PDF से CSV में
|
||||
home.tableExtraxt.desc=CSV में बदलते हुए पीडीएफ़ से तालिकाएँ निकालता है
|
||||
tableExtraxt.tags=CSV, तालिका निकालना, निकालना, परिवर्तित करना
|
||||
|
||||
|
||||
home.autoSizeSplitPDF.title=साइज़/गणना के आधार पर स्वतः विभाजित करें
|
||||
home.autoSizeSplitPDF.desc=आकार, पृष्ठ संख्या या दस्तावेज़ संख्या के आधार पर एक पीडीएफ़ को विभाजित करें
|
||||
autoSizeSplitPDF.tags=पीडीएफ़, विभाजन, दस्तावेज़, संगठन
|
||||
|
||||
|
||||
home.overlay-pdfs.title=पीडीएफ़ ओवरले
|
||||
home.overlay-pdfs.desc=एक पीडीएफ़ के ऊपर दूसरे पीडीएफ़ को ओवरले करता है
|
||||
overlay-pdfs.tags=ओवरले
|
||||
|
||||
home.split-by-sections.title=खंडों से पीडीएफ़ विभाजित करें
|
||||
home.split-by-sections.desc=पीडीएफ़ के प्रत्येक पृष्ठ को छोटे से छोटे क्षैतिज और ऊर्ध्वाधर खंडों में विभाजित करें
|
||||
split-by-sections.tags=खंड विभाजन, विभाजित करें, अनुकूलित
|
||||
|
||||
###########################
|
||||
# #
|
||||
# WEB PAGES #
|
||||
# #
|
||||
###########################
|
||||
#login
|
||||
login.title=साइन इन करें
|
||||
login.signin=साइन इन करें
|
||||
login.rememberme=मुझे याद रखें
|
||||
login.invalid=अमान्य उपयोगकर्ता नाम या पासवर्ड।
|
||||
login.locked=आपका खाता लॉक कर दिया गया है।
|
||||
login.signinTitle=कृपया साइन इन करें
|
||||
|
||||
|
||||
#auto-redact
|
||||
autoRedact.title=स्वत: गोपनीयकरण
|
||||
autoRedact.header=स्वत: गोपनीयकरण
|
||||
autoRedact.colorLabel=रंग
|
||||
autoRedact.textsToRedactLabel=गोपनीयकरण के लिए पाठ (लाइनों में अलग)
|
||||
autoRedact.textsToRedactPlaceholder=जैसे \nगोपनीय \nटॉप-सीक्रेट
|
||||
autoRedact.useRegexLabel=रेगेक्स का प्रयोग करें
|
||||
autoRedact.wholeWordSearchLabel=पूरे शब्द की खोज
|
||||
autoRedact.customPaddingLabel=कस्टम अतिरिक्त पैडिंग
|
||||
autoRedact.convertPDFToImageLabel=PDF को छवि में बदलें (बॉक्स के पीछे पाठ को हटाने के लिए प्रयोग किया जाता है)
|
||||
autoRedact.submitButton=प्रस्तुत करें
|
||||
|
||||
|
||||
#showJS
|
||||
showJS.title=जावास्क्रिप्ट दिखाएं
|
||||
showJS.header=जावास्क्रिप्ट दिखाएं
|
||||
showJS.downloadJS=जावास्क्रिप्ट डाउनलोड करें
|
||||
showJS.submit=दिखाएं
|
||||
|
||||
|
||||
#pdfToSinglePage
|
||||
pdfToSinglePage.title=पीडीएफ़ से एकल पृष्ठ
|
||||
pdfToSinglePage.header=पीडीएफ़ से एकल पृष्ठ
|
||||
pdfToSinglePage.submit=एकल पृष्ठ में परिवर्तित करें
|
||||
|
||||
|
||||
#pageExtracter
|
||||
pageExtracter.title=पृष्ठों को निकालें
|
||||
pageExtracter.header=पृष्ठों को निकालें
|
||||
pageExtracter.submit=निकालें
|
||||
|
||||
|
||||
#getPdfInfo
|
||||
getPdfInfo.title=पीडीएफ़ पर जानकारी प्राप्त करें
|
||||
getPdfInfo.header=पीडीएफ़ पर जानकारी प्राप्त करें
|
||||
getPdfInfo.submit=जानकारी प्राप्त करें
|
||||
getPdfInfo.downloadJson=JSON डाउनलोड करें
|
||||
|
||||
|
||||
#markdown-to-pdf
|
||||
MarkdownToPDF.title=मार्कडाउन से पीडीएफ़
|
||||
MarkdownToPDF.header=मार्कडाउन से पीडीएफ़
|
||||
MarkdownToPDF.submit=रूपांतरित करें
|
||||
MarkdownToPDF.help=काम चल रहा है
|
||||
MarkdownToPDF.credit=WeasyPrint का प्रयोग होता है
|
||||
|
||||
|
||||
|
||||
#url-to-pdf
|
||||
URLToPDF.title=URL से पीडीएफ़
|
||||
URLToPDF.header=URL से पीडीएफ़
|
||||
URLToPDF.submit=रूपांतरित करें
|
||||
URLToPDF.credit=WeasyPrint का प्रयोग होता है
|
||||
|
||||
|
||||
#html-to-pdf
|
||||
HTMLToPDF.title=HTML से पीडीएफ़
|
||||
HTMLToPDF.header=HTML से पीडीएफ़
|
||||
HTMLToPDF.help=HTML फ़ाइलों और html/css/images आदि को आत्मसात करने वाले ZIPs को स्वीकार करता है
|
||||
HTMLToPDF.submit=रूपांतरित करें
|
||||
HTMLToPDF.credit=WeasyPrint का प्रयोग होता है
|
||||
|
||||
|
||||
#sanitizePDF
|
||||
sanitizePDF.title=पीडीएफ़ को सफाई करें
|
||||
sanitizePDF.header=एक पीडीएफ़ फ़ाइल को सफाई करें
|
||||
sanitizePDF.selectText.1=जावास्क्रिप्ट क्रियाएँ हटाएं
|
||||
sanitizePDF.selectText.2=एम्बेडेड फ़ाइलें हटाएं
|
||||
sanitizePDF.selectText.3=मेटाडेटा हटाएं
|
||||
sanitizePDF.selectText.4=लिंक हटाएं
|
||||
sanitizePDF.selectText.5=फ़ॉन्ट्स हटाएं
|
||||
sanitizePDF.submit=पीडीएफ़ को सफाई करें
|
||||
|
||||
|
||||
#addPageNumbers
|
||||
addPageNumbers.title=पृष्ठ संख्या जोड़ें
|
||||
addPageNumbers.header=पृष्ठ संख्या जोड़ें
|
||||
addPageNumbers.selectText.1=पीडीएफ़ फ़ाइल का चयन करें:
|
||||
addPageNumbers.selectText.2=मार्जिन आकार
|
||||
addPageNumbers.selectText.3=स्थान
|
||||
addPageNumbers.selectText.4=शुरुआती संख्या
|
||||
addPageNumbers.selectText.5=संख्यांकित करने के लिए पृष्ठ
|
||||
addPageNumbers.selectText.6=कस्टम पाठ
|
||||
addPageNumbers.customTextDesc=कस्टम पाठ
|
||||
addPageNumbers.numberPagesDesc=किस पृष्ठों को संख्यांकित करना है, डिफ़ॉल्ट 'सभी', यहां 1-5 या 2,5,9 भी स्वीकार करता है
|
||||
addPageNumbers.customNumberDesc=डिफ़ॉल्ट {n}, 'पेज {n} का {total}', 'पाठ-{n}', '{filename}-{n}' भी स्वीकार करता है
|
||||
addPageNumbers.submit=पृष्ठ संख्या जोड़ें
|
||||
|
||||
|
||||
#auto-rename
|
||||
auto-rename.title=स्वतः नाम परिवर्तन (खुद ब खुद नाम बदलें)
|
||||
auto-rename.header=स्वतः नाम परिवर्तन पीडीएफ़
|
||||
auto-rename.submit=स्वतः नाम परिवर्तन
|
||||
|
||||
|
||||
#adjustContrast
|
||||
adjustContrast.title=कंट्रास्ट समायोजित करें
|
||||
adjustContrast.header=कंट्रास्ट समायोजित करें
|
||||
adjustContrast.contrast=कंट्रास्ट:
|
||||
adjustContrast.brightness=चमक:
|
||||
adjustContrast.saturation=संतृप्ति:
|
||||
adjustContrast.download=डाउनलोड
|
||||
|
||||
|
||||
#crop
|
||||
crop.title=कटौती
|
||||
crop.header=छवि काटो
|
||||
crop.submit=प्रस्तुत करें
|
||||
|
||||
|
||||
#autoSplitPDF
|
||||
autoSplitPDF.title=ऑटो स्प्लिट पीडीएफ
|
||||
autoSplitPDF.header=ऑटो स्प्लिट पीडीएफ
|
||||
autoSplitPDF.description=प्रिंट, इंसर्ट, स्कैन, अपलोड करें, और हमें आपके दस्तावेजों को स्वत: अलग करने दें। कोई मैनुअल सॉर्टिंग की आवश्यकता नहीं है।
|
||||
autoSplitPDF.selectText.1=नीचे से कुछ विभाजक शीट्स प्रिंट करें (काला और सफेद ठीक है)।
|
||||
autoSplitPDF.selectText.2=सभी दस्तावेजों को एक साथ स्कैन करें, उनके बीच में विभाजक शीट डालें।
|
||||
autoSplitPDF.selectText.3=एक ही बड़ी स्कैन की गई पीडीएफ फ़ाइल अपलोड करें और स्टर्लिंग पीडीएफ से बाकी का संबोधन करें।
|
||||
autoSplitPDF.selectText.4=विभाजक पृष्ठ स्वत: पहचाने जाते हैं और हटाए जाते हैं, एक साफ़ अंतिम दस्तावेज़ की गारंटी करते हैं।
|
||||
autoSplitPDF.formPrompt=स्टर्लिंग-पीडीएफ पेज विभाजक शामिल पीडीएफ प्रस्तुत करें:
|
||||
autoSplitPDF.duplexMode=डुप्लेक्स मोड (सामने और पीछे स्कैनिंग)
|
||||
autoSplitPDF.dividerDownload1='ऑटो स्प्लिटर विभाजक (न्यूनतम).pdf' डाउनलोड करें
|
||||
autoSplitPDF.dividerDownload2='ऑटो स्प्लिटर विभाजक (निर्देशों के साथ).pdf' डाउनलोड करें
|
||||
autoSplitPDF.submit=प्रस्तुत करें
|
||||
|
||||
|
||||
#pipeline
|
||||
pipeline.title=पाइपलाइन
|
||||
|
||||
|
||||
#pageLayout
|
||||
pageLayout.title=मल्टी पेज लेआउट
|
||||
pageLayout.header=मल्टी पेज लेआउट
|
||||
pageLayout.pagesPerSheet=प्रति पृष्ठ पेज:
|
||||
pageLayout.addBorder=सीमा जोड़ें
|
||||
pageLayout.submit=प्रस्तुत क
|
||||
|
||||
|
||||
#scalePages
|
||||
scalePages.title=पृष्ठ-स्केल समायोजित करें
|
||||
scalePages.header=पृष्ठ-स्केल समायोजित करें
|
||||
scalePages.pageSize=दस्तावेज़ के पृष्ठ का आकार।
|
||||
scalePages.scaleFactor=पृष्ठ का ज़ूम स्तर (क्रॉप)।
|
||||
scalePages.submit=प्रस्तुत करें
|
||||
|
||||
|
||||
#certSign
|
||||
certSign.title=प्रमाणपत्र साइनिंग
|
||||
certSign.header=अपने प्रमाणपत्र के साथ एक पीडीएफ़ पर हस्ताक्षर करें (काम जारी है)
|
||||
certSign.selectPDF=साइन करने के लिए एक पीडीएफ़ फ़ाइल का चयन करें:
|
||||
certSign.selectKey=अपनी निजी कुंजी फ़ाइल का चयन करें (PKCS#8 प्रारूप, .pem या .der हो सकता है):
|
||||
certSign.selectCert=अपनी प्रमाणपत्र फ़ाइल का चयन करें (X.509 प्रारूप, .pem या .der हो सकता है):
|
||||
certSign.selectP12=अपनी PKCS#12 कीस्टोर फ़ाइल का चयन करें (.p12 या .pfx) (वैकल्पिक, यदि प्रदान की गई हो, तो इसमें आपकी निजी कुंजी और प्रमाणपत्र होना चाहिए):
|
||||
certSign.certType=प्रमाणपत्र प्रकार
|
||||
certSign.password=अपनी कीस्टोर या निजी कुंजी पासवर्ड दर्ज करें (यदि कोई हो):
|
||||
certSign.showSig=हस्ताक्षर दिखाएं
|
||||
certSign.reason=कारण
|
||||
certSign.location=स्थान
|
||||
certSign.name=नाम
|
||||
certSign.submit=पीडीएफ़ पर हस्ताक्षर करें
|
||||
|
||||
|
||||
#removeBlanks
|
||||
removeBlanks.title=खाली पेज हटाएं
|
||||
removeBlanks.header=खाली पेज हटाएं
|
||||
removeBlanks.threshold=पिक्सेल गोराई थ्रेशोल्ड:
|
||||
removeBlanks.thresholdDesc='सफेद' क्लास बनाने के लिए पिक्सेल कितना सफेद होना चाहिए यह तय करने के लिए थ्रेशोल्ड। 0 = काला, 255 पूरी सफेद।
|
||||
removeBlanks.whitePercent=सफेद प्रतिशत (%):
|
||||
removeBlanks.whitePercentDesc='सफेद' पिक्सेल हटाए जाने के लिए पृष्ठ का प्रतिशत।
|
||||
removeBlanks.submit=खाली पेज हटाएं
|
||||
|
||||
|
||||
#removeAnnotations
|
||||
removeAnnotations.title=एनोटेशन्स हटाएं
|
||||
removeAnnotations.header=एनोटेशन्स हटाएं
|
||||
removeAnnotations.submit=हटाएं
|
||||
|
||||
|
||||
#compare
|
||||
compare.title=तुलना करें
|
||||
compare.header=पीडीएफ़ तुलना करें
|
||||
compare.document.1=दस्तावेज़ 1
|
||||
compare.document.2=दस्तावेज़ 2
|
||||
compare.submit=तुलना करें
|
||||
|
||||
|
||||
#sign
|
||||
sign.title=हस्ताक्षर
|
||||
sign.header=पीडीएफ़ पर हस्ताक्षर करें
|
||||
sign.upload=छवि अपलोड करें
|
||||
sign.draw=हस्ताक्षर बनाएँ
|
||||
sign.text=पाठ इनपुट
|
||||
sign.clear=साफ़ करें
|
||||
sign.add=जोड़ें
|
||||
|
||||
|
||||
#repair
|
||||
repair.title=मरम्मत
|
||||
repair.header=पीडीएफ़ मरम्मत करें
|
||||
repair.submit=मरम्मत
|
||||
|
||||
|
||||
#flatten
|
||||
flatten.title=समतल करें
|
||||
flatten.header=पीडीएफ़ समतल करें
|
||||
flatten.submit=समतल करें
|
||||
|
||||
|
||||
#ScannerImageSplit
|
||||
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)।
|
||||
|
||||
|
||||
#OCR
|
||||
ocr.title=OCR / स्कैन सफाई
|
||||
ocr.header=स्कैन सफाई / OCR (ऑप्टिकल कैरेक्टर रिकग्निशन)
|
||||
ocr.selectText.1=PDF में जिन भाषाओं का पता लगाया जाना है (जो वर्तमान में पता लगाए गए हैं):
|
||||
ocr.selectText.2=OCR के साथ OCR टेक्स्ट को सहित टेक्स्ट फ़ाइल बनाएँ
|
||||
ocr.selectText.3=यहाँ चयन करें की क्या आप OCR'ed पीडीएफ़ के साथ स्क्यूड एंगल पर स्कैन किये गए पेज को सही करना चाहते हैं।
|
||||
ocr.selectText.4=पेज को साफ करें ताकि OCR बैकग्राउंड नॉइज़ में टेक्स्ट न ढूंढे। (कोई आउटपुट परिवर्तन नहीं होगा)
|
||||
ocr.selectText.5=पेज को साफ करें ताकि OCR बैकग्राउंड नॉइज़ में टेक्स्ट कम से कम ढूंढे, आउटपुट में सफाई बनाए रखता है।
|
||||
ocr.selectText.6=यह पेज को अन्दर्स्टैंड करने वाले पेज को छोड़ देगा, जो सिर्फ़ इमेजेस पर OCR करेगा
|
||||
ocr.selectText.7=जब OCR चालू होता है, तो हर पेज को OCR करेगा और सभी मूल टेक्स्ट तत्वों को हटा देगा
|
||||
ocr.selectText.8=सामान्य (यदि पीडीएफ़ में टेक्स्ट है तो त्रुटि होगी)
|
||||
ocr.selectText.9=अतिरिक्त सेटिंग्स
|
||||
ocr.selectText.10=OCR मोड
|
||||
ocr.selectText.11=OCR के बाद छवियां हटाएँ (सभी छवियां हटाएँ, केवल परिवर्तन चरण का हिस्सा होता है)
|
||||
ocr.selectText.12=रेंडर टाइप (उन्नत)
|
||||
ocr.help=कृपया इस डॉक्यूमेंटेशन को पढ़ें कि इसे अन्य भाषाओं के लिए कैसे उपयोग किया जाता है और/या डॉकर में नहीं हैं
|
||||
ocr.credit=इस सेवा में OCRmyPDF और टेसरेक्ट का उपयोग होता है।
|
||||
ocr.submit=OCR के साथ PDF प्रोसेस करें
|
||||
|
||||
|
||||
#extractImages
|
||||
extractImages.title=छवियां निकालें
|
||||
extractImages.header=छवियां निकालें
|
||||
extractImages.selectText=निकाली गई छवियों को कन्वर्ट करने के लिए छवि प्रारूप चुनें
|
||||
extractImages.submit=निकालें
|
||||
|
||||
|
||||
#File to PDF
|
||||
fileToPDF.title=फ़ाइल से पीडीएफ़
|
||||
fileToPDF.header=किसी भी फ़ाइल को पीडीएफ़ में बदलें
|
||||
fileToPDF.credit=यह सेवा फ़ाइल परिवर्तन के लिए LibreOffice और Unoconv का उपयोग करती है।
|
||||
fileToPDF.supportedFileTypes=समर्थित फ़ाइल प्रकार नीचे दिए गए होने चाहिए हालांकि समर्थित प्रारूपों की पूरी अद्यतन सूची के लिए कृपया LibreOffice दस्तावेज़ीकरण से संदर्भित करें
|
||||
fileToPDF.submit=पीडीएफ़ में बदलें
|
||||
|
||||
|
||||
#compress
|
||||
compress.title=संकुचित करें
|
||||
compress.header=PDF को संकुचित करें
|
||||
compress.credit=यह सेवा PDF संकुचन/अनुकूलन के लिए Ghostscript का उपयोग करती है।
|
||||
compress.selectText.1=मैनुअल मोड - 1 से 4 तक
|
||||
compress.selectText.2=अनुकूलन स्तर:
|
||||
compress.selectText.3=4 (पाठ छवियों के लिए अत्यधिक)
|
||||
compress.selectText.4=स्वत: मोड - निर्धारित आकार पाने के लिए गुणवत्ता को स्वत: समायोजित करता है
|
||||
compress.selectText.5=प्रत्याशित PDF आकार (जैसे 25MB, 10.8MB, 25KB)
|
||||
compress.submit=संकुचित करें
|
||||
|
||||
|
||||
#Add image
|
||||
addImage.title=छवि जोड़ें
|
||||
addImage.header=PDF में छवि जोड़ें
|
||||
addImage.everyPage=हर पृष्ठ?
|
||||
addImage.upload=छवि जोड़ें
|
||||
addImage.submit=छवि जोड़ें
|
||||
|
||||
|
||||
#merge
|
||||
merge.title=मर्ज
|
||||
merge.header=एक से अधिक PDF एक साथ मर्ज करें (2+)
|
||||
merge.sortByName=नाम से क्रमबद्ध करें
|
||||
merge.sortByDate=तारीख से क्रमबद्ध करें
|
||||
merge.submit=मर्ज करें
|
||||
|
||||
|
||||
#pdfOrganiser
|
||||
pdfOrganiser.title=पेज व्यवस्थापक
|
||||
pdfOrganiser.header=PDF पेज व्यवस्थापक
|
||||
pdfOrganiser.submit=पृष्ठों को पुनः व्यवस्थित करें
|
||||
|
||||
|
||||
#multiTool
|
||||
multiTool.title=पीडीएफ मल्टी टूल
|
||||
multiTool.header=पीडीएफ मल्टी टूल
|
||||
|
||||
#view pdf
|
||||
viewPdf.title=पीडीएफ देखें
|
||||
viewPdf.header=पीडीएफ देखें
|
||||
|
||||
#pageRemover
|
||||
pageRemover.title=पेज हटाने वाला
|
||||
pageRemover.header=पीडीएफ पेज हटाने वाला
|
||||
pageRemover.pagesToDelete=हटाने के पेज (पृष्ठ संख्याओं की व्यवस्था के लिए एक कॉमा से अलग संख्याओं की सूची दर्ज करें):
|
||||
pageRemover.submit=पेज हटाएं
|
||||
|
||||
|
||||
#rotate
|
||||
rotate.title=पीडीएफ घुमाएं
|
||||
rotate.header=पीडीएफ घुमाएं
|
||||
rotate.selectAngle=चुनें घुमाने का कोण (90 डिग्री के गुणकों में):
|
||||
rotate.submit=घुमाएं
|
||||
|
||||
|
||||
#merge
|
||||
split.title=पीडीएफ को विभाजित करें
|
||||
split.header=पीडीएफ को विभाजित करें
|
||||
split.desc.1=जिन नंबरों को आप चुनते हैं, वे पृष्ठ संख्या होती हैं जिन पर आप विभाजन करना चाहते हैं।
|
||||
split.desc.2=इसलिए, 1,3,7-8 का चयन करना एक 10 पृष्ठों के दस्तावेज़ को 6 अलग-अलग पीडीएफ में विभाजित करेगा जैसे:
|
||||
split.desc.3=दस्तावेज़ #1: पृष्ठ 1
|
||||
split.desc.4=दस्तावेज़ #2: पृष्ठ 2 और 3
|
||||
split.desc.5=दस्तावेज़ #3: पृष्ठ 4, 5 और 6
|
||||
split.desc.6=दस्तावेज़ #4: पृष्ठ 7
|
||||
split.desc.7=दस्तावेज़ #5: पृष्ठ 8
|
||||
split.desc.8=दस्तावेज़ #6: पृष्ठ 9 और 10
|
||||
split.splitPages=विभाजन करने के लिए पृष्ठ दर्ज करें:
|
||||
split.submit=विभाजित करें
|
||||
|
||||
|
||||
#merge
|
||||
imageToPDF.title=छवि से पीडीएफ में
|
||||
imageToPDF.header=छवि से पीडीएफ में
|
||||
imageToPDF.submit=परिवर्तित करें
|
||||
imageToPDF.selectLabel=छवि फिट विकल्प
|
||||
imageToPDF.fillPage=पेज भरें
|
||||
imageToPDF.fitDocumentToImage=पेज को छवि के आकार में फिट करें
|
||||
imageToPDF.maintainAspectRatio=प्रमाण अनुपात बनाए रखें
|
||||
imageToPDF.selectText.2=पीडीएफ को ऑटो रोटेट करें
|
||||
imageToPDF.selectText.3=मल्टी फ़ाइल तर्क (केवल यदि कई छवियों के साथ काम किया जा रहा है)
|
||||
imageToPDF.selectText.4=एक ही पीडीएफ में मर्ज करें
|
||||
imageToPDF.selectText.5=अलग-अलग पीडीएफ में परिवर्तित करें
|
||||
|
||||
|
||||
#pdfToImage
|
||||
pdfToImage.title=पीडीएफ से छवि
|
||||
pdfToImage.header=पीडीएफ से छवि
|
||||
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=एन्क्रिप्ट करने के लिए पीडीएफ चुनें
|
||||
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.selectText.14=स्वामी पासवर्ड
|
||||
addPassword.selectText.15=दस्तावेज़ के साथ क्या किया जा सकता है, इसे खोलने के बाद (सभी रीडर्स द्वारा समर्थित नहीं है)
|
||||
addPassword.selectText.16=दस्तावेज़ का खोलना परिमित करें
|
||||
addPassword.submit=एन्क्रिप्ट करें
|
||||
|
||||
|
||||
#watermark
|
||||
watermark.title=वॉटरमार्क जोड़ें
|
||||
watermark.header=वॉटरमार्क जोड़ें
|
||||
watermark.selectText.1=वॉटरमार्क जोड़ने के लिए पीडीएफ चुनें:
|
||||
watermark.selectText.2=वॉटरमार्क टेक्स्ट:
|
||||
watermark.selectText.3=फ़ॉन्ट साइज़:
|
||||
watermark.selectText.4=रोटेशन (0-360):
|
||||
watermark.selectText.5=चौड़ाई स्पेसर (प्रत्येक वॉटरमार्क के बीच की जगह आंतरिक रूप से):
|
||||
watermark.selectText.6=ऊंचाई स्पेसर (प्रत्येक वॉटरमार्क के बीच की जगह लंबवती रूप से):
|
||||
watermark.selectText.7=अपारदर्शिता (0% - 100%):
|
||||
watermark.selectText.8=वॉटरमार्क प्रकार:
|
||||
watermark.selectText.9=वॉटरमार्क छवि:
|
||||
watermark.submit=वॉटरमार्क जोड़ें
|
||||
|
||||
|
||||
#Change permissions
|
||||
permissions.title=अनुमतियाँ बदलें
|
||||
permissions.header=अनुमतियाँ बदलें
|
||||
permissions.warning=इन अनुमतियों को अथवा इन्हें बदलने से बचाने के लिए, यह सुनिश्चित करने के लिए सुझाव दिया जाता है कि आप इन्हें एक पासवर्ड के साथ जोड़े, जैसे कि विशेषज्ञता जोड़ने पृष्ठ पर
|
||||
permissions.selectText.1=अनुमतियाँ बदलने के लिए पीडीएफ चुनें
|
||||
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=डिक्रिप्ट करने के लिए पीडीएफ चुनें
|
||||
removePassword.selectText.2=पासवर्ड
|
||||
removePassword.submit=हटाएं
|
||||
|
||||
|
||||
#changeMetadata
|
||||
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=बदलें
|
||||
|
||||
|
||||
#pdfToPDFA
|
||||
pdfToPDFA.title=PDF से PDF/A में
|
||||
pdfToPDFA.header=PDF से PDF/A में
|
||||
pdfToPDFA.credit=इस सेवा में PDF/A परिवर्तन के लिए OCRmyPDF का उपयोग किया जाता है।
|
||||
pdfToPDFA.submit=परिवर्तित करें
|
||||
|
||||
|
||||
#PDFToWord
|
||||
PDFToWord.title=PDF से वर्ड
|
||||
PDFToWord.header=PDF से वर्ड
|
||||
PDFToWord.selectText.1=आउटपुट फ़ाइल प्रारूप
|
||||
PDFToWord.credit=यह सेवा फ़ाइल परिवर्तन के लिए LibreOffice का उपयोग करती है।
|
||||
PDFToWord.submit=परिवर्तित करें
|
||||
|
||||
|
||||
#PDFToPresentation
|
||||
PDFToPresentation.title=PDF से प्रस्तुति
|
||||
PDFToPresentation.header=PDF से प्रस्तुति
|
||||
PDFToPresentation.selectText.1=आउटपुट फ़ाइल प्रारूप
|
||||
PDFToPresentation.credit=यह सेवा फ़ाइल परिवर्तन के लिए LibreOffice का उपयोग करती है।
|
||||
PDFToPresentation.submit=परिवर्तित करें
|
||||
|
||||
|
||||
#PDFToText
|
||||
PDFToText.title=PDF से RTF (पाठ)
|
||||
PDFToText.header=PDF से RTF (पाठ)
|
||||
PDFToText.selectText.1=आउटपुट फ़ाइल प्रारूप
|
||||
PDFToText.credit=यह सेवा फ़ाइल परिवर्तन के लिए LibreOffice का उपयोग करती है।
|
||||
PDFToText.submit=परिवर्तित करें
|
||||
|
||||
|
||||
#PDFToHTML
|
||||
PDFToHTML.title=PDF से HTML
|
||||
PDFToHTML.header=PDF से HTML
|
||||
PDFToHTML.credit=यह सेवा फ़ाइल परिवर्तन के लिए LibreOffice का उपयोग करती है।
|
||||
PDFToHTML.submit=परिवर्तित करें
|
||||
|
||||
|
||||
#PDFToXML
|
||||
PDFToXML.title=PDF से XML
|
||||
PDFToXML.header=PDF से XML
|
||||
PDFToXML.credit=यह सेवा फ़ाइल परिवर्तन के लिए LibreOffice का उपयोग करती है।
|
||||
PDFToXML.submit=परिवर्तित करें
|
||||
|
||||
#PDFToCSV
|
||||
PDFToCSV.title=PDF से CSV
|
||||
PDFToCSV.header=PDF से CSV
|
||||
PDFToCSV.prompt=टेबल निकालने के लिए पृष्ठ चुनें
|
||||
PDFToCSV.submit=निकालें
|
||||
|
||||
#split-by-size-or-count
|
||||
split-by-size-or-count.header=आकार या गणना द्वारा PDF को विभाजित करें
|
||||
split-by-size-or-count.type.label=स्प्लिट प्रकार चुनें
|
||||
split-by-size-or-count.type.size=आकार द्वारा
|
||||
split-by-size-or-count.type.pageCount=पृष्ठ गणना द्वारा
|
||||
split-by-size-or-count.type.docCount=दस्तावेज़ गणना द्वारा
|
||||
split-by-size-or-count.value.label=मूल्य दर्ज करें
|
||||
split-by-size-or-count.value.placeholder=आकार दर्ज करें (उदाहरण के लिए, 2MB या 3KB) या गणना (उदाहरण के लिए, 5)
|
||||
split-by-size-or-count.submit=प्रस्तुत करें
|
||||
|
||||
|
||||
#overlay-pdfs
|
||||
overlay-pdfs.header=PDF फ़ाइलों को ओवरले करें
|
||||
overlay-pdfs.baseFile.label=मूल PDF फ़ाइल का चयन करें
|
||||
overlay-pdfs.overlayFiles.label=ओवरले करने वाली PDF फ़ाइलें चुनें
|
||||
overlay-pdfs.mode.label=ओवरले मोड का चयन करें
|
||||
overlay-pdfs.mode.sequential=क्रमशः ओवरले करें
|
||||
overlay-pdfs.mode.interleaved=इंटरलीव्ड ओवरले करें
|
||||
overlay-pdfs.mode.fixedRepeat=निश्चित पुनरावृत्ति ओवरले करें
|
||||
overlay-pdfs.counts.label=ओवरले की संख्या (निश्चित पुनरावृत्ति मोड के लिए)
|
||||
overlay-pdfs.counts.placeholder=कमा-विराम दिया गया संख्या दर्ज करें (उदाहरण के लिए, 2,3,1)
|
||||
overlay-pdfs.position.label=ओवरले की स्थिति का चयन करें
|
||||
overlay-pdfs.position.foreground=पृष्ठरेखा
|
||||
overlay-pdfs.position.background=पृष्ठमाध्य
|
||||
overlay-pdfs.submit=प्रस्तुत करें
|
||||
|
||||
|
||||
#split-by-sections
|
||||
split-by-sections.title=अनुभागों में PDF को विभाजित करें
|
||||
split-by-sections.header=खंडों में PDF को विभाजित करें
|
||||
split-by-sections.horizontal.label=क्षैतिज विभाजन
|
||||
split-by-sections.vertical.label=लंबवत विभाजन
|
||||
split-by-sections.horizontal.placeholder=क्षैतिज विभाजन की संख्या दर्ज करें
|
||||
split-by-sections.vertical.placeholder=लंबवत विभाजन की संख्या दर्ज करें
|
||||
split-by-sections.submit=PDF को विभाजित करें
|
||||
896
src/main/resources/messages_hu_HU.properties
Normal file
896
src/main/resources/messages_hu_HU.properties
Normal file
@@ -0,0 +1,896 @@
|
||||
###########
|
||||
# Generic #
|
||||
###########
|
||||
# the direction that the language is written (ltr=left to right, rtl = right to left)
|
||||
language.direction=ltr
|
||||
|
||||
pdfPrompt=Válasszon PDF-fájlokat
|
||||
multiPdfPrompt=Válasszon PDF-fájlokat (2+)
|
||||
multiPdfDropPrompt=Válassza ki (vagy húzza ide) az összes szükséges PDF-fájlt
|
||||
imgPrompt=Válasszon képeket
|
||||
genericSubmit=Beküldés
|
||||
processTimeWarning=Figyelmeztetés: Ez a folyamat akár egy percig is eltarthat a fájlmérettől függően
|
||||
pageOrderPrompt=Egyedi oldalsorrend (Adjon meg vesszővel elválasztott oldalszámokat vagy függvényeket, például 2n+1):
|
||||
goToPage=Ugrás
|
||||
true=Igaz
|
||||
false=Hamis
|
||||
unknown=Ismeretlen
|
||||
save=Mentés
|
||||
close=Bezárás
|
||||
filesSelected=kiválasztott fájlok
|
||||
noFavourites=Nincs hozzáadva kedvenc
|
||||
bored=Unatkozol?
|
||||
alphabet=Ábécé
|
||||
downloadPdf=PDF letöltése
|
||||
text=Szöveg
|
||||
font=Betűtípus
|
||||
selectFillter=-- Válasszon --
|
||||
pageNum=Oldalszám
|
||||
sizes.small=Kicsi
|
||||
sizes.medium=Közepes
|
||||
sizes.large=Nagy
|
||||
sizes.x-large=X-Nagy
|
||||
error.pdfPassword=A PDF-dokumentum jelszóval védett, és vagy nem adták meg a jelszót, vagy helytelen volt
|
||||
delete=Törlés
|
||||
username=Felhasználónév
|
||||
password=Jelszó
|
||||
welcome=Üdvözöljük
|
||||
property=Tulajdonság
|
||||
black=Fekete
|
||||
white=Fehér
|
||||
red=Piros
|
||||
green=Zöld
|
||||
blue=Kék
|
||||
custom=Egyedi...
|
||||
|
||||
changedCredsMessage=A hitelek megváltoztak!
|
||||
notAuthenticatedMessage=Felhasználó nincs hitelesítve.
|
||||
userNotFoundMessage=A felhasználó nem található.
|
||||
incorrectPasswordMessage=A jelenlegi jelszó helytelen.
|
||||
usernameExistsMessage=Az új felhasználónév már létezik.
|
||||
|
||||
|
||||
|
||||
#############
|
||||
# NAVBAR #
|
||||
#############
|
||||
navbar.convert=Átalakítás
|
||||
navbar.security=Biztonság
|
||||
navbar.other=Egyéb
|
||||
navbar.darkmode=Sötét mód
|
||||
navbar.pageOps=Lap műveletek
|
||||
navbar.settings=Beállítások
|
||||
|
||||
#############
|
||||
# SETTINGS #
|
||||
#############
|
||||
settings.title=Beállítások
|
||||
settings.update=Frisítés elérhető
|
||||
settings.appVersion=App Verzió:
|
||||
settings.downloadOption.title=Válassza ki a letöltési lehetőséget (Egyetlen fájl esetén a nem tömörített letöltésekhez):
|
||||
settings.downloadOption.1=Nyissa meg ugyanabban az ablakban
|
||||
settings.downloadOption.2=Nyissa meg új ablakban
|
||||
settings.downloadOption.3=Töltse le a fájlt
|
||||
settings.zipThreshold=Fájlok tömörítése, ha a letöltött fájlok száma meghaladja
|
||||
settings.signOut=Kijelentkezés
|
||||
settings.accountSettings=Fiókbeállítások
|
||||
|
||||
|
||||
|
||||
changeCreds.title=Hitelesítés megváltoztatása
|
||||
changeCreds.header=Frissítse fiókadatait
|
||||
changeCreds.changeUserAndPassword=Alapértelmezett bejelentkezési adatokat használ. Adjon meg egy új jelszót (és felhasználónevet, ha szeretné)
|
||||
changeCreds.newUsername=Új felhasználónév
|
||||
changeCreds.oldPassword=Jelenlegi jelszó
|
||||
changeCreds.newPassword=Új jelszó
|
||||
changeCreds.confirmNewPassword=Új jelszó megerősítése
|
||||
changeCreds.submit=Változtatások elküldése
|
||||
|
||||
|
||||
|
||||
account.title=Fiókbeállítások
|
||||
account.accountSettings=Fiókbeállítások
|
||||
account.adminSettings=Admin Beállítások - Felhasználók megtekintése és hozzáadása
|
||||
account.userControlSettings=Felhasználói vezérlési beállítások
|
||||
account.changeUsername=Új felhasználónév
|
||||
account.changeUsername=Új felhasználónév
|
||||
account.password=Megerősítő jelszó
|
||||
account.oldPassword=Régi jelszó
|
||||
account.newPassword=Új jelszó
|
||||
account.changePassword=Jelszó módosítása
|
||||
account.confirmNewPassword=Új jelszó megerősítése
|
||||
account.signOut=Kijelentkezés
|
||||
account.yourApiKey=Az Ön API-kulcsa
|
||||
account.syncTitle=Böngészőbeállítások szinkronizálása a fiókkal
|
||||
account.settingsCompare=Beállítások összehasonlítása:
|
||||
account.property=Ingatlan
|
||||
account.webBrowserSettings=Web Böngésző beállítása
|
||||
account.syncToBrowser=Szinkronizálás Fiók -> Böngésző
|
||||
account.syncToAccount=Szinkronizálás Fiók <- Böngésző
|
||||
|
||||
|
||||
adminUserSettings.title=Felhasználói Vezérlési Beállítások
|
||||
adminUserSettings.header=Adminisztrátori Felhasználói Vezérlési Beállítások
|
||||
adminUserSettings.admin=Adminisztrátor
|
||||
adminUserSettings.user=Felhasználó
|
||||
adminUserSettings.addUser=Új felhasználó hozzáadása
|
||||
adminUserSettings.roles=Szerepek
|
||||
adminUserSettings.role=Szerep
|
||||
adminUserSettings.actions=Műveletek
|
||||
adminUserSettings.apiUser=Korlátozott API-felhasználó
|
||||
adminUserSettings.webOnlyUser=Csak webes felhasználó
|
||||
adminUserSettings.demoUser=Demo User (No custom settings)
|
||||
adminUserSettings.forceChange=Kényszerítse a felhasználót a felhasználónév/jelszó megváltoztatására bejelentkezéskor
|
||||
adminUserSettings.submit=Felhasználó mentése
|
||||
|
||||
#############
|
||||
# HOME-PAGE #
|
||||
#############
|
||||
home.desc=Lokálisan hostolt egyszerű megoldás minden PDF igényéhez.
|
||||
home.searchBar=Keresés funkciókra...
|
||||
|
||||
|
||||
home.viewPdf.title=PDF Megtekintése
|
||||
home.viewPdf.desc=Megtekintés, annotálás, szöveg vagy képek hozzáadása
|
||||
viewPdf.tags=megtekintés,olvasás,annotálás,szöveg,kép
|
||||
|
||||
home.multiTool.title=PDF Multi Eszköz
|
||||
home.multiTool.desc=Egyesítés, forgatás, átrendezés és lapok eltávolítása
|
||||
multiTool.tags=Multi Eszköz,Multi művelet,UI,kattintás húzás,előtér,ügyfél oldal,interaktív,megfogható,mozgás
|
||||
|
||||
home.merge.title=Egyesítés
|
||||
home.merge.desc=Több PDF egyszerű egyesítése.
|
||||
merge.tags=egyesítés,Lapműveletek,Háttér,server oldal
|
||||
|
||||
home.split.title=Osztás
|
||||
home.split.desc=PDF-ek felosztása több dokumentumra
|
||||
split.tags=Lapműveletek,osztás,Több oldal,vágás,server oldal
|
||||
|
||||
home.rotate.title=Forgatás
|
||||
home.rotate.desc=PDF-ek egyszerű forgatása.
|
||||
rotate.tags=server oldal
|
||||
|
||||
|
||||
home.imageToPdf.title=Kép PDF-be
|
||||
home.imageToPdf.desc=Kép (PNG, JPEG, GIF) konvertálása PDF-fé.
|
||||
imageToPdf.tags=konverzió,img,jpg,kép,fotó
|
||||
|
||||
home.pdfToImage.title=PDF képpé
|
||||
home.pdfToImage.desc=PDF konvertálása képpé. (PNG, JPEG, GIF)
|
||||
pdfToImage.tags=konverzió,img,jpg,kép,fotó
|
||||
|
||||
home.pdfOrganiser.title=Szervezés
|
||||
home.pdfOrganiser.desc=Lapok eltávolítása/átszervezése bármilyen sorrendben
|
||||
pdfOrganiser.tags=duplex,páros,páratlan,rendezés,mozgatás
|
||||
|
||||
|
||||
home.addImage.title=Kép hozzáadása
|
||||
home.addImage.desc=Kép hozzáadása a PDF megadott helyére
|
||||
addImage.tags=img,jpg,kép,fotó
|
||||
|
||||
home.watermark.title=Vízjel hozzáadása
|
||||
home.watermark.desc=Egyéni vízjel hozzáadása a PDF dokumentumhoz.
|
||||
watermark.tags=Szöveg,ismétlődő,címke,saját,szerzői jog,védjegy,img,jpg,kép,fotó
|
||||
|
||||
home.permissions.title=Engedélyek módosítása
|
||||
home.permissions.desc=Változtassa meg a PDF dokumentum engedélyeit
|
||||
permissions.tags=olvasás,írás,szerkesztés,nyomtatás
|
||||
|
||||
|
||||
home.removePages.title=Eltávolítás
|
||||
home.removePages.desc=Szükségtelen lapok törlése a PDF dokumentumból.
|
||||
removePages.tags=Lapok eltávolítása,lapok törlése
|
||||
|
||||
home.addPassword.title=Jelszó hozzáadása
|
||||
home.addPassword.desc=Titkosítsa a PDF dokumentumát jelszóval.
|
||||
addPassword.tags=biztonság,biztonságos
|
||||
|
||||
home.removePassword.title=Jelszó eltávolítása
|
||||
home.removePassword.desc=Vegye le a jelszóvédelmet a PDF dokumentumáról.
|
||||
removePassword.tags=biztonság,Lezárat, biztonság,jelszó törlése
|
||||
|
||||
home.compressPdfs.title=Tömörítés
|
||||
home.compressPdfs.desc=PDF-ek tömörítése a fájlméret csökkentése érdekében.
|
||||
compressPdfs.tags=szorít,kicsi,miniatűr
|
||||
|
||||
|
||||
home.changeMetadata.title=Metaadatok Módosítása
|
||||
home.changeMetadata.desc=Metaadatok Módosítása/Eltávolítása/Hozzáadása egy PDF dokumentumból
|
||||
changeMetadata.tags=Cím,szerző,dátum,alkotás,idő,közzétevő,gyártó,statisztika
|
||||
|
||||
home.fileToPDF.title=Fájl konvertálása PDF-be
|
||||
home.fileToPDF.desc=Közel minden fájl konvertálása PDF-be (DOCX, PNG, XLS, PPT, TXT és más)
|
||||
fileToPDF.tags=transzformáció,formátum,dokumentum,kép,dia,szöveg,konverzió,iroda,dokumentumok,word,excel,powerpoint
|
||||
|
||||
home.ocr.title=OCR / Tisztítás szkennelésekből
|
||||
home.ocr.desc=Tisztítás szkennelésekből és szöveg észlelése képeken belül egy PDF-ben, majd visszahozza szövegként.
|
||||
ocr.tags=felismerés,szöveg,kép,szken,gép,felismert,azonosítás,szerkeszthető
|
||||
|
||||
|
||||
home.extractImages.title=Képek kinyerése
|
||||
home.extractImages.desc=Az összes kép kinyerése egy PDF-ből és mentése zip-be
|
||||
extractImages.tags=kép,fotó,mentés,archívum,zip,rögzítés,gyűjtés
|
||||
|
||||
home.pdfToPDFA.title=PDF >> PDF/A
|
||||
home.pdfToPDFA.desc=PDF konvertálása PDF/A formátumra hosszú távú tárolás céljából
|
||||
pdfToPDFA.tags=archívum,hosszú távú,standard,konverzió,tárolás,megőrzés
|
||||
|
||||
home.PDFToWord.title=PDF >> Word
|
||||
home.PDFToWord.desc=PDF konvertálása Word formátumokra (DOC, DOCX és ODT)
|
||||
PDFToWord.tags=doc,docx,odt,word,transzformáció,formátum,konverzió,iroda,microsoft,docfile
|
||||
|
||||
home.PDFToPresentation.title=PDF >> Presentation
|
||||
home.PDFToPresentation.desc=PDF konvertálása prezentációs formátumokra (PPT, PPTX és ODP)
|
||||
PDFToPresentation.tags=dia,bemutatás,iroda,microsoft
|
||||
|
||||
home.PDFToText.title=PDF >> RTF (szöveg)
|
||||
home.PDFToText.desc=PDF konvertálása Szöveg vagy RTF formátumra
|
||||
PDFToText.tags=richformat,rich text format
|
||||
|
||||
home.PDFToHTML.title=PDF >> HTML
|
||||
home.PDFToHTML.desc=PDF konvertálása HTML formátumra
|
||||
PDFToHTML.tags=web tartalom,böngészőbarát
|
||||
|
||||
|
||||
home.PDFToXML.title=PDF >> XML
|
||||
home.PDFToXML.desc=PDF konvertálása XML formátumra
|
||||
PDFToXML.tags=adat-kinyerés,strukturált tartalom,interop,konverzió
|
||||
|
||||
home.ScannerImageSplit.title=Szkennelt fotók észlelése/Osztás
|
||||
home.ScannerImageSplit.desc=Több fénykép szétválasztása egy fénykép/PDF-ben belül
|
||||
ScannerImageSplit.tags=külön,kép észlelés,szkennelés,többfénykép,szervez
|
||||
|
||||
home.sign.title=Aláírás
|
||||
home.sign.desc=Aláírás hozzáadása PDF-hez rajzzal, szöveggel vagy képpel
|
||||
sign.tags=jogosítvány,jegyzék,rajzolt-aláírás,szöveg-aláírás,kép-aláírás
|
||||
|
||||
home.flatten.title=Lapok laposítása
|
||||
home.flatten.desc=Minden interaktív elem és űrlap eltávolítása egy PDF-ből
|
||||
flatten.tags=statikus,dezaktiválás,nem-interaktív,egyszerűsít
|
||||
|
||||
home.repair.title=Javítás
|
||||
home.repair.desc=Megpróbálja helyreállítani a sérült/hiba PDF-t
|
||||
repair.tags=javítás,visszaállítás,korrektúra,visszaszerzés
|
||||
|
||||
home.removeBlanks.title=Üres lapok eltávolítása
|
||||
home.removeBlanks.desc=Felismeri és eltávolítja az üres lapokat a dokumentumból
|
||||
removeBlanks.tags=takarítás,egyszerűsítés,nem-tartalom,szervez
|
||||
|
||||
home.removeAnnotations.title=Remove Annotations
|
||||
home.removeAnnotations.desc=Removes all comments/annotations from a PDF
|
||||
removeAnnotations.tags=comments,highlight,notes,markup,remove
|
||||
|
||||
home.compare.title=Összehasonlítás
|
||||
home.compare.desc=Összehasonlítja és megmutatja a különbségeket két PDF dokumentum között
|
||||
compare.tags=kiemel,ellentét,változások,elemzés
|
||||
|
||||
home.certSign.title=Aláírás Tanúsítvánnyal
|
||||
home.certSign.desc=PDF aláírása tanúsítvánnyal/kulccsal (PEM/P12)
|
||||
certSign.tags=hitelesítés,PEM,P12,hivatalos,segitít,álca
|
||||
|
||||
home.pageLayout.title=Több oldal elrendezése
|
||||
home.pageLayout.desc=Több oldal egyesítése egy PDF dokumentumban egyetlen oldallá
|
||||
pageLayout.tags=egyesítés,kompozit,egy oldal,megszervez
|
||||
|
||||
home.scalePages.title=Oldalméret/skála beállítása
|
||||
home.scalePages.desc=Oldal méretének/skálájának megváltoztatása és/vagy tartalmának
|
||||
scalePages.tags=átalakítás,módosítás,méret,alkalmazkodik
|
||||
|
||||
home.pipeline.title=Csővezeték (Haladó)
|
||||
home.pipeline.desc=Több művelet futtatása PDF-eken csővezeték scriptek definiálásával
|
||||
pipeline.tags=automatizálás,sorrend,szkriptelt,csomag-feldolgozás
|
||||
|
||||
home.add-page-numbers.title=Lapok számának hozzáadása
|
||||
home.add-page-numbers.desc=Lapszám hozzáadása a dokumentumhoz egy meghatározott helyen
|
||||
add-page-numbers.tags=lapszámozás,címke,szervez,index
|
||||
|
||||
home.auto-rename.title=Automatikus átnevezés PDF fájl
|
||||
home.auto-rename.desc=Automatikusan átnevezi a PDF fájlt a felderített fejléc alapján
|
||||
auto-rename.tags=auto-felismerés,fejléc-alapú,szervezés,címkézés
|
||||
|
||||
home.adjust-contrast.title=Színek/Kontraszt beállítása
|
||||
home.adjust-contrast.desc=PDF kontrasztjának, telítettségének és világosságának beállítása
|
||||
adjust-contrast.tags=szín-korrekció,beállítás,módosítás,fokoz
|
||||
|
||||
home.crop.title=Crop PDF
|
||||
home.crop.desc=PDF vágása a méret csökkentése érdekében (a szöveg megőrzése mellett!)
|
||||
crop.tags=vágás,csökkentés,szerkesztés,forma
|
||||
|
||||
home.autoSplitPDF.title=Automatikus oldalak szétválasztása
|
||||
home.autoSplitPDF.desc=Automatikus szétválasztás a beolvasott oldalas PDF alapján fizikai szkennelt oldal szétválasztó QR kóddal
|
||||
autoSplitPDF.tags=QR-alapú,külön,szkennel-oldal,szervez
|
||||
|
||||
home.sanitizePdf.title=Szankcionálás
|
||||
home.sanitizePdf.desc=Szankcionálja a szkripteket és egyéb elemeket a PDF fájlokból
|
||||
sanitizePdf.tags=tisztítás,biztonságos,biztonság,töröl-veszélyek
|
||||
|
||||
home.URLToPDF.title=URL/Weboldal PDF-be
|
||||
home.URLToPDF.desc=Bármely http(s) URL átalakítása PDF-be
|
||||
URLToPDF.tags=web-rögzítés,oldal-mentés,web-dokumentum,archívum
|
||||
|
||||
home.HTMLToPDF.title=HTML PDF-be
|
||||
home.HTMLToPDF.desc=Bármely HTML fájl vagy tömörített fájl átalakítása PDF-be
|
||||
HTMLToPDF.tags=markup,web-tartalom,transzformáció,konverzió
|
||||
|
||||
|
||||
home.MarkdownToPDF.title=Markdown PDF-be
|
||||
home.MarkdownToPDF.desc=Bármely Markdown fájl átalakítása PDF-be
|
||||
MarkdownToPDF.tags=markup,web-tartalom,transzformáció,konverzió
|
||||
|
||||
|
||||
home.getPdfInfo.title=Összes információ a PDF-ről
|
||||
home.getPdfInfo.desc=Az összes lehetséges információ beszerzése a PDF-ekről
|
||||
getPdfInfo.tags=információ,adat,statisztika,statisztika
|
||||
|
||||
|
||||
home.extractPage.title=Lapok kinyerése
|
||||
home.extractPage.desc=Válassza ki a lapokat a PDF-ből
|
||||
extractPage.tags=kinyer
|
||||
|
||||
|
||||
home.PdfToSinglePage.title=PDF egyetlen nagy lapba
|
||||
home.PdfToSinglePage.desc=Az összes PDF lap egyesítése egyetlen nagy lapba
|
||||
PdfToSinglePage.tags=egyetlen lap
|
||||
|
||||
|
||||
home.showJS.title=JavaScript megjelenítése
|
||||
home.showJS.desc=Keres és megjelenít bármilyen JS-t, amit beinjektáltak a PDF-be
|
||||
showJS.tags=Elrejt,Elrejtés,kitakarás,fekete,fekete,marker,elrejtett
|
||||
|
||||
home.autoRedact.title=Automatikus Elrejtés
|
||||
home.autoRedact.desc=Automatikusan kitakar (elrejt) szöveget egy PDF-ben az input szöveg alapján
|
||||
showJS.tags=Elrejt,Elrejtés,kitakarás,fekete,fekete,marker,elrejtett
|
||||
|
||||
home.tableExtraxt.title=PDF to CSV
|
||||
home.tableExtraxt.desc=Táblázatok kinyerése a PDF-ből CSV formátumra konvertálva
|
||||
tableExtraxt.tags=CSV,Táblázat kinyerése,kinyer,konvertál
|
||||
|
||||
|
||||
home.autoSizeSplitPDF.title=Automatikus szétválasztás méret/számláló alapján
|
||||
home.autoSizeSplitPDF.desc=Egyetlen PDF szétválasztása több dokumentummá méret, oldalszám vagy dokumentum szám alapján
|
||||
autoSizeSplitPDF.tags=pdf,szétválasztás,dokumentum,szervezet
|
||||
|
||||
|
||||
home.overlay-pdfs.title=PDF fájlok átlapolása
|
||||
home.overlay-pdfs.desc=PDF fájlok átlapolása egyik dokumentum a másik fölé helyezésével
|
||||
overlay-pdfs.tags=Átlapolás
|
||||
|
||||
home.split-by-sections.title=PDF Szakaszokra osztása
|
||||
home.split-by-sections.desc=Minden oldal felosztása kisebb vízszintes és függőleges szakaszokra
|
||||
split-by-sections.tags=Szakasz elosztás, felosztás, testreszabás
|
||||
|
||||
###########################
|
||||
# #
|
||||
# WEB PAGES #
|
||||
# #
|
||||
###########################
|
||||
#login
|
||||
login.title=Bejelentkezés
|
||||
login.signin=Bejelentkezés
|
||||
login.rememberme=Emlékezz rám
|
||||
login.invalid=Érvénytelen felhasználónév vagy jelszó!
|
||||
login.locked=A fiókja zárolva lett!
|
||||
login.signinTitle=Kérjük, jelentkezzen be!
|
||||
|
||||
|
||||
#auto-redact
|
||||
autoRedact.title=Érzékeny tartalom eltávolítása
|
||||
autoRedact.header=Érzékeny tartalom eltávolítása
|
||||
autoRedact.colorLabel=Szín
|
||||
autoRedact.textsToRedactLabel=Kivonand szövegek (sorokra bontva)
|
||||
autoRedact.textsToRedactPlaceholder=például \nBizalmas \nLegfelsőbb Titok
|
||||
autoRedact.useRegexLabel=Reguláris kifejezés használata
|
||||
autoRedact.wholeWordSearchLabel=Teljes szó keresése
|
||||
autoRedact.customPaddingLabel=Egyedi extra kitöltés
|
||||
autoRedact.convertPDFToImageLabel=PDF átalakítása PDF-képé (a doboz mögötti szöveg eltávolításához)
|
||||
autoRedact.submitButton=Elküld
|
||||
|
||||
|
||||
#showJS
|
||||
showJS.title=JavaScript megjelenítése
|
||||
showJS.header=JavaScript megjelenítése
|
||||
showJS.downloadJS=JavaScript letöltése
|
||||
showJS.submit=Megjelenítés
|
||||
|
||||
|
||||
#pdfToSinglePage
|
||||
pdfToSinglePage.title=PDF egyetlen oldalra
|
||||
pdfToSinglePage.header=PDF egyetlen oldalra
|
||||
pdfToSinglePage.submit=Átalakítás egyetlen oldallá
|
||||
|
||||
|
||||
#pageExtracter
|
||||
pageExtracter.title=Oldalak kinyerése
|
||||
pageExtracter.header=Oldalak kinyerése
|
||||
pageExtracter.submit=Kinyerés
|
||||
|
||||
|
||||
#getPdfInfo
|
||||
getPdfInfo.title=Információk kinyerése a PDF-ről
|
||||
getPdfInfo.header=Információk kinyerése a PDF-ről
|
||||
getPdfInfo.submit=Információk kinyerése
|
||||
getPdfInfo.downloadJson=JSON letöltése
|
||||
|
||||
|
||||
#markdown-to-pdf
|
||||
MarkdownToPDF.title=Markdown >> PDF
|
||||
MarkdownToPDF.header=Markdown >> PDF
|
||||
MarkdownToPDF.submit=Átalakítás
|
||||
MarkdownToPDF.help=Az átalakítás folyamatban
|
||||
MarkdownToPDF.credit=WeasyPrint alkalmazása
|
||||
|
||||
|
||||
|
||||
#url-to-pdf
|
||||
URLToPDF.title=URL >> PDF
|
||||
URLToPDF.header=URL >> PDF
|
||||
URLToPDF.submit=Átalakítás
|
||||
URLToPDF.credit=WeasyPrint alkalmazása
|
||||
|
||||
|
||||
#html-to-pdf
|
||||
HTMLToPDF.title=HTML >> PDF
|
||||
HTMLToPDF.header=HTML >> PDF
|
||||
HTMLToPDF.help=Elfogad HTML fájlokat és ZIP-fájlokat, amelyek tartalmaznak html/css/képeket stb.
|
||||
HTMLToPDF.submit=Átalakítás
|
||||
HTMLToPDF.credit=WeasyPrint alkalmazása
|
||||
|
||||
|
||||
#sanitizePDF
|
||||
sanitizePDF.title=PDF tisztítása
|
||||
sanitizePDF.header=PDF fájl megtisztítása
|
||||
sanitizePDF.selectText.1=JavaScript műveletek eltávolítása
|
||||
sanitizePDF.selectText.2=Beágyazott fájlok eltávolítása
|
||||
sanitizePDF.selectText.3=Metaadatok eltávolítása
|
||||
sanitizePDF.selectText.4=Hivatkozások eltávolítása
|
||||
sanitizePDF.selectText.5=Betűtípusok eltávolítása
|
||||
sanitizePDF.submit=PDF tisztítása
|
||||
|
||||
|
||||
#addPageNumbers
|
||||
addPageNumbers.title=Oldalszámok hozzáadása
|
||||
addPageNumbers.header=Oldalszámok hozzáadása
|
||||
addPageNumbers.selectText.1=Válassza ki a PDF fájlt:
|
||||
addPageNumbers.selectText.2=Margó mérete
|
||||
addPageNumbers.selectText.3=Pozíció
|
||||
addPageNumbers.selectText.4=Kezdő szám
|
||||
addPageNumbers.selectText.5=Oldalak számozása
|
||||
addPageNumbers.selectText.6=Egyedi szöveg
|
||||
addPageNumbers.customTextDesc=Egyedi szöveg
|
||||
addPageNumbers.numberPagesDesc=Melyik oldalakat számozzuk? Alapértelmezés szerint 'mind', vagy megadhatja még így: 1-5, esetleg 2,5,9.
|
||||
addPageNumbers.customNumberDesc=Alapértelmezés szerint {n}, megadhatja még 'Oldal {n} a {teljes}', 'Szöveg-{n}', '{fájlnév}-{n}' formában.
|
||||
addPageNumbers.submit=Oldalszámok hozzáadása
|
||||
|
||||
|
||||
#auto-rename
|
||||
auto-rename.title=Automatikus átnevezés
|
||||
auto-rename.header=Automatikus átnevezés PDF
|
||||
auto-rename.submit=Automatikus átnevezés
|
||||
|
||||
|
||||
#adjustContrast
|
||||
adjustContrast.title=Kontraszt beállítása
|
||||
adjustContrast.header=Kontraszt beállítása
|
||||
adjustContrast.contrast=Kontraszt:
|
||||
adjustContrast.brightness=Fényerő:
|
||||
adjustContrast.saturation=Színtelítettség:
|
||||
adjustContrast.download=Letöltés
|
||||
|
||||
|
||||
#crop
|
||||
crop.title=Körülvágás
|
||||
crop.header=Kép körülvégésa
|
||||
crop.submit=Elküldés
|
||||
|
||||
|
||||
#autoSplitPDF
|
||||
autoSplitPDF.title=Automatikus PDF szétválasztás
|
||||
autoSplitPDF.header=Automatikus PDF szétválasztás
|
||||
autoSplitPDF.description=Nyomtasson, helyezzen be, szkenneljen, töltse fel, és hagyja, hogy az automatikus szétválasztás elvégezze a dokumentumok rendezését. A rendezéshez nincs szükség manuális beavatkozásra.
|
||||
autoSplitPDF.selectText.1=Nyomtasson ki néhány elválasztó lapot lentebb (Fekete-fehér is megfelel).
|
||||
autoSplitPDF.selectText.2=Helyezze be az összes dokumentumot egyszerre az elválasztó lapok közé történő beillesztéssel.
|
||||
autoSplitPDF.selectText.3=Töltse fel a készített egyetlen nagy szkennelt PDF fájlt, és hagyja, hogy a Stirling PDF megoldja a többit.
|
||||
autoSplitPDF.selectText.4=Az elválasztó lapok automatikusan észlelhetők és eltávolíthatók, így garantáltan tiszta lesz a végső dokumentum.
|
||||
autoSplitPDF.formPrompt=Küldje el a Stirling-PDF oldal-elválasztót tartalmazó PDF-t:
|
||||
autoSplitPDF.duplexMode=Duplex mód (Elő- és hátlapi szkennelés)
|
||||
autoSplitPDF.dividerDownload1='Automatikus Szétválasztó Elválasztólap letöltése (minimális).pdf'
|
||||
autoSplitPDF.dividerDownload2='Automatikus Szétválasztó Elválasztólap letöltése (utasításokkal).pdf'
|
||||
autoSplitPDF.submit=Elküldés
|
||||
|
||||
|
||||
#pipeline
|
||||
pipeline.title=Folyamat
|
||||
|
||||
|
||||
#pageLayout
|
||||
pageLayout.title=Többoldalas elrendezés
|
||||
pageLayout.header=Többoldalas elrendezés
|
||||
pageLayout.pagesPerSheet=Oldalak laponként:
|
||||
pageLayout.addBorder=Keret hozzáadása
|
||||
pageLayout.submit=Elküldés
|
||||
|
||||
|
||||
#scalePages
|
||||
scalePages.title=Oldalméret beállítása
|
||||
scalePages.header=Oldalméret beállítása
|
||||
scalePages.pageSize=A dokumentum egy oldalának mérete.
|
||||
scalePages.scaleFactor=Az oldal nagyításának szintje (vágás).
|
||||
scalePages.submit=Küldés
|
||||
|
||||
|
||||
#certSign
|
||||
certSign.title=Tanúsítvánnyal történő aláírás
|
||||
certSign.header=Aláírás PDF tanúsítvánnyal (fejlesztés alatt)
|
||||
certSign.selectPDF=Válasszon PDF fájlt az aláíráshoz:
|
||||
certSign.selectKey=Válassza ki a saját kulcsfájlját (PKCS#8 formátum, lehet .pem vagy .der kiterjesztésű):
|
||||
certSign.selectCert=Válassza ki a tanúsítványfájlját (X.509 formátum, lehet .pem vagy .der kiterjesztésű):
|
||||
certSign.selectP12=Válassza ki a PKCS#12 kulcstár fájlját (.p12 vagy .pfx) (Opcionális, ha rendelkezésre áll, tartalmaznia kell a privát kulcsot és a tanúsítványt.):
|
||||
certSign.certType=Tanúsítvány típusa
|
||||
certSign.password=Adja meg a kulcstár vagy a privát kulcs jelszavát (ha van):
|
||||
certSign.showSig=Aláírás megjelenítése
|
||||
certSign.reason=Ok
|
||||
certSign.location=Hely
|
||||
certSign.name=Név
|
||||
certSign.submit=PDF aláírása
|
||||
|
||||
|
||||
#removeBlanks
|
||||
removeBlanks.title=Üres oldalak eltávolítása
|
||||
removeBlanks.header=Üres oldalak eltávolítása
|
||||
removeBlanks.threshold=Pixel fehérség küszöbértéke:
|
||||
removeBlanks.thresholdDesc=A "fehér" pixel meghatározásához szükséges küszöbérték. 0 = fekete, 255 = tiszta fehér.
|
||||
removeBlanks.whitePercent=Fehér százalék (%):
|
||||
removeBlanks.whitePercentDesc=Az oldalakon található 'fehér' pixelek százaléka, amelyek eltávolításra kerülnek.
|
||||
removeBlanks.submit=Üres oldalak eltávolítása
|
||||
|
||||
|
||||
#removeAnnotations
|
||||
removeAnnotations.title=Remove Annotations
|
||||
removeAnnotations.header=Remove Annotations
|
||||
removeAnnotations.submit=Remove
|
||||
|
||||
|
||||
#compare
|
||||
compare.title=Összehasonlítás
|
||||
compare.header=PDF-ek összehasonlítása
|
||||
compare.document.1=Dokumentum 1
|
||||
compare.document.2=Dokumentum 2
|
||||
compare.submit=Összehasonlítás
|
||||
|
||||
|
||||
#sign
|
||||
sign.title=Aláírás
|
||||
sign.header=PDF-ek aláírása
|
||||
sign.upload=Kép feltöltése
|
||||
sign.draw=Aláírás rajzolása
|
||||
sign.text=Szöveg beírása
|
||||
sign.clear=Törlés
|
||||
sign.add=Hozzáadás
|
||||
|
||||
|
||||
#repair
|
||||
repair.title=Javítás
|
||||
repair.header=PDF-ek javítása
|
||||
repair.submit=Javítás
|
||||
|
||||
|
||||
#flatten
|
||||
flatten.title=Kiegyenlítés
|
||||
flatten.header=PDF-ek kiegyenlítése
|
||||
flatten.submit=Kiegyenlítés
|
||||
|
||||
|
||||
#ScannerImageSplit
|
||||
ScannerImageSplit.selectText.1=Szög küszöbértéke:
|
||||
ScannerImageSplit.selectText.2=Minimális forgatás abszolút szögének meghatározása (alapértelmezett: 10).
|
||||
ScannerImageSplit.selectText.3=Tolerancia:
|
||||
ScannerImageSplit.selectText.4=A háttér szín körülbelüli változási tartományának meghatározása (alapértelmezett: 30).
|
||||
ScannerImageSplit.selectText.5=Minimális terület:
|
||||
ScannerImageSplit.selectText.6=A fotók minimális területének beállítása (alapértelmezett: 10000).
|
||||
ScannerImageSplit.selectText.7=Minimális kontúr terület:
|
||||
ScannerImageSplit.selectText.8=A fotók minimális kontúrterületének beállítása
|
||||
ScannerImageSplit.selectText.9=Keret mérete:
|
||||
ScannerImageSplit.selectText.10=A hozzáadott és eltávolított keret méretének beállítása a fehér keretek elkerülése érdekében a kimeneten (alapértelmezett: 1).
|
||||
|
||||
|
||||
#OCR
|
||||
ocr.title=OCR / szkennelés tisztázása
|
||||
ocr.header=Szkennelés tisztázása / OCR (Optikai karakterfelismerés)
|
||||
ocr.selectText.1=Válassza ki azokat a nyelveket, amelyeket fel kell ismerni a PDF-ben (az alább felsoroltak azok, amelyeket jelenleg képesek vagyunk azonosítani):
|
||||
ocr.selectText.2=Hozzon létre szövegfájlt az OCR szöveg mellett az OCR-es PDF-ben
|
||||
ocr.selectText.3=Korrigálja azokat az oldalakat, amelyek ferdén lettek beolvasva, és fogassa vissza őket a helyes szögbe
|
||||
ocr.selectText.4=Tisztítsa meg az oldalt, hogy az OCR pontosabban dolgozzon, ne érzékeljen szöveget a háttérzajban. (Nincs kimeneti változás)
|
||||
ocr.selectText.5=Tisztítsa meg az oldalt, hogy az OCR pontosabban dolgozzon, ne érzékeljen szöveget a háttérzajban, és a tisztítást a kimeneten is őrizze meg.
|
||||
ocr.selectText.6=Hagyja figyelmen kívül azokat az oldalakat, amelyeken interaktív szöveg található, csak azon oldalakat dolgozza el OCR-rel, amelyek képek
|
||||
ocr.selectText.7=OCR kényszerítése, minden oldalt OCR-el, eltávolítva az összes eredeti szövegelemet
|
||||
ocr.selectText.8=Normál (Hiba esetén, ha a PDF szöveget tartalmaz)
|
||||
ocr.selectText.9=További beállítások
|
||||
ocr.selectText.10=OCR mód
|
||||
ocr.selectText.11=Képek eltávolítása OCR után (Az ÖSSZES kép eltávolítása, csak akkor hasznos, ha a konverzió része)
|
||||
ocr.selectText.12=Render típusa (Speciális)
|
||||
ocr.help=Kérjük, olvassa el ezt a dokumentációt az egyéb nyelvek használatához és/vagy a nem Docker-es használathoz.
|
||||
ocr.credit=Ez a szolgáltatás az OCRmyPDF és a Tesseract OCR használatával működik.
|
||||
ocr.submit=PDF feldolgozása OCR-rel
|
||||
|
||||
|
||||
#extractImages
|
||||
extractImages.title=Képek kinyerése
|
||||
extractImages.header=Képek kinyerése
|
||||
extractImages.selectText=Válassza ki a képformátumot a kinyert képek konvertálásához
|
||||
extractImages.submit=Kinyerés
|
||||
|
||||
|
||||
#File to PDF
|
||||
fileToPDF.title=Fájl PDF dokumentummá alakítása
|
||||
fileToPDF.header=Konvertáljon bármilyen fájlt PDF dokumentummá
|
||||
fileToPDF.credit=Ez a szolgáltatás a LibreOffice-t és az Unoconv-ot használja a fájlkonverzióhoz.
|
||||
fileToPDF.supportedFileTypes=A funkció az alábbi fájltípusokat támogatja, azonban a teljesen friss támogatott formátumok listájáért kérjük, tekintse meg a LibreOffice dokumentációját
|
||||
fileToPDF.submit=Konvertálás PDF dokumentummá
|
||||
|
||||
|
||||
#compress
|
||||
compress.title=Tömörítés
|
||||
compress.header=PDF tömörítése
|
||||
compress.credit=Ez a szolgáltatás a Ghostscript-et használja a PDF tömörítéséhez/optimalizálásához.
|
||||
compress.selectText.1=Kézi mód - 1-től 4-ig
|
||||
compress.selectText.2=Optimalizálási szint:
|
||||
compress.selectText.3=4 (nem ajánlott a szöveges képekhez)
|
||||
compress.selectText.4=Automatikus mód - Az automata állítja a minőséget úgy, hogy a beállított méretű PDF-et generálja.
|
||||
compress.selectText.5=Várt PDF méret (pl. 25MB, 10,8MB, 25KB)
|
||||
compress.submit=Tömörítés
|
||||
|
||||
|
||||
#Add image
|
||||
addImage.title=Kép hozzáadása
|
||||
addImage.header=Kép hozzáadása a PDF-hez
|
||||
addImage.everyPage=Minden oldal?
|
||||
addImage.upload=Kép hozzáadása
|
||||
addImage.submit=Kép hozzáadása
|
||||
|
||||
|
||||
#merge
|
||||
merge.title=Összevonás
|
||||
merge.header=Több PDF összevonása (2+)
|
||||
merge.sortByName=Név szerinti rendezés
|
||||
merge.sortByDate=Dátum szerinti rendezés
|
||||
merge.submit=Összevonás
|
||||
|
||||
|
||||
#pdfOrganiser
|
||||
pdfOrganiser.title=Oldalszervező
|
||||
pdfOrganiser.header=PDF Oldalszervező
|
||||
pdfOrganiser.submit=Oldalak átrendezése
|
||||
|
||||
|
||||
#multiTool
|
||||
multiTool.title=PDF többfunkciós eszköz
|
||||
multiTool.header=PDF többfunkciós eszköz
|
||||
|
||||
#view pdf
|
||||
viewPdf.title=PDF megtekintése
|
||||
viewPdf.header=PDF megtekintése
|
||||
|
||||
#pageRemover
|
||||
pageRemover.title=Oldaltörlő
|
||||
pageRemover.header=PDF oldaltörlő
|
||||
pageRemover.pagesToDelete=Törlendő oldalak (adja meg az oldalszámok vesszővel elválasztott listáját):
|
||||
pageRemover.submit=Oldalak törlése
|
||||
|
||||
|
||||
#rotate
|
||||
rotate.title=PDF forgatás
|
||||
rotate.header=PDF forgatás
|
||||
rotate.selectAngle=Válassza ki a forgatási szöget (90 fok egész számú többszörösei):
|
||||
rotate.submit=Forgatás
|
||||
|
||||
|
||||
#merge
|
||||
split.title=PDF szétválasztás
|
||||
split.header=PDF szétválasztás
|
||||
split.desc.1=A kiválasztott számok a szétválasztani kívánt oldalszámok
|
||||
split.desc.2=Például az 1,3,7-8 kiválasztása egy 10 oldalas dokumentumot 6 különálló PDF-fé szétválaszt
|
||||
split.desc.3=Dokumentum #1: Oldal 1
|
||||
split.desc.4=Dokumentum #2: Oldal 2 és 3
|
||||
split.desc.5=Dokumentum #3: Oldal 4, 5 és 6
|
||||
split.desc.6=Dokumentum #4: Oldal 7
|
||||
split.desc.7=Dokumentum #5: Oldal 8
|
||||
split.desc.8=Dokumentum #6: Oldal 9 és 10
|
||||
split.splitPages=Adja meg az oldalakat, amelyekre szét akarja választani:
|
||||
split.submit=Szétválasztás
|
||||
|
||||
|
||||
#merge
|
||||
imageToPDF.title=Kép PDF-be
|
||||
imageToPDF.header=Kép PDF-be
|
||||
imageToPDF.submit=Átalakítás
|
||||
imageToPDF.selectLabel=Kép illesztési beállítások
|
||||
imageToPDF.fillPage=Teljes oldal kitöltése
|
||||
imageToPDF.fitDocumentToImage=Oldal illesztése a képhez
|
||||
imageToPDF.maintainAspectRatio=Arányok megtartása
|
||||
imageToPDF.selectText.2=Automatikus forgatás PDF
|
||||
imageToPDF.selectText.3=Több fájl logika (csak akkor engedélyezett, ha több képpel dolgozik)
|
||||
imageToPDF.selectText.4=Egyesítse egyetlen PDF-fé
|
||||
imageToPDF.selectText.5=Átalakítás különálló PDF-fé
|
||||
|
||||
|
||||
#pdfToImage
|
||||
pdfToImage.title=PDF képpé alakítása
|
||||
pdfToImage.header=PDF képpé alakítása
|
||||
pdfToImage.selectText=Képformátum
|
||||
pdfToImage.singleOrMultiple=Oldal kép típusa
|
||||
pdfToImage.single=Egyetlen nagy kép az összes oldallal
|
||||
pdfToImage.multi=Több kép, egy kép oldalanként
|
||||
pdfToImage.colorType=Szín típusa
|
||||
pdfToImage.color=színes
|
||||
pdfToImage.grey=szürkeárnyalatos
|
||||
pdfToImage.blackwhite=fekete-fehér (adatvesztéssel járhat!)
|
||||
pdfToImage.submit=Átalakítás
|
||||
|
||||
|
||||
#addPassword
|
||||
addPassword.title=Jelszó hozzáadása
|
||||
addPassword.header=Jelszó hozzáadása (Titkosítás)
|
||||
addPassword.selectText.1=Válassza ki a titkosítandó PDF-t
|
||||
addPassword.selectText.2=Felhasználói jelszó
|
||||
addPassword.selectText.3=Titkosítási kulcs hossza
|
||||
addPassword.selectText.4=A magasabb értékek erősebbek, de az alacsonyabb értékek jobb kompatibilitást biztosítanak.
|
||||
addPassword.selectText.5=Beállítandó engedélyek (Ajánlott a Tulajdonos jelszóval együtt használni)
|
||||
addPassword.selectText.6=Más dokumentummal történő egyesítés megakadályozása
|
||||
addPassword.selectText.7=Tartalomkivonás megakadályozása
|
||||
addPassword.selectText.8=Elérhetőség kivonásának megakadályozása
|
||||
addPassword.selectText.9=Űrlap kitöltésének megakadályozása
|
||||
addPassword.selectText.10=Módosítás megakadályozása
|
||||
addPassword.selectText.11=Jegyzet módosításának megakadályozása
|
||||
addPassword.selectText.12=Nyomtatás megakadályozása
|
||||
addPassword.selectText.13=Többféle formátumú nyomtatás megakadályozása
|
||||
addPassword.selectText.14=Tulajdonos jelszó
|
||||
addPassword.selectText.15=Korlátozza, hogy mi végezhető el a dokumentum megnyitása után (Nem minden olvasó támogatja)
|
||||
addPassword.selectText.16=Korlátozza a dokumentum megnyithatságát
|
||||
addPassword.submit=Titkosítás
|
||||
|
||||
|
||||
#watermark
|
||||
watermark.title=Vízjel hozzáadása
|
||||
watermark.header=Vízjel hozzáadása
|
||||
watermark.selectText.1=Válassza ki a PDF-t, amelyhez vízjelet kíván hozzáadni:
|
||||
watermark.selectText.2=Vízjel szövege:
|
||||
watermark.selectText.3=Betűméret:
|
||||
watermark.selectText.4=Forgatás (0-360):
|
||||
watermark.selectText.5=widthSpacer (Hely a vízjelek között vízszintesen):
|
||||
watermark.selectText.6=heightSpacer (Hely a vízjelek között függőlegesen):
|
||||
watermark.selectText.7=Átlátszóság (0% - 100%):
|
||||
watermark.selectText.8=Vízjel típusa:
|
||||
watermark.selectText.9=Vízjel képe:
|
||||
watermark.submit=Vízjel hozzáadása
|
||||
|
||||
|
||||
#Change permissions
|
||||
permissions.title=Jogosultságok módosítása
|
||||
permissions.header=Jogosultságok módosítása
|
||||
permissions.warning=Figyelem: Ahhoz, hogy ezek a jogosultságok ne legyenek később módosíthatók, javasolt a dokumentumhoz jelszót beállítani a Jelszó hozzáadása funkcióval.
|
||||
permissions.selectText.1=Válassza ki a PDF-fájlt a jogosultságok módosításához
|
||||
permissions.selectText.2=Beállítandó jogosultságok
|
||||
permissions.selectText.3=Más dokumentummal történő egyesítés megakadályozása
|
||||
permissions.selectText.4=Tartalom kivonásának megakadályozása
|
||||
permissions.selectText.5=Elérhetőség kivonásának megakadályozása
|
||||
permissions.selectText.6=Űrlap kitöltésének megakadályozása
|
||||
permissions.selectText.7=Módosítás megakadályozása
|
||||
permissions.selectText.8=Jegyzet módosításának megakadályozása
|
||||
permissions.selectText.9=Nyomtatás megakadályozása
|
||||
permissions.selectText.10=Többféle formátumú nyomtatás megakadályozása
|
||||
permissions.submit=Módosítás
|
||||
|
||||
|
||||
#remove password
|
||||
removePassword.title=Jelszó eltávolítása
|
||||
removePassword.header=Jelszó eltávolítása (dekódolás)
|
||||
removePassword.selectText.1=Válassza ki a PDF-fájlt a dekódoláshoz
|
||||
removePassword.selectText.2=Jelszó
|
||||
removePassword.submit=Eltávolítás
|
||||
|
||||
|
||||
#changeMetadata
|
||||
changeMetadata.title=Cím:
|
||||
changeMetadata.header=Metaadatok módosítása
|
||||
changeMetadata.selectText.1=Kérjük, szerkessze azokat a változókat, amelyeket módosítani szeretne
|
||||
changeMetadata.selectText.2=Minden metaadat törlése
|
||||
changeMetadata.selectText.3=Egyedi metaadatok megjelenítése:
|
||||
changeMetadata.author=Szerző:
|
||||
changeMetadata.creationDate=Létrehozás dátuma (éééé/hh/nn ÓÓ:PP:MM):
|
||||
changeMetadata.creator=Létrehozó:
|
||||
changeMetadata.keywords=Kulcsszavak:
|
||||
changeMetadata.modDate=Módosítás dátuma (éééé/hh/nn ÓÓ:PP:MM):
|
||||
changeMetadata.producer=Készítő:
|
||||
changeMetadata.subject=Tárgy:
|
||||
changeMetadata.title=Cím:
|
||||
changeMetadata.trapped=Trapped:
|
||||
changeMetadata.selectText.4=Egyéb metaadatok:
|
||||
changeMetadata.selectText.5=Egyedi metaadatbejegyzés hozzáadása
|
||||
changeMetadata.submit=Módosítás
|
||||
|
||||
|
||||
#pdfToPDFA
|
||||
pdfToPDFA.title=PDF >> PDF/A
|
||||
pdfToPDFA.header=PDF >> PDF/A
|
||||
pdfToPDFA.credit=Ez a szolgáltatás az OCRmyPDF-t használja a PDF/A konverzióhoz
|
||||
pdfToPDFA.submit=Konvertálás
|
||||
|
||||
|
||||
#PDFToWord
|
||||
PDFToWord.title=PDF >> Word
|
||||
PDFToWord.header=PDF >> Word
|
||||
PDFToWord.selectText.1=Kimeneti fájlformátum
|
||||
PDFToWord.credit=Ez a szolgáltatás a LibreOffice-t használja a fájlkonverzióhoz.
|
||||
PDFToWord.submit=Konvertálás
|
||||
|
||||
|
||||
#PDFToPresentation
|
||||
PDFToPresentation.title=PDF >> prezentáció
|
||||
PDFToPresentation.header=PDF >> prezentáció
|
||||
PDFToPresentation.selectText.1=Kimeneti fájlformátum
|
||||
PDFToPresentation.credit=Ez a szolgáltatás a LibreOffice-t használja a fájlkonverzióhoz.
|
||||
PDFToPresentation.submit=Konvertálás
|
||||
|
||||
|
||||
#PDFToText
|
||||
PDFToText.title=PDF >> RTF (szöveg)
|
||||
PDFToText.header=PDF >> RTF (szöveg)
|
||||
PDFToText.selectText.1=Kimeneti fájlformátum
|
||||
PDFToText.credit=Ez a szolgáltatás a LibreOffice-t használja a fájlkonverzióhoz.
|
||||
PDFToText.submit=Konvertálás
|
||||
|
||||
|
||||
#PDFToHTML
|
||||
PDFToHTML.title=PDF >> HTML
|
||||
PDFToHTML.header=PDF >> HTML
|
||||
PDFToHTML.credit=Ez a szolgáltatás a LibreOffice-t használja a fájlkonverzióhoz.
|
||||
PDFToHTML.submit=Konvertálás
|
||||
|
||||
|
||||
#PDFToXML
|
||||
PDFToXML.title=PDF >> XML
|
||||
PDFToXML.header=PDF >> XML
|
||||
PDFToXML.credit=Ez a szolgáltatás a LibreOffice-t használja a fájlkonverzióhoz.
|
||||
PDFToXML.submit=Konvertálás
|
||||
|
||||
#PDFToCSV
|
||||
PDFToCSV.title=PDF >> CSV
|
||||
PDFToCSV.header=PDF >> CSV
|
||||
PDFToCSV.prompt=Válassza ki az oldalt a táblázat kinyeréséhez
|
||||
PDFToCSV.submit=Kinyerés
|
||||
|
||||
#split-by-size-or-count
|
||||
split-by-size-or-count.header=PDF felosztása méret vagy oldalszám alapján
|
||||
split-by-size-or-count.type.label=Válassza ki a felosztás típusát
|
||||
split-by-size-or-count.type.size=Méret alapján
|
||||
split-by-size-or-count.type.pageCount=Oldalszám alapján
|
||||
split-by-size-or-count.type.docCount=Dokumentumok száma alapján
|
||||
split-by-size-or-count.value.label=Adja meg az értéket
|
||||
split-by-size-or-count.value.placeholder=Adja meg a méretet (pl. 2 MB vagy 3 KB) vagy az oldalszámot (pl. 5)
|
||||
split-by-size-or-count.submit=Elküld
|
||||
|
||||
|
||||
#overlay-pdfs
|
||||
overlay-pdfs.header=PDF fájlok átfedése
|
||||
overlay-pdfs.baseFile.label=Válassza ki az alap PDF fájlt
|
||||
overlay-pdfs.overlayFiles.label=Válassza ki az átfedő PDF fájlokat
|
||||
overlay-pdfs.mode.label=Válassza ki az átfedés módját
|
||||
overlay-pdfs.mode.sequential=Soros átfedés
|
||||
overlay-pdfs.mode.interleaved=Váltott átfedés
|
||||
overlay-pdfs.mode.fixedRepeat=Rögzített ismétlődő átfedés
|
||||
overlay-pdfs.counts.label=Átfedések száma (rögzített ismétlődő mód esetén)
|
||||
overlay-pdfs.counts.placeholder=Adja meg a vesszővel elválasztott számokat (pl. 2,3,1)
|
||||
overlay-pdfs.position.label=Válassza ki az átfedés pozícióját
|
||||
overlay-pdfs.position.foreground=Előtér
|
||||
overlay-pdfs.position.background=Háttér
|
||||
overlay-pdfs.submit=Elküld
|
||||
|
||||
|
||||
#split-by-sections
|
||||
split-by-sections.title=PDF szakaszokra osztása
|
||||
split-by-sections.header=PDF szakaszokra osztása
|
||||
split-by-sections.horizontal.label=Függőleges szakaszok
|
||||
split-by-sections.vertical.label=Vízszintes szakaszok
|
||||
split-by-sections.horizontal.placeholder=Adja meg a vízszintes szakaszok számát
|
||||
split-by-sections.vertical.placeholder=Adja meg a függőleges szakaszok számát
|
||||
split-by-sections.submit=Felosztás
|
||||
896
src/main/resources/messages_id_ID.properties
Normal file
896
src/main/resources/messages_id_ID.properties
Normal file
@@ -0,0 +1,896 @@
|
||||
###########
|
||||
# Generic #
|
||||
###########
|
||||
# the direction that the language is written (ltr=left to right, rtl=right to left)
|
||||
language.direction=ltr
|
||||
|
||||
pdfPrompt=Pilih PDF
|
||||
multiPdfPrompt=Pilih PDF (2+)
|
||||
multiPdfDropPrompt=Pilih (atau seret & letakkan)) semua PDF yang Anda butuhkan
|
||||
imgPrompt=Pilih Gambar
|
||||
genericSubmit=Kirim
|
||||
processTimeWarning=Peringatan: Proses ini dapat memakan waktu hingga satu menit, tergantung pada ukuran berkas
|
||||
pageOrderPrompt=Urutan Halaman Khusus (Masukkan daftar nomor halaman yang dipisahkan dengan koma atau Fungsi seperti 2n + 1) :
|
||||
goToPage=Ke
|
||||
true=Benar
|
||||
false=Salah
|
||||
unknown=Tidak diketahui
|
||||
save=Simpan
|
||||
close=Tutup
|
||||
filesSelected=berkas dipilih
|
||||
noFavourites=Tidak ada favorit yang ditambahkan
|
||||
bored=Bosan Menunggu?
|
||||
alphabet=Abjad
|
||||
downloadPdf=Unduh PDF
|
||||
text=Teks
|
||||
font=Jenis huruf
|
||||
selectFillter=-- Pilih --
|
||||
pageNum=Nomor Halaman
|
||||
sizes.small=Kecil
|
||||
sizes.medium=Sedang
|
||||
sizes.large=Besar
|
||||
sizes.x-large=Sangat Besar
|
||||
error.pdfPassword=Dokumen PDF disandikan dan kata sandi tidak diberikan atau kata sandi salah
|
||||
delete=Hapus
|
||||
username=Nama pengguna
|
||||
password=Kata sandi
|
||||
welcome=Selamat Datang
|
||||
property=Properti
|
||||
black=Hitam
|
||||
white=Putih
|
||||
red=Merah
|
||||
green=Hijau
|
||||
blue=Biru
|
||||
custom=Kustom...
|
||||
|
||||
changedCredsMessage=Kredensial berubah!!
|
||||
notAuthenticatedMessage=Pengguna tidak ter-autentikasi.
|
||||
userNotFoundMessage=Pengguna tidak ditemukan.
|
||||
incorrectPasswordMessage=Kata sandi saat ini salah.
|
||||
usernameExistsMessage=Nama pengguna baru sudah ada.
|
||||
|
||||
|
||||
|
||||
#############
|
||||
# NAVBAR #
|
||||
#############
|
||||
navbar.convert=Konversi
|
||||
navbar.security=Keamanan
|
||||
navbar.other=Lain-lain
|
||||
navbar.darkmode=Mode Gelap
|
||||
navbar.pageOps=Operasi Halaman
|
||||
navbar.settings=Pengaturan
|
||||
|
||||
#############
|
||||
# SETTINGS #
|
||||
#############
|
||||
settings.title=Pengaturan
|
||||
settings.update=Pembaruan tersedia
|
||||
settings.appVersion=Versi Aplikasi:
|
||||
settings.downloadOption.title=Pilih opsi unduhan (Untuk unduhan berkas tunggal non zip):
|
||||
settings.downloadOption.1=Buka di jendela yang sama
|
||||
settings.downloadOption.2=Buka di jendela baru
|
||||
settings.downloadOption.3=Unduh berkas
|
||||
settings.zipThreshold=Berkas zip ketika jumlah berkas yang diunduh melebihi
|
||||
settings.signOut=Keluar
|
||||
settings.accountSettings=Pengaturan Akun
|
||||
|
||||
|
||||
|
||||
changeCreds.title=Ubah Kredensial
|
||||
changeCreds.header=Perbarui Detail Akun Anda
|
||||
changeCreds.changeUserAndPassword=Anda menggunakan kredensial masuk default. Masukkan kata sandi baru (dan nama pengguna jika diinginkan)
|
||||
changeCreds.newUsername=Nama Pengguna Baru
|
||||
changeCreds.oldPassword=Kata Sandi Saat Ini
|
||||
changeCreds.newPassword=Kata Sandi Baru
|
||||
changeCreds.confirmNewPassword=Konfirmasi Kata Sandi Baru
|
||||
changeCreds.submit=Kirim Perubahan
|
||||
|
||||
|
||||
|
||||
account.title=Pengaturan Akun
|
||||
account.accountSettings=Pengaturan Akun
|
||||
account.adminSettings=Pengaturan Admin - Melihat dan Menambahkan Pengguna
|
||||
account.userControlSettings=Pengaturan Kontrol Pengguna
|
||||
account.changeUsername=Ubah Nama Pengguna
|
||||
account.changeUsername=Ubah Nama Pengguna
|
||||
account.password=Konfirmasi Kata sandi
|
||||
account.oldPassword=Kata sandi lama
|
||||
account.newPassword=Kata Sandi Baru
|
||||
account.changePassword=Ubah Kata Sandi
|
||||
account.confirmNewPassword=Konfirmasi Kata Sandi Baru
|
||||
account.signOut=Keluar
|
||||
account.yourApiKey=API Key Anda
|
||||
account.syncTitle=Menyinkronkan pengaturan browser dengan Akun
|
||||
account.settingsCompare=Perbandingan Pengaturan:
|
||||
account.property=Properti
|
||||
account.webBrowserSettings=Pengaturan Peramban Web
|
||||
account.syncToBrowser=Sinkronisasi Akun -> Browser
|
||||
account.syncToAccount=Sinkronisasi Akun <- Browser
|
||||
|
||||
|
||||
adminUserSettings.title=Pengaturan Kontrol Pengguna
|
||||
adminUserSettings.header=Pengaturan Kontrol Admin
|
||||
adminUserSettings.admin=Admin
|
||||
adminUserSettings.user=Pengguna
|
||||
adminUserSettings.addUser=Tambahkan Pengguna Baru
|
||||
adminUserSettings.roles=Peran
|
||||
adminUserSettings.role=Peran
|
||||
adminUserSettings.actions=Tindakan
|
||||
adminUserSettings.apiUser=Pengguna API Terbatas
|
||||
adminUserSettings.webOnlyUser=Pengguna Khusus Web
|
||||
adminUserSettings.demoUser=Demo User (No custom settings)
|
||||
adminUserSettings.forceChange=Memaksa pengguna untuk mengubah nama pengguna/kata sandi saat masuk
|
||||
adminUserSettings.submit=Simpan Pengguna
|
||||
|
||||
#############
|
||||
# HOME-PAGE #
|
||||
#############
|
||||
home.desc=Semua kebutuhan PDF Anda, langsung dari komputer lokal Anda.
|
||||
home.searchBar=Mencari fitur...
|
||||
|
||||
|
||||
home.viewPdf.title=Lihat PDF
|
||||
home.viewPdf.desc=Melihat, membuat anotasi, menambahkan teks atau gambar
|
||||
viewPdf.tags=melihat,membaca,membuat anotasi,teks,gambar
|
||||
|
||||
home.multiTool.title=Alat Multi PDF
|
||||
home.multiTool.desc=Menggabungkan, Memutar, Mengatur Ulang, dan Menghapus halaman
|
||||
multiTool.tags=Alat multi,Operasi multi,UI,klik seret,front end,sisi klien,interaktif,sulit diatur,pindah
|
||||
|
||||
home.merge.title=Menggabungkan
|
||||
home.merge.desc=Gabungkan beberapa PDF dengan mudah menjadi satu.
|
||||
merge.tags=menggabungkan,Pengoperasian halaman,Back end,sisi server
|
||||
|
||||
home.split.title=Membagi
|
||||
home.split.desc=Membagi PDF menjadi beberapa dokumen
|
||||
split.tags=Pengoperasian halaman,membagi,Multi Halaman,memotong,sisi server
|
||||
|
||||
home.rotate.title=Putar
|
||||
home.rotate.desc=Memutar PDF Anda dengan mudah.
|
||||
rotate.tags=sisi server
|
||||
|
||||
|
||||
home.imageToPdf.title=Gambar ke PDF
|
||||
home.imageToPdf.desc=Mengonversi gambar (PNG, JPEG, GIF) ke PDF.
|
||||
imageToPdf.tags=konversi,img,jpg,gambar,foto
|
||||
|
||||
home.pdfToImage.title=PDF ke Gambar
|
||||
home.pdfToImage.desc=Mengonversi PDF ke gambar. (PNG, JPEG, GIF)
|
||||
pdfToImage.tags=konversi,img,jpg,gambar,foto
|
||||
|
||||
home.pdfOrganiser.title=Mengatur
|
||||
home.pdfOrganiser.desc=Menghapus/Mengatur ulang halaman dalam urutan apa pun
|
||||
pdfOrganiser.tags=dupleks,genap,ganjil,sortir,pindah
|
||||
|
||||
|
||||
home.addImage.title=Tambahkan gambar
|
||||
home.addImage.desc=Menambahkan gambar ke lokasi yang ditentukan pada PDF
|
||||
addImage.tags=img,jpg,gambar,foto
|
||||
|
||||
home.watermark.title=Tambahkan watermark
|
||||
home.watermark.desc=Menambahkan watermark khusus ke dokumen PDF Anda.
|
||||
watermark.tags=Teks,berulang,label,sendiri,hak cipta,watermark,img,jpg,picture,photo
|
||||
|
||||
home.permissions.title=Izin Perubahan
|
||||
home.permissions.desc=Mengubah izin dokumen PDF Anda
|
||||
permissions.tags=baca,tulis,sunting,cetak
|
||||
|
||||
|
||||
home.removePages.title=Menghapus
|
||||
home.removePages.desc=Menghapus halaman yang tidak diinginkan dari dokumen PDF Anda.
|
||||
removePages.tags=Menghapus halaman,menghapus halaman
|
||||
|
||||
home.addPassword.title=Tambahkan Kata Sandi
|
||||
home.addPassword.desc=Enkripsi dokumen PDF Anda dengan kata sandi.
|
||||
addPassword.tags=aman,Keamanan
|
||||
|
||||
home.removePassword.title=Hapus Kata Sandi
|
||||
home.removePassword.desc=Menghapus perlindungan kata sandi dari dokumen PDF Anda.
|
||||
removePassword.tags=aman,Dekripsi,keamanan,buka kata sandi,hapus kata sandi
|
||||
|
||||
home.compressPdfs.title=Kompres
|
||||
home.compressPdfs.desc=Kompres PDF untuk mengurangi ukuran berkas.
|
||||
compressPdfs.tags=squish, kecil, kecil
|
||||
|
||||
|
||||
home.changeMetadata.title=Ubah Metadata
|
||||
home.changeMetadata.desc=Mengubah/Menghapus/Menambahkan metadata dari dokumen PDF
|
||||
changeMetadata.tags==Judul,penulis,tanggal,pembuatan,waktu,penerbit,produser,statistik
|
||||
|
||||
home.fileToPDF.title=Mengonversi berkas ke PDF
|
||||
home.fileToPDF.desc=Mengonversi hampir semua berkas ke PDF (DOCX, PNG, XLS, PPT, TXT dan lain-lain)
|
||||
fileToPDF.tags=transformasi,format,dokumen,gambar,slide,text,konversi,office,docs,word,excel,powerpoint
|
||||
|
||||
home.ocr.title=Pemindaian/Pembersihan OCR
|
||||
home.ocr.desc=Memindai dan mendeteksi teks dari gambar di dalam PDF dan menambahkannya kembali sebagai teks.
|
||||
ocr.tags=rekognisi,teks,gambar,pindai,baca,identifikasi,deteksi,dapat diedit
|
||||
|
||||
|
||||
home.extractImages.title=Ekstrak Gambar
|
||||
home.extractImages.desc=Mengekstrak semua gambar dari PDF dan menyimpannya ke zip
|
||||
extractImages.tags=gambar, foto, simpan, arsip, zip, tangkap, ambil
|
||||
|
||||
home.pdfToPDFA.title=PDF ke PDF/A
|
||||
home.pdfToPDFA.desc=Konversi PDF ke PDF/A untuk penyimpanan jangka panjang
|
||||
pdfToPDFA.tags=arsip, jangka panjang, standar, konversi, penyimpanan, pelestarian
|
||||
|
||||
home.PDFToWord.title=PDF ke Word
|
||||
home.PDFToWord.desc=Mengonversi format PDF ke Word (DOC, DOCX, dan ODT)
|
||||
PDFToWord.tags=doc, docx, odt, kata, transformasi, format, konversi, kantor, microsoft, docfile
|
||||
|
||||
home.PDFToPresentation.title=PDF ke Presentasi
|
||||
home.PDFToPresentation.desc=Mengonversi PDF ke format Presentasi (PPT, PPTX, dan ODP)
|
||||
PDFToPresentation.tags=slide, pertunjukan, kantor, microsoft
|
||||
|
||||
home.PDFToText.title=PDF ke RTF (Teks)
|
||||
home.PDFToText.desc=Konversi PDF ke format Teks atau RTF
|
||||
PDFToText.tags=format kaya, format teks kaya, format teks kaya
|
||||
|
||||
home.PDFToHTML.title=PDF ke HTML
|
||||
home.PDFToHTML.desc=Mengonversi PDF ke format HTML
|
||||
PDFToHTML.tags=konten web, ramah browser
|
||||
|
||||
|
||||
home.PDFToXML.title=PDF ke XML
|
||||
home.PDFToXML.desc=Mengonversi PDF ke format XML
|
||||
PDFToXML.tags=ekstraksi data, konten terstruktur, interop, transformasi, konversi
|
||||
|
||||
home.ScannerImageSplit.title=Mendeteksi/Memisahkan foto yang dipindai
|
||||
home.ScannerImageSplit.desc=Memisahkan beberapa foto dari dalam sebuah foto/PDF
|
||||
ScannerImageSplit.tags=pisahkan, deteksi otomatis, pindai, multi-foto, atur
|
||||
|
||||
home.sign.title=Tanda Tangan
|
||||
home.sign.desc=Menambahkan tanda tangan ke PDF dengan gambar, teks, atau gambar
|
||||
sign.tags=mengesahkan, inisial, tanda tangan yang digambar, tanda tangan teks, tanda tangan gambar
|
||||
|
||||
home.flatten.title=Meratakan
|
||||
home.flatten.desc=Menghapus semua elemen dan formulir interaktif dari PDF
|
||||
flatten.tags=statis, nonaktif, non-interaktif, ramping
|
||||
|
||||
home.repair.title=Perbaikan
|
||||
home.repair.desc=Melakukan perbaikan PDF yang rusak/rusak
|
||||
repair.tags=perbaiki, pulihkan, koreksi, pulihkan
|
||||
|
||||
home.removeBlanks.title=Menghapus halaman kosong
|
||||
home.removeBlanks.desc=Mendeteksi dan menghapus halaman kosong dari dokumen
|
||||
removeBlanks.tags=membersihkan, merampingkan, non-konten, mengatur
|
||||
|
||||
home.removeAnnotations.title=Menghapus Anotasi
|
||||
home.removeAnnotations.desc=Menghapus semua komentar/anotasi dari PDF
|
||||
removeAnnotations.tags=komentar, sorot, catatan, markup, hapus
|
||||
|
||||
home.compare.title=Bandingkan
|
||||
home.compare.desc=Membandingkan dan menunjukkan perbedaan antara 2 Dokumen PDF
|
||||
compare.tags=membedakan, kontras, perubahan, analisis
|
||||
|
||||
home.certSign.title=Tanda tangani dengan Sertifikat
|
||||
home.certSign.desc=Menandatangani PDF dengan Certificate/Key (PEM/P12)
|
||||
certSign.tags=mengotentikasi, PEM, P12, resmi, mengenkripsi
|
||||
|
||||
home.pageLayout.title=Tata Letak Multi-Halaman
|
||||
home.pageLayout.desc=Menggabungkan beberapa halaman dokumen PDF menjadi satu halaman
|
||||
pageLayout.tags=menggabungkan, komposit, tampilan tunggal, mengatur
|
||||
|
||||
home.scalePages.title=Menyesuaikan ukuran/skala halaman
|
||||
home.scalePages.desc=Mengubah ukuran/skala halaman dan/atau isinya.
|
||||
scalePages.tags=mengubah ukuran, memodifikasi, dimensi, mengadaptasi
|
||||
|
||||
home.pipeline.title=Pipeline (Lanjutan)
|
||||
home.pipeline.desc=Menjalankan beberapa tindakan pada PDF dengan mendefinisikan skrip pipeline
|
||||
pipeline.tags=mengotomatiskan, mengurutkan, menulis, proses batch
|
||||
|
||||
home.add-page-numbers.title=Tambahkan Nomor Halaman
|
||||
home.add-page-numbers.desc=Menambahkan nomor Halaman di seluruh dokumen di lokasi yang ditetapkan
|
||||
add-page-numbers.tags=beri halaman, beri label, atur, indeks
|
||||
|
||||
home.auto-rename.title=Ubah Nama Berkas PDF Secara Otomatis
|
||||
home.auto-rename.desc=Mengganti nama berkas PDF secara otomatis berdasarkan tajuk yang terdeteksi
|
||||
auto-rename.tags=deteksi otomatis, berbasis tajuk, atur, beri label ulang
|
||||
|
||||
home.adjust-contrast.title=Menyesuaikan Warna/Kontras
|
||||
home.adjust-contrast.desc=Sesuaikan Kontras, Saturasi, dan Kecerahan PDF
|
||||
adjust-contrast.tags=koreksi warna, menyetel, memodifikasi, meningkatkan
|
||||
|
||||
home.crop.title=Pangkas PDF
|
||||
home.crop.desc=Pangkas PDF untuk memperkecil ukurannya (mempertahankan teks!)
|
||||
crop.tags=memangkas, mengecilkan, mengedit, membentuk
|
||||
|
||||
home.autoSplitPDF.title=Membagi Halaman Secara Otomatis
|
||||
home.autoSplitPDF.desc=Membagi PDF yang dipindai secara otomatis dengan Kode QR pembagi halaman yang dipindai secara fisik
|
||||
autoSplitPDF.tags=Berbasis QR, pisahkan, pindai segmen, atur
|
||||
|
||||
home.sanitizePdf.title=Sanitasi
|
||||
home.sanitizePdf.desc=Menghapus skrip dan elemen lain dari file PDF
|
||||
sanitizePdf.tags=bersih, terlindungi, aman, menghilangkan ancaman
|
||||
|
||||
home.URLToPDF.title=URL/Situs Web ke PDF
|
||||
home.URLToPDF.desc=Mengonversi URL http apa pun ke PDF
|
||||
URLToPDF.tags=tangkap web, simpan halaman, web-ke-dok, arsip
|
||||
|
||||
home.HTMLToPDF.title=HTML ke PDF
|
||||
home.HTMLToPDF.desc=Mengonversi berkas HTML atau zip ke PDF
|
||||
HTMLToPDF.tags=markup, konten web, transformasi, konversi
|
||||
|
||||
|
||||
home.MarkdownToPDF.title=Penurunan harga ke PDF
|
||||
home.MarkdownToPDF.desc=Mengonversi berkas Markdown apa pun ke PDF
|
||||
MarkdownToPDF.tags=markup, konten web, transformasi, konversi
|
||||
|
||||
|
||||
home.getPdfInfo.title=Dapatkan Semua Info tentang PDF
|
||||
home.getPdfInfo.desc=Mengambil setiap dan semua informasi yang mungkin ada pada PDF
|
||||
getPdfInfo.tags=informasi, data, statistik, statistik
|
||||
|
||||
|
||||
home.extractPage.title=Ekstrak halaman
|
||||
home.extractPage.desc=Mengekstrak halaman tertentu dari PDF
|
||||
extractPage.tags=ekstrak
|
||||
|
||||
|
||||
home.PdfToSinglePage.title=PDF ke Satu Halaman Besar
|
||||
home.PdfToSinglePage.desc=Menggabungkan semua halaman PDF menjadi satu halaman besar
|
||||
PdfToSinglePage.tags=halaman tunggal
|
||||
|
||||
|
||||
home.showJS.title=Tampilkan Javascript
|
||||
home.showJS.desc=Mencari dan menampilkan JS apa pun yang disuntikkan ke dalam PDF
|
||||
showJS.tags=Hapus, Sembunyikan, padamkan, hitam, hitam, penanda, tersembunyi
|
||||
|
||||
home.autoRedact.title=Redaksional Otomatis
|
||||
home.autoRedact.desc=Menyunting Otomatis (Menghitamkan) teks dalam PDF berdasarkan teks masukan
|
||||
showJS.tags=Hapus, Sembunyikan, padamkan, hitam, hitam, penanda, tersembunyi
|
||||
|
||||
home.tableExtraxt.title=PDF ke CSV
|
||||
home.tableExtraxt.desc=Mengekstrak Tabel dari PDF yang mengonversinya menjadi CSV
|
||||
tableExtraxt.tags=CSV, Ekstraksi Tabel, ekstrak, konversi
|
||||
|
||||
|
||||
home.autoSizeSplitPDF.title=Pemisahan Otomatis berdasarkan Ukuran/Hitungan
|
||||
home.autoSizeSplitPDF.desc=Membagi satu PDF menjadi beberapa dokumen berdasarkan ukuran, jumlah halaman, atau jumlah dokumen
|
||||
autoSizeSplitPDF.tags=pdf, membagi, dokumen, organisasi
|
||||
|
||||
|
||||
home.overlay-pdfs.title=Tumpuk PDF
|
||||
home.overlay-pdfs.desc=Menumpuk PDF di atas PDF lain
|
||||
overlay-pdfs.tags=Overlay
|
||||
|
||||
home.split-by-sections.title=Membagi PDF berdasarkan Bagian
|
||||
home.split-by-sections.desc=Membagi setiap halaman PDF menjadi beberapa bagian horizontal dan vertikal yang lebih kecil
|
||||
split-by-sections.tags=Membagi Bagian, Membagi, Menyesuaikan
|
||||
|
||||
###########################
|
||||
# #
|
||||
# WEB PAGES #
|
||||
# #
|
||||
###########################
|
||||
#login
|
||||
login.title=Masuk
|
||||
login.signin=Masuk
|
||||
login.rememberme=Ingat saya
|
||||
login.invalid=Nama pengguna atau kata sandi tidak valid.
|
||||
login.locked=Akun Anda telah dikunci.
|
||||
login.signinTitle=Silakan masuk
|
||||
|
||||
|
||||
#auto-redact
|
||||
autoRedact.title=Redaksional Otomatis
|
||||
autoRedact.header=Redaksional Otomatis
|
||||
autoRedact.colorLabel=Warna
|
||||
autoRedact.textsToRedactLabel=Teks untuk Disunting (dipisahkan baris)
|
||||
autoRedact.textsToRedactPlaceholder=misalnya \nRahasia \nRahasia Tertinggi
|
||||
autoRedact.useRegexLabel=Gunakan Regex
|
||||
autoRedact.wholeWordSearchLabel=Pencarian Seluruh Kata
|
||||
autoRedact.customPaddingLabel=Padding Ekstra Kustom
|
||||
autoRedact.convertPDFToImageLabel=Konversi PDF ke PDF-Gambar (Digunakan untuk menghapus teks di belakang kotak)
|
||||
autoRedact.submitButton=Kirim
|
||||
|
||||
|
||||
#showJS
|
||||
showJS.title=Tampilkan Javascript
|
||||
showJS.header=Tampilkan Javascript
|
||||
showJS.downloadJS=Unduh Javascript
|
||||
showJS.submit=Tampilkan
|
||||
|
||||
|
||||
#pdfToSinglePage
|
||||
pdfToSinglePage.title=PDF Ke Halaman Tunggal
|
||||
pdfToSinglePage.header=PDF Ke Halaman Tunggal
|
||||
pdfToSinglePage.submit=Konversi ke Halaman Tunggal
|
||||
|
||||
|
||||
#pageExtracter
|
||||
pageExtracter.title=Ekstrak Halaman
|
||||
pageExtracter.header=Ekstrak Halaman
|
||||
pageExtracter.submit=Ekstrak
|
||||
|
||||
|
||||
#getPdfInfo
|
||||
getPdfInfo.title=Dapatkan Info tentang PDF
|
||||
getPdfInfo.header=Dapatkan Info tentang PDF
|
||||
getPdfInfo.submit=Dapatkan Info
|
||||
getPdfInfo.downloadJson=Unduh JSON
|
||||
|
||||
|
||||
#markdown-to-pdf
|
||||
MarkdownToPDF.title=Markdown ke PDF
|
||||
MarkdownToPDF.header=Markdown Ke PDF
|
||||
MarkdownToPDF.submit=Konversi
|
||||
MarkdownToPDF.help=Pekerjaan sedang berlangsung
|
||||
MarkdownToPDF.credit=Menggunakan WeasyPrint
|
||||
|
||||
|
||||
|
||||
#url-to-pdf
|
||||
URLToPDF.title=URL ke PDF
|
||||
URLToPDF.header=URL Ke PDF
|
||||
URLToPDF.submit=Konversi
|
||||
URLToPDF.credit=Menggunakan WeasyPrint
|
||||
|
||||
|
||||
#html-to-pdf
|
||||
HTMLToPDF.title=HTML Ke PDF
|
||||
HTMLToPDF.header=HTML Ke PDF
|
||||
HTMLToPDF.help=Menerima berkas HTML dan ZIP yang berisi html / css / gambar, dll yang diperlukan
|
||||
HTMLToPDF.submit=Konversi
|
||||
HTMLToPDF.credit=Menggunakan WeasyPrint
|
||||
|
||||
|
||||
#sanitizePDF
|
||||
sanitizePDF.title=Bersihkan PDF
|
||||
sanitizePDF.header=Membersihkan berkas PDF
|
||||
sanitizePDF.selectText.1=Hapus tindakan JavaScript
|
||||
sanitizePDF.selectText.2=Hapus berkas yang disematkan
|
||||
sanitizePDF.selectText.3=Hapus metadata
|
||||
sanitizePDF.selectText.4=Hapus tautan
|
||||
sanitizePDF.selectText.5=Hapus font
|
||||
sanitizePDF.submit=Membersihkan PDF
|
||||
|
||||
|
||||
#addPageNumbers
|
||||
addPageNumbers.title=Tambahkan Nomor Halaman
|
||||
addPageNumbers.header=Tambahkan Nomor Halaman
|
||||
addPageNumbers.selectText.1=Pilih berkas PDF:
|
||||
addPageNumbers.selectText.2=Ukuran Margin
|
||||
addPageNumbers.selectText.3=Posisi
|
||||
addPageNumbers.selectText.4=Nomor Awal
|
||||
addPageNumbers.selectText.5=Halaman ke Nomor
|
||||
addPageNumbers.selectText.6=Teks Khusus
|
||||
addPageNumbers.customTextDesc=Teks Khusus
|
||||
addPageNumbers.numberPagesDesc=Halaman mana yang akan diberi nomor, default 'semua', juga menerima 1-5 atau 2,5,9, dll.
|
||||
addPageNumbers.customNumberDesc=Default untuk {n}, juga menerima 'Halaman {n} dari {total}', 'Teks-{n}', '{nama berkas}-{n}'
|
||||
addPageNumbers.submit=Tambahkan Nomor Halaman
|
||||
|
||||
|
||||
#auto-rename
|
||||
auto-rename.title=Ganti Nama Otomatis
|
||||
auto-rename.header=Ganti Nama PDF Otomatis
|
||||
auto-rename.submit=Ganti Nama Otomatis
|
||||
|
||||
|
||||
#adjustContrast
|
||||
adjustContrast.title=Sesuaikan Kontras
|
||||
adjustContrast.header=Sesuaikan Kontras
|
||||
adjustContrast.contrast=Kontras:
|
||||
adjustContrast.brightness=Kecerahan:
|
||||
adjustContrast.saturation=Saturasi:
|
||||
adjustContrast.download=Unduh
|
||||
|
||||
|
||||
#crop
|
||||
crop.title=Pangkas
|
||||
crop.header=Pangkas Gambar
|
||||
crop.submit=Kirim
|
||||
|
||||
|
||||
#autoSplitPDF
|
||||
autoSplitPDF.title=PDF Pisah Otomatis
|
||||
autoSplitPDF.header=Pisahkan PDF secara otomatis
|
||||
autoSplitPDF.description=Cetak, Sisipkan, Pindai, unggah, dan biarkan kami memisahkan dokumen Anda secara otomatis. Tidak perlu menyortir secara manual.
|
||||
autoSplitPDF.selectText.1=Cetak beberapa lembar pembatas dari bawah (Hitam putih tidak masalah).
|
||||
autoSplitPDF.selectText.2=Pindai semua dokumen Anda sekaligus dengan memasukkan lembar pembatas di antaranya.
|
||||
autoSplitPDF.selectText.3=Unggah satu berkas PDF besar yang dipindai dan biarkan Stirling PDF menangani sisanya.
|
||||
autoSplitPDF.selectText.4=Halaman pembatas secara otomatis terdeteksi dan dihapus, menjamin dokumen akhir yang rapi.
|
||||
autoSplitPDF.formPrompt=Kirimkan PDF yang berisi pembagi Halaman Stirling-PDF:
|
||||
autoSplitPDF.duplexMode=Mode Dupleks (Pemindaian depan dan belakang)
|
||||
autoSplitPDF.dividerDownload1=Unduh 'Pembagi Pembagi Otomatis (minimal).pdf'
|
||||
autoSplitPDF.dividerDownload2=Unduh 'Pembagi Pembagi Otomatis (dengan instruksi).pdf'
|
||||
autoSplitPDF.submit=Kirim
|
||||
|
||||
|
||||
#pipeline
|
||||
pipeline.title=Pipeline
|
||||
|
||||
|
||||
#pageLayout
|
||||
pageLayout.title=Tata Letak Multi Halaman
|
||||
pageLayout.header=Tata Letak Multi Halaman
|
||||
pageLayout.pagesPerSheet=Halaman per lembar:
|
||||
pageLayout.addBorder=Menambahkan Batas
|
||||
pageLayout.submit=Kirim
|
||||
|
||||
|
||||
#scalePages
|
||||
scalePages.title=Sesuaikan skala halaman
|
||||
scalePages.header=Sesuaikan skala halaman
|
||||
scalePages.pageSize=Ukuran halaman dokumen.
|
||||
scalePages.scaleFactor=Tingkat zoom (potong) halaman.
|
||||
scalePages.submit=Kirim
|
||||
|
||||
|
||||
#certSign
|
||||
certSign.title=Penandatanganan Sertifikat
|
||||
certSign.header=Menandatangani PDF dengan sertifikat Anda (Sedang dalam proses)
|
||||
certSign.selectPDF=Pilih Berkas PDF untuk Penandatanganan:
|
||||
certSign.selectKey=Pilih Berkas Kunci Pribadi Anda (format PKCS # 8, bisa .pem atau .der):
|
||||
certSign.selectCert=Pilih Berkas Sertifikat Anda (format X.509, bisa .pem atau .der):
|
||||
certSign.selectP12=Pilih Berkas Keystore PKCS #12 Anda (.p12 atau .pfx) (Opsional, Jika disediakan, berkas tersebut harus berisi kunci pribadi dan sertifikat Anda):
|
||||
certSign.certType=Jenis Sertifikat
|
||||
certSign.password=Masukkan Kata Sandi Kunci atau Kunci Pribadi Anda (Jika Ada):
|
||||
certSign.showSig=Tampilkan Tanda Tangan
|
||||
certSign.reason=Alasan
|
||||
certSign.location=Lokasi
|
||||
certSign.name=Nama
|
||||
certSign.submit=Tanda tangani PDF
|
||||
|
||||
|
||||
#removeBlanks
|
||||
removeBlanks.title=Hapus Halaman Kosong
|
||||
removeBlanks.header=Remove Blank Pages
|
||||
removeBlanks.threshold=Ambang Batas Keputihan Piksel:
|
||||
removeBlanks.thresholdDesc=Ambang batas untuk menentukan seberapa putih piksel putih yang harus diklasifikasikan sebagai 'Putih'. 0=Hitam, 255 putih murni.
|
||||
removeBlanks.whitePercent=Persen Putih (%):
|
||||
removeBlanks.whitePercentDesc=Persentase halaman yang harus berupa piksel 'putih' yang akan dihapus
|
||||
removeBlanks.submit=Hapus Kosong
|
||||
|
||||
|
||||
#removeAnnotations
|
||||
removeAnnotations.title=Hapus Anotasi
|
||||
removeAnnotations.header=Hapus Anotasi
|
||||
removeAnnotations.submit=Hapus
|
||||
|
||||
|
||||
#compare
|
||||
compare.title=Bandingkan
|
||||
compare.header=Bandingkan PDF
|
||||
compare.document.1=Dokumen 1
|
||||
compare.document.2=Dokumen 2
|
||||
compare.submit=Bandingkan
|
||||
|
||||
|
||||
#sign
|
||||
sign.title=Tanda
|
||||
sign.header=Tandatangani PDF
|
||||
sign.upload=Unggah Gambar
|
||||
sign.draw=Gambar Tanda Tangan
|
||||
sign.text=Masukan Teks
|
||||
sign.clear=Hapus
|
||||
sign.add=Tambah
|
||||
|
||||
|
||||
#repair
|
||||
repair.title=Perbaiki
|
||||
repair.header=Perbaiki PDF
|
||||
repair.submit=Perbaiki
|
||||
|
||||
|
||||
#flatten
|
||||
flatten.title=Ratakan
|
||||
flatten.header=Ratakan PDF
|
||||
flatten.submit=Ratakan
|
||||
|
||||
|
||||
#ScannerImageSplit
|
||||
ScannerImageSplit.selectText.1=Ambang Batas Sudut:
|
||||
ScannerImageSplit.selectText.2=Menetapkan sudut absolut minimum yang diperlukan agar gambar dapat diputar (default: 10).
|
||||
ScannerImageSplit.selectText.3=Toleransi:
|
||||
ScannerImageSplit.selectText.4=Menentukan kisaran variasi warna di sekitar perkiraan warna latar belakang (default: 30).
|
||||
ScannerImageSplit.selectText.5=Area Minimum:
|
||||
ScannerImageSplit.selectText.6=Menetapkan ambang batas area minimum untuk foto (default: 10000).
|
||||
ScannerImageSplit.selectText.7=Area Kontur Minimum:
|
||||
ScannerImageSplit.selectText.8=Menetapkan ambang batas area kontur minimum untuk foto
|
||||
ScannerImageSplit.selectText.9=Ukuran Batas:
|
||||
ScannerImageSplit.selectText.10=Menetapkan ukuran batas yang ditambahkan dan dihapus untuk mencegah batas putih pada output (default: 1).
|
||||
|
||||
|
||||
#OCR
|
||||
ocr.title=OCR / Pembersihan Pindaian
|
||||
ocr.header=Pemindaian Pembersihan / OCR (Pengenalan Karakter Optik)
|
||||
ocr.selectText.1=Pilih bahasa yang akan dideteksi di dalam PDF (Bahasa yang terdaftar adalah bahasa yang saat ini terdeteksi):
|
||||
ocr.selectText.2=Menghasilkan berkas teks yang berisi teks OCR di samping PDF yang di-OCR
|
||||
ocr.selectText.3=Halaman yang benar dipindai pada sudut miring dengan memutarnya kembali ke tempatnya
|
||||
ocr.selectText.4=Halaman yang bersih sehingga kecil kemungkinan OCR akan menemukan teks dalam kebisingan latar belakang. (Tidak ada perubahan output)
|
||||
ocr.selectText.5=Bersihkan halaman sehingga kecil kemungkinan OCR akan menemukan teks dalam kebisingan latar belakang, mempertahankan pembersihan pada keluaran.
|
||||
ocr.selectText.6=Mengabaikan halaman yang memiliki teks interaktif, hanya halaman OCR yang berupa gambar
|
||||
ocr.selectText.7=Memaksa OCR, akan meng-OCR setiap halaman dengan menghapus semua elemen teks asli
|
||||
ocr.selectText.8=Normal (Akan terjadi kesalahan jika PDF berisi teks)
|
||||
ocr.selectText.9=Pengaturan Tambahan
|
||||
ocr.selectText.10=Mode OCR
|
||||
ocr.selectText.11=Hapus gambar setelah OCR (Menghapus Semua gambar, hanya berguna jika merupakan bagian dari langkah konversi)
|
||||
ocr.selectText.12=Jenis Render (Lanjutan)
|
||||
ocr.help=Silakan baca dokumentasi ini tentang cara menggunakan ini untuk bahasa lain dan/atau penggunaan yang tidak ada di docker
|
||||
ocr.credit=Layanan ini menggunakan OCRmyPDF dan Tesseract untuk OCR.
|
||||
ocr.submit=Memproses PDF dengan OCR
|
||||
|
||||
|
||||
#extractImages
|
||||
extractImages.title=Ekstrak Gambar
|
||||
extractImages.header=Mengekstrak Gambar
|
||||
extractImages.selectText=Pilih format gambar yang akan dikonversi
|
||||
extractImages.submit=Ekstrak
|
||||
|
||||
|
||||
#File to PDF
|
||||
fileToPDF.title=Berkas ke PDF
|
||||
fileToPDF.header=Mengonversi berkas apa pun ke PDF
|
||||
fileToPDF.credit=Layanan ini menggunakan LibreOffice dan Unoconv untuk konversi berkas.
|
||||
fileToPDF.supportedFileTypes=Jenis berkas yang didukung harus mencakup yang di bawah ini, namun untuk daftar lengkap format yang didukung, silakan lihat dokumentasi LibreOffice
|
||||
fileToPDF.submit=Konversi ke PDF
|
||||
|
||||
|
||||
#compress
|
||||
compress.title=Kompres
|
||||
compress.header=Kompres PDF
|
||||
compress.credit=Layanan ini menggunakan Ghostscript untuk Kompresi/Optimalisasi PDF.
|
||||
compress.selectText.1=Mode Manual - Dari 1 hingga 4
|
||||
compress.selectText.2=Tingkat Optimalisasi:
|
||||
compress.selectText.3=4 (Buruk untuk gambar teks)
|
||||
compress.selectText.4=Mode Otomatis - Menyesuaikan kualitas secara otomatis untuk mendapatkan PDF dengan ukuran yang tepat
|
||||
compress.selectText.5=Ukuran PDF yang diharapkan (mis. 25MB, 10,8MB, 25KB)
|
||||
compress.submit=Kompres
|
||||
|
||||
|
||||
#Add image
|
||||
addImage.title=Tambahkan Gambar
|
||||
addImage.header=Tambahkan Gambar ke PDF
|
||||
addImage.everyPage=Setiap Halaman?
|
||||
addImage.upload=Tambahkan Gambar
|
||||
addImage.submit=Tambahkan Gambar
|
||||
|
||||
|
||||
#merge
|
||||
merge.title=Gabungkan
|
||||
merge.header=Gabungkan beberapa PDFs (2+)
|
||||
merge.sortByName=Sortir berdasarkan nama
|
||||
merge.sortByDate=Sortir berdasrkan tanggal
|
||||
merge.submit=Gabungkan
|
||||
|
||||
|
||||
#pdfOrganiser
|
||||
pdfOrganiser.title=Pengaturan Halaman
|
||||
pdfOrganiser.header=Pengaturan Halaman PDF
|
||||
pdfOrganiser.submit=Susun ulang halaman
|
||||
|
||||
|
||||
#multiTool
|
||||
multiTool.title=Alat Multi PDF
|
||||
multiTool.header=Alat Multi PDF
|
||||
|
||||
#view pdf
|
||||
viewPdf.title=Lihat PDF
|
||||
viewPdf.header=Lihat PDF
|
||||
|
||||
#pageRemover
|
||||
pageRemover.title=Penghapus Halaman
|
||||
pageRemover.header=Penghapus Halaman PDF
|
||||
pageRemover.pagesToDelete=Halaman yang akan dihapus (Masukkan daftar nomor halaman yang dipisahkan dengan koma) :
|
||||
pageRemover.submit=Hapus Halaman
|
||||
|
||||
|
||||
#rotate
|
||||
rotate.title=Rotasi PDF
|
||||
rotate.header=Rotasi PDF
|
||||
rotate.selectAngle=Pilih sudut rotasi (dalam kelipatan 90 derajat):
|
||||
rotate.submit=Rotasi
|
||||
|
||||
|
||||
#merge
|
||||
split.title=Membagi PDF
|
||||
split.header=Membagi PDF
|
||||
split.desc.1=Angka yang Anda pilih adalah nomor halaman yang ingin Anda pisahkan
|
||||
split.desc.2=Dengan demikian, memilih 1,3,7-8 akan membagi dokumen 10 halaman menjadi 6 PDF terpisah:
|
||||
split.desc.3=Dokumen #1: Halaman 1
|
||||
split.desc.4=Dokumen #2: Halaman 2 dan 3
|
||||
split.desc.5=Dokumen #3: Halaman 4, 5 dan 6
|
||||
split.desc.6=Dokumen #4: Halaman 7
|
||||
split.desc.7=Dokumen #5: Halaman 8
|
||||
split.desc.8=Dokumen #6: Halaman 9 dan 10
|
||||
split.splitPages=Masukkan halaman yang akan dipisah:
|
||||
split.submit=Pisahkan
|
||||
|
||||
|
||||
#merge
|
||||
imageToPDF.title=Gambar ke PDF
|
||||
imageToPDF.header=Gambar ke PDF
|
||||
imageToPDF.submit=Konversi
|
||||
imageToPDF.selectLabel=Opsi Kesesuaian Gambar
|
||||
imageToPDF.fillPage=Isi Halaman
|
||||
imageToPDF.fitDocumentToImage=Isi Dokumen dengan Gambar
|
||||
imageToPDF.maintainAspectRatio=Pertahankan aspek rasio
|
||||
imageToPDF.selectText.2=Putar PDF secara otomatis
|
||||
imageToPDF.selectText.3=Logika multi berkas (Hanya diaktifkan jika bekerja dengan banyak gambar)
|
||||
imageToPDF.selectText.4=Gabungkan menjadi satu PDF
|
||||
imageToPDF.selectText.5=Mengonversi ke PDF yang terpisah
|
||||
|
||||
|
||||
#pdfToImage
|
||||
pdfToImage.title=PDF ke Gambar
|
||||
pdfToImage.header=PDF ke Gambar
|
||||
pdfToImage.selectText=Format Gambar
|
||||
pdfToImage.singleOrMultiple=Tipe hasil halaman ke gambar
|
||||
pdfToImage.single=Gambar Besar Tunggal Menggabungkan semua halaman
|
||||
pdfToImage.multi=Beberapa Gambar, satu gambar per halaman
|
||||
pdfToImage.colorType=Tipe warna
|
||||
pdfToImage.color=Warna
|
||||
pdfToImage.grey=Skala abu-abu
|
||||
pdfToImage.blackwhite=Black and White (Bisa kehilangan data!)
|
||||
pdfToImage.submit=Konversi
|
||||
|
||||
|
||||
#addPassword
|
||||
addPassword.title=Tambahkan kata sandi
|
||||
addPassword.header=Tambahkan kata sandi (Enkrip)
|
||||
addPassword.selectText.1=Pilih PDF untuk enkripsi
|
||||
addPassword.selectText.2=Kata sandi Pengguna
|
||||
addPassword.selectText.3=Panjang kunci enkripsi
|
||||
addPassword.selectText.4=Nilai yang lebih tinggi lebih kuat, tetapi nilai yang lebih rendah memiliki kompatibilitas yang lebih baik.
|
||||
addPassword.selectText.5=Perizinan untuk diubah (Disarankan untuk digunakan bersama dengan kata sandi Pemilik)
|
||||
addPassword.selectText.6=Pencegahan untuk penyusunan dokumen
|
||||
addPassword.selectText.7=Pencegahan untuk ekstraksi konten
|
||||
addPassword.selectText.8=Pencegahan ekstraksi untuk aksesibilitas
|
||||
addPassword.selectText.9=Pencegahan untuk mengisi formulir
|
||||
addPassword.selectText.10=Pencegahan untuk pengubahan
|
||||
addPassword.selectText.11=Pencegahan untuk perubahan anotasi
|
||||
addPassword.selectText.12=Pencegahan untuk mencetak
|
||||
addPassword.selectText.13=Pencegahan untuk mencetak format yang berbeda
|
||||
addPassword.selectText.14=Kata sandi Pemilik
|
||||
addPassword.selectText.15=Membatasi apa yang dapat dilakukan dengan dokumen setelah dibuka (Tidak didukung oleh semua pembaca)
|
||||
addPassword.selectText.16=Membatasi pembukaan dokumen itu sendiri
|
||||
addPassword.submit=Enkripsi
|
||||
|
||||
|
||||
#watermark
|
||||
watermark.title=Tambahkan Watermark
|
||||
watermark.header=Tambahkan Watermark
|
||||
watermark.selectText.1=Pilih PDF untuk menambahkan watermark:
|
||||
watermark.selectText.2=Text Watermark:
|
||||
watermark.selectText.3=Ukuran Huruf:
|
||||
watermark.selectText.4=Rotasi (0-360):
|
||||
watermark.selectText.5=widthSpacer (Spasi diantara setiap watermark horisontal):
|
||||
watermark.selectText.6=heightSpacer (Spasi diantara setiap watermark vertikal):
|
||||
watermark.selectText.7=Opacity (0% - 100%):
|
||||
watermark.selectText.8=Tipe Watermark:
|
||||
watermark.selectText.9=Gambar Watermark:
|
||||
watermark.submit=Tambahkan Watermark
|
||||
|
||||
|
||||
#Change permissions
|
||||
permissions.title=Ganti Perizinan
|
||||
permissions.header=Ganti Perizinan
|
||||
permissions.warning=Peringatan untuk menyetel izin yang tidak dapat diubah, disarankan untuk menyetel izin dengan kata sandi melalui halaman tambah kata sandi
|
||||
permissions.selectText.1=Pilih PDF untuk mengubah izin
|
||||
permissions.selectText.2=Perizinan untuk diubah
|
||||
permissions.selectText.3=Pencegahan untuk penyusunan dokumen
|
||||
permissions.selectText.4=Pencegahan untuk ekstraksi konten
|
||||
permissions.selectText.5=Pencegahan ekstraksi untuk aksesibilitas
|
||||
permissions.selectText.6=Pencegahan untuk mengisi formulir
|
||||
permissions.selectText.7=Pencegahan untuk pengubahan
|
||||
permissions.selectText.8=Pencegahan untuk perubahan anotasi
|
||||
permissions.selectText.9=Pencegahan untuk mencetak
|
||||
permissions.selectText.10=Pencegahan untuk mencetak format yang berbeda
|
||||
permissions.submit=Ganti
|
||||
|
||||
|
||||
#remove password
|
||||
removePassword.title=Hapus kata sandi
|
||||
removePassword.header=Hapus kata sandi (Dekrip)
|
||||
removePassword.selectText.1=Pilih PDF yang akan di Dekrip
|
||||
removePassword.selectText.2=Kata Sandi
|
||||
removePassword.submit=Hapus
|
||||
|
||||
|
||||
#changeMetadata
|
||||
changeMetadata.title=Judul:
|
||||
changeMetadata.header=Ganti Metadata
|
||||
changeMetadata.selectText.1=Silakan edit variabel yang ingin Anda ubah
|
||||
changeMetadata.selectText.2=Hapus semua metadata
|
||||
changeMetadata.selectText.3=Tampilkan Metadata Khusus:
|
||||
changeMetadata.author=Penulis:
|
||||
changeMetadata.creationDate=Tanggal Dibuat (yyyy/MM/dd HH:mm:ss):
|
||||
changeMetadata.creator=Pencipta:
|
||||
changeMetadata.keywords=Kata kunci:
|
||||
changeMetadata.modDate=Tangal Diupdate (yyyy/MM/dd HH:mm:ss):
|
||||
changeMetadata.producer=Produser:
|
||||
changeMetadata.subject=Subjek:
|
||||
changeMetadata.title=Judul:
|
||||
changeMetadata.trapped=Terperangkap:
|
||||
changeMetadata.selectText.4=Metadata Lain-lain:
|
||||
changeMetadata.selectText.5=Tambahkan Metadata Khusus
|
||||
changeMetadata.submit=Ganti
|
||||
|
||||
|
||||
#pdfToPDFA
|
||||
pdfToPDFA.title=PDF Ke PDF/A
|
||||
pdfToPDFA.header=PDF ke PDF/A
|
||||
pdfToPDFA.credit=Layanan ini menggunakan OCRmyPDF untuk konversi PDF/A.
|
||||
pdfToPDFA.submit=Konversi
|
||||
|
||||
|
||||
#PDFToWord
|
||||
PDFToWord.title=PDF Ke Word
|
||||
PDFToWord.header=PDF ke Word
|
||||
PDFToWord.selectText.1=Hasil format berkas
|
||||
PDFToWord.credit=Layanan ini menggunakan LibreOffice untuk konversi berkas.
|
||||
PDFToWord.submit=Konversi
|
||||
|
||||
|
||||
#PDFToPresentation
|
||||
PDFToPresentation.title=PDF Ke Presentation
|
||||
PDFToPresentation.header=PDF ke Presentation
|
||||
PDFToPresentation.selectText.1=Hasil format berkas
|
||||
PDFToPresentation.credit=Layanan ini menggunakan LibreOffice untuk konversi berkas.
|
||||
PDFToPresentation.submit=Konversi
|
||||
|
||||
|
||||
#PDFToText
|
||||
PDFToText.title=PDF Ke RTF (Text)
|
||||
PDFToText.header=PDF ke RTF (Text)
|
||||
PDFToText.selectText.1=Hasil format berkas
|
||||
PDFToText.credit=Layanan ini menggunakan LibreOffice untuk konversi berkas.
|
||||
PDFToText.submit=Konversi
|
||||
|
||||
|
||||
#PDFToHTML
|
||||
PDFToHTML.title=PDF Ke HTML
|
||||
PDFToHTML.header=PDF ke HTML
|
||||
PDFToHTML.credit=Layanan ini menggunakan LibreOffice untuk konversi berkas.
|
||||
PDFToHTML.submit=Konversi
|
||||
|
||||
|
||||
#PDFToXML
|
||||
PDFToXML.title=PDF Ke XML
|
||||
PDFToXML.header=PDF ke XML
|
||||
PDFToXML.credit=Layanan ini menggunakan LibreOffice untuk konversi berkas.
|
||||
PDFToXML.submit=Konversi
|
||||
|
||||
#PDFToCSV
|
||||
PDFToCSV.title=PDF Ke CSV
|
||||
PDFToCSV.header=PDF ke CSV
|
||||
PDFToCSV.prompt=Pilih halaman untuk mengambil tabel
|
||||
PDFToCSV.submit=Ektraksi
|
||||
|
||||
#split-by-size-or-count
|
||||
split-by-size-or-count.header=Pisahkan PDF berdasarkan ukuran atau jumlah
|
||||
split-by-size-or-count.type.label=Pilih Tipe Split
|
||||
split-by-size-or-count.type.size=Berdasarkan Ukuran
|
||||
split-by-size-or-count.type.pageCount=Berdasarkan Jumlah Halaman
|
||||
split-by-size-or-count.type.docCount=Berdasarkan Jumlah Dokumen
|
||||
split-by-size-or-count.value.label=Masukkan Jumlah
|
||||
split-by-size-or-count.value.placeholder=Masukkan ukuran (e.g., 2MB or 3KB) atau hitungan (e.g., 5)
|
||||
split-by-size-or-count.submit=Kirim
|
||||
|
||||
|
||||
#overlay-pdfs
|
||||
overlay-pdfs.header=Hamparan berkas PDF
|
||||
overlay-pdfs.baseFile.label=Pilih basis berkas PDF
|
||||
overlay-pdfs.overlayFiles.label=Pilih hamparan berkas PDF
|
||||
overlay-pdfs.mode.label=Pilih Mode Hamparan
|
||||
overlay-pdfs.mode.sequential=Hamparan Sequential
|
||||
overlay-pdfs.mode.interleaved=Hamparan Interleaved
|
||||
overlay-pdfs.mode.fixedRepeat=Hamparan Fixed Repeat
|
||||
overlay-pdfs.counts.label=Jumlah Overlay (Untuk hamparan fixed repeat)
|
||||
overlay-pdfs.counts.placeholder=Masukkan hitungan yang dipisahkan oleh koma (e.g., 2,3,1)
|
||||
overlay-pdfs.position.label=Pilih posisi hamparan
|
||||
overlay-pdfs.position.foreground=Latar depan
|
||||
overlay-pdfs.position.background=Latar belakang
|
||||
overlay-pdfs.submit=Kirim
|
||||
|
||||
|
||||
#split-by-sections
|
||||
split-by-sections.title=Pisahkan PDF berdasarkan bagian
|
||||
split-by-sections.header=Pisahkan PDF menjadi beberapa bagian
|
||||
split-by-sections.horizontal.label=Pembagian Horizontal
|
||||
split-by-sections.vertical.label=Pembagian Vertikal
|
||||
split-by-sections.horizontal.placeholder=Input angka untuk pembagian horizontal
|
||||
split-by-sections.vertical.placeholder=Input angka untuk pembagian vertikal
|
||||
split-by-sections.submit=Pisahkan PDF
|
||||
@@ -119,6 +119,7 @@ adminUserSettings.role=Ruolo
|
||||
adminUserSettings.actions=Azioni
|
||||
adminUserSettings.apiUser=Utente API limitato
|
||||
adminUserSettings.webOnlyUser=Utente solo Web
|
||||
adminUserSettings.demoUser=Demo User (No custom settings)
|
||||
adminUserSettings.forceChange=Forza l'utente a cambiare nome username/password all'accesso
|
||||
adminUserSettings.submit=Salva utente
|
||||
|
||||
@@ -255,6 +256,10 @@ home.removeBlanks.title=Rimuovi pagine vuote
|
||||
home.removeBlanks.desc=Trova e rimuovi pagine vuote da un PDF.
|
||||
removeBlanks.tags=pulire,semplificare,non contenere contenuti,organizzare
|
||||
|
||||
home.removeAnnotations.title=Remove Annotations
|
||||
home.removeAnnotations.desc=Removes all comments/annotations from a PDF
|
||||
removeAnnotations.tags=comments,highlight,notes,markup,remove
|
||||
|
||||
home.compare.title=Compara
|
||||
home.compare.desc=Vedi e compara le differenze tra due PDF.
|
||||
compare.tags=differenziare,contrastare,cambiare,analisi
|
||||
@@ -295,7 +300,7 @@ home.autoSplitPDF.title=Pagine divise automaticamente
|
||||
home.autoSplitPDF.desc=Dividi automaticamente il PDF scansionato con il codice QR dello divisore di pagina fisico scansionato
|
||||
autoSplitPDF.tags=Basato su QR,separato,scansiona segmenti,organizza
|
||||
|
||||
home.sanitizePdf.title=Igienizzare
|
||||
home.sanitizePdf.title=Pulire
|
||||
home.sanitizePdf.desc=Rimuovi script e altri elementi dai file PDF
|
||||
sanitizePdf.tags=pulire,proteggere,rimuovere le minacce
|
||||
|
||||
@@ -336,23 +341,23 @@ home.autoRedact.title=Redazione automatica
|
||||
home.autoRedact.desc=Redige automaticamente (oscura) il testo in un PDF in base al testo immesso
|
||||
showJS.tags=JS
|
||||
|
||||
home.tableExtraxt.title=PDF to CSV
|
||||
home.tableExtraxt.desc=Extracts Tables from a PDF converting it to CSV
|
||||
tableExtraxt.tags=CSV,Table Extraction,extract,convert
|
||||
home.tableExtraxt.title=Da PDF a CSV
|
||||
home.tableExtraxt.desc=Estrae tabelle da un PDF convertendolo in CSV
|
||||
tableExtraxt.tags=CSV,Estrazione tabella,estrai,converti
|
||||
|
||||
|
||||
home.autoSizeSplitPDF.title=Auto Split by Size/Count
|
||||
home.autoSizeSplitPDF.desc=Split a single PDF into multiple documents based on size, page count, or document count
|
||||
autoSizeSplitPDF.tags=pdf,split,document,organization
|
||||
home.autoSizeSplitPDF.title=Divisione automatica per dimensione/numero
|
||||
home.autoSizeSplitPDF.desc=Dividi un singolo PDF in più documenti in base alle dimensioni, al numero di pagine o al numero di documenti
|
||||
autoSizeSplitPDF.tags=pdf,diviso,documento,organizzazione
|
||||
|
||||
|
||||
home.overlay-pdfs.title=Overlay PDFs
|
||||
home.overlay-pdfs.desc=Overlays PDFs on-top of another PDF
|
||||
overlay-pdfs.tags=Overlay
|
||||
home.overlay-pdfs.title=Sovrapposizione di PDF
|
||||
home.overlay-pdfs.desc=Sovrappone i PDF sopra un altro PDF
|
||||
overlay-pdfs.tags=Svrapponi
|
||||
|
||||
home.split-by-sections.title=Split PDF by Sections
|
||||
home.split-by-sections.desc=Divide each page of a PDF into smaller horizontal and vertical sections
|
||||
split-by-sections.tags=Section Split, Divide, Customize
|
||||
home.split-by-sections.title=Dividi PDF per sezioni
|
||||
home.split-by-sections.desc=Dividi ciascuna pagina di un PDF in sezioni orizzontali e verticali più piccole
|
||||
split-by-sections.tags=Dividi sezione, dividi, personalizza
|
||||
|
||||
###########################
|
||||
# #
|
||||
@@ -432,14 +437,14 @@ HTMLToPDF.credit=Utilizza WeasyPrint
|
||||
|
||||
|
||||
#sanitizePDF
|
||||
sanitizePDF.title=Disinfetta PDF
|
||||
sanitizePDF.header=Disinfettare un file PDF
|
||||
sanitizePDF.title=Pulire PDF
|
||||
sanitizePDF.header=Pulisci un file PDF
|
||||
sanitizePDF.selectText.1=Rimuovi le azioni JavaScript
|
||||
sanitizePDF.selectText.2=Rimuovi i file incorporati
|
||||
sanitizePDF.selectText.3=Rimuovi i metadati
|
||||
sanitizePDF.selectText.4=Rimuovi collegamenti
|
||||
sanitizePDF.selectText.5=Rimuovi i fonts
|
||||
sanitizePDF.submit=Disinfetta PDF
|
||||
sanitizePDF.submit=Pulisci PDF
|
||||
|
||||
|
||||
#addPageNumbers
|
||||
@@ -539,6 +544,12 @@ removeBlanks.whitePercentDesc=Percentuale della pagina che deve essere bianca pe
|
||||
removeBlanks.submit=Rimuovi
|
||||
|
||||
|
||||
#removeAnnotations
|
||||
removeAnnotations.title=Remove Annotations
|
||||
removeAnnotations.header=Remove Annotations
|
||||
removeAnnotations.submit=Remove
|
||||
|
||||
|
||||
#compare
|
||||
compare.title=Compara
|
||||
compare.header=Compara PDF
|
||||
@@ -692,10 +703,10 @@ split.submit=Dividi
|
||||
imageToPDF.title=Immagine a PDF
|
||||
imageToPDF.header=Immagine a PDF
|
||||
imageToPDF.submit=Converti
|
||||
imageToPDF.selectLabel=Image Fit Options
|
||||
imageToPDF.fillPage=Fill Page
|
||||
imageToPDF.fitDocumentToImage=Fit Page to Image
|
||||
imageToPDF.maintainAspectRatio=Maintain Aspect Ratios
|
||||
imageToPDF.selectLabel=Opzioni di adattamento immagine
|
||||
imageToPDF.fillPage=Riempi la pagina
|
||||
imageToPDF.fitDocumentToImage=Adatta la pagina all'immagine
|
||||
imageToPDF.maintainAspectRatio=Mantieni le proporzioni
|
||||
imageToPDF.selectText.2=Ruota automaticamente PDF
|
||||
imageToPDF.selectText.3=Logica multi-file (funziona solo se ci sono più immagini)
|
||||
imageToPDF.selectText.4=Unisci in un unico PDF
|
||||
@@ -845,41 +856,41 @@ PDFToXML.submit=Converti
|
||||
#PDFToCSV
|
||||
PDFToCSV.title=Da PDF a CSV
|
||||
PDFToCSV.header=Da PDF a CSV
|
||||
PDFToCSV.prompt=Choose page to extract table
|
||||
PDFToCSV.prompt=Scegli la pagina per estrarre la tabella
|
||||
PDFToCSV.submit=Estratto
|
||||
|
||||
#split-by-size-or-count
|
||||
split-by-size-or-count.header=Split PDF by Size or Count
|
||||
split-by-size-or-count.type.label=Select Split Type
|
||||
split-by-size-or-count.type.size=By Size
|
||||
split-by-size-or-count.type.pageCount=By Page Count
|
||||
split-by-size-or-count.type.docCount=By Document Count
|
||||
split-by-size-or-count.value.label=Enter Value
|
||||
split-by-size-or-count.value.placeholder=Enter size (e.g., 2MB or 3KB) or count (e.g., 5)
|
||||
split-by-size-or-count.submit=Submit
|
||||
split-by-size-or-count.header=Dividi il PDF per dimensione o numero
|
||||
split-by-size-or-count.type.label=Seleziona il tipo di divisione
|
||||
split-by-size-or-count.type.size=Per dimensione
|
||||
split-by-size-or-count.type.pageCount=Per numero di pagine
|
||||
split-by-size-or-count.type.docCount=Per numero di documento
|
||||
split-by-size-or-count.value.label=Inserire il valore
|
||||
split-by-size-or-count.value.placeholder=Inserisci la dimensione (ad esempio, 2 MB o 3 KB) o il numero (ad esempio, 5)
|
||||
split-by-size-or-count.submit=Separa
|
||||
|
||||
|
||||
#overlay-pdfs
|
||||
overlay-pdfs.header=Overlay PDF Files
|
||||
overlay-pdfs.baseFile.label=Select Base PDF File
|
||||
overlay-pdfs.overlayFiles.label=Select Overlay PDF Files
|
||||
overlay-pdfs.mode.label=Select Overlay Mode
|
||||
overlay-pdfs.mode.sequential=Sequential Overlay
|
||||
overlay-pdfs.header=Invia file PDF in sovrapposizione
|
||||
overlay-pdfs.baseFile.label=Seleziona File PDF di base
|
||||
overlay-pdfs.overlayFiles.label=Seleziona sovrapposizione file PDF
|
||||
overlay-pdfs.mode.label=Seleziona la modalità di sovrapposizione
|
||||
overlay-pdfs.mode.sequential=Sovrapposizione sequenziale
|
||||
overlay-pdfs.mode.interleaved=Interleaved Overlay
|
||||
overlay-pdfs.mode.fixedRepeat=Fixed Repeat Overlay
|
||||
overlay-pdfs.counts.label=Overlay Counts (for Fixed Repeat Mode)
|
||||
overlay-pdfs.counts.placeholder=Enter comma-separated counts (e.g., 2,3,1)
|
||||
overlay-pdfs.position.label=Select Overlay Position
|
||||
overlay-pdfs.position.foreground=Foreground
|
||||
overlay-pdfs.position.background=Background
|
||||
overlay-pdfs.submit=Submit
|
||||
overlay-pdfs.counts.placeholder=Inserisci i numeri separati da virgole (ad esempio, 2,3,1)
|
||||
overlay-pdfs.position.label=Seleziona posizione di sovrapposizione
|
||||
overlay-pdfs.position.foreground=Primo piano
|
||||
overlay-pdfs.position.background=Sfondo
|
||||
overlay-pdfs.submit=Sovrapponi
|
||||
|
||||
|
||||
#split-by-sections
|
||||
split-by-sections.title=Split PDF by Sections
|
||||
split-by-sections.header=Split PDF into Sections
|
||||
split-by-sections.horizontal.label=Horizontal Divisions
|
||||
split-by-sections.vertical.label=Vertical Divisions
|
||||
split-by-sections.horizontal.placeholder=Enter number of horizontal divisions
|
||||
split-by-sections.vertical.placeholder=Enter number of vertical divisions
|
||||
split-by-sections.submit=Split PDF
|
||||
split-by-sections.title=Dividi PDF per sezioni
|
||||
split-by-sections.header=Dividi il PDF in sezioni
|
||||
split-by-sections.horizontal.label=Divisioni orizzontali
|
||||
split-by-sections.vertical.label=Divisioni verticali
|
||||
split-by-sections.horizontal.placeholder=Inserire il numero di divisioni orizzontali
|
||||
split-by-sections.vertical.placeholder=Inserire il numero di divisioni verticali
|
||||
split-by-sections.submit=Dividi PDF
|
||||
|
||||
@@ -119,6 +119,7 @@ adminUserSettings.role=役割
|
||||
adminUserSettings.actions=アクション
|
||||
adminUserSettings.apiUser=限定されたAPIユーザー
|
||||
adminUserSettings.webOnlyUser=ウェブ専用ユーザー
|
||||
adminUserSettings.demoUser=Demo User (No custom settings)
|
||||
adminUserSettings.forceChange=ログイン時にユーザー名/パスワードを強制的に変更する
|
||||
adminUserSettings.submit=ユーザーの保存
|
||||
|
||||
@@ -255,6 +256,10 @@ home.removeBlanks.title=空白ページの削除
|
||||
home.removeBlanks.desc=ドキュメントから空白ページを検出して削除します。
|
||||
removeBlanks.tags=cleanup,streamline,non-content,organize
|
||||
|
||||
home.removeAnnotations.title=Remove Annotations
|
||||
home.removeAnnotations.desc=Removes all comments/annotations from a PDF
|
||||
removeAnnotations.tags=comments,highlight,notes,markup,remove
|
||||
|
||||
home.compare.title=比較
|
||||
home.compare.desc=2つのPDFを比較して表示します。
|
||||
compare.tags=differentiate,contrast,changes,analysis
|
||||
@@ -539,6 +544,12 @@ removeBlanks.whitePercentDesc=削除するページの白の割合
|
||||
removeBlanks.submit=空白ページの削除
|
||||
|
||||
|
||||
#removeAnnotations
|
||||
removeAnnotations.title=Remove Annotations
|
||||
removeAnnotations.header=Remove Annotations
|
||||
removeAnnotations.submit=Remove
|
||||
|
||||
|
||||
#compare
|
||||
compare.title=比較
|
||||
compare.header=PDFの比較
|
||||
|
||||
@@ -119,6 +119,7 @@ adminUserSettings.role=역할
|
||||
adminUserSettings.actions=동작
|
||||
adminUserSettings.apiUser=제한된 API 사용
|
||||
adminUserSettings.webOnlyUser=웹 사용만 허용
|
||||
adminUserSettings.demoUser=Demo User (No custom settings)
|
||||
adminUserSettings.forceChange=다음 로그인 때 사용자명과 비밀번호를 변경하도록 강제
|
||||
adminUserSettings.submit=사용자 저장
|
||||
|
||||
@@ -255,6 +256,10 @@ home.removeBlanks.title=빈 페이지 제거
|
||||
home.removeBlanks.desc=PDF 문서에서 빈 페이지를 감지하고 제거합니다.
|
||||
removeBlanks.tags=cleanup,streamline,non-content,organize
|
||||
|
||||
home.removeAnnotations.title=Remove Annotations
|
||||
home.removeAnnotations.desc=Removes all comments/annotations from a PDF
|
||||
removeAnnotations.tags=comments,highlight,notes,markup,remove
|
||||
|
||||
home.compare.title=비교
|
||||
home.compare.desc=2개의 PDF 문서를 비교하고 차이를 표시합니다.
|
||||
compare.tags=differentiate,contrast,changes,analysis
|
||||
@@ -539,6 +544,12 @@ removeBlanks.whitePercentDesc=제거될 페이지의 흰색 픽셀 비율
|
||||
removeBlanks.submit=빈 페이지 제거
|
||||
|
||||
|
||||
#removeAnnotations
|
||||
removeAnnotations.title=Remove Annotations
|
||||
removeAnnotations.header=Remove Annotations
|
||||
removeAnnotations.submit=Remove
|
||||
|
||||
|
||||
#compare
|
||||
compare.title=비교
|
||||
compare.header=PDF 문서 비교
|
||||
|
||||
@@ -119,6 +119,7 @@ adminUserSettings.role=Rol
|
||||
adminUserSettings.actions=Acties
|
||||
adminUserSettings.apiUser=Beperkte API gebruiker
|
||||
adminUserSettings.webOnlyUser=Alleen web gebruiker
|
||||
adminUserSettings.demoUser=Demo User (No custom settings)
|
||||
adminUserSettings.forceChange=Force user to change username/password on login
|
||||
adminUserSettings.submit=Sla gebruiker op
|
||||
|
||||
@@ -255,6 +256,10 @@ home.removeBlanks.title=Verwijder lege pagina''s
|
||||
home.removeBlanks.desc=Detecteert en verwijdert lege pagina''s uit een document
|
||||
removeBlanks.tags=opruimen,stroomlijnen,geen-inhoud,organiseren
|
||||
|
||||
home.removeAnnotations.title=Remove Annotations
|
||||
home.removeAnnotations.desc=Removes all comments/annotations from a PDF
|
||||
removeAnnotations.tags=comments,highlight,notes,markup,remove
|
||||
|
||||
home.compare.title=Vergelijken
|
||||
home.compare.desc=Vergelijkt en toont de verschillen tussen 2 PDF-documenten
|
||||
compare.tags=onderscheiden,contrasteren,veranderingen,analyse
|
||||
@@ -539,6 +544,12 @@ removeBlanks.whitePercentDesc=Percentage van de pagina dat ''witte'' pixels moet
|
||||
removeBlanks.submit=Blanco''s verwijderen
|
||||
|
||||
|
||||
#removeAnnotations
|
||||
removeAnnotations.title=Remove Annotations
|
||||
removeAnnotations.header=Remove Annotations
|
||||
removeAnnotations.submit=Remove
|
||||
|
||||
|
||||
#compare
|
||||
compare.title=Vergelijken
|
||||
compare.header=PDF''s vergelijken
|
||||
|
||||
@@ -119,6 +119,7 @@ adminUserSettings.role=Role
|
||||
adminUserSettings.actions=Actions
|
||||
adminUserSettings.apiUser=Limited API User
|
||||
adminUserSettings.webOnlyUser=Web Only User
|
||||
adminUserSettings.demoUser=Demo User (No custom settings)
|
||||
adminUserSettings.forceChange=Force user to change username/password on login
|
||||
adminUserSettings.submit=Save User
|
||||
|
||||
@@ -255,6 +256,10 @@ home.removeBlanks.title=Usuń puste strony
|
||||
home.removeBlanks.desc=Wykrywa i usuwa puste strony z dokumentu PDF
|
||||
removeBlanks.tags=cleanup,streamline,non-content,organize
|
||||
|
||||
home.removeAnnotations.title=Remove Annotations
|
||||
home.removeAnnotations.desc=Removes all comments/annotations from a PDF
|
||||
removeAnnotations.tags=comments,highlight,notes,markup,remove
|
||||
|
||||
home.compare.title=Porównaj
|
||||
home.compare.desc=Porównuje i pokazuje różnice między dwoma dokumentami PDF
|
||||
compare.tags=differentiate,contrast,changes,analysis
|
||||
@@ -539,6 +544,12 @@ removeBlanks.whitePercentDesc=Procent strony, która musi być biała, aby zosta
|
||||
removeBlanks.submit=Usuń puste
|
||||
|
||||
|
||||
#removeAnnotations
|
||||
removeAnnotations.title=Remove Annotations
|
||||
removeAnnotations.header=Remove Annotations
|
||||
removeAnnotations.submit=Remove
|
||||
|
||||
|
||||
#compare
|
||||
compare.title=Porównaj
|
||||
compare.header=Porównaj PDF(y)
|
||||
|
||||
@@ -119,6 +119,7 @@ adminUserSettings.role=Role
|
||||
adminUserSettings.actions=Actions
|
||||
adminUserSettings.apiUser=Limited API User
|
||||
adminUserSettings.webOnlyUser=Web Only User
|
||||
adminUserSettings.demoUser=Demo User (No custom settings)
|
||||
adminUserSettings.forceChange=Force user to change username/password on login
|
||||
adminUserSettings.submit=Save User
|
||||
|
||||
@@ -255,6 +256,10 @@ home.removeBlanks.title=Удалить пустые страницы
|
||||
home.removeBlanks.desc=Обнаруживает и удаляет пустые страницы из документа
|
||||
removeBlanks.tags=cleanup,streamline,non-content,organize
|
||||
|
||||
home.removeAnnotations.title=Remove Annotations
|
||||
home.removeAnnotations.desc=Removes all comments/annotations from a PDF
|
||||
removeAnnotations.tags=comments,highlight,notes,markup,remove
|
||||
|
||||
home.compare.title=Сравнение
|
||||
home.compare.desc=Сравнивает и показывает различия между двумя PDF-документами
|
||||
compare.tags=differentiate,contrast,changes,analysis
|
||||
@@ -539,6 +544,12 @@ removeBlanks.whitePercentDesc=Общий процент белого на стр
|
||||
removeBlanks.submit=Удалить Пустые
|
||||
|
||||
|
||||
#removeAnnotations
|
||||
removeAnnotations.title=Remove Annotations
|
||||
removeAnnotations.header=Remove Annotations
|
||||
removeAnnotations.submit=Remove
|
||||
|
||||
|
||||
#compare
|
||||
compare.title=Сравнение
|
||||
compare.header=Сравнение PDFы
|
||||
|
||||
@@ -119,6 +119,7 @@ adminUserSettings.role=Role
|
||||
adminUserSettings.actions=Actions
|
||||
adminUserSettings.apiUser=Limited API User
|
||||
adminUserSettings.webOnlyUser=Web Only User
|
||||
adminUserSettings.demoUser=Demo User (No custom settings)
|
||||
adminUserSettings.forceChange=Force user to change username/password on login
|
||||
adminUserSettings.submit=Save User
|
||||
|
||||
@@ -255,6 +256,10 @@ home.removeBlanks.title=Ta bort tomma sidor
|
||||
home.removeBlanks.desc=Känner av och tar bort tomma sidor från ett dokument
|
||||
removeBlanks.tags=cleanup,streamline,non-content,organize
|
||||
|
||||
home.removeAnnotations.title=Remove Annotations
|
||||
home.removeAnnotations.desc=Removes all comments/annotations from a PDF
|
||||
removeAnnotations.tags=comments,highlight,notes,markup,remove
|
||||
|
||||
home.compare.title=Jämför
|
||||
home.compare.desc=Jämför och visar skillnaderna mellan 2 PDF-dokument
|
||||
compare.tags=differentiate,contrast,changes,analysis
|
||||
@@ -539,6 +544,12 @@ removeBlanks.whitePercentDesc=Procentandel av sidan som måste vara vit för att
|
||||
removeBlanks.submit=Ta bort tomrum
|
||||
|
||||
|
||||
#removeAnnotations
|
||||
removeAnnotations.title=Remove Annotations
|
||||
removeAnnotations.header=Remove Annotations
|
||||
removeAnnotations.submit=Remove
|
||||
|
||||
|
||||
#compare
|
||||
compare.title=Jämför
|
||||
compare.header=Jämför PDF-filer
|
||||
|
||||
@@ -119,6 +119,7 @@ adminUserSettings.role=Rol
|
||||
adminUserSettings.actions=Eylemler
|
||||
adminUserSettings.apiUser=Sınırlı API Kullanıcısı
|
||||
adminUserSettings.webOnlyUser=Sadece Web Kullanıcısı
|
||||
adminUserSettings.demoUser=Demo User (No custom settings)
|
||||
adminUserSettings.forceChange=Kullanıcının girişte kullanıcı adı/şifre değiştirmesini zorla
|
||||
adminUserSettings.submit=Kullanıcıyı Kaydet
|
||||
|
||||
@@ -255,6 +256,10 @@ home.removeBlanks.title=Boş Sayfaları Kaldır
|
||||
home.removeBlanks.desc=Bir belgeden boş sayfaları tespit eder ve kaldırır
|
||||
removeBlanks.tags=temizle,sadeleştir,içeriksiz,düzenle
|
||||
|
||||
home.removeAnnotations.title=Remove Annotations
|
||||
home.removeAnnotations.desc=Removes all comments/annotations from a PDF
|
||||
removeAnnotations.tags=comments,highlight,notes,markup,remove
|
||||
|
||||
home.compare.title=Karşılaştır
|
||||
home.compare.desc=2 PDF Belgesi arasındaki farkları karşılaştırır ve gösterir
|
||||
compare.tags=farklılaştır,karşılaştır,değişiklikler,analiz
|
||||
@@ -539,6 +544,12 @@ removeBlanks.whitePercentDesc=Bir sayfanın 'beyaz' pixel olması gereken yüzde
|
||||
removeBlanks.submit=Boşları Kaldır
|
||||
|
||||
|
||||
#removeAnnotations
|
||||
removeAnnotations.title=Remove Annotations
|
||||
removeAnnotations.header=Remove Annotations
|
||||
removeAnnotations.submit=Remove
|
||||
|
||||
|
||||
#compare
|
||||
compare.title=Karşılaştır
|
||||
compare.header=PDF'leri Karşılaştır
|
||||
|
||||
@@ -119,6 +119,7 @@ adminUserSettings.role=角色
|
||||
adminUserSettings.actions=操作
|
||||
adminUserSettings.apiUser=有限 API 用户
|
||||
adminUserSettings.webOnlyUser=仅限 Web 用户
|
||||
adminUserSettings.demoUser=Demo User (No custom settings)
|
||||
adminUserSettings.forceChange=强制用户在登录时更改用户名/密码
|
||||
adminUserSettings.submit=保存用户
|
||||
|
||||
@@ -126,12 +127,12 @@ adminUserSettings.submit=保存用户
|
||||
# HOME-PAGE #
|
||||
#############
|
||||
home.desc=CZL一站式服务,满足您的所有PDF需求。
|
||||
home.searchBar=Search for features...
|
||||
home.searchBar=搜索您需要的功能...
|
||||
|
||||
|
||||
home.viewPdf.title=View PDF
|
||||
home.viewPdf.desc=View, annotate, add text or images
|
||||
viewPdf.tags=view,read,annotate,text,image
|
||||
home.viewPdf.title=浏览PDF
|
||||
home.viewPdf.desc=浏览、注释、添加文本或图像
|
||||
viewPdf.tags=浏览、阅读、注释、文本、图像
|
||||
|
||||
home.multiTool.title=PDF多功能工具
|
||||
home.multiTool.desc=合并、旋转、重新排列和删除PDF页面
|
||||
@@ -255,6 +256,10 @@ home.removeBlanks.title=删除空白页
|
||||
home.removeBlanks.desc=检测并删除文档中的空白页
|
||||
removeBlanks.tags=清理、简化、非内容、整理
|
||||
|
||||
home.removeAnnotations.title=删除标注
|
||||
home.removeAnnotations.desc=删除PDF中的所有标注/评论
|
||||
removeAnnotations.tags=评论、高亮、笔记、标注、删除
|
||||
|
||||
home.compare.title=比较
|
||||
home.compare.desc=比较并显示两个PDF文档之间的差异
|
||||
compare.tags=区分、对比、更改、分析
|
||||
@@ -337,22 +342,22 @@ home.autoRedact.desc=根据输入文本自动删除(覆盖)PDF中的文本
|
||||
showJS.tags=JavaScript
|
||||
|
||||
home.tableExtraxt.title=PDF to CSV
|
||||
home.tableExtraxt.desc=Extracts Tables from a PDF converting it to CSV
|
||||
tableExtraxt.tags=CSV,Table Extraction,extract,convert
|
||||
home.tableExtraxt.desc=从PDF中提取表格并将其转换为CSV
|
||||
tableExtraxt.tags=CSV、表格提取、提取、转换
|
||||
|
||||
|
||||
home.autoSizeSplitPDF.title=Auto Split by Size/Count
|
||||
home.autoSizeSplitPDF.desc=Split a single PDF into multiple documents based on size, page count, or document count
|
||||
autoSizeSplitPDF.tags=pdf,split,document,organization
|
||||
home.autoSizeSplitPDF.title=自动根据大小/数目拆分PDF
|
||||
home.autoSizeSplitPDF.desc=将单个PDF拆分为多个文档,基于大小、页数或文档数
|
||||
autoSizeSplitPDF.tags=pdf、拆分、文件、组织
|
||||
|
||||
|
||||
home.overlay-pdfs.title=Overlay PDFs
|
||||
home.overlay-pdfs.desc=Overlays PDFs on-top of another PDF
|
||||
overlay-pdfs.tags=Overlay
|
||||
home.overlay-pdfs.title=叠加PDF
|
||||
home.overlay-pdfs.desc=将PDF叠加在另一个PDF上
|
||||
overlay-pdfs.tags=叠加
|
||||
|
||||
home.split-by-sections.title=Split PDF by Sections
|
||||
home.split-by-sections.desc=Divide each page of a PDF into smaller horizontal and vertical sections
|
||||
split-by-sections.tags=Section Split, Divide, Customize
|
||||
home.split-by-sections.title=拆分PDF成小块
|
||||
home.split-by-sections.desc=将PDF的每一页分割成更小的水平和垂直的部分
|
||||
split-by-sections.tags=章节拆分、分割、自定义
|
||||
|
||||
###########################
|
||||
# #
|
||||
@@ -539,6 +544,12 @@ removeBlanks.whitePercentDesc=必须为白色才能删除的页面百分比
|
||||
removeBlanks.submit=删除空白
|
||||
|
||||
|
||||
#removeAnnotations
|
||||
removeAnnotations.title=Remove Annotations
|
||||
removeAnnotations.header=Remove Annotations
|
||||
removeAnnotations.submit=Remove
|
||||
|
||||
|
||||
#compare
|
||||
compare.title=比较
|
||||
compare.header=比较 PDF
|
||||
@@ -843,43 +854,43 @@ PDFToXML.credit=此服务使用LibreOffice进行文件转换。
|
||||
PDFToXML.submit=转换
|
||||
|
||||
#PDFToCSV
|
||||
PDFToCSV.title=PDF ? CSV
|
||||
PDFToCSV.header=PDF ? CSV
|
||||
PDFToCSV.prompt=Choose page to extract table
|
||||
PDFToCSV.submit=??
|
||||
PDFToCSV.title=PDF To CSV
|
||||
PDFToCSV.header=将 PDF 转换为 CSV
|
||||
PDFToCSV.prompt=选择需要提取表格的页面
|
||||
PDFToCSV.submit=提取
|
||||
|
||||
#split-by-size-or-count
|
||||
split-by-size-or-count.header=Split PDF by Size or Count
|
||||
split-by-size-or-count.type.label=Select Split Type
|
||||
split-by-size-or-count.type.size=By Size
|
||||
split-by-size-or-count.type.pageCount=By Page Count
|
||||
split-by-size-or-count.type.docCount=By Document Count
|
||||
split-by-size-or-count.value.label=Enter Value
|
||||
split-by-size-or-count.value.placeholder=Enter size (e.g., 2MB or 3KB) or count (e.g., 5)
|
||||
split-by-size-or-count.submit=Submit
|
||||
split-by-size-or-count.header=按照大小或数目拆分PDF
|
||||
split-by-size-or-count.type.label=选择拆分类型
|
||||
split-by-size-or-count.type.size=按照大小
|
||||
split-by-size-or-count.type.pageCount=按照页数
|
||||
split-by-size-or-count.type.docCount=按照文档数
|
||||
split-by-size-or-count.value.label=输入数值
|
||||
split-by-size-or-count.value.placeholder=输入大小(例如,2MB或3KB)或数目(例如,5)
|
||||
split-by-size-or-count.submit=提交
|
||||
|
||||
|
||||
#overlay-pdfs
|
||||
overlay-pdfs.header=Overlay PDF Files
|
||||
overlay-pdfs.baseFile.label=Select Base PDF File
|
||||
overlay-pdfs.overlayFiles.label=Select Overlay PDF Files
|
||||
overlay-pdfs.mode.label=Select Overlay Mode
|
||||
overlay-pdfs.mode.sequential=Sequential Overlay
|
||||
overlay-pdfs.mode.interleaved=Interleaved Overlay
|
||||
overlay-pdfs.mode.fixedRepeat=Fixed Repeat Overlay
|
||||
overlay-pdfs.counts.label=Overlay Counts (for Fixed Repeat Mode)
|
||||
overlay-pdfs.counts.placeholder=Enter comma-separated counts (e.g., 2,3,1)
|
||||
overlay-pdfs.position.label=Select Overlay Position
|
||||
overlay-pdfs.position.foreground=Foreground
|
||||
overlay-pdfs.position.background=Background
|
||||
overlay-pdfs.submit=Submit
|
||||
overlay-pdfs.header=叠加PDF文件
|
||||
overlay-pdfs.baseFile.label=选择基础PDF文件
|
||||
overlay-pdfs.overlayFiles.label=选择需要叠加在基础上的PDF文件
|
||||
overlay-pdfs.mode.label=选择叠加模式
|
||||
overlay-pdfs.mode.sequential=按顺序叠加
|
||||
overlay-pdfs.mode.interleaved=交错叠加
|
||||
overlay-pdfs.mode.fixedRepeat=固定重复叠加
|
||||
overlay-pdfs.counts.label=叠加次数(仅限固定重复叠加模式)
|
||||
overlay-pdfs.counts.placeholder=输入用逗号分隔的次数(例如,2,3,1)
|
||||
overlay-pdfs.position.label=选择叠加位置
|
||||
overlay-pdfs.position.foreground=前面(上面)
|
||||
overlay-pdfs.position.background=后面(下面)
|
||||
overlay-pdfs.submit=提交
|
||||
|
||||
|
||||
#split-by-sections
|
||||
split-by-sections.title=Split PDF by Sections
|
||||
split-by-sections.header=Split PDF into Sections
|
||||
split-by-sections.horizontal.label=Horizontal Divisions
|
||||
split-by-sections.vertical.label=Vertical Divisions
|
||||
split-by-sections.horizontal.placeholder=Enter number of horizontal divisions
|
||||
split-by-sections.vertical.placeholder=Enter number of vertical divisions
|
||||
split-by-sections.submit=Split PDF
|
||||
split-by-sections.title=按照块(Section)拆分PDF
|
||||
split-by-sections.header=将PDF拆分成块
|
||||
split-by-sections.horizontal.label=水平分割
|
||||
split-by-sections.vertical.label=垂直分割
|
||||
split-by-sections.horizontal.placeholder=输入水平分割数
|
||||
split-by-sections.vertical.placeholder=输入垂直分割数
|
||||
split-by-sections.submit=分割PDF
|
||||
|
||||
@@ -5,10 +5,13 @@
|
||||
security:
|
||||
enableLogin: false # set to 'true' to enable login
|
||||
csrfDisabled: true
|
||||
|
||||
loginAttemptCount: 5 # lock user account after 5 tries
|
||||
loginResetTimeMinutes : 120 # lock account for 2 hours after x attempts
|
||||
|
||||
system:
|
||||
defaultLocale: 'en-US' # Set the default language (e.g. 'de-DE', 'fr-FR', etc)
|
||||
googlevisibility: false # 'true' to allow Google visibility (via robots.txt), 'false' to disallow
|
||||
enableAlphaFunctionality: false # Set to enable functionality which might need more testing before it fully goes live (This feature might make no changes)
|
||||
|
||||
#ui:
|
||||
# appName: exampleAppName # Application's visible name
|
||||
|
||||
5
src/main/resources/static/css/bootstrap-icons.min.css
vendored
Normal file
5
src/main/resources/static/css/bootstrap-icons.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -75,6 +75,13 @@ table th, table td {
|
||||
border: none;
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
.btn-warning {
|
||||
background-color: #ffc107 !important;
|
||||
border: none;
|
||||
color: #000 !important;
|
||||
}
|
||||
|
||||
.btn-outline-secondary {
|
||||
color: #fff !important;
|
||||
border-color: #fff;
|
||||
@@ -92,6 +99,11 @@ hr {
|
||||
background-color: rgba(255, 255, 255, 0.6); /* for some browsers that might use background instead of border for <hr> */
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
color: #fff !important;
|
||||
border-color: #fff;
|
||||
}
|
||||
|
||||
#global-buttons-container input {
|
||||
background-color: #323948;
|
||||
caret-color: #ffffff;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#searchBar {
|
||||
background-image: url('/images/search.svg');
|
||||
background-image: url('../images/search.svg');
|
||||
background-position: 16px 16px;
|
||||
background-repeat: no-repeat;
|
||||
width: 100%;
|
||||
|
||||
5
src/main/resources/static/images/flags/hu.svg
Normal file
5
src/main/resources/static/images/flags/hu.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-hu" viewBox="0 0 640 480">
|
||||
<path fill="#339933" d="M0 320h640v160H0z"/>
|
||||
<path fill="#cc0033" d="M0 0h640v160H0z"/>
|
||||
<path fill="#ffffff" d="M0 160h640v160H0z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 228 B |
4
src/main/resources/static/images/flags/id.svg
Normal file
4
src/main/resources/static/images/flags/id.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-id" viewBox="0 0 640 480">
|
||||
<path fill="#e70011" d="M0 0h640v240H0Z"/>
|
||||
<path fill="#fff" d="M0 240h640v240H0Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 178 B |
25
src/main/resources/static/images/flags/in.svg
Normal file
25
src/main/resources/static/images/flags/in.svg
Normal file
@@ -0,0 +1,25 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="flag-icons-in" viewBox="0 0 640 480">
|
||||
<path fill="#f93" d="M0 0h640v160H0z"/>
|
||||
<path fill="#fff" d="M0 160h640v160H0z"/>
|
||||
<path fill="#128807" d="M0 320h640v160H0z"/>
|
||||
<g transform="matrix(3.2 0 0 3.2 320 240)">
|
||||
<circle r="20" fill="#008"/>
|
||||
<circle r="17.5" fill="#fff"/>
|
||||
<circle r="3.5" fill="#008"/>
|
||||
<g id="in-d">
|
||||
<g id="in-c">
|
||||
<g id="in-b">
|
||||
<g id="in-a" fill="#008">
|
||||
<circle r=".9" transform="rotate(7.5 -8.8 133.5)"/>
|
||||
<path d="M0 17.5.6 7 0 2l-.6 5z"/>
|
||||
</g>
|
||||
<use xlink:href="#in-a" width="100%" height="100%" transform="rotate(15)"/>
|
||||
</g>
|
||||
<use xlink:href="#in-b" width="100%" height="100%" transform="rotate(30)"/>
|
||||
</g>
|
||||
<use xlink:href="#in-c" width="100%" height="100%" transform="rotate(60)"/>
|
||||
</g>
|
||||
<use xlink:href="#in-d" width="100%" height="100%" transform="rotate(120)"/>
|
||||
<use xlink:href="#in-d" width="100%" height="100%" transform="rotate(-120)"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
6
src/main/resources/static/images/no-chat.svg
Normal file
6
src/main/resources/static/images/no-chat.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg width="16" height="16" fill="currentColor" class="bi bi-chat-left-text" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M14 1a1 1 0 0 1 1 1v8a1 1 0 0 1-1 1H4.414A2 2 0 0 0 3 11.586l-2 2V2a1 1 0 0 1 1-1zM2 0a2 2 0 0 0-2 2v12.793a.5.5 0 0 0 .854.353l2.853-2.853A1 1 0 0 1 4.414 12H14a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2z" style="fill: rgb(0, 0, 0);"/>
|
||||
<path d="M3 3.5a.5.5 0 0 1 .5-.5h9a.5.5 0 0 1 0 1h-9a.5.5 0 0 1-.5-.5M3 6a.5.5 0 0 1 .5-.5h9a.5.5 0 0 1 0 1h-9A.5.5 0 0 1 3 6m0 2.5a.5.5 0 0 1 .5-.5h5a.5.5 0 0 1 0 1h-5a.5.5 0 0 1-.5-.5" style="fill: rgb(0, 0, 0);"/>
|
||||
<path d="M 0.125 0.178 C 0.312 -0.024 0.636 -0.043 0.831 0.152 L 15.771 15.1 C 16.242 15.571 15.561 16.304 15.09 15.832 L 0.151 0.885 C -0.045 0.69 -0.063 0.381 0.125 0.178 Z" id="path937" style="fill: rgb(0, 0, 0);"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 853 B |
@@ -1,5 +1,3 @@
|
||||
|
||||
|
||||
class DragDropManager {
|
||||
dragContainer;
|
||||
wrapper;
|
||||
@@ -47,16 +45,15 @@ class DragDropManager {
|
||||
const img = div.querySelector('img');
|
||||
div.classList.add('drag-manager_dragging');
|
||||
const imageSrc = img.src;
|
||||
|
||||
|
||||
const imgEl = document.createElement('img');
|
||||
imgEl.classList.add('dragged-img');
|
||||
imgEl.src = imageSrc;
|
||||
this.draggedImageEl = imgEl;
|
||||
imgEl.style.left = screenX;
|
||||
imgEl.style.right = screenY;
|
||||
imgEl.style.visibility = 'hidden';
|
||||
imgEl.style.transform = `rotate(${img.style.rotate === '' ? '0deg' : img.style.rotate}) translate(-50%, -50%)`;
|
||||
this.dragContainer.appendChild(imgEl);
|
||||
|
||||
|
||||
window.addEventListener('mouseup', this.stopDraggingPage)
|
||||
window.addEventListener('mousemove', this.onDragEl)
|
||||
this.wrapper.classList.add('drag-manager_dragging-container');
|
||||
@@ -66,12 +63,13 @@ class DragDropManager {
|
||||
onDragEl(mouseEvent) {
|
||||
const { clientX, clientY } = mouseEvent;
|
||||
if(this.draggedImageEl) {
|
||||
this.draggedImageEl.style.visibility = 'visible';
|
||||
this.draggedImageEl.style.left = `${clientX}px`;
|
||||
this.draggedImageEl.style.top = `${clientY}px`;
|
||||
this.draggedImageEl.style.top = `${clientY}px`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
stopDraggingPage() {
|
||||
window.removeEventListener('mousemove', this.onDragEl);
|
||||
this.wrapper.classList.remove('drag-manager_dragging-container');
|
||||
@@ -103,7 +101,7 @@ class DragDropManager {
|
||||
const onDragStart = () => {
|
||||
this.startDraggingPage(div);
|
||||
}
|
||||
|
||||
|
||||
const onMouseEnter = () => {
|
||||
if (this.pageDragging) {
|
||||
this.hoveredEl = div;
|
||||
@@ -124,4 +122,4 @@ class DragDropManager {
|
||||
}
|
||||
}
|
||||
|
||||
export default DragDropManager;
|
||||
export default DragDropManager;
|
||||
|
||||
@@ -12,20 +12,17 @@ function validatePipeline() {
|
||||
if (currentOperation === '/add-password') {
|
||||
containsAddPassword = true;
|
||||
}
|
||||
console.log(currentOperation);
|
||||
console.log(apiDocs[currentOperation]);
|
||||
|
||||
let currentOperationDescription = apiDocs[currentOperation]?.post?.description || "";
|
||||
let nextOperationDescription = apiDocs[nextOperation]?.post?.description || "";
|
||||
|
||||
console.log("currentOperationDescription", currentOperationDescription);
|
||||
console.log("nextOperationDescription", nextOperationDescription);
|
||||
|
||||
// Strip off 'ZIP-' prefix
|
||||
currentOperationDescription = currentOperationDescription.replace("ZIP-", '');
|
||||
nextOperationDescription = nextOperationDescription.replace("ZIP-", '');
|
||||
|
||||
let currentOperationOutput = currentOperationDescription.match(/Output:([A-Z\/]*)/)?.[1] || "";
|
||||
let nextOperationInput = nextOperationDescription.match(/Input:([A-Z\/]*)/)?.[1] || "";
|
||||
|
||||
console.log("Operation " + currentOperation + " Output: " + currentOperationOutput);
|
||||
console.log("Operation " + nextOperation + " Input: " + nextOperationInput);
|
||||
|
||||
// Splitting in case of multiple possible output/input
|
||||
let currentOperationOutputArr = currentOperationOutput.split('/');
|
||||
let nextOperationInputArr = nextOperationInput.split('/');
|
||||
@@ -35,6 +32,7 @@ function validatePipeline() {
|
||||
console.log(`Intersection: ${intersection}`);
|
||||
|
||||
if (intersection.length === 0) {
|
||||
updateValidateButton(false);
|
||||
isValid = false;
|
||||
console.log(`Incompatible operations: The output of operation '${currentOperation}' (${currentOperationOutput}) is not compatible with the input of the following operation '${nextOperation}' (${nextOperationInput}).`);
|
||||
alert(`Incompatible operations: The output of operation '${currentOperation}' (${currentOperationOutput}) is not compatible with the input of the following operation '${nextOperation}' (${nextOperationInput}).`);
|
||||
@@ -43,6 +41,7 @@ function validatePipeline() {
|
||||
}
|
||||
}
|
||||
if (containsAddPassword && pipelineListItems[pipelineListItems.length - 1].querySelector('.operationName').textContent !== '/add-password') {
|
||||
updateValidateButton(false);
|
||||
alert('The "add-password" operation should be at the end of the operations sequence. Please adjust the operations order.');
|
||||
return false;
|
||||
}
|
||||
@@ -53,10 +52,20 @@ function validatePipeline() {
|
||||
console.error('Pipeline is not valid');
|
||||
// Stop operation, maybe display an error to the user
|
||||
}
|
||||
|
||||
updateValidateButton(isValid);
|
||||
return isValid;
|
||||
}
|
||||
|
||||
function updateValidateButton(isValid) {
|
||||
var validateButton = document.getElementById('validateButton');
|
||||
if (isValid) {
|
||||
validateButton.classList.remove('btn-danger');
|
||||
validateButton.classList.add('btn-success');
|
||||
} else {
|
||||
validateButton.classList.remove('btn-success');
|
||||
validateButton.classList.add('btn-danger');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -67,14 +76,14 @@ document.getElementById('submitConfigBtn').addEventListener('click', function()
|
||||
return;
|
||||
}
|
||||
let selectedOperation = document.getElementById('operationsDropdown').value;
|
||||
let parameters = operationSettings[selectedOperation] || {};
|
||||
|
||||
|
||||
|
||||
var pipelineName = document.getElementById('pipelineName').value;
|
||||
let pipelineList = document.getElementById('pipelineList').children;
|
||||
let pipelineConfig = {
|
||||
"name": "uniquePipelineName",
|
||||
"pipeline": [{
|
||||
"operation": selectedOperation,
|
||||
"parameters": parameters
|
||||
}],
|
||||
"name": pipelineName,
|
||||
"pipeline": [],
|
||||
"_examples": {
|
||||
"outputDir": "{outputFolder}/{folderName}",
|
||||
"outputFileName": "{filename}-{pipelineName}-{date}-{time}"
|
||||
@@ -83,6 +92,28 @@ document.getElementById('submitConfigBtn').addEventListener('click', function()
|
||||
"outputFileName": "{filename}"
|
||||
};
|
||||
|
||||
for (let i = 0; i < pipelineList.length; i++) {
|
||||
let operationName = pipelineList[i].querySelector('.operationName').textContent;
|
||||
let parameters = operationSettings[operationName] || {};
|
||||
|
||||
pipelineConfig.pipeline.push({
|
||||
"operation": operationName,
|
||||
"parameters": parameters
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
let pipelineConfigJson = JSON.stringify(pipelineConfig, null, 2);
|
||||
|
||||
let formData = new FormData();
|
||||
@@ -99,37 +130,50 @@ document.getElementById('submitConfigBtn').addEventListener('click', function()
|
||||
formData.append('json', pipelineConfigJson);
|
||||
console.log("formData", formData);
|
||||
|
||||
fetch('/handleData', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
fetch('api/v1/pipeline/handleData', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
})
|
||||
.then(response => response.blob())
|
||||
.then(blob => {
|
||||
.then(response => {
|
||||
// Save the response to use it later
|
||||
const responseToUseLater = response;
|
||||
|
||||
return response.blob().then(blob => {
|
||||
let url = window.URL.createObjectURL(blob);
|
||||
let a = document.createElement('a');
|
||||
a.href = url;
|
||||
|
||||
// Use responseToUseLater instead of response
|
||||
const contentDisposition = responseToUseLater.headers.get('Content-Disposition');
|
||||
let filename = 'download';
|
||||
if (contentDisposition && contentDisposition.indexOf('attachment') !== -1) {
|
||||
filename = decodeURIComponent(contentDisposition.split('filename=')[1].replace(/"/g, '')).trim();
|
||||
}
|
||||
a.download = filename;
|
||||
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
a.remove();
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Error:', error);
|
||||
});
|
||||
|
||||
let url = window.URL.createObjectURL(blob);
|
||||
let a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = 'outputfile';
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
a.remove();
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Error:', error);
|
||||
});
|
||||
});
|
||||
|
||||
let apiDocs = {};
|
||||
|
||||
let apiSchemas = {};
|
||||
let operationSettings = {};
|
||||
|
||||
fetch('v3/api-docs')
|
||||
fetch('v1/api-docs')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
|
||||
apiDocs = data.paths;
|
||||
apiSchemas = data.components.schemas;
|
||||
let operationsDropdown = document.getElementById('operationsDropdown');
|
||||
const ignoreOperations = ["/handleData", "operationToIgnore"]; // Add the operations you want to ignore here
|
||||
const ignoreOperations = ["/api/v1/pipeline/handleData", "/api/v1/pipeline/operationToIgnore"]; // Add the operations you want to ignore here
|
||||
|
||||
operationsDropdown.innerHTML = '';
|
||||
|
||||
@@ -138,6 +182,9 @@ fetch('v3/api-docs')
|
||||
// Group operations by tags
|
||||
Object.keys(data.paths).forEach(operationPath => {
|
||||
let operation = data.paths[operationPath].post;
|
||||
if(!operation || !operation.description) {
|
||||
console.log(operationPath);
|
||||
}
|
||||
if (operation && !ignoreOperations.includes(operationPath) && !operation.description.includes("Type:MISO")) {
|
||||
let operationTag = operation.tags[0]; // This assumes each operation has exactly one tag
|
||||
if (!operationsByTag[operationTag]) {
|
||||
@@ -146,9 +193,8 @@ fetch('v3/api-docs')
|
||||
operationsByTag[operationTag].push(operationPath);
|
||||
}
|
||||
});
|
||||
|
||||
// Specify the order of tags
|
||||
let tagOrder = ["General", "Security", "Convert", "Other", "Filter"];
|
||||
let tagOrder = ["General", "Security", "Convert", "Misc", "Filter"];
|
||||
|
||||
// Create dropdown options
|
||||
tagOrder.forEach(tag => {
|
||||
@@ -158,8 +204,18 @@ fetch('v3/api-docs')
|
||||
|
||||
operationsByTag[tag].forEach(operationPath => {
|
||||
let option = document.createElement('option');
|
||||
let operationWithoutSlash = operationPath.replace(/\//g, ''); // Remove slashes
|
||||
option.textContent = operationWithoutSlash;
|
||||
|
||||
let operationPathDisplay = operationPath
|
||||
operationPathDisplay = operationPath.replace(new RegExp("api/v1/" + tag.toLowerCase() + "/", 'i'), "");
|
||||
|
||||
|
||||
if(operationPath.includes("/convert")){
|
||||
operationPathDisplay = operationPathDisplay.replace(/^\//, '').replaceAll("/", " to ");
|
||||
} else {
|
||||
operationPathDisplay = operationPathDisplay.replace(/\//g, ''); // Remove slashes
|
||||
}
|
||||
operationPathDisplay = operationPathDisplay.replaceAll(" ","-");
|
||||
option.textContent = operationPathDisplay;
|
||||
option.value = operationPath; // Keep the value with slashes for querying
|
||||
group.appendChild(option);
|
||||
});
|
||||
@@ -176,25 +232,40 @@ document.getElementById('addOperationBtn').addEventListener('click', function()
|
||||
|
||||
let listItem = document.createElement('li');
|
||||
listItem.className = "list-group-item";
|
||||
let hasSettings = (apiDocs[selectedOperation] && apiDocs[selectedOperation].post &&
|
||||
((apiDocs[selectedOperation].post.parameters && apiDocs[selectedOperation].post.parameters.length > 0) ||
|
||||
(apiDocs[selectedOperation].post.requestBody &&
|
||||
apiDocs[selectedOperation].post.requestBody.content['multipart/form-data'].schema.properties)));
|
||||
let hasSettings = false;
|
||||
if (apiDocs[selectedOperation] && apiDocs[selectedOperation].post) {
|
||||
const postMethod = apiDocs[selectedOperation].post;
|
||||
|
||||
// Check if parameters exist
|
||||
if (postMethod.parameters && postMethod.parameters.length > 0) {
|
||||
hasSettings = true;
|
||||
} else if (postMethod.requestBody && postMethod.requestBody.content['multipart/form-data']) {
|
||||
// Extract the reference key
|
||||
const refKey = postMethod.requestBody.content['multipart/form-data'].schema['$ref'].split('/').pop();
|
||||
// Check if the referenced schema exists and has properties
|
||||
if (apiSchemas[refKey] && Object.keys(apiSchemas[refKey].properties).length > 0) {
|
||||
hasSettings = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
listItem.innerHTML = `
|
||||
<div class="d-flex justify-content-between align-items-center w-100">
|
||||
<div class="operationName">${selectedOperation}</div>
|
||||
<div class="arrows d-flex">
|
||||
<button class="btn btn-secondary move-up btn-margin"><span>↑</span></button>
|
||||
<button class="btn btn-secondary move-down btn-margin"><span>↓</span></button>
|
||||
<button class="btn btn-warning pipelineSettings btn-margin" ${hasSettings ? "" : "disabled"}><span style="color: ${hasSettings ? "black" : "grey"};">⚙️</span></button>
|
||||
<button class="btn btn-danger remove"><span>X</span></button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
<div class="d-flex justify-content-between align-items-center w-100">
|
||||
<div class="operationName">${selectedOperation}</div>
|
||||
<div class="arrows d-flex">
|
||||
<button class="btn btn-secondary move-up ms-1"><span>↑</span></button>
|
||||
<button class="btn btn-secondary move-down ms-1"><span>↓</span></button>
|
||||
<button class="btn ${hasSettings ? 'btn-warning' : 'btn-secondary'} pipelineSettings ms-1" ${hasSettings ? "" : "disabled"}>
|
||||
<span style="color: ${hasSettings ? "white" : "grey"};">⚙️</span>
|
||||
</button>
|
||||
<button class="btn btn-danger remove ms-1"><span>X</span></button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
|
||||
pipelineList.appendChild(listItem);
|
||||
|
||||
@@ -215,23 +286,28 @@ document.getElementById('addOperationBtn').addEventListener('click', function()
|
||||
listItem.querySelector('.remove').addEventListener('click', function(event) {
|
||||
event.preventDefault();
|
||||
pipelineList.removeChild(listItem);
|
||||
hideOrShowPipelineHeader();
|
||||
});
|
||||
|
||||
listItem.querySelector('.pipelineSettings').addEventListener('click', function(event) {
|
||||
event.preventDefault();
|
||||
showpipelineSettingsModal(selectedOperation);
|
||||
hideOrShowPipelineHeader();
|
||||
});
|
||||
|
||||
function showpipelineSettingsModal(operation) {
|
||||
let pipelineSettingsModal = document.getElementById('pipelineSettingsModal');
|
||||
let pipelineSettingsContent = document.getElementById('pipelineSettingsContent');
|
||||
let operationData = apiDocs[operation].post.parameters || [];
|
||||
let requestBodyData = apiDocs[operation].post.requestBody.content['multipart/form-data'].schema.properties || {};
|
||||
|
||||
// Resolve the $ref reference to get actual schema properties
|
||||
let refKey = apiDocs[operation].post.requestBody.content['multipart/form-data'].schema['$ref'].split('/').pop();
|
||||
let requestBodyData = apiSchemas[refKey].properties || {};
|
||||
|
||||
// Combine operationData and requestBodyData into a single array
|
||||
operationData = operationData.concat(Object.keys(requestBodyData).map(key => ({
|
||||
name: key,
|
||||
schema: requestBodyData[key]
|
||||
name: key,
|
||||
schema: requestBodyData[key]
|
||||
})));
|
||||
|
||||
pipelineSettingsContent.innerHTML = '';
|
||||
@@ -245,11 +321,15 @@ document.getElementById('addOperationBtn').addEventListener('click', function()
|
||||
|
||||
let parameterLabel = document.createElement('label');
|
||||
parameterLabel.textContent = `${parameter.name} (${parameter.schema.type}): `;
|
||||
parameterLabel.title = parameter.description;
|
||||
parameterLabel.title = parameter.schema.description;
|
||||
parameterLabel.setAttribute('for', parameter.name);
|
||||
parameterDiv.appendChild(parameterLabel);
|
||||
|
||||
let defaultValue = parameter.schema.example;
|
||||
if (defaultValue === undefined) defaultValue = parameter.schema.default;
|
||||
|
||||
let parameterInput;
|
||||
|
||||
|
||||
// check if enum exists in schema
|
||||
if (parameter.schema.enum) {
|
||||
// if enum exists, create a select element
|
||||
@@ -277,11 +357,12 @@ document.getElementById('addOperationBtn').addEventListener('click', function()
|
||||
parameterInput = document.createElement('input');
|
||||
parameterInput.type = 'text';
|
||||
parameterInput.className = "form-control";
|
||||
parameterInput.value = "automatedFileInput";
|
||||
parameterInput.value = "FileInputPathToBeInputtedManuallyOffline";
|
||||
} else {
|
||||
parameterInput = document.createElement('input');
|
||||
parameterInput.type = 'text';
|
||||
parameterInput.className = "form-control";
|
||||
if (defaultValue !== undefined) parameterInput.value = defaultValue;
|
||||
}
|
||||
break;
|
||||
case 'number':
|
||||
@@ -289,10 +370,12 @@ document.getElementById('addOperationBtn').addEventListener('click', function()
|
||||
parameterInput = document.createElement('input');
|
||||
parameterInput.type = 'number';
|
||||
parameterInput.className = "form-control";
|
||||
if (defaultValue !== undefined) parameterInput.value = defaultValue;
|
||||
break;
|
||||
case 'boolean':
|
||||
parameterInput = document.createElement('input');
|
||||
parameterInput.type = 'checkbox';
|
||||
if (defaultValue === true) parameterInput.checked = true;
|
||||
break;
|
||||
case 'array':
|
||||
case 'object':
|
||||
@@ -304,10 +387,13 @@ document.getElementById('addOperationBtn').addEventListener('click', function()
|
||||
parameterInput = document.createElement('input');
|
||||
parameterInput.type = 'text';
|
||||
parameterInput.className = "form-control";
|
||||
if (defaultValue !== undefined) parameterInput.value = defaultValue;
|
||||
}
|
||||
}
|
||||
parameterInput.id = parameter.name;
|
||||
|
||||
console.log("defaultValue", defaultValue);
|
||||
console.log("parameterInput", parameterInput);
|
||||
if (operationSettings[operation] && operationSettings[operation][parameter.name] !== undefined) {
|
||||
let savedValue = operationSettings[operation][parameter.name];
|
||||
|
||||
@@ -327,7 +413,7 @@ document.getElementById('addOperationBtn').addEventListener('click', function()
|
||||
parameterInput.value = savedValue;
|
||||
}
|
||||
}
|
||||
|
||||
console.log("parameterInput2", parameterInput);
|
||||
parameterDiv.appendChild(parameterInput);
|
||||
|
||||
pipelineSettingsContent.appendChild(parameterDiv);
|
||||
@@ -340,50 +426,65 @@ document.getElementById('addOperationBtn').addEventListener('click', function()
|
||||
event.preventDefault();
|
||||
let settings = {};
|
||||
operationData.forEach(parameter => {
|
||||
let value = document.getElementById(parameter.name).value;
|
||||
switch (parameter.schema.type) {
|
||||
case 'number':
|
||||
case 'integer':
|
||||
settings[parameter.name] = Number(value);
|
||||
break;
|
||||
case 'boolean':
|
||||
settings[parameter.name] = document.getElementById(parameter.name).checked;
|
||||
break;
|
||||
case 'array':
|
||||
case 'object':
|
||||
try {
|
||||
settings[parameter.name] = JSON.parse(value);
|
||||
} catch (err) {
|
||||
console.error(`Invalid JSON format for ${parameter.name}`);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
settings[parameter.name] = value;
|
||||
if(parameter.name !== "fileInput"){
|
||||
let value = document.getElementById(parameter.name).value;
|
||||
switch (parameter.schema.type) {
|
||||
case 'number':
|
||||
case 'integer':
|
||||
settings[parameter.name] = Number(value);
|
||||
break;
|
||||
case 'boolean':
|
||||
settings[parameter.name] = document.getElementById(parameter.name).checked;
|
||||
break;
|
||||
case 'array':
|
||||
case 'object':
|
||||
try {
|
||||
settings[parameter.name] = JSON.parse(value);
|
||||
} catch (err) {
|
||||
console.error(`Invalid JSON format for ${parameter.name}`);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
settings[parameter.name] = value;
|
||||
}
|
||||
}
|
||||
});
|
||||
operationSettings[operation] = settings;
|
||||
console.log(settings);
|
||||
pipelineSettingsModal.style.display = "none";
|
||||
//pipelineSettingsModal.style.display = "none";
|
||||
});
|
||||
pipelineSettingsContent.appendChild(saveButton);
|
||||
|
||||
pipelineSettingsModal.style.display = "block";
|
||||
//pipelineSettingsModal.style.display = "block";
|
||||
|
||||
pipelineSettingsModal.getElementsByClassName("close")[0].onclick = function() {
|
||||
pipelineSettingsModal.style.display = "none";
|
||||
}
|
||||
//pipelineSettingsModal.getElementsByClassName("close")[0].onclick = function() {
|
||||
// pipelineSettingsModal.style.display = "none";
|
||||
//}
|
||||
|
||||
window.onclick = function(event) {
|
||||
if (event.target == pipelineSettingsModal) {
|
||||
pipelineSettingsModal.style.display = "none";
|
||||
}
|
||||
}
|
||||
//window.onclick = function(event) {
|
||||
// if (event.target == pipelineSettingsModal) {
|
||||
// pipelineSettingsModal.style.display = "none";
|
||||
// }
|
||||
//}
|
||||
}
|
||||
hideOrShowPipelineHeader();
|
||||
});
|
||||
|
||||
|
||||
|
||||
var saveBtn = document.getElementById('savePipelineBtn');
|
||||
|
||||
document.getElementById('savePipelineBtn').addEventListener('click', function() {
|
||||
// Remove any existing event listeners
|
||||
saveBtn.removeEventListener('click', savePipeline);
|
||||
|
||||
// Add the event listener
|
||||
saveBtn.addEventListener('click', savePipeline);
|
||||
console.log("saveBtn", saveBtn)
|
||||
function savePipeline() {
|
||||
|
||||
if (validatePipeline() === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
var pipelineName = document.getElementById('pipelineName').value;
|
||||
let pipelineList = document.getElementById('pipelineList').children;
|
||||
let pipelineConfig = {
|
||||
@@ -393,31 +494,33 @@ document.getElementById('addOperationBtn').addEventListener('click', function()
|
||||
"outputDir": "{outputFolder}/{folderName}",
|
||||
"outputFileName": "{filename}-{pipelineName}-{date}-{time}"
|
||||
},
|
||||
"outputDir": "httpWebRequest",
|
||||
"outputDir": "{outputFolder}",
|
||||
"outputFileName": "{filename}"
|
||||
};
|
||||
|
||||
for (let i = 0; i < pipelineList.length; i++) {
|
||||
let operationName = pipelineList[i].querySelector('.operationName').textContent;
|
||||
let parameters = operationSettings[operationName] || {};
|
||||
|
||||
|
||||
parameters['fileInput'] = 'automated';
|
||||
|
||||
pipelineConfig.pipeline.push({
|
||||
"operation": operationName,
|
||||
"parameters": parameters
|
||||
});
|
||||
}
|
||||
|
||||
console.log("Downloading..");
|
||||
let a = document.createElement('a');
|
||||
a.href = URL.createObjectURL(new Blob([JSON.stringify(pipelineConfig, null, 2)], {
|
||||
type: 'application/json'
|
||||
}));
|
||||
a.download = 'pipelineConfig.json';
|
||||
a.download = pipelineName + '.json';
|
||||
a.style.display = 'none';
|
||||
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
});
|
||||
}
|
||||
|
||||
async function processPipelineConfig(configString) {
|
||||
let pipelineConfig = JSON.parse(configString);
|
||||
@@ -483,6 +586,7 @@ document.getElementById('addOperationBtn').addEventListener('click', function()
|
||||
processPipelineConfig(event.target.result);
|
||||
};
|
||||
reader.readAsText(e.target.files[0]);
|
||||
hideOrShowPipelineHeader();
|
||||
});
|
||||
|
||||
document.getElementById('pipelineSelect').addEventListener('change', function(e) {
|
||||
@@ -491,4 +595,15 @@ document.getElementById('addOperationBtn').addEventListener('click', function()
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
function hideOrShowPipelineHeader() {
|
||||
var pipelineHeader = document.getElementById('pipelineHeader');
|
||||
var pipelineList = document.getElementById('pipelineList');
|
||||
|
||||
if (pipelineList.children.length === 0) {
|
||||
// Hide the pipeline header if there are no items in the pipeline list
|
||||
pipelineHeader.style.display = 'none';
|
||||
} else {
|
||||
// Show the pipeline header if there are items in the pipeline list
|
||||
pipelineHeader.style.display = 'block';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -306,7 +306,7 @@
|
||||
|
||||
|
||||
<div class="mb-3 mt-4">
|
||||
<a href="/logout">
|
||||
<a href="logout">
|
||||
<button type="button" class="btn btn-danger" th:text="#{account.signOut}">Sign Out</button>
|
||||
</a>
|
||||
<a th:if="${role == 'ROLE_ADMIN'}" href="addUsers" target="_blank">
|
||||
|
||||
@@ -62,6 +62,7 @@
|
||||
<option value="ROLE_USER" th:text="#{adminUserSettings.user}">User</option>
|
||||
<option value="ROLE_LIMITED_API_USER" th:text="#{adminUserSettings.apiUser}">Limited API User</option>
|
||||
<option value="ROLE_WEB_ONLY_USER" th:text="#{adminUserSettings.webOnlyUser}">Web Only User</option>
|
||||
<option value="ROLE_DEMO_USER" th:text="#{adminUserSettings.demoUser}">Demo User</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6">
|
||||
<h2 th:text="#{PDFToCSV.header}"></h2>
|
||||
<form id="PDFToCSVForm" th:action="@{api/v1/convert/pdf-to-csv}" method="post" enctype="multipart/form-data">
|
||||
<form id="PDFToCSVForm" th:action="@{api/v1/convert/pdf/csv}" method="post" enctype="multipart/form-data">
|
||||
<input id="pageId" type="hidden" name="pageId" />
|
||||
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
|
||||
<button type="submit" class="btn btn-primary" th:text="#{PDFToCSV.submit}"></button>
|
||||
|
||||
@@ -26,14 +26,13 @@
|
||||
<option value="gif">GIF</option>
|
||||
<option value="tiff">TIFF</option>
|
||||
<option value="bmp">BMP</option>
|
||||
<option value="wbmp">WBMP</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label th:text="#{pdfToImage.singleOrMultiple}"></label>
|
||||
<select class="form-control" name="singleOrMultiple">
|
||||
<option value="single" th:text="#{pdfToImage.single}"></option>
|
||||
<option value="multiple" th:text="#{pdfToImage.multi}"></option>
|
||||
<option value="single" th:text="#{pdfToImage.single}"></option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
|
||||
@@ -36,6 +36,9 @@
|
||||
<script src="js/thirdParty/bootstrap.min.js"></script>
|
||||
<link rel="stylesheet" href="css/bootstrap.min.css">
|
||||
|
||||
<!-- Bootstrap Icons -->
|
||||
<link rel="stylesheet" href="css/bootstrap-icons.min.css">
|
||||
|
||||
<!-- PDF.js -->
|
||||
<script src="pdfjs/pdf.js"></script>
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<th:block th:fragment="langs">
|
||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="bg_BG">
|
||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="bg_BG">
|
||||
<img src="images/flags/bg.svg" alt="icon" width="20" height="15"> Български
|
||||
</a>
|
||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="ar_AR">
|
||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="ar_AR">
|
||||
<img src="images/flags/sa.svg" alt="icon" width="20" height="15"> العربية
|
||||
</a>
|
||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="ca_CA">
|
||||
@@ -20,15 +20,18 @@
|
||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="en_US">
|
||||
<img src="images/flags/us.svg" alt="icon" width="20" height="15"> English (US)
|
||||
</a>
|
||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="eu_ES">
|
||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="eu_ES">
|
||||
<img src="images/flags/eu.svg" alt="icon" width="20" height="15"> Euskara
|
||||
</a>
|
||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="es_ES">
|
||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="es_ES">
|
||||
<img src="images/flags/es.svg" alt="icon" width="20" height="15"> Español
|
||||
</a>
|
||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="fr_FR">
|
||||
<img src="images/flags/fr.svg" alt="icon" width="20" height="15"> Français
|
||||
</a>
|
||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="id_ID">
|
||||
<img src="images/flags/id.svg" alt="icon" width="20" height="15"> Indonesia
|
||||
</a>
|
||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="it_IT">
|
||||
<img src="images/flags/it.svg" alt="icon" width="20" height="15"> Italiano
|
||||
</a>
|
||||
@@ -49,7 +52,7 @@
|
||||
</a>
|
||||
<a class="dropdown-item lang_dropdown-item" href="" data-language-code="tr_TR">
|
||||
<img src="images/flags/tr.svg" alt="icon" width="20" height="15"> Türkçe
|
||||
</a>
|
||||
</a>
|
||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="ru_RU">
|
||||
<img src="images/flags/ru.svg" alt="icon" width="20" height="15"> Русский
|
||||
</a>
|
||||
@@ -59,7 +62,13 @@
|
||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="ja_JP">
|
||||
<img src="images/flags/jp.svg" alt="icon" width="20" height="15"> 日本語
|
||||
</a>
|
||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="el_GR">
|
||||
<img src="images/flags/gr.svg" alt="icon" width="20" height="15"> Ελληνικά
|
||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="el_GR">
|
||||
<img src="images/flags/gr.svg" alt="icon" width="20" height="15"> Ελληνικά
|
||||
</a>
|
||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="hu_HU">
|
||||
<img src="images/flags/hu.svg" alt="icon" width="20" height="15"> Hungarian
|
||||
</a>
|
||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="hi_IN">
|
||||
<img src="images/flags/in.svg" alt="icon" width="20" height="15"> हिन्दी
|
||||
</a>
|
||||
</th:block>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user