1
0
mirror of https://github.com/Stirling-Tools/Stirling-PDF.git synced 2024-11-13 03:00:10 +01:00
This commit is contained in:
jimdouk 2024-06-07 10:38:21 +03:00
commit fc249661e2
104 changed files with 3804 additions and 408 deletions

View File

@ -110,3 +110,31 @@ jobs:
labels: ${{ steps.meta2.outputs.labels }} labels: ${{ steps.meta2.outputs.labels }}
build-args: VERSION_TAG=${{ steps.versionNumber.outputs.versionNumber }} build-args: VERSION_TAG=${{ steps.versionNumber.outputs.versionNumber }}
platforms: linux/amd64,linux/arm64/v8 platforms: linux/amd64,linux/arm64/v8
- name: Generate tags fat
id: meta3
uses: docker/metadata-action@v5
if: github.ref != 'refs/heads/main'
with:
images: |
${{ secrets.DOCKER_HUB_USERNAME }}/s-pdf
ghcr.io/${{ steps.repoowner.outputs.lowercase }}/s-pdf
tags: |
type=raw,value=${{ steps.versionNumber.outputs.versionNumber }}-fat,enable=${{ github.ref == 'refs/heads/master' }}
type=raw,value=latest-fat,enable=${{ github.ref == 'refs/heads/master' }}
- name: Build and push main Dockerfile fat
uses: docker/build-push-action@v5
if: github.ref != 'refs/heads/main'
with:
builder: ${{ steps.buildx.outputs.name }}
context: .
file: ./Dockerfile-fat
push: true
cache-from: type=gha
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

84
Dockerfile-fat Normal file
View File

@ -0,0 +1,84 @@
# Build the application
FROM gradle:7.6-jdk17 AS build
# Set the working directory
WORKDIR /app
# Copy the entire project to the working directory
COPY . .
# Build the application with DOCKER_ENABLE_SECURITY=false
RUN DOCKER_ENABLE_SECURITY=true \
./gradlew clean build
# Main stage
FROM alpine:3.20.0
# Copy necessary files
COPY scripts /scripts
COPY pipeline /pipeline
COPY src/main/resources/static/fonts/*.ttf /usr/share/fonts/opentype/noto/
COPY --from=build /app/build/libs/*.jar app.jar
ARG VERSION_TAG
# Set Environment Variables
ENV DOCKER_ENABLE_SECURITY=false \
VERSION_TAG=$VERSION_TAG \
JAVA_TOOL_OPTIONS="$JAVA_TOOL_OPTIONS -XX:MaxRAMPercentage=75" \
HOME=/home/stirlingpdfuser \
PUID=1000 \
PGID=1000 \
UMASK=022 \
FAT_DOCKER=true \
INSTALL_BOOK_AND_ADVANCED_HTML_OPS=true
# JDK for app
RUN echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/main" | tee -a /etc/apk/repositories && \
echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/community" | tee -a /etc/apk/repositories && \
echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/testing" | tee -a /etc/apk/repositories && \
apk upgrade --no-cache -a && \
apk add --no-cache \
ca-certificates \
tzdata \
tini \
bash \
curl \
calibre@testing \
shadow \
su-exec \
openssl \
openssl-dev \
openjdk21-jre \
# Doc conversion
libreoffice \
# pdftohtml
poppler-utils \
# OCR MY PDF (unpaper for descew and other advanced featues)
ocrmypdf \
tesseract-ocr-data-eng \
font-terminus font-dejavu font-noto font-noto-cjk font-awesome font-noto-extra \
# CV
py3-opencv \
# python3/pip
python3 && \
wget https://bootstrap.pypa.io/get-pip.py -qO - | python3 - --break-system-packages --no-cache-dir --upgrade && \
# uno unoconv and HTML
pip install --break-system-packages --no-cache-dir --upgrade unoconv WeasyPrint && \
mv /usr/share/tessdata /usr/share/tessdata-original && \
mkdir -p $HOME /configs /logs /customFiles /pipeline/watchedFolders /pipeline/finishedFolders && \
fc-cache -f -v && \
chmod +x /scripts/* && \
chmod +x /scripts/init.sh && \
# User permissions
addgroup -S stirlingpdfgroup && adduser -S stirlingpdfuser -G stirlingpdfgroup && \
chown -R stirlingpdfuser:stirlingpdfgroup $HOME /scripts /usr/share/fonts/opentype/noto /configs /customFiles /pipeline && \
chown stirlingpdfuser:stirlingpdfgroup /app.jar && \
tesseract --list-langs
EXPOSE 8080/tcp
# Set user and run command
ENTRYPOINT ["tini", "--", "/scripts/init.sh"]
CMD ["java", "-Dfile.encoding=UTF-8", "-jar", "/app.jar"]

View File

@ -1,46 +1,47 @@
| Operation | PageOps | Convert | Security | Other | CLI | Python | OpenCV | LibreOffice | OCRmyPDF | Java | Javascript | | Operation | PageOps | Convert | Security | Other | CLI | Python | OpenCV | LibreOffice | OCRmyPDF | Java | Javascript |
|---------------------|---------|---------|----------|-------|------|--------|--------|-------------|----------|----------|------------| | ------------------- | ------- | ------- | -------- | ----- | --- | ------ | ------ | ----------- | -------- | ---- | ---------- |
| adjust-contrast | ✔️ | | | | | | | | | | ✔️ | | adjust-contrast | ✔️ | | | | | | | | | | ✔️ |
| auto-split-pdf | ✔️ | | | | | | | | | ✔️ | | | auto-split-pdf | ✔️ | | | | | | | | | ✔️ | |
| crop | ✔️ | | | | | | | | | ✔️ | | | crop | ✔️ | | | | | | | | | ✔️ | |
| extract-page | ✔️ | | | | | | | | | ✔️ | | | extract-page | ✔️ | | | | | | | | | ✔️ | |
| merge-pdfs | ✔️ | | | | | | | | | ✔️ | | | merge-pdfs | ✔️ | | | | | | | | | ✔️ | |
| multi-page-layout | ✔️ | | | | | | | | | ✔️ | | | multi-page-layout | ✔️ | | | | | | | | | ✔️ | |
| pdf-organizer | ✔️ | | | | | | | | | ✔️ | ✔️ | | pdf-organizer | ✔️ | | | | | | | | | ✔️ | ✔️ |
| pdf-to-single-page | ✔️ | | | | | | | | | ✔️ | | | pdf-to-single-page | ✔️ | | | | | | | | | ✔️ | |
| remove-pages | ✔️ | | | | | | | | | ✔️ | | | remove-pages | ✔️ | | | | | | | | | ✔️ | |
| rotate-pdf | ✔️ | | | | | | | | | ✔️ | | | rotate-pdf | ✔️ | | | | | | | | | ✔️ | |
| scale-pages | ✔️ | | | | | | | | | ✔️ | | | scale-pages | ✔️ | | | | | | | | | ✔️ | |
| split-pdfs | ✔️ | | | | | | | | | ✔️ | | | split-pdfs | ✔️ | | | | | | | | | ✔️ | |
| file-to-pdf | | ✔️ | | | ✔️ | | | ✔️ | | | | | file-to-pdf | | ✔️ | | | ✔️ | | | ✔️ | | | |
| img-to-pdf | | ✔️ | | | | | | | | ✔️ | | | img-to-pdf | | ✔️ | | | | | | | | ✔️ | |
| pdf-to-html | | ✔️ | | | ✔️ | | | ✔️ | | | | | pdf-to-html | | ✔️ | | | ✔️ | | | ✔️ | | | |
| pdf-to-img | | ✔️ | | | | | | | | ✔️ | | | pdf-to-img | | ✔️ | | | | | | | | ✔️ | |
| pdf-to-pdfa | | ✔️ | | | ✔️ | | | | ✔️ | | | | pdf-to-pdfa | | ✔️ | | | ✔️ | | | | ✔️ | | |
| pdf-to-markdown | | ✔️ | | | | | | | | ✔️ | | | pdf-to-markdown | | ✔️ | | | | | | | | ✔️ | |
| pdf-to-presentation | | ✔️ | | | ✔️ | | | ✔️ | | | | | pdf-to-presentation | | ✔️ | | | ✔️ | | | ✔️ | | | |
| pdf-to-text | | ✔️ | | | ✔️ | | | ✔️ | | | | | pdf-to-text | | ✔️ | | | ✔️ | | | ✔️ | | | |
| pdf-to-word | | ✔️ | | | ✔️ | | | ✔️ | | | | | pdf-to-word | | ✔️ | | | ✔️ | | | ✔️ | | | |
| pdf-to-xml | | ✔️ | | | ✔️ | | | ✔️ | | | | | pdf-to-xml | | ✔️ | | | ✔️ | | | ✔️ | | | |
| xlsx-to-pdf | | ✔️ | | | ✔️ | | | ✔️ | | | | | xlsx-to-pdf | | ✔️ | | | ✔️ | | | ✔️ | | | |
| add-password | | | ✔️ | | | | | | | ✔️ | | | add-password | | | ✔️ | | | | | | | ✔️ | |
| add-watermark | | | ✔️ | | | | | | | ✔️ | | | add-watermark | | | ✔️ | | | | | | | ✔️ | |
| cert-sign | | | ✔️ | | | | | | | ✔️ | | | cert-sign | | | ✔️ | | | | | | | ✔️ | |
| change-permissions | | | ✔️ | | | | | | | ✔️ | | | remove-cert-sign | | | ✔️ | | | | | | | ✔️ | |
| remove-password | | | ✔️ | | | | | | | ✔️ | | | change-permissions | | | ✔️ | | | | | | | ✔️ | |
| sanitize-pdf | | | ✔️ | | | | | | | ✔️ | | | remove-password | | | ✔️ | | | | | | | ✔️ | |
| add-image | | | | ✔️ | | | | | | ✔️ | | | sanitize-pdf | | | ✔️ | | | | | | | ✔️ | |
| add-page-numbers | | | | ✔️ | | | | | | ✔️ | | | add-image | | | | ✔️ | | | | | | ✔️ | |
| auto-rename | | | | ✔️ | | | | | | ✔️ | | | add-page-numbers | | | | ✔️ | | | | | | ✔️ | |
| change-metadata | | | | ✔️ | | | | | | ✔️ | | | auto-rename | | | | ✔️ | | | | | | ✔️ | |
| compare | | | | ✔️ | | | | | | | ✔️ | | change-metadata | | | | ✔️ | | | | | | ✔️ | |
| compress-pdf | | | | ✔️ | ✔️ | | | | ✔️ | | | | compare | | | | ✔️ | | | | | | | ✔️ |
| extract-image-scans | | | | ✔️ | ✔️ | ✔️ | ✔️ | | | | | | compress-pdf | | | | ✔️ | ✔️ | | | | ✔️ | | |
| extract-images | | | | ✔️ | | | | | | ✔️ | | | extract-image-scans | | | | ✔️ | ✔️ | ✔️ | ✔️ | | | | |
| flatten | | | | ✔️ | | | | | | | ✔️ | | extract-images | | | | ✔️ | | | | | | ✔️ | |
| get-info-on-pdf | | | | ✔️ | | | | | | ✔️ | | | flatten | | | | ✔️ | | | | | | | ✔️ |
| ocr-pdf | | | | ✔️ | ✔️ | | | | ✔️ | | | | get-info-on-pdf | | | | ✔️ | | | | | | ✔️ | |
| remove-blanks | | | | ✔️ | ✔️ | ✔️ | ✔️ | | | | | | ocr-pdf | | | | ✔️ | ✔️ | | | | ✔️ | | |
| repair | | | | ✔️ | ✔️ | | | ✔️ | | | | | remove-blanks | | | | ✔️ | ✔️ | ✔️ | ✔️ | | | | |
| show-javascript | | | | ✔️ | | | | | | | ✔️ | | repair | | | | ✔️ | ✔️ | | | ✔️ | | | |
| sign | | | | ✔️ | | | | | | | ✔️ | | show-javascript | | | | ✔️ | | | | | | | ✔️ |
| sign | | | | ✔️ | | | | | | | ✔️ |

View File

@ -159,39 +159,41 @@ Please view https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToUseOCR
## Supported Languages ## Supported Languages
Stirling PDF currently supports 28! Stirling PDF currently supports 32!
| Language | Progress | | Language | Progress |
| ------------------------------------------- | -------------------------------------- | | ------------------------------------------- | -------------------------------------- |
| English (English) (en_GB) | ![100%](https://geps.dev/progress/100) | | English (English) (en_GB) | ![100%](https://geps.dev/progress/100) |
| English (US) (en_US) | ![100%](https://geps.dev/progress/100) | | English (US) (en_US) | ![100%](https://geps.dev/progress/100) |
| Arabic (العربية) (ar_AR) | ![41%](https://geps.dev/progress/41) | | Arabic (العربية) (ar_AR) | ![40%](https://geps.dev/progress/40) |
| German (Deutsch) (de_DE) | ![100%](https://geps.dev/progress/100) | | German (Deutsch) (de_DE) | ![99%](https://geps.dev/progress/99) |
| French (Français) (fr_FR) | ![95%](https://geps.dev/progress/95) | | French (Français) (fr_FR) | ![93%](https://geps.dev/progress/93) |
| Spanish (Español) (es_ES) | ![96%](https://geps.dev/progress/96) | | Spanish (Español) (es_ES) | ![95%](https://geps.dev/progress/95) |
| Simplified Chinese (简体中文) (zh_CN) | ![96%](https://geps.dev/progress/96) | | Simplified Chinese (简体中文) (zh_CN) | ![95%](https://geps.dev/progress/95) |
| Traditional Chinese (繁體中文) (zh_TW) | ![95%](https://geps.dev/progress/95) | | Traditional Chinese (繁體中文) (zh_TW) | ![94%](https://geps.dev/progress/94) |
| Catalan (Català) (ca_CA) | ![49%](https://geps.dev/progress/49) | | Catalan (Català) (ca_CA) | ![49%](https://geps.dev/progress/49) |
| Italian (Italiano) (it_IT) | ![99%](https://geps.dev/progress/99) | | Italian (Italiano) (it_IT) | ![98%](https://geps.dev/progress/98) |
| Swedish (Svenska) (sv_SE) | ![41%](https://geps.dev/progress/41) | | Swedish (Svenska) (sv_SE) | ![40%](https://geps.dev/progress/40) |
| Polish (Polski) (pl_PL) | ![43%](https://geps.dev/progress/43) | | Polish (Polski) (pl_PL) | ![42%](https://geps.dev/progress/42) |
| Romanian (Română) (ro_RO) | ![40%](https://geps.dev/progress/40) | | Romanian (Română) (ro_RO) | ![39%](https://geps.dev/progress/39) |
| Korean (한국어) (ko_KR) | ![88%](https://geps.dev/progress/88) | | Korean (한국어) (ko_KR) | ![87%](https://geps.dev/progress/87) |
| Portuguese Brazilian (Português) (pt_BR) | ![62%](https://geps.dev/progress/62) | | Portuguese Brazilian (Português) (pt_BR) | ![61%](https://geps.dev/progress/61) |
| Russian (Русский) (ru_RU) | ![88%](https://geps.dev/progress/88) | | Russian (Русский) (ru_RU) | ![87%](https://geps.dev/progress/87) |
| Basque (Euskara) (eu_ES) | ![64%](https://geps.dev/progress/64) | | Basque (Euskara) (eu_ES) | ![63%](https://geps.dev/progress/63) |
| Japanese (日本語) (ja_JP) | ![88%](https://geps.dev/progress/88) | | Japanese (日本語) (ja_JP) | ![87%](https://geps.dev/progress/87) |
| Dutch (Nederlands) (nl_NL) | ![86%](https://geps.dev/progress/86) | | Dutch (Nederlands) (nl_NL) | ![85%](https://geps.dev/progress/85) |
| Greek (Ελληνικά) (el_GR) | ![86%](https://geps.dev/progress/86) | | Greek (Ελληνικά) (el_GR) | ![85%](https://geps.dev/progress/85) |
| Turkish (Türkçe) (tr_TR) | ![99%](https://geps.dev/progress/99) | | Turkish (Türkçe) (tr_TR) | ![97%](https://geps.dev/progress/97) |
| Indonesia (Bahasa Indonesia) (id_ID) | ![79%](https://geps.dev/progress/79) | | Indonesia (Bahasa Indonesia) (id_ID) | ![78%](https://geps.dev/progress/78) |
| Hindi (हिंदी) (hi_IN) | ![80%](https://geps.dev/progress/80) | | Hindi (हिंदी) (hi_IN) | ![79%](https://geps.dev/progress/79) |
| Hungarian (Magyar) (hu_HU) | ![79%](https://geps.dev/progress/79) | | Hungarian (Magyar) (hu_HU) | ![78%](https://geps.dev/progress/78) |
| Bulgarian (Български) (bg_BG) | ![96%](https://geps.dev/progress/96) | | Bulgarian (Български) (bg_BG) | ![98%](https://geps.dev/progress/98) |
| Sebian Latin alphabet (Srpski) (sr_LATN_RS) | ![81%](https://geps.dev/progress/81) | | Sebian Latin alphabet (Srpski) (sr_LATN_RS) | ![80%](https://geps.dev/progress/80) |
| Ukrainian (Українська) (uk_UA) | ![87%](https://geps.dev/progress/87) | | Ukrainian (Українська) (uk_UA) | ![86%](https://geps.dev/progress/86) |
| Slovakian (Slovensky) (sk_SK) | ![96%](https://geps.dev/progress/96) | | Slovakian (Slovensky) (sk_SK) | ![95%](https://geps.dev/progress/95) |
| Czech (Česky) (cs_CZ) | ![94%](https://geps.dev/progress/94) | | Czech (Česky) (cs_CZ) | ![94%](https://geps.dev/progress/94) |
| Croatian (Hrvatski) (hr_HR) | ![98%](https://geps.dev/progress/98) |
| Norwegian (Norsk) (no_NB) | ![98%](https://geps.dev/progress/98) |
## Contributing (creating issues, translations, fixing bugs, etc.) ## Contributing (creating issues, translations, fixing bugs, etc.)
@ -213,10 +215,10 @@ For example in the settings.yml you have
```yaml ```yaml
system: system:
defaultLocale: 'en-US' enableLogin: 'true'
``` ```
To have this via an environment variable you would have ``SYSTEM_DEFAULTLOCALE`` To have this via an environment variable you would have ``SYSTEM_ENABLELOGIN``
The Current list of settings is The Current list of settings is

View File

@ -1,52 +1,53 @@
| Technology | Ultra-Lite | Full | | Technology | Ultra-Lite | Full |
|----------------|:----------:|:----:| | ---------- | :--------: | :---: |
| Java | ✔️ | ✔️ | | Java | ✔️ | ✔️ |
| JavaScript | ✔️ | ✔️ | | JavaScript | ✔️ | ✔️ |
| Libre | | ✔️ | | Libre | | ✔️ |
| Python | | ✔️ | | Python | | ✔️ |
| OpenCV | | ✔️ | | OpenCV | | ✔️ |
| OCRmyPDF | | ✔️ | | OCRmyPDF | | ✔️ |
Operation | Ultra-Lite | Full | Operation | Ultra-Lite | Full |
-------------------------|------------|----- | ---------------------- | ---------- | ---- |
add-page-numbers | ✔️ | ✔️ | add-page-numbers | ✔️ | ✔️ |
add-password | ✔️ | ✔️ | add-password | ✔️ | ✔️ |
add-image | ✔️ | ✔️ | add-image | ✔️ | ✔️ |
add-watermark | ✔️ | ✔️ | add-watermark | ✔️ | ✔️ |
adjust-contrast | ✔️ | ✔️ | adjust-contrast | ✔️ | ✔️ |
auto-split-pdf | ✔️ | ✔️ | auto-split-pdf | ✔️ | ✔️ |
auto-redact | ✔️ | ✔️ | auto-redact | ✔️ | ✔️ |
auto-rename | ✔️ | ✔️ | auto-rename | ✔️ | ✔️ |
cert-sign | ✔️ | ✔️ | cert-sign | ✔️ | ✔️ |
crop | ✔️ | ✔️ | remove-cert-sign | ✔️ | ✔️ |
change-metadata | ✔️ | ✔️ | crop | ✔️ | ✔️ |
change-permissions | ✔️ | ✔️ | change-metadata | ✔️ | ✔️ |
compare | ✔️ | ✔️ | change-permissions | ✔️ | ✔️ |
extract-page | ✔️ | ✔️ | compare | ✔️ | ✔️ |
extract-images | ✔️ | ✔️ | extract-page | ✔️ | ✔️ |
flatten | ✔️ | ✔️ | extract-images | ✔️ | ✔️ |
get-info-on-pdf | ✔️ | ✔️ | flatten | ✔️ | ✔️ |
img-to-pdf | ✔️ | ✔️ | get-info-on-pdf | ✔️ | ✔️ |
markdown-to-pdf | ✔️ | ✔️ | img-to-pdf | ✔️ | ✔️ |
merge-pdfs | ✔️ | ✔️ | markdown-to-pdf | ✔️ | ✔️ |
multi-page-layout | ✔️ | ✔️ | merge-pdfs | ✔️ | ✔️ |
overlay-pdf | ✔️ | ✔️ | multi-page-layout | ✔️ | ✔️ |
pdf-organizer | ✔️ | ✔️ | overlay-pdf | ✔️ | ✔️ |
pdf-to-csv | ✔️ | ✔️ | pdf-organizer | ✔️ | ✔️ |
pdf-to-img | ✔️ | ✔️ | pdf-to-csv | ✔️ | ✔️ |
pdf-to-single-page | ✔️ | ✔️ | pdf-to-img | ✔️ | ✔️ |
remove-pages | ✔️ | ✔️ | pdf-to-single-page | ✔️ | ✔️ |
remove-password | ✔️ | ✔️ | remove-pages | ✔️ | ✔️ |
rotate-pdf | ✔️ | ✔️ | remove-password | ✔️ | ✔️ |
sanitize-pdf | ✔️ | ✔️ | rotate-pdf | ✔️ | ✔️ |
scale-pages | ✔️ | ✔️ | sanitize-pdf | ✔️ | ✔️ |
sign | ✔️ | ✔️ | scale-pages | ✔️ | ✔️ |
show-javascript | ✔️ | ✔️ | sign | ✔️ | ✔️ |
split-by-size-or-count | ✔️ | ✔️ | show-javascript | ✔️ | ✔️ |
split-pdf-by-sections | ✔️ | ✔️ | split-by-size-or-count | ✔️ | ✔️ |
split-pdfs | ✔️ | ✔️ | split-pdf-by-sections | ✔️ | ✔️ |
compress-pdf | | ✔️ | split-pdfs | ✔️ | ✔️ |
extract-image-scans | | ✔️ | compress-pdf | | ✔️ |
ocr-pdf | | ✔️ | extract-image-scans | | ✔️ |
pdf-to-pdfa | | ✔️ | ocr-pdf | | ✔️ |
remove-blanks | | ✔️ | pdf-to-pdfa | | ✔️ |
| remove-blanks | | ✔️ |

View File

@ -12,7 +12,7 @@ plugins {
import com.github.jk1.license.render.* import com.github.jk1.license.render.*
group = 'stirling.software' group = 'stirling.software'
version = '0.24.6' version = '0.25.2'
//17 is lowest but we support and recommend 21 //17 is lowest but we support and recommend 21
sourceCompatibility = '17' sourceCompatibility = '17'
@ -21,7 +21,6 @@ repositories {
mavenCentral() mavenCentral()
} }
licenseReport { licenseReport {
renderers = [new JsonReportRenderer()] renderers = [new JsonReportRenderer()]
} }
@ -172,14 +171,14 @@ dependencies {
annotationProcessor 'org.projectlombok:lombok:1.18.32' annotationProcessor 'org.projectlombok:lombok:1.18.32'
} }
tasks.withType(JavaCompile) { tasks.withType(JavaCompile).configureEach {
dependsOn 'spotlessApply' dependsOn 'spotlessApply'
} }
compileJava { compileJava {
options.compilerArgs << '-parameters' options.compilerArgs << '-parameters'
} }
task writeVersion { task writeVersion {
def propsFile = file('src/main/resources/version.properties') def propsFile = file('src/main/resources/version.properties')
def props = new Properties() def props = new Properties()
props.setProperty('version', version) props.setProperty('version', version)
@ -196,8 +195,6 @@ swaggerhubUpload {
oas '3.0.0' // The version of the OpenAPI Specification you're using oas '3.0.0' // The version of the OpenAPI Specification you're using
} }
jar { jar {
enabled = false enabled = false
manifest { manifest {
@ -211,6 +208,6 @@ tasks.named('test') {
useJUnitPlatform() useJUnitPlatform()
} }
task printVersion { task printVersion {
println project.version println project.version
} }

View File

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

View File

@ -0,0 +1,34 @@
version: '3.3'
services:
stirling-pdf:
container_name: Stirling-PDF-Security-Fat
image: frooodle/s-pdf:latest-fat
deploy:
resources:
limits:
memory: 4G
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:8080/api/v1/info/status | grep -q 'UP' && curl -fL http://localhost:8080/ | grep -q 'Please sign in'"]
interval: 5s
timeout: 10s
retries: 16
ports:
- 8080:8080
volumes:
- /stirling/latest/data:/usr/share/tessdata:rw
- /stirling/latest/config:/configs:rw
- /stirling/latest/logs:/logs:rw
environment:
DOCKER_ENABLE_SECURITY: "true"
SECURITY_ENABLELOGIN: "true"
PUID: 1002
PGID: 1002
UMASK: "022"
SYSTEM_DEFAULTLOCALE: en-US
UI_APPNAME: Stirling-PDF
UI_HOMEDESCRIPTION: Demo site for Stirling-PDF Latest-fat with Security
UI_APPNAMENAVBAR: Stirling-PDF Latest-fat
SYSTEM_MAXFILESIZE: "100"
METRICS_ENABLED: "true"
SYSTEM_GOOGLEVISIBILITY: "true"
restart: on-failure:5

View File

@ -13,7 +13,7 @@ services:
timeout: 10s timeout: 10s
retries: 16 retries: 16
ports: ports:
- 8080:8080 - "8080:8080"
volumes: volumes:
- /stirling/latest/data:/usr/share/tessdata:rw - /stirling/latest/data:/usr/share/tessdata:rw
- /stirling/latest/config:/configs:rw - /stirling/latest/config:/configs:rw
@ -27,6 +27,8 @@ services:
SECURITY_OAUTH2_CLIENTID: "<YOUR CLIENT ID>.apps.googleusercontent.com" # Client ID from your provider SECURITY_OAUTH2_CLIENTID: "<YOUR CLIENT ID>.apps.googleusercontent.com" # Client ID from your provider
SECURITY_OAUTH2_CLIENTSECRET: "<YOUR CLIENT SECRET>" # Client Secret from your provider SECURITY_OAUTH2_CLIENTSECRET: "<YOUR CLIENT SECRET>" # Client Secret from your provider
SECURITY_OAUTH2_SCOPES: "openid,profile,email" # Expected OAuth2 Scope SECURITY_OAUTH2_SCOPES: "openid,profile,email" # Expected OAuth2 Scope
SECURITY_OAUTH2_USEASUSERNAME: "email" # Default is 'email'; custom fields can be used as the username
SECURITY_OAUTH2_PROVIDER: "google" # Set this to your OAuth provider's name, e.g., 'google' or 'keycloak'
PUID: 1002 PUID: 1002
PGID: 1002 PGID: 1002
UMASK: "022" UMASK: "022"

View File

@ -13,7 +13,7 @@ services:
timeout: 10s timeout: 10s
retries: 16 retries: 16
ports: ports:
- 8080:8080 - "8080:8080"
volumes: volumes:
- /stirling/latest/data:/usr/share/tessdata:rw - /stirling/latest/data:/usr/share/tessdata:rw
- /stirling/latest/config:/configs:rw - /stirling/latest/config:/configs:rw

View File

@ -13,7 +13,7 @@ services:
timeout: 10s timeout: 10s
retries: 16 retries: 16
ports: ports:
- 8080:8080 - "8080:8080"
volumes: volumes:
- /stirling/latest/data:/usr/share/tessdata:rw - /stirling/latest/data:/usr/share/tessdata:rw
- /stirling/latest/config:/configs:rw - /stirling/latest/config:/configs:rw

View File

@ -13,7 +13,7 @@ services:
timeout: 10s timeout: 10s
retries: 16 retries: 16
ports: ports:
- 8080:8080 - "8080:8080"
volumes: volumes:
- /stirling/latest/config:/configs:rw - /stirling/latest/config:/configs:rw
- /stirling/latest/logs:/logs:rw - /stirling/latest/logs:/logs:rw

View File

@ -13,7 +13,7 @@ services:
timeout: 10s timeout: 10s
retries: 16 retries: 16
ports: ports:
- 8080:8080 - "8080:8080"
volumes: volumes:
- /stirling/latest/data:/usr/share/tessdata:rw - /stirling/latest/data:/usr/share/tessdata:rw
- /stirling/latest/config:/configs:rw - /stirling/latest/config:/configs:rw

View File

@ -11,14 +11,16 @@ if [ ! -z "$PGID" ] && [ "$PGID" != "$(getent group stirlingpdfgroup | cut -d: -
fi fi
umask "$UMASK" || true umask "$UMASK" || true
if [[ "$INSTALL_BOOK_AND_ADVANCED_HTML_OPS" == "true" ]]; then if [[ "$INSTALL_BOOK_AND_ADVANCED_HTML_OPS" == "true" && "$FAT_DOCKER" != "true" ]]; then
apk add --no-cache calibre@testing apk add --no-cache calibre@testing
fi fi
/scripts/download-security-jar.sh if [[ "$FAT_DOCKER" != "true" ]]; then
/scripts/download-security-jar.sh
fi
if [[ -n "$LANGS" ]]; then if [[ -n "$LANGS" ]]; then
/scripts/installFonts.sh $LANGS /scripts/installFonts.sh $LANGS
fi fi
echo "Setting permissions and ownership for necessary directories..." echo "Setting permissions and ownership for necessary directories..."

View File

@ -15,7 +15,10 @@ ignore = [
[cs_CZ] [cs_CZ]
ignore = [ ignore = [
'info',
'language.direction', 'language.direction',
'pipeline.header',
'text',
] ]
[de_DE] [de_DE]
@ -65,6 +68,16 @@ ignore = [
'language.direction', 'language.direction',
] ]
[hr_HR]
ignore = [
'font',
'home.pipeline.title',
'info',
'language.direction',
'pdfOrganiser.tags',
'showJS.tags',
]
[hu_HU] [hu_HU]
ignore = [ ignore = [
'language.direction', 'language.direction',
@ -103,6 +116,11 @@ ignore = [
'language.direction', 'language.direction',
] ]
[no_NB]
ignore = [
'language.direction',
]
[pl_PL] [pl_PL]
ignore = [ ignore = [
'language.direction', 'language.direction',

View File

@ -6,11 +6,15 @@ import java.net.Socket;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.github.pixee.security.SystemCommand; import io.github.pixee.security.SystemCommand;
public class LibreOfficeListener { public class LibreOfficeListener {
private static final long ACTIVITY_TIMEOUT = 20 * 60 * 1000; // 20 minutes private static final Logger logger = LoggerFactory.getLogger(LibreOfficeListener.class);
private static final long ACTIVITY_TIMEOUT = 20L * 60 * 1000; // 20 minutes
private static final LibreOfficeListener INSTANCE = new LibreOfficeListener(); private static final LibreOfficeListener INSTANCE = new LibreOfficeListener();
private static final int LISTENER_PORT = 2002; private static final int LISTENER_PORT = 2002;
@ -27,14 +31,12 @@ public class LibreOfficeListener {
private LibreOfficeListener() {} private LibreOfficeListener() {}
private boolean isListenerRunning() { private boolean isListenerRunning() {
try { System.out.println("waiting for listener to start");
System.out.println("waiting for listener to start"); try (Socket socket = new Socket()) {
Socket socket = new Socket();
socket.connect( socket.connect(
new InetSocketAddress("localhost", 2002), 1000); // Timeout after 1 second new InetSocketAddress("localhost", 2002), 1000); // Timeout after 1 second
socket.close();
return true; return true;
} catch (IOException e) { } catch (Exception e) {
return false; return false;
} }
} }
@ -63,6 +65,7 @@ public class LibreOfficeListener {
try { try {
Thread.sleep(5000); // Check for inactivity every 5 seconds Thread.sleep(5000); // Check for inactivity every 5 seconds
} catch (InterruptedException e) { } catch (InterruptedException e) {
Thread.currentThread().interrupt();
break; break;
} }
} }
@ -80,8 +83,8 @@ public class LibreOfficeListener {
try { try {
Thread.sleep(1000); Thread.sleep(1000);
} catch (InterruptedException e) { } catch (InterruptedException e) {
// TODO Auto-generated catch block Thread.currentThread().interrupt();
e.printStackTrace(); logger.error("exception", e);
} // Check every 1 second } // Check every 1 second
} }
} }

View File

@ -2,9 +2,13 @@ package stirling.software.SPDF.config;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.Properties; import java.util.Properties;
import java.util.function.Predicate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@ -22,6 +26,8 @@ import stirling.software.SPDF.model.ApplicationProperties;
@Lazy @Lazy
public class AppConfig { public class AppConfig {
private static final Logger logger = LoggerFactory.getLogger(AppConfig.class);
@Autowired ApplicationProperties applicationProperties; @Autowired ApplicationProperties applicationProperties;
@Bean @Bean
@ -54,7 +60,7 @@ public class AppConfig {
props.load(resource.getInputStream()); props.load(resource.getInputStream());
return props.getProperty("version"); return props.getProperty("version");
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); logger.error("exception", e);
} }
return "0.0.0"; return "0.0.0";
} }
@ -108,4 +114,26 @@ public class AppConfig {
public boolean missingActivSecurity() { public boolean missingActivSecurity() {
return false; return false;
} }
@Bean(name = "watchedFoldersDir")
public String watchedFoldersDir() {
return "./pipeline/watchedFolders/";
}
@Bean(name = "finishedFoldersDir")
public String finishedFoldersDir() {
return "./pipeline/finishedFolders/";
}
@Bean(name = "directoryFilter")
public Predicate<Path> processPDFOnlyFilter() {
return path -> {
if (Files.isDirectory(path)) {
return !path.toString().contains("processing");
} else {
String fileName = path.getFileName().toString();
return fileName.endsWith(".pdf");
}
};
}
} }

View File

@ -7,7 +7,6 @@ import java.net.URISyntaxException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.ApplicationContextInitializer;
@ -45,46 +44,47 @@ public class ConfigInitializer
} }
} }
} else { } else {
Path templatePath = // Path templatePath =
Paths.get( // Paths.get(
getClass() // getClass()
.getClassLoader() // .getClassLoader()
.getResource("settings.yml.template") // .getResource("settings.yml.template")
.toURI()); // .toURI());
Path userPath = Paths.get("configs", "settings.yml"); // Path userPath = Paths.get("configs", "settings.yml");
//
List<String> templateLines = Files.readAllLines(templatePath); // List<String> templateLines = Files.readAllLines(templatePath);
List<String> userLines = // List<String> userLines =
Files.exists(userPath) ? Files.readAllLines(userPath) : new ArrayList<>(); // Files.exists(userPath) ? Files.readAllLines(userPath) : new
// ArrayList<>();
List<String> resultLines = new ArrayList<>(); //
int position = 0; // List<String> resultLines = new ArrayList<>();
for (String templateLine : templateLines) { // int position = 0;
// Check if the line is a comment // for (String templateLine : templateLines) {
if (templateLine.trim().startsWith("#")) { // // Check if the line is a comment
String entry = templateLine.trim().substring(1).trim(); // if (templateLine.trim().startsWith("#")) {
if (!entry.isEmpty()) { // String entry = templateLine.trim().substring(1).trim();
// Check if this comment has been uncommented in userLines // if (!entry.isEmpty()) {
String key = entry.split(":")[0].trim(); // // Check if this comment has been uncommented in userLines
addLine(resultLines, userLines, templateLine, key, position); // String key = entry.split(":")[0].trim();
} else { // addLine(resultLines, userLines, templateLine, key, position);
resultLines.add(templateLine); // } else {
} // resultLines.add(templateLine);
} // }
// Check if the line is a key-value pair // }
else if (templateLine.contains(":")) { // // Check if the line is a key-value pair
String key = templateLine.split(":")[0].trim(); // else if (templateLine.contains(":")) {
addLine(resultLines, userLines, templateLine, key, position); // String key = templateLine.split(":")[0].trim();
} // addLine(resultLines, userLines, templateLine, key, position);
// Handle empty lines // }
else if (templateLine.trim().length() == 0) { // // Handle empty lines
resultLines.add(""); // else if (templateLine.trim().length() == 0) {
} // resultLines.add("");
position++; // }
} // position++;
// }
// Write the result to the user settings file //
Files.write(userPath, resultLines); // // Write the result to the user settings file
// Files.write(userPath, resultLines);
} }
Path customSettingsPath = Paths.get("configs", "custom_settings.yml"); Path customSettingsPath = Paths.get("configs", "custom_settings.yml");

View File

@ -116,6 +116,7 @@ public class EndpointConfiguration {
addEndpointToGroup("Security", "change-permissions"); addEndpointToGroup("Security", "change-permissions");
addEndpointToGroup("Security", "add-watermark"); addEndpointToGroup("Security", "add-watermark");
addEndpointToGroup("Security", "cert-sign"); addEndpointToGroup("Security", "cert-sign");
addEndpointToGroup("Security", "remove-cert-sign");
addEndpointToGroup("Security", "sanitize-pdf"); addEndpointToGroup("Security", "sanitize-pdf");
addEndpointToGroup("Security", "auto-redact"); addEndpointToGroup("Security", "auto-redact");
@ -200,6 +201,7 @@ public class EndpointConfiguration {
addEndpointToGroup("Java", "extract-images"); addEndpointToGroup("Java", "extract-images");
addEndpointToGroup("Java", "change-metadata"); addEndpointToGroup("Java", "change-metadata");
addEndpointToGroup("Java", "cert-sign"); addEndpointToGroup("Java", "cert-sign");
addEndpointToGroup("Java", "remove-cert-sign");
addEndpointToGroup("Java", "multi-page-layout"); addEndpointToGroup("Java", "multi-page-layout");
addEndpointToGroup("Java", "scale-pages"); addEndpointToGroup("Java", "scale-pages");
addEndpointToGroup("Java", "add-page-numbers"); addEndpointToGroup("Java", "add-page-numbers");

View File

@ -1,16 +1,18 @@
package stirling.software.SPDF.config; package stirling.software.SPDF.config;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.util.Map; import java.util.Map;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader; import org.springframework.core.io.ResourceLoader;
import org.thymeleaf.IEngineConfiguration; import org.thymeleaf.IEngineConfiguration;
import org.thymeleaf.templateresolver.AbstractConfigurableTemplateResolver; import org.thymeleaf.templateresolver.AbstractConfigurableTemplateResolver;
import org.thymeleaf.templateresource.ClassLoaderTemplateResource;
import org.thymeleaf.templateresource.FileTemplateResource; import org.thymeleaf.templateresource.FileTemplateResource;
import org.thymeleaf.templateresource.ITemplateResource; import org.thymeleaf.templateresource.ITemplateResource;
import stirling.software.SPDF.model.InputStreamTemplateResource;
public class FileFallbackTemplateResolver extends AbstractConfigurableTemplateResolver { public class FileFallbackTemplateResolver extends AbstractConfigurableTemplateResolver {
private final ResourceLoader resourceLoader; private final ResourceLoader resourceLoader;
@ -40,9 +42,13 @@ public class FileFallbackTemplateResolver extends AbstractConfigurableTemplateRe
} }
return new ClassLoaderTemplateResource( InputStream inputStream =
Thread.currentThread().getContextClassLoader(), Thread.currentThread()
"classpath:/templates/" + resourceName, .getContextClassLoader()
characterEncoding); .getResourceAsStream("templates/" + resourceName);
if (inputStream != null) {
return new InputStreamTemplateResource(inputStream, "UTF-8");
}
return null;
} }
} }

View File

@ -36,7 +36,6 @@ public class MetricsFilter extends OncePerRequestFilter {
|| uri.startsWith("/v1/api-docs") || uri.startsWith("/v1/api-docs")
|| uri.endsWith("robots.txt") || uri.endsWith("robots.txt")
|| uri.startsWith("/images") || uri.startsWith("/images")
|| uri.startsWith("/images")
|| uri.endsWith(".png") || uri.endsWith(".png")
|| uri.endsWith(".ico") || uri.endsWith(".ico")
|| uri.endsWith(".css") || uri.endsWith(".css")

View File

@ -18,6 +18,7 @@ class AppUpdateAuthService implements ShowAdminInterface {
@Autowired private UserRepository userRepository; @Autowired private UserRepository userRepository;
@Autowired private ApplicationProperties applicationProperties; @Autowired private ApplicationProperties applicationProperties;
@Override
public boolean getShowUpdateOnlyAdmins() { public boolean getShowUpdateOnlyAdmins() {
boolean showUpdate = applicationProperties.getSystem().getShowUpdate(); boolean showUpdate = applicationProperties.getSystem().getShowUpdate();
if (!showUpdate) { if (!showUpdate) {

View File

@ -49,10 +49,12 @@ public class CustomAuthenticationFailureHandler extends SimpleUrlAuthenticationF
} }
String username = request.getParameter("username"); String username = request.getParameter("username");
if (username != null && !isDemoUser(username)) { Optional<User> optUser = userService.findByUsernameIgnoreCase(username);
if (username != null && optUser.isPresent() && !isDemoUser(optUser)) {
logger.info( logger.info(
"Remaining attempts for user {}: {}", "Remaining attempts for user {}: {}",
username, optUser.get().getUsername(),
loginAttemptService.getRemainingAttempts(username)); loginAttemptService.getRemainingAttempts(username));
loginAttemptService.loginFailed(username); loginAttemptService.loginFailed(username);
if (loginAttemptService.isBlocked(username) if (loginAttemptService.isBlocked(username)
@ -70,8 +72,7 @@ public class CustomAuthenticationFailureHandler extends SimpleUrlAuthenticationF
super.onAuthenticationFailure(request, response, exception); super.onAuthenticationFailure(request, response, exception);
} }
private boolean isDemoUser(String username) { private boolean isDemoUser(Optional<User> user) {
Optional<User> user = userService.findByUsernameIgnoreCase(username);
return user.isPresent() return user.isPresent()
&& user.get().getAuthorities().stream() && user.get().getAuthorities().stream()
.anyMatch(authority -> "ROLE_DEMO_USER".equals(authority.getAuthority())); .anyMatch(authority -> "ROLE_DEMO_USER".equals(authority.getAuthority()));

View File

@ -33,7 +33,6 @@ public class LoginAttemptService {
} }
public void loginSucceeded(String key) { public void loginSucceeded(String key) {
logger.info(key + " " + attemptsCache.mappingCount());
if (key == null || key.trim().isEmpty()) { if (key == null || key.trim().isEmpty()) {
return; return;
} }

View File

@ -238,7 +238,7 @@ public class SecurityConfiguration {
GoogleProvider google = client.getGoogle(); GoogleProvider google = client.getGoogle();
return google != null && google.isSettingsValid() return google != null && google.isSettingsValid()
? Optional.of( ? Optional.of(
ClientRegistration.withRegistrationId("google") ClientRegistration.withRegistrationId(google.getName())
.clientId(google.getClientId()) .clientId(google.getClientId())
.clientSecret(google.getClientSecret()) .clientSecret(google.getClientSecret())
.scope(google.getScopes()) .scope(google.getScopes())
@ -246,8 +246,8 @@ public class SecurityConfiguration {
.tokenUri(google.getTokenuri()) .tokenUri(google.getTokenuri())
.userInfoUri(google.getUserinfouri()) .userInfoUri(google.getUserinfouri())
.userNameAttributeName(google.getUseAsUsername()) .userNameAttributeName(google.getUseAsUsername())
.clientName("Google") .clientName(google.getClientName())
.redirectUri("{baseUrl}/login/oauth2/code/google") .redirectUri("{baseUrl}/login/oauth2/code/" + google.getName())
.authorizationGrantType( .authorizationGrantType(
org.springframework.security.oauth2.core org.springframework.security.oauth2.core
.AuthorizationGrantType.AUTHORIZATION_CODE) .AuthorizationGrantType.AUTHORIZATION_CODE)
@ -269,12 +269,12 @@ public class SecurityConfiguration {
return keycloak != null && keycloak.isSettingsValid() return keycloak != null && keycloak.isSettingsValid()
? Optional.of( ? Optional.of(
ClientRegistrations.fromIssuerLocation(keycloak.getIssuer()) ClientRegistrations.fromIssuerLocation(keycloak.getIssuer())
.registrationId("keycloak") .registrationId(keycloak.getName())
.clientId(keycloak.getClientId()) .clientId(keycloak.getClientId())
.clientSecret(keycloak.getClientSecret()) .clientSecret(keycloak.getClientSecret())
.scope(keycloak.getScopes()) .scope(keycloak.getScopes())
.userNameAttributeName(keycloak.getUseAsUsername()) .userNameAttributeName(keycloak.getUseAsUsername())
.clientName("Keycloak") .clientName(keycloak.getClientName())
.build()) .build())
: Optional.empty(); : Optional.empty();
} }
@ -291,7 +291,7 @@ public class SecurityConfiguration {
GithubProvider github = client.getGithub(); GithubProvider github = client.getGithub();
return github != null && github.isSettingsValid() return github != null && github.isSettingsValid()
? Optional.of( ? Optional.of(
ClientRegistration.withRegistrationId("github") ClientRegistration.withRegistrationId(github.getName())
.clientId(github.getClientId()) .clientId(github.getClientId())
.clientSecret(github.getClientSecret()) .clientSecret(github.getClientSecret())
.scope(github.getScopes()) .scope(github.getScopes())
@ -299,8 +299,8 @@ public class SecurityConfiguration {
.tokenUri(github.getTokenuri()) .tokenUri(github.getTokenuri())
.userInfoUri(github.getUserinfouri()) .userInfoUri(github.getUserinfouri())
.userNameAttributeName(github.getUseAsUsername()) .userNameAttributeName(github.getUseAsUsername())
.clientName("GitHub") .clientName(github.getClientName())
.redirectUri("{baseUrl}/login/oauth2/code/github") .redirectUri("{baseUrl}/login/oauth2/code/" + github.getName())
.authorizationGrantType( .authorizationGrantType(
org.springframework.security.oauth2.core org.springframework.security.oauth2.core
.AuthorizationGrantType.AUTHORIZATION_CODE) .AuthorizationGrantType.AUTHORIZATION_CODE)

View File

@ -52,7 +52,7 @@ public class CustomOAuth2LogoutSuccessHandler extends SimpleUrlLogoutSuccessHand
issuer = provider.getIssuer(); issuer = provider.getIssuer();
clientId = provider.getClientId(); clientId = provider.getClientId();
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); logger.error("exception", e);
} }
} else { } else {
@ -60,13 +60,13 @@ public class CustomOAuth2LogoutSuccessHandler extends SimpleUrlLogoutSuccessHand
issuer = oauth.getIssuer(); issuer = oauth.getIssuer();
clientId = oauth.getClientId(); clientId = oauth.getClientId();
} }
String errorMessage = "";
if (request.getParameter("oauth2AuthenticationErrorWeb") != null) { if (request.getParameter("oauth2AuthenticationErrorWeb") != null) {
param = "erroroauth=oauth2AuthenticationErrorWeb"; param = "erroroauth=oauth2AuthenticationErrorWeb";
} else if (request.getParameter("error") != null) { } else if ((errorMessage = request.getParameter("error")) != null) {
param = "error=" + request.getParameter("error"); param = "error=" + sanitizeInput(errorMessage);
} else if (request.getParameter("erroroauth") != null) { } else if ((errorMessage = request.getParameter("erroroauth")) != null) {
param = "erroroauth=" + request.getParameter("erroroauth"); param = "erroroauth=" + sanitizeInput(errorMessage);
} else if (request.getParameter("oauth2AutoCreateDisabled") != null) { } else if (request.getParameter("oauth2AutoCreateDisabled") != null) {
param = "error=oauth2AutoCreateDisabled"; param = "error=oauth2AutoCreateDisabled";
} }
@ -81,7 +81,7 @@ public class CustomOAuth2LogoutSuccessHandler extends SimpleUrlLogoutSuccessHand
logger.info("Session invalidated: " + sessionId); logger.info("Session invalidated: " + sessionId);
} }
switch (registrationId) { switch (registrationId.toLowerCase()) {
case "keycloak": case "keycloak":
// Add Keycloak specific logout URL if needed // Add Keycloak specific logout URL if needed
String logoutUrl = String logoutUrl =
@ -115,4 +115,8 @@ public class CustomOAuth2LogoutSuccessHandler extends SimpleUrlLogoutSuccessHand
break; break;
} }
} }
private String sanitizeInput(String input) {
return input.replaceAll("[^a-zA-Z0-9 ]", "");
}
} }

View File

@ -16,6 +16,8 @@ import org.springframework.security.oauth2.core.oidc.user.OidcUser;
import stirling.software.SPDF.config.security.LoginAttemptService; import stirling.software.SPDF.config.security.LoginAttemptService;
import stirling.software.SPDF.config.security.UserService; import stirling.software.SPDF.config.security.UserService;
import stirling.software.SPDF.model.ApplicationProperties; import stirling.software.SPDF.model.ApplicationProperties;
import stirling.software.SPDF.model.ApplicationProperties.Security.OAUTH2;
import stirling.software.SPDF.model.ApplicationProperties.Security.OAUTH2.Client;
import stirling.software.SPDF.model.User; import stirling.software.SPDF.model.User;
public class CustomOAuth2UserService implements OAuth2UserService<OidcUserRequest, OidcUser> { public class CustomOAuth2UserService implements OAuth2UserService<OidcUserRequest, OidcUser> {
@ -41,11 +43,27 @@ public class CustomOAuth2UserService implements OAuth2UserService<OidcUserReques
@Override @Override
public OidcUser loadUser(OidcUserRequest userRequest) throws OAuth2AuthenticationException { public OidcUser loadUser(OidcUserRequest userRequest) throws OAuth2AuthenticationException {
String usernameAttribute = OAUTH2 oauth2 = applicationProperties.getSecurity().getOAUTH2();
applicationProperties.getSecurity().getOAUTH2().getUseAsUsername(); String usernameAttribute = oauth2.getUseAsUsername();
if (usernameAttribute == null || usernameAttribute.trim().isEmpty()) {
Client client = oauth2.getClient();
if (client != null && client.getKeycloak() != null) {
usernameAttribute = client.getKeycloak().getUseAsUsername();
} else {
usernameAttribute = "email";
}
}
try { try {
OidcUser user = delegate.loadUser(userRequest); OidcUser user = delegate.loadUser(userRequest);
String username = user.getUserInfo().getClaimAsString(usernameAttribute); String username = user.getUserInfo().getClaimAsString(usernameAttribute);
// Check if the username claim is null or empty
if (username == null || username.trim().isEmpty()) {
throw new IllegalArgumentException(
"Claim '" + usernameAttribute + "' cannot be null or empty");
}
Optional<User> duser = userService.findByUsernameIgnoreCase(username); Optional<User> duser = userService.findByUsernameIgnoreCase(username);
if (duser.isPresent()) { if (duser.isPresent()) {
if (loginAttemptService.isBlocked(username)) { if (loginAttemptService.isBlocked(username)) {
@ -56,13 +74,14 @@ public class CustomOAuth2UserService implements OAuth2UserService<OidcUserReques
throw new IllegalArgumentException("Password must not be null"); throw new IllegalArgumentException("Password must not be null");
} }
} }
// Return a new OidcUser with adjusted attributes // Return a new OidcUser with adjusted attributes
return new DefaultOidcUser( return new DefaultOidcUser(
user.getAuthorities(), user.getAuthorities(),
userRequest.getIdToken(), userRequest.getIdToken(),
user.getUserInfo(), user.getUserInfo(),
usernameAttribute); usernameAttribute);
} catch (java.lang.IllegalArgumentException e) { } catch (IllegalArgumentException e) {
logger.error("Error loading OIDC user: {}", e.getMessage()); logger.error("Error loading OIDC user: {}", e.getMessage());
throw new OAuth2AuthenticationException(new OAuth2Error(e.getMessage()), e); throw new OAuth2AuthenticationException(new OAuth2Error(e.getMessage()), e);
} catch (Exception e) { } catch (Exception e) {

View File

@ -18,6 +18,8 @@ import org.apache.pdfbox.pdmodel.PDPageContentStream.AppendMode;
import org.apache.pdfbox.pdmodel.common.PDRectangle; import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject; import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject;
import org.apache.pdfbox.util.Matrix; import org.apache.pdfbox.util.Matrix;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.ModelAttribute;
@ -38,6 +40,9 @@ import stirling.software.SPDF.utils.WebResponseUtils;
@Tag(name = "General", description = "General APIs") @Tag(name = "General", description = "General APIs")
public class SplitPdfBySectionsController { public class SplitPdfBySectionsController {
private static final Logger logger =
LoggerFactory.getLogger(SplitPdfBySectionsController.class);
@PostMapping(value = "/split-pdf-by-sections", consumes = "multipart/form-data") @PostMapping(value = "/split-pdf-by-sections", consumes = "multipart/form-data")
@Operation( @Operation(
summary = "Split PDF pages into smaller sections", summary = "Split PDF pages into smaller sections",
@ -92,7 +97,7 @@ public class SplitPdfBySectionsController {
if (sectionNum == horiz * verti) pageNum++; if (sectionNum == horiz * verti) pageNum++;
} }
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); logger.error("exception", e);
} finally { } finally {
data = Files.readAllBytes(zipFile); data = Files.readAllBytes(zipFile);
Files.deleteIfExists(zipFile); Files.deleteIfExists(zipFile);

View File

@ -10,6 +10,8 @@ import java.util.zip.ZipOutputStream;
import org.apache.pdfbox.Loader; import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage; import org.apache.pdfbox.pdmodel.PDPage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.ModelAttribute;
@ -31,6 +33,8 @@ import stirling.software.SPDF.utils.WebResponseUtils;
@Tag(name = "General", description = "General APIs") @Tag(name = "General", description = "General APIs")
public class SplitPdfBySizeController { public class SplitPdfBySizeController {
private static final Logger logger = LoggerFactory.getLogger(SplitPdfBySizeController.class);
@PostMapping(value = "/split-by-size-or-count", consumes = "multipart/form-data") @PostMapping(value = "/split-by-size-or-count", consumes = "multipart/form-data")
@Operation( @Operation(
summary = "Auto split PDF pages into separate documents based on size or count", summary = "Auto split PDF pages into separate documents based on size or count",
@ -66,7 +70,7 @@ public class SplitPdfBySizeController {
} }
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); logger.error("exception", e);
} finally { } finally {
data = Files.readAllBytes(zipFile); data = Files.readAllBytes(zipFile);
Files.deleteIfExists(zipFile); Files.deleteIfExists(zipFile);

View File

@ -66,46 +66,46 @@ public class UserController {
RedirectAttributes redirectAttributes) { RedirectAttributes redirectAttributes) {
if (!userService.isUsernameValid(newUsername)) { if (!userService.isUsernameValid(newUsername)) {
return new RedirectView("/account?messageType=invalidUsername"); return new RedirectView("/account?messageType=invalidUsername", true);
} }
if (principal == null) { if (principal == null) {
return new RedirectView("/account?messageType=notAuthenticated"); return new RedirectView("/account?messageType=notAuthenticated", true);
} }
// The username MUST be unique when renaming // The username MUST be unique when renaming
Optional<User> userOpt = userService.findByUsername(principal.getName()); Optional<User> userOpt = userService.findByUsername(principal.getName());
if (userOpt == null || userOpt.isEmpty()) { if (userOpt == null || userOpt.isEmpty()) {
return new RedirectView("/account?messageType=userNotFound"); return new RedirectView("/account?messageType=userNotFound", true);
} }
User user = userOpt.get(); User user = userOpt.get();
if (user.getUsername().equals(newUsername)) { if (user.getUsername().equals(newUsername)) {
return new RedirectView("/account?messageType=usernameExists"); return new RedirectView("/account?messageType=usernameExists", true);
} }
if (!userService.isPasswordCorrect(user, currentPassword)) { if (!userService.isPasswordCorrect(user, currentPassword)) {
return new RedirectView("/account?messageType=incorrectPassword"); return new RedirectView("/account?messageType=incorrectPassword", true);
} }
if (!user.getUsername().equals(newUsername) && userService.usernameExists(newUsername)) { if (!user.getUsername().equals(newUsername) && userService.usernameExists(newUsername)) {
return new RedirectView("/account?messageType=usernameExists"); return new RedirectView("/account?messageType=usernameExists", true);
} }
if (newUsername != null && newUsername.length() > 0) { if (newUsername != null && newUsername.length() > 0) {
try { try {
userService.changeUsername(user, newUsername); userService.changeUsername(user, newUsername);
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
return new RedirectView("/account?messageType=invalidUsername"); return new RedirectView("/account?messageType=invalidUsername", true);
} }
} }
// Logout using Spring's utility // Logout using Spring's utility
new SecurityContextLogoutHandler().logout(request, response, null); new SecurityContextLogoutHandler().logout(request, response, null);
return new RedirectView(LOGIN_MESSAGETYPE_CREDSUPDATED); return new RedirectView(LOGIN_MESSAGETYPE_CREDSUPDATED, true);
} }
@PreAuthorize("!hasAuthority('ROLE_DEMO_USER')") @PreAuthorize("!hasAuthority('ROLE_DEMO_USER')")
@ -118,19 +118,19 @@ public class UserController {
HttpServletResponse response, HttpServletResponse response,
RedirectAttributes redirectAttributes) { RedirectAttributes redirectAttributes) {
if (principal == null) { if (principal == null) {
return new RedirectView("/change-creds?messageType=notAuthenticated"); return new RedirectView("/change-creds?messageType=notAuthenticated", true);
} }
Optional<User> userOpt = userService.findByUsernameIgnoreCase(principal.getName()); Optional<User> userOpt = userService.findByUsernameIgnoreCase(principal.getName());
if (userOpt == null || userOpt.isEmpty()) { if (userOpt == null || userOpt.isEmpty()) {
return new RedirectView("/change-creds?messageType=userNotFound"); return new RedirectView("/change-creds?messageType=userNotFound", true);
} }
User user = userOpt.get(); User user = userOpt.get();
if (!userService.isPasswordCorrect(user, currentPassword)) { if (!userService.isPasswordCorrect(user, currentPassword)) {
return new RedirectView("/change-creds?messageType=incorrectPassword"); return new RedirectView("/change-creds?messageType=incorrectPassword", true);
} }
userService.changePassword(user, newPassword); userService.changePassword(user, newPassword);
@ -138,7 +138,7 @@ public class UserController {
// Logout using Spring's utility // Logout using Spring's utility
new SecurityContextLogoutHandler().logout(request, response, null); new SecurityContextLogoutHandler().logout(request, response, null);
return new RedirectView(LOGIN_MESSAGETYPE_CREDSUPDATED); return new RedirectView(LOGIN_MESSAGETYPE_CREDSUPDATED, true);
} }
@PreAuthorize("!hasAuthority('ROLE_DEMO_USER')") @PreAuthorize("!hasAuthority('ROLE_DEMO_USER')")
@ -151,19 +151,19 @@ public class UserController {
HttpServletResponse response, HttpServletResponse response,
RedirectAttributes redirectAttributes) { RedirectAttributes redirectAttributes) {
if (principal == null) { if (principal == null) {
return new RedirectView("/account?messageType=notAuthenticated"); return new RedirectView("/account?messageType=notAuthenticated", true);
} }
Optional<User> userOpt = userService.findByUsernameIgnoreCase(principal.getName()); Optional<User> userOpt = userService.findByUsernameIgnoreCase(principal.getName());
if (userOpt == null || userOpt.isEmpty()) { if (userOpt == null || userOpt.isEmpty()) {
return new RedirectView("/account?messageType=userNotFound"); return new RedirectView("/account?messageType=userNotFound", true);
} }
User user = userOpt.get(); User user = userOpt.get();
if (!userService.isPasswordCorrect(user, currentPassword)) { if (!userService.isPasswordCorrect(user, currentPassword)) {
return new RedirectView("/account?messageType=incorrectPassword"); return new RedirectView("/account?messageType=incorrectPassword", true);
} }
userService.changePassword(user, newPassword); userService.changePassword(user, newPassword);
@ -171,7 +171,7 @@ public class UserController {
// Logout using Spring's utility // Logout using Spring's utility
new SecurityContextLogoutHandler().logout(request, response, null); new SecurityContextLogoutHandler().logout(request, response, null);
return new RedirectView(LOGIN_MESSAGETYPE_CREDSUPDATED); return new RedirectView(LOGIN_MESSAGETYPE_CREDSUPDATED, true);
} }
@PreAuthorize("!hasAuthority('ROLE_DEMO_USER')") @PreAuthorize("!hasAuthority('ROLE_DEMO_USER')")
@ -204,7 +204,7 @@ public class UserController {
boolean forceChange) { boolean forceChange) {
if (!userService.isUsernameValid(username)) { if (!userService.isUsernameValid(username)) {
return new RedirectView("/addUsers?messageType=invalidUsername"); return new RedirectView("/addUsers?messageType=invalidUsername", true);
} }
Optional<User> userOpt = userService.findByUsernameIgnoreCase(username); Optional<User> userOpt = userService.findByUsernameIgnoreCase(username);
@ -212,26 +212,27 @@ public class UserController {
if (userOpt.isPresent()) { if (userOpt.isPresent()) {
User user = userOpt.get(); User user = userOpt.get();
if (user != null && user.getUsername().equalsIgnoreCase(username)) { if (user != null && user.getUsername().equalsIgnoreCase(username)) {
return new RedirectView("/addUsers?messageType=usernameExists"); return new RedirectView("/addUsers?messageType=usernameExists", true);
} }
} }
if (userService.usernameExistsIgnoreCase(username)) { if (userService.usernameExistsIgnoreCase(username)) {
return new RedirectView("/addUsers?messageType=usernameExists"); return new RedirectView("/addUsers?messageType=usernameExists", true);
} }
try { try {
// Validate the role // Validate the role
Role roleEnum = Role.fromString(role); Role roleEnum = Role.fromString(role);
if (roleEnum == Role.INTERNAL_API_USER) { if (roleEnum == Role.INTERNAL_API_USER) {
// If the role is INTERNAL_API_USER, reject the request // If the role is INTERNAL_API_USER, reject the request
return new RedirectView("/addUsers?messageType=invalidRole"); return new RedirectView("/addUsers?messageType=invalidRole", true);
} }
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
// If the role ID is not valid, redirect with an error message // If the role ID is not valid, redirect with an error message
return new RedirectView("/addUsers?messageType=invalidRole"); return new RedirectView("/addUsers?messageType=invalidRole", true);
} }
userService.saveUser(username, password, role, forceChange); userService.saveUser(username, password, role, forceChange);
return new RedirectView("/addUsers"); // Redirect to account page after adding the user return new RedirectView(
"/addUsers", true); // Redirect to account page after adding the user
} }
@PreAuthorize("hasRole('ROLE_ADMIN')") @PreAuthorize("hasRole('ROLE_ADMIN')")
@ -244,33 +245,34 @@ public class UserController {
Optional<User> userOpt = userService.findByUsernameIgnoreCase(username); Optional<User> userOpt = userService.findByUsernameIgnoreCase(username);
if (!userOpt.isPresent()) { if (!userOpt.isPresent()) {
return new RedirectView("/addUsers?messageType=userNotFound"); return new RedirectView("/addUsers?messageType=userNotFound", true);
} }
if (!userService.usernameExistsIgnoreCase(username)) { if (!userService.usernameExistsIgnoreCase(username)) {
return new RedirectView("/addUsers?messageType=userNotFound"); return new RedirectView("/addUsers?messageType=userNotFound", true);
} }
// Get the currently authenticated username // Get the currently authenticated username
String currentUsername = authentication.getName(); String currentUsername = authentication.getName();
// Check if the provided username matches the current session's username // Check if the provided username matches the current session's username
if (currentUsername.equalsIgnoreCase(username)) { if (currentUsername.equalsIgnoreCase(username)) {
return new RedirectView("/addUsers?messageType=downgradeCurrentUser"); return new RedirectView("/addUsers?messageType=downgradeCurrentUser", true);
} }
try { try {
// Validate the role // Validate the role
Role roleEnum = Role.fromString(role); Role roleEnum = Role.fromString(role);
if (roleEnum == Role.INTERNAL_API_USER) { if (roleEnum == Role.INTERNAL_API_USER) {
// If the role is INTERNAL_API_USER, reject the request // If the role is INTERNAL_API_USER, reject the request
return new RedirectView("/addUsers?messageType=invalidRole"); return new RedirectView("/addUsers?messageType=invalidRole", true);
} }
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
// If the role ID is not valid, redirect with an error message // If the role ID is not valid, redirect with an error message
return new RedirectView("/addUsers?messageType=invalidRole"); return new RedirectView("/addUsers?messageType=invalidRole", true);
} }
User user = userOpt.get(); User user = userOpt.get();
userService.changeRole(user, role); userService.changeRole(user, role);
return new RedirectView("/addUsers"); // Redirect to account page after adding the user return new RedirectView(
"/addUsers", true); // Redirect to account page after adding the user
} }
@PreAuthorize("hasRole('ROLE_ADMIN')") @PreAuthorize("hasRole('ROLE_ADMIN')")
@ -279,7 +281,7 @@ public class UserController {
@PathVariable(name = "username") String username, Authentication authentication) { @PathVariable(name = "username") String username, Authentication authentication) {
if (!userService.usernameExistsIgnoreCase(username)) { if (!userService.usernameExistsIgnoreCase(username)) {
return new RedirectView("/addUsers?messageType=deleteUsernameExists"); return new RedirectView("/addUsers?messageType=deleteUsernameExists", true);
} }
// Get the currently authenticated username // Get the currently authenticated username
@ -287,11 +289,11 @@ public class UserController {
// Check if the provided username matches the current session's username // Check if the provided username matches the current session's username
if (currentUsername.equalsIgnoreCase(username)) { if (currentUsername.equalsIgnoreCase(username)) {
return new RedirectView("/addUsers?messageType=deleteCurrentUser"); return new RedirectView("/addUsers?messageType=deleteCurrentUser", true);
} }
invalidateUserSessions(username); invalidateUserSessions(username);
userService.deleteUser(username); userService.deleteUser(username);
return new RedirectView("/addUsers"); return new RedirectView("/addUsers", true);
} }
@Autowired private SessionRegistry sessionRegistry; @Autowired private SessionRegistry sessionRegistry;

View File

@ -1,10 +1,22 @@
package stirling.software.SPDF.controller.api.converters; package stirling.software.SPDF.controller.api.converters;
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.stream.Collectors;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDDocumentCatalog;
import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm;
import org.apache.pdfbox.pdmodel.interactive.form.PDField;
import org.apache.pdfbox.pdmodel.interactive.form.PDSignatureField;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
@ -26,6 +38,8 @@ import stirling.software.SPDF.utils.WebResponseUtils;
@Tag(name = "Convert", description = "Convert APIs") @Tag(name = "Convert", description = "Convert APIs")
public class ConvertPDFToPDFA { public class ConvertPDFToPDFA {
private static final Logger logger = LoggerFactory.getLogger(ConvertPDFToPDFA.class);
@PostMapping(consumes = "multipart/form-data", value = "/pdf/pdfa") @PostMapping(consumes = "multipart/form-data", value = "/pdf/pdfa")
@Operation( @Operation(
summary = "Convert a PDF to a PDF/A", summary = "Convert a PDF to a PDF/A",
@ -36,9 +50,39 @@ public class ConvertPDFToPDFA {
MultipartFile inputFile = request.getFileInput(); MultipartFile inputFile = request.getFileInput();
String outputFormat = request.getOutputFormat(); String outputFormat = request.getOutputFormat();
// Save the uploaded file to a temporary location // Convert MultipartFile to byte[]
byte[] pdfBytes = inputFile.getBytes();
// Load the PDF document
PDDocument document = Loader.loadPDF(pdfBytes);
// Get the document catalog
PDDocumentCatalog catalog = document.getDocumentCatalog();
// Get the AcroForm
PDAcroForm acroForm = catalog.getAcroForm();
if (acroForm != null) {
// Remove signature fields safely
List<PDField> fieldsToRemove =
acroForm.getFields().stream()
.filter(field -> field instanceof PDSignatureField)
.collect(Collectors.toList());
if (!fieldsToRemove.isEmpty()) {
acroForm.flatten(fieldsToRemove, false);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
document.save(baos);
pdfBytes = baos.toByteArray();
}
}
document.close();
// Save the uploaded (and possibly modified) file to a temporary location
Path tempInputFile = Files.createTempFile("input_", ".pdf"); Path tempInputFile = Files.createTempFile("input_", ".pdf");
inputFile.transferTo(tempInputFile.toFile()); try (OutputStream outputStream = new FileOutputStream(tempInputFile.toFile())) {
outputStream.write(pdfBytes);
}
// Prepare the output file path // Prepare the output file path
Path tempOutputFile = Files.createTempFile("output_", ".pdf"); Path tempOutputFile = Files.createTempFile("output_", ".pdf");
@ -58,7 +102,7 @@ public class ConvertPDFToPDFA {
.runCommandWithOutputHandling(command); .runCommandWithOutputHandling(command);
// Read the optimized PDF file // Read the optimized PDF file
byte[] pdfBytes = Files.readAllBytes(tempOutputFile); byte[] optimizedPdfBytes = Files.readAllBytes(tempOutputFile);
// Clean up the temporary files // Clean up the temporary files
Files.deleteIfExists(tempInputFile); Files.deleteIfExists(tempInputFile);
@ -69,6 +113,6 @@ public class ConvertPDFToPDFA {
Filenames.toSimpleFileName(inputFile.getOriginalFilename()) Filenames.toSimpleFileName(inputFile.getOriginalFilename())
.replaceFirst("[.][^.]+$", "") .replaceFirst("[.][^.]+$", "")
+ "_PDFA.pdf"; + "_PDFA.pdf";
return WebResponseUtils.bytesToWebResponse(pdfBytes, outputFilename); return WebResponseUtils.bytesToWebResponse(optimizedPdfBytes, outputFilename);
} }
} }

View File

@ -15,6 +15,8 @@ import java.util.zip.ZipOutputStream;
import org.apache.pdfbox.Loader; import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.rendering.PDFRenderer; import org.apache.pdfbox.rendering.PDFRenderer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.ModelAttribute;
@ -43,6 +45,7 @@ import stirling.software.SPDF.utils.WebResponseUtils;
@Tag(name = "Misc", description = "Miscellaneous APIs") @Tag(name = "Misc", description = "Miscellaneous APIs")
public class AutoSplitPdfController { public class AutoSplitPdfController {
private static final Logger logger = LoggerFactory.getLogger(AutoSplitPdfController.class);
private static final String QR_CONTENT = "https://github.com/Stirling-Tools/Stirling-PDF"; private static final String QR_CONTENT = "https://github.com/Stirling-Tools/Stirling-PDF";
private static final String QR_CONTENT_OLD = "https://github.com/Frooodle/Stirling-PDF"; private static final String QR_CONTENT_OLD = "https://github.com/Frooodle/Stirling-PDF";
@ -115,7 +118,7 @@ public class AutoSplitPdfController {
zipOut.closeEntry(); zipOut.closeEntry();
} }
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); logger.error("exception", e);
} finally { } finally {
data = Files.readAllBytes(zipFile); data = Files.readAllBytes(zipFile);
Files.deleteIfExists(zipFile); Files.deleteIfExists(zipFile);

View File

@ -106,7 +106,7 @@ public class BlankPageController {
.replaceFirst("[.][^.]+$", "") .replaceFirst("[.][^.]+$", "")
+ "_blanksRemoved.pdf"); + "_blanksRemoved.pdf");
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); logger.error("exception", e);
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
} finally { } finally {
if (document != null) document.close(); if (document != null) document.close();

View File

@ -110,8 +110,8 @@ public class FakeScanControllerWIP {
private BufferedImage rotate(BufferedImage image, double rotation) { private BufferedImage rotate(BufferedImage image, double rotation) {
double rotationRequired = Math.toRadians(rotation); double rotationRequired = Math.toRadians(rotation);
double locationX = image.getWidth() / 2; double locationX = (double) image.getWidth() / 2;
double locationY = image.getHeight() / 2; double locationY = (double) image.getHeight() / 2;
AffineTransform tx = AffineTransform tx =
AffineTransform.getRotateInstance(rotationRequired, locationX, locationY); AffineTransform.getRotateInstance(rotationRequired, locationX, locationY);
AffineTransformOp op = new AffineTransformOp(tx, AffineTransformOp.TYPE_BICUBIC); AffineTransformOp op = new AffineTransformOp(tx, AffineTransformOp.TYPE_BICUBIC);
@ -127,8 +127,8 @@ public class FakeScanControllerWIP {
for (int i = -radius; i <= radius; i++) { for (int i = -radius; i <= radius; i++) {
for (int j = -radius; j <= radius; j++) { for (int j = -radius; j <= radius; j++) {
double xDistance = i * i; double xDistance = (double) i * i;
double yDistance = j * j; double yDistance = (double) j * j;
double g = Math.exp(-(xDistance + yDistance) / (2 * sigma * sigma)); double g = Math.exp(-(xDistance + yDistance) / (2 * sigma * sigma));
data[(i + radius) * size + j + radius] = (float) g; data[(i + radius) * size + j + radius] = (float) g;
sum += g; sum += g;
@ -137,7 +137,7 @@ public class FakeScanControllerWIP {
// Normalize the kernel // Normalize the kernel
for (int i = 0; i < data.length; i++) { for (int i = 0; i < data.length; i++) {
data[i] /= sum; if (sum != 0) data[i] /= sum;
} }
Kernel kernel = new Kernel(size, size, data); Kernel kernel = new Kernel(size, size, data);
@ -166,7 +166,7 @@ public class FakeScanControllerWIP {
0, 0,
new Color(0, 0, 0, 1f), new Color(0, 0, 0, 1f),
0, 0,
featherRadius * 2, featherRadius * 2f,
new Color(0, 0, 0, 0f))); new Color(0, 0, 0, 0f)));
g2.fillRect(0, 0, width, featherRadius); g2.fillRect(0, 0, width, featherRadius);
@ -174,7 +174,7 @@ public class FakeScanControllerWIP {
g2.setPaint( g2.setPaint(
new GradientPaint( new GradientPaint(
0, 0,
height - featherRadius * 2, height - featherRadius * 2f,
new Color(0, 0, 0, 0f), new Color(0, 0, 0, 0f),
0, 0,
height, height,
@ -187,7 +187,7 @@ public class FakeScanControllerWIP {
0, 0,
0, 0,
new Color(0, 0, 0, 1f), new Color(0, 0, 0, 1f),
featherRadius * 2, featherRadius * 2f,
0, 0,
new Color(0, 0, 0, 0f))); new Color(0, 0, 0, 0f)));
g2.fillRect(0, 0, featherRadius, height); g2.fillRect(0, 0, featherRadius, height);
@ -195,7 +195,7 @@ public class FakeScanControllerWIP {
// Right edge // Right edge
g2.setPaint( g2.setPaint(
new GradientPaint( new GradientPaint(
width - featherRadius * 2, width - featherRadius * 2f,
0, 0,
new Color(0, 0, 0, 0f), new Color(0, 0, 0, 0f),
width, width,
@ -244,7 +244,7 @@ public class FakeScanControllerWIP {
int y2 = y1 + random.nextInt(20) - 10; int y2 = y1 + random.nextInt(20) - 10;
Path2D.Double hair = new Path2D.Double(); Path2D.Double hair = new Path2D.Double();
hair.moveTo(x1, y1); hair.moveTo(x1, y1);
hair.curveTo(x1, y1, (x1 + x2) / 2, (y1 + y2) / 2, x2, y2); hair.curveTo(x1, y1, (double) (x1 + x2) / 2, (double) (y1 + y2) / 2, x2, y2);
g2d.draw(hair); g2d.draw(hair);
} }

View File

@ -12,6 +12,8 @@ import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm; import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm;
import org.apache.pdfbox.rendering.ImageType; import org.apache.pdfbox.rendering.ImageType;
import org.apache.pdfbox.rendering.PDFRenderer; import org.apache.pdfbox.rendering.PDFRenderer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
@ -33,6 +35,8 @@ import stirling.software.SPDF.utils.WebResponseUtils;
@Tag(name = "Misc", description = "Miscellaneous APIs") @Tag(name = "Misc", description = "Miscellaneous APIs")
public class FlattenController { public class FlattenController {
private static final Logger logger = LoggerFactory.getLogger(FlattenController.class);
@PostMapping(consumes = "multipart/form-data", value = "/flatten") @PostMapping(consumes = "multipart/form-data", value = "/flatten")
@Operation( @Operation(
summary = "Flatten PDF form fields or full page", summary = "Flatten PDF form fields or full page",
@ -73,7 +77,7 @@ public class FlattenController {
contentStream.drawImage(pdImage, 0, 0, pageWidth, pageHeight); contentStream.drawImage(pdImage, 0, 0, pageWidth, pageHeight);
} }
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); logger.error("exception", e);
} }
} }
PdfUtils.setMetadataToPdf(newDocument, metadata); PdfUtils.setMetadataToPdf(newDocument, metadata);

View File

@ -11,6 +11,8 @@ import org.apache.pdfbox.Loader;
import org.apache.pdfbox.cos.COSName; import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDDocumentInformation; import org.apache.pdfbox.pdmodel.PDDocumentInformation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
@ -30,6 +32,8 @@ import stirling.software.SPDF.utils.WebResponseUtils;
@Tag(name = "Misc", description = "Miscellaneous APIs") @Tag(name = "Misc", description = "Miscellaneous APIs")
public class MetadataController { public class MetadataController {
private static final Logger logger = LoggerFactory.getLogger(MetadataController.class);
private String checkUndefined(String entry) { private String checkUndefined(String entry) {
// Check if the string is "undefined" // Check if the string is "undefined"
if ("undefined".equals(entry)) { if ("undefined".equals(entry)) {
@ -136,7 +140,7 @@ public class MetadataController {
creationDateCal.setTime( creationDateCal.setTime(
new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").parse(creationDate)); new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").parse(creationDate));
} catch (ParseException e) { } catch (ParseException e) {
e.printStackTrace(); logger.error("exception", e);
} }
info.setCreationDate(creationDateCal); info.setCreationDate(creationDateCal);
} else { } else {
@ -148,7 +152,7 @@ public class MetadataController {
modificationDateCal.setTime( modificationDateCal.setTime(
new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").parse(modificationDate)); new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").parse(modificationDate));
} catch (ParseException e) { } catch (ParseException e) {
e.printStackTrace(); logger.error("exception", e);
} }
info.setModificationDate(modificationDateCal); info.setModificationDate(modificationDateCal);
} else { } else {

View File

@ -19,6 +19,7 @@ import java.util.stream.Stream;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.io.ByteArrayResource; import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import org.springframework.scheduling.annotation.Scheduled; import org.springframework.scheduling.annotation.Scheduled;
@ -28,6 +29,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import stirling.software.SPDF.model.PipelineConfig; import stirling.software.SPDF.model.PipelineConfig;
import stirling.software.SPDF.model.PipelineOperation; import stirling.software.SPDF.model.PipelineOperation;
import stirling.software.SPDF.utils.FileMonitor;
@Service @Service
public class PipelineDirectoryProcessor { public class PipelineDirectoryProcessor {
@ -35,11 +37,18 @@ public class PipelineDirectoryProcessor {
private static final Logger logger = LoggerFactory.getLogger(PipelineDirectoryProcessor.class); private static final Logger logger = LoggerFactory.getLogger(PipelineDirectoryProcessor.class);
@Autowired private ObjectMapper objectMapper; @Autowired private ObjectMapper objectMapper;
@Autowired private ApiDocService apiDocService; @Autowired private ApiDocService apiDocService;
final String watchedFoldersDir = "./pipeline/watchedFolders/";
final String finishedFoldersDir = "./pipeline/finishedFolders/";
@Autowired PipelineProcessor processor; @Autowired PipelineProcessor processor;
@Autowired FileMonitor fileMonitor;
final String watchedFoldersDir;
final String finishedFoldersDir;
public PipelineDirectoryProcessor(
@Qualifier("watchedFoldersDir") String watchedFoldersDir,
@Qualifier("finishedFoldersDir") String finishedFoldersDir) {
this.watchedFoldersDir = watchedFoldersDir;
this.finishedFoldersDir = finishedFoldersDir;
}
@Scheduled(fixedRate = 60000) @Scheduled(fixedRate = 60000)
public void scanFolders() { public void scanFolders() {
@ -130,7 +139,11 @@ public class PipelineDirectoryProcessor {
throws IOException { throws IOException {
try (Stream<Path> paths = Files.list(dir)) { try (Stream<Path> paths = Files.list(dir)) {
if ("automated".equals(operation.getParameters().get("fileInput"))) { if ("automated".equals(operation.getParameters().get("fileInput"))) {
return paths.filter(path -> !Files.isDirectory(path) && !path.equals(jsonFile)) return paths.filter(
path ->
!Files.isDirectory(path)
&& !path.equals(jsonFile)
&& fileMonitor.isFileReadyForProcessing(path))
.map(Path::toFile) .map(Path::toFile)
.toArray(File[]::new); .toArray(File[]::new);
} else { } else {

View File

@ -148,7 +148,7 @@ public class CertSignController {
doc.addSignature(signature, instance); doc.addSignature(signature, instance);
doc.saveIncremental(output); doc.saveIncremental(output);
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); logger.error("exception", e);
} }
} }

View File

@ -56,6 +56,8 @@ import org.apache.xmpbox.XMPMetadata;
import org.apache.xmpbox.xml.DomXmpParser; import org.apache.xmpbox.xml.DomXmpParser;
import org.apache.xmpbox.xml.XmpParsingException; import org.apache.xmpbox.xml.XmpParsingException;
import org.apache.xmpbox.xml.XmpSerializer; import org.apache.xmpbox.xml.XmpSerializer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.ModelAttribute;
@ -79,6 +81,8 @@ import stirling.software.SPDF.utils.WebResponseUtils;
@Tag(name = "Security", description = "Security APIs") @Tag(name = "Security", description = "Security APIs")
public class GetInfoOnPDF { public class GetInfoOnPDF {
private static final Logger logger = LoggerFactory.getLogger(GetInfoOnPDF.class);
static ObjectMapper objectMapper = new ObjectMapper(); static ObjectMapper objectMapper = new ObjectMapper();
@PostMapping(consumes = "multipart/form-data", value = "/get-info-on-pdf") @PostMapping(consumes = "multipart/form-data", value = "/get-info-on-pdf")
@ -220,7 +224,7 @@ public class GetInfoOnPDF {
javascriptArray.add(jsNode); javascriptArray.add(jsNode);
} }
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); logger.error("exception", e);
} }
} }
} }
@ -253,7 +257,7 @@ public class GetInfoOnPDF {
} }
} catch (Exception e) { } catch (Exception e) {
// TODO Auto-generated catch block // TODO Auto-generated catch block
e.printStackTrace(); logger.error("exception", e);
} }
boolean isPdfACompliant = checkForStandard(pdfBoxDoc, "PDF/A"); boolean isPdfACompliant = checkForStandard(pdfBoxDoc, "PDF/A");
@ -305,7 +309,7 @@ public class GetInfoOnPDF {
new XmpSerializer().serialize(xmpMeta, os, true); new XmpSerializer().serialize(xmpMeta, os, true);
xmpString = new String(os.toByteArray(), StandardCharsets.UTF_8); xmpString = new String(os.toByteArray(), StandardCharsets.UTF_8);
} catch (XmpParsingException | IOException e) { } catch (XmpParsingException | IOException e) {
e.printStackTrace(); logger.error("exception", e);
} }
} }
@ -593,7 +597,7 @@ public class GetInfoOnPDF {
MediaType.APPLICATION_JSON); MediaType.APPLICATION_JSON);
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); logger.error("exception", e);
} }
return null; return null;
} }
@ -691,7 +695,7 @@ public class GetInfoOnPDF {
Exception Exception
e) { // Catching general exception for brevity, ideally you'd catch specific e) { // Catching general exception for brevity, ideally you'd catch specific
// exceptions. // exceptions.
e.printStackTrace(); logger.error("exception", e);
} }
return false; return false;

View File

@ -0,0 +1,81 @@
package stirling.software.SPDF.controller.api.security;
import java.io.ByteArrayOutputStream;
import java.util.List;
import java.util.stream.Collectors;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDDocumentCatalog;
import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm;
import org.apache.pdfbox.pdmodel.interactive.form.PDField;
import org.apache.pdfbox.pdmodel.interactive.form.PDSignatureField;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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 io.github.pixee.security.Filenames;
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;
@RestController
@RequestMapping("/api/v1/security")
@Tag(name = "Security", description = "Security APIs")
public class RemoveCertSignController {
private static final Logger logger = LoggerFactory.getLogger(RemoveCertSignController.class);
@PostMapping(consumes = "multipart/form-data", value = "/remove-cert-sign")
@Operation(
summary = "Remove digital signature from PDF",
description =
"This endpoint accepts a PDF file and returns the PDF file without the digital signature. Input: PDF, Output: PDF")
public ResponseEntity<byte[]> removeCertSignPDF(@ModelAttribute PDFFile request)
throws Exception {
MultipartFile pdf = request.getFileInput();
// Convert MultipartFile to byte[]
byte[] pdfBytes = pdf.getBytes();
// Create a ByteArrayOutputStream to hold the resulting PDF
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// Load the PDF document
PDDocument document = Loader.loadPDF(pdfBytes);
// Get the document catalog
PDDocumentCatalog catalog = document.getDocumentCatalog();
// Get the AcroForm
PDAcroForm acroForm = catalog.getAcroForm();
if (acroForm != null) {
// Remove signature fields safely
List<PDField> fieldsToRemove =
acroForm.getFields().stream()
.filter(field -> field instanceof PDSignatureField)
.collect(Collectors.toList());
if (!fieldsToRemove.isEmpty()) {
acroForm.flatten(fieldsToRemove, false);
}
}
// Save the modified document to the ByteArrayOutputStream
document.save(baos);
document.close();
// Return the modified PDF as a response
return WebResponseUtils.boasToWebResponse(
baos,
Filenames.toSimpleFileName(pdf.getOriginalFilename()).replaceFirst("[.][^.]+$", "")
+ "_unsigned.pdf");
}
}

View File

@ -117,7 +117,6 @@ public class PDFTableStripper extends PDFTextStripper {
/** /**
* Instantiate a new PDFTableStripper object. * Instantiate a new PDFTableStripper object.
* *
* @param document
* @throws IOException If there is an error loading the properties. * @throws IOException If there is an error loading the properties.
*/ */
public PDFTableStripper() throws IOException { public PDFTableStripper() throws IOException {

View File

@ -52,23 +52,23 @@ public class AccountWebController {
OAUTH2 oauth = applicationProperties.getSecurity().getOAUTH2(); OAUTH2 oauth = applicationProperties.getSecurity().getOAUTH2();
if (oauth != null) { if (oauth != null) {
if (oauth.isSettingsValid()) { if (oauth.isSettingsValid()) {
providerList.put("oidc", "OpenID Connect"); providerList.put("oidc", oauth.getProvider());
} }
Client client = oauth.getClient(); Client client = oauth.getClient();
if (client != null) { if (client != null) {
GoogleProvider google = client.getGoogle(); GoogleProvider google = client.getGoogle();
if (google.isSettingsValid()) { if (google.isSettingsValid()) {
providerList.put("google", "Google"); providerList.put(google.getName(), google.getClientName());
} }
GithubProvider github = client.getGithub(); GithubProvider github = client.getGithub();
if (github.isSettingsValid()) { if (github.isSettingsValid()) {
providerList.put("github", "Github"); providerList.put(github.getName(), github.getClientName());
} }
KeycloakProvider keycloak = client.getKeycloak(); KeycloakProvider keycloak = client.getKeycloak();
if (keycloak.isSettingsValid()) { if (keycloak.isSettingsValid()) {
providerList.put("keycloak", "Keycloak"); providerList.put(keycloak.getName(), keycloak.getClientName());
} }
} }
} }
@ -129,6 +129,8 @@ public class AccountWebController {
case "invalid_request": case "invalid_request":
erroroauth = "login.oauth2invalidRequest"; erroroauth = "login.oauth2invalidRequest";
break; break;
case "invalid_id_token":
erroroauth = "login.oauth2InvalidIdToken";
default: default:
break; break;
} }
@ -260,8 +262,7 @@ public class AccountWebController {
userRepository.findByUsernameIgnoreCase( userRepository.findByUsernameIgnoreCase(
username); // Assuming findByUsername method exists username); // Assuming findByUsername method exists
if (!user.isPresent()) { if (!user.isPresent()) {
// Handle error appropriately return "redirect:/error";
return "redirect:/error"; // Example redirection in case of error
} }
// Convert settings map to JSON string // Convert settings map to JSON string
@ -271,8 +272,8 @@ public class AccountWebController {
settingsJson = objectMapper.writeValueAsString(user.get().getSettings()); settingsJson = objectMapper.writeValueAsString(user.get().getSettings());
} catch (JsonProcessingException e) { } catch (JsonProcessingException e) {
// Handle JSON conversion error // Handle JSON conversion error
e.printStackTrace(); logger.error("exception", e);
return "redirect:/error"; // Example redirection in case of error return "redirect:/error";
} }
String messageType = request.getParameter("messageType"); String messageType = request.getParameter("messageType");

View File

@ -15,6 +15,8 @@ import java.util.Objects;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader; import org.springframework.core.io.ResourceLoader;
@ -33,6 +35,8 @@ import io.swagger.v3.oas.annotations.tags.Tag;
@Tag(name = "General", description = "General APIs") @Tag(name = "General", description = "General APIs")
public class GeneralWebController { public class GeneralWebController {
private static final Logger logger = LoggerFactory.getLogger(GeneralWebController.class);
@GetMapping("/pipeline") @GetMapping("/pipeline")
@Hidden @Hidden
public String pipelineForm(Model model) { public String pipelineForm(Model model) {
@ -74,7 +78,7 @@ public class GeneralWebController {
} }
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); logger.error("exception", e);
} }
} }
if (pipelineConfigsWithNames.size() == 0) { if (pipelineConfigsWithNames.size() == 0) {

View File

@ -6,6 +6,8 @@ import java.nio.charset.StandardCharsets;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
@ -26,6 +28,8 @@ import stirling.software.SPDF.model.Dependency;
@Controller @Controller
public class HomeWebController { public class HomeWebController {
private static final Logger logger = LoggerFactory.getLogger(HomeWebController.class);
@GetMapping("/about") @GetMapping("/about")
@Hidden @Hidden
public String gameForm(Model model) { public String gameForm(Model model) {
@ -46,7 +50,7 @@ public class HomeWebController {
mapper.readValue(json, new TypeReference<Map<String, List<Dependency>>>() {}); mapper.readValue(json, new TypeReference<Map<String, List<Dependency>>>() {});
model.addAttribute("dependencies", data.get("dependencies")); model.addAttribute("dependencies", data.get("dependencies"));
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); logger.error("exception", e);
} }
return "licenses"; return "licenses";
} }

View File

@ -53,6 +53,13 @@ public class SecurityWebController {
return "security/cert-sign"; return "security/cert-sign";
} }
@GetMapping("/remove-cert-sign")
@Hidden
public String certUnSignForm(Model model) {
model.addAttribute("currentPage", "remove-cert-sign");
return "security/remove-cert-sign";
}
@GetMapping("/sanitize-pdf") @GetMapping("/sanitize-pdf")
@Hidden @Hidden
public String sanitizeForm(Model model) { public String sanitizeForm(Model model) {

View File

@ -356,8 +356,8 @@ public class ApplicationProperties {
private KeycloakProvider keycloak = new KeycloakProvider(); private KeycloakProvider keycloak = new KeycloakProvider();
public Provider get(String registrationId) throws Exception { public Provider get(String registrationId) throws Exception {
switch (registrationId) { switch (registrationId.toLowerCase()) {
case "gogole": case "google":
return getGoogle(); return getGoogle();
case "github": case "github":
return getGithub(); return getGithub();
@ -455,6 +455,7 @@ public class ApplicationProperties {
@Override @Override
public Collection<String> getScopes() { public Collection<String> getScopes() {
if (scopes == null || scopes.isEmpty()) { if (scopes == null || scopes.isEmpty()) {
scopes = new ArrayList<>();
scopes.add("https://www.googleapis.com/auth/userinfo.email"); scopes.add("https://www.googleapis.com/auth/userinfo.email");
scopes.add("https://www.googleapis.com/auth/userinfo.profile"); scopes.add("https://www.googleapis.com/auth/userinfo.profile");
} }
@ -495,6 +496,11 @@ public class ApplicationProperties {
return "google"; return "google";
} }
@Override
public String getClientName() {
return "Google";
}
public boolean isSettingsValid() { public boolean isSettingsValid() {
return super.isValid(this.getClientId(), "clientId") return super.isValid(this.getClientId(), "clientId")
&& super.isValid(this.getClientSecret(), "clientSecret") && super.isValid(this.getClientSecret(), "clientSecret")
@ -553,8 +559,10 @@ public class ApplicationProperties {
this.clientSecret = clientSecret; this.clientSecret = clientSecret;
} }
@Override
public Collection<String> getScopes() { public Collection<String> getScopes() {
if (scopes == null || scopes.isEmpty()) { if (scopes == null || scopes.isEmpty()) {
scopes = new ArrayList<>();
scopes.add("read:user"); scopes.add("read:user");
} }
return scopes; return scopes;
@ -594,6 +602,11 @@ public class ApplicationProperties {
return "github"; return "github";
} }
@Override
public String getClientName() {
return "GitHub";
}
public boolean isSettingsValid() { public boolean isSettingsValid() {
return super.isValid(this.getClientId(), "clientId") return super.isValid(this.getClientId(), "clientId")
&& super.isValid(this.getClientSecret(), "clientSecret") && super.isValid(this.getClientSecret(), "clientSecret")
@ -642,13 +655,14 @@ public class ApplicationProperties {
@Override @Override
public Collection<String> getScopes() { public Collection<String> getScopes() {
if (scopes == null || scopes.isEmpty()) { if (scopes == null || scopes.isEmpty()) {
scopes.add("openid"); scopes = new ArrayList<>();
scopes.add("profile"); scopes.add("profile");
scopes.add("email"); scopes.add("email");
} }
return scopes; return scopes;
} }
@Override
public void setScopes(String scopes) { public void setScopes(String scopes) {
this.scopes = this.scopes =
Arrays.stream(scopes.split(",")).map(String::trim).collect(Collectors.toList()); Arrays.stream(scopes.split(",")).map(String::trim).collect(Collectors.toList());
@ -684,6 +698,11 @@ public class ApplicationProperties {
return "keycloak"; return "keycloak";
} }
@Override
public String getClientName() {
return "Keycloak";
}
public boolean isSettingsValid() { public boolean isSettingsValid() {
return isValid(this.getIssuer(), "issuer") return isValid(this.getIssuer(), "issuer")
&& isValid(this.getClientId(), "clientId") && isValid(this.getClientId(), "clientId")

View File

@ -0,0 +1,45 @@
package stirling.software.SPDF.model;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import org.thymeleaf.templateresource.ITemplateResource;
public class InputStreamTemplateResource implements ITemplateResource {
private InputStream inputStream;
private String characterEncoding;
public InputStreamTemplateResource(InputStream inputStream, String characterEncoding) {
this.inputStream = inputStream;
this.characterEncoding = characterEncoding;
}
@Override
public Reader reader() throws IOException {
return new InputStreamReader(inputStream, characterEncoding);
}
@Override
public ITemplateResource relative(String relativeLocation) {
// Implement logic for relative resources, if needed
throw new UnsupportedOperationException("Relative resources not supported");
}
@Override
public String getDescription() {
return "InputStream resource [Stream]";
}
@Override
public String getBaseName() {
return "streamResource";
}
@Override
public boolean exists() {
// TODO Auto-generated method stub
return false;
}
}

View File

@ -4,11 +4,16 @@ import java.util.Collection;
public class Provider implements ProviderInterface { public class Provider implements ProviderInterface {
private String name; private String name;
private String clientName;
public String getName() { public String getName() {
return name; return name;
} }
public String getClientName() {
return clientName;
}
protected boolean isValid(String value, String name) { protected boolean isValid(String value, String name) {
if (value != null && !value.trim().isEmpty()) { if (value != null && !value.trim().isEmpty()) {
return true; return true;

View File

@ -5,6 +5,8 @@ import java.util.List;
import org.apache.pdfbox.Loader; import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocument;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.swagger.v3.oas.annotations.Hidden; import io.swagger.v3.oas.annotations.Hidden;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
@ -19,6 +21,8 @@ import stirling.software.SPDF.utils.GeneralUtils;
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
public class PDFWithPageNums extends PDFFile { public class PDFWithPageNums extends PDFFile {
private static final Logger logger = LoggerFactory.getLogger(PDFWithPageNums.class);
@Schema( @Schema(
description = 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')\"") "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')\"")
@ -31,7 +35,7 @@ public class PDFWithPageNums extends PDFFile {
pageCount = Loader.loadPDF(getFileInput().getBytes()).getNumberOfPages(); pageCount = Loader.loadPDF(getFileInput().getBytes()).getNumberOfPages();
} catch (IOException e) { } catch (IOException e) {
// TODO Auto-generated catch block // TODO Auto-generated catch block
e.printStackTrace(); logger.error("exception", e);
} }
return GeneralUtils.parsePageList(pageNumbers, pageCount, zeroCount); return GeneralUtils.parsePageList(pageNumbers, pageCount, zeroCount);
} }

View File

@ -0,0 +1,168 @@
package stirling.software.SPDF.utils;
import static java.nio.file.StandardWatchEventKinds.*;
import java.io.IOException;
import java.nio.file.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Predicate;
import java.util.stream.Stream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Component
public class FileMonitor {
private static final Logger logger = LoggerFactory.getLogger(FileMonitor.class);
private final Map<Path, WatchKey> path2KeyMapping;
private final Set<Path> newlyDiscoveredFiles;
private final ConcurrentHashMap.KeySetView<Path, Boolean> readyForProcessingFiles;
private final WatchService watchService;
private final Predicate<Path> pathFilter;
private final Path rootDir;
private Set<Path> stagingFiles;
/**
* @param rootDirectory the root directory to monitor
* @param pathFilter the filter to apply to the paths, return true if the path should be
* monitored, false otherwise
*/
@Autowired
public FileMonitor(
@Qualifier("watchedFoldersDir") String rootDirectory,
@Qualifier("directoryFilter") Predicate<Path> pathFilter)
throws IOException {
this.newlyDiscoveredFiles = new HashSet<>();
this.path2KeyMapping = new HashMap<>();
this.stagingFiles = new HashSet<>();
this.pathFilter = pathFilter;
this.readyForProcessingFiles = ConcurrentHashMap.newKeySet();
this.watchService = FileSystems.getDefault().newWatchService();
this.rootDir = Path.of(rootDirectory);
}
private boolean shouldNotProcess(Path path) {
return !pathFilter.test(path);
}
private void recursivelyRegisterEntry(Path dir) throws IOException {
WatchKey key = dir.register(watchService, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
path2KeyMapping.put(dir, key);
logger.info("Registered directory: {}", dir);
try (Stream<Path> directoryVisitor = Files.walk(dir, 1)) {
final Iterator<Path> iterator = directoryVisitor.iterator();
while (iterator.hasNext()) {
Path path = iterator.next();
if (path.equals(dir) || shouldNotProcess(path)) continue;
if (Files.isDirectory(path)) {
recursivelyRegisterEntry(path);
} else if (Files.isRegularFile(path)) {
handleFileCreation(path);
}
}
}
}
@Scheduled(fixedRate = 5000)
public void trackFiles() {
/*
All files observed changes in the last iteration will be considered as staging files.
If those files are not modified in current iteration, they will be considered as ready for processing.
*/
stagingFiles = new HashSet<>(newlyDiscoveredFiles);
readyForProcessingFiles.clear();
if (path2KeyMapping.isEmpty()) {
logger.warn(
"not monitoring any directory, even the root directory itself: {}", rootDir);
if (Files.exists(
rootDir)) { // if the root directory exists, re-register the root directory
try {
recursivelyRegisterEntry(rootDir);
} catch (IOException e) {
logger.error("unable to register monitoring", e);
}
}
}
WatchKey key;
while ((key = watchService.poll()) != null) {
final Path watchingDir = (Path) key.watchable();
key.pollEvents()
.forEach(
(evt) -> {
final Path path = (Path) evt.context();
final WatchEvent.Kind<?> kind = evt.kind();
if (shouldNotProcess(path)) return;
try {
if (Files.isDirectory(path)) {
if (kind == ENTRY_CREATE) {
handleDirectoryCreation(path);
}
/*
we don't need to handle directory deletion or modification
- directory deletion will be handled by key.reset()
- directory modification indicates a new file creation or deletion, which is handled by below
*/
}
Path relativePathFromRoot = watchingDir.resolve(path);
if (kind == ENTRY_CREATE) {
handleFileCreation(relativePathFromRoot);
} else if (kind == ENTRY_DELETE) {
handleFileRemoval(relativePathFromRoot);
} else if (kind == ENTRY_MODIFY) {
handleFileModification(relativePathFromRoot);
}
} catch (Exception e) {
logger.error("Error while processing file: {}", path, e);
}
});
boolean isKeyValid = key.reset();
if (!isKeyValid) { // key is invalid when the directory itself is no longer exists
path2KeyMapping.remove((Path) key.watchable());
}
}
readyForProcessingFiles.addAll(stagingFiles);
}
private void handleDirectoryCreation(Path dir) throws IOException {
WatchKey key = dir.register(watchService, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
path2KeyMapping.put(dir, key);
}
private void handleFileRemoval(Path path) {
newlyDiscoveredFiles.remove(path);
stagingFiles.remove(path);
}
private void handleFileCreation(Path path) {
newlyDiscoveredFiles.add(path);
stagingFiles.remove(path);
}
private void handleFileModification(Path path) {
// the logic is the same
handleFileCreation(path);
}
/**
* Check if the file is ready for processing.
*
* <p>A file is ready for processing if it is not being modified for 5000ms.
*
* @param path the path of the file
* @return true if the file is ready for processing, false otherwise
*/
public boolean isFileReadyForProcessing(Path path) {
return readyForProcessingFiles.contains(path);
}
}

View File

@ -42,6 +42,7 @@ public class FileToPdf {
List<String> command = new ArrayList<>(); List<String> command = new ArrayList<>();
if (!htmlFormatsInstalled) { if (!htmlFormatsInstalled) {
command.add("weasyprint"); command.add("weasyprint");
command.add("-e utf-8");
command.add(tempInputFile.toString()); command.add(tempInputFile.toString());
command.add(tempOutputFile.toString()); command.add(tempOutputFile.toString());
@ -52,7 +53,7 @@ public class FileToPdf {
command.add("--paper-size"); command.add("--paper-size");
command.add("a4"); command.add("a4");
if (request.getZoom() != 1.0) { if (request != null && request.getZoom() != 1.0) {
// Create a temporary CSS file // Create a temporary CSS file
File tempCssFile = Files.createTempFile("customStyle", ".css").toFile(); File tempCssFile = Files.createTempFile("customStyle", ".css").toFile();
try (FileWriter writer = new FileWriter(tempCssFile)) { try (FileWriter writer = new FileWriter(tempCssFile)) {

View File

@ -14,6 +14,8 @@ import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import com.fathzer.soft.javaluator.DoubleEvaluator; import com.fathzer.soft.javaluator.DoubleEvaluator;
@ -23,6 +25,8 @@ import io.github.pixee.security.Urls;
public class GeneralUtils { public class GeneralUtils {
private static final Logger logger = LoggerFactory.getLogger(GeneralUtils.class);
public static File convertMultipartFileToFile(MultipartFile multipartFile) throws IOException { public static File convertMultipartFileToFile(MultipartFile multipartFile) throws IOException {
File tempFile = Files.createTempFile("temp", null).toFile(); File tempFile = Files.createTempFile("temp", null).toFile();
try (FileOutputStream os = new FileOutputStream(tempFile)) { try (FileOutputStream os = new FileOutputStream(tempFile)) {
@ -234,7 +238,7 @@ public class GeneralUtils {
try { try {
Files.createDirectories(folder); Files.createDirectories(folder);
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); logger.error("exception", e);
return false; return false;
} }
} }

View File

@ -14,6 +14,8 @@ import java.util.zip.ZipOutputStream;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
@ -24,6 +26,7 @@ import io.github.pixee.security.Filenames;
import stirling.software.SPDF.utils.ProcessExecutor.ProcessExecutorResult; import stirling.software.SPDF.utils.ProcessExecutor.ProcessExecutorResult;
public class PDFToFile { public class PDFToFile {
private static final Logger logger = LoggerFactory.getLogger(PDFToFile.class);
public ResponseEntity<byte[]> processPdfToHtml(MultipartFile inputFile) public ResponseEntity<byte[]> processPdfToHtml(MultipartFile inputFile)
throws IOException, InterruptedException { throws IOException, InterruptedException {
@ -67,18 +70,20 @@ public class PDFToFile {
// Return output files in a ZIP archive // Return output files in a ZIP archive
fileName = pdfBaseName + "ToHtml.zip"; fileName = pdfBaseName + "ToHtml.zip";
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ZipOutputStream zipOutputStream = new ZipOutputStream(byteArrayOutputStream); try (ZipOutputStream zipOutputStream = new ZipOutputStream(byteArrayOutputStream)) {
for (File outputFile : outputFiles) {
for (File outputFile : outputFiles) { ZipEntry entry = new ZipEntry(outputFile.getName());
ZipEntry entry = new ZipEntry(outputFile.getName()); zipOutputStream.putNextEntry(entry);
zipOutputStream.putNextEntry(entry); try (FileInputStream fis = new FileInputStream(outputFile)) {
FileInputStream fis = new FileInputStream(outputFile); IOUtils.copy(fis, zipOutputStream);
IOUtils.copy(fis, zipOutputStream); } catch (IOException e) {
fis.close(); logger.error("Exception writing zip entry", e);
zipOutputStream.closeEntry(); }
zipOutputStream.closeEntry();
}
} catch (IOException e) {
logger.error("Exception writing zip", e);
} }
zipOutputStream.close();
fileBytes = byteArrayOutputStream.toByteArray(); fileBytes = byteArrayOutputStream.toByteArray();
} finally { } finally {
@ -160,18 +165,22 @@ public class PDFToFile {
// Return output files in a ZIP archive // Return output files in a ZIP archive
fileName = pdfBaseName + "To" + outputFormat + ".zip"; fileName = pdfBaseName + "To" + outputFormat + ".zip";
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ZipOutputStream zipOutputStream = new ZipOutputStream(byteArrayOutputStream); try (ZipOutputStream zipOutputStream = new ZipOutputStream(byteArrayOutputStream)) {
for (File outputFile : outputFiles) {
ZipEntry entry = new ZipEntry(outputFile.getName());
zipOutputStream.putNextEntry(entry);
try (FileInputStream fis = new FileInputStream(outputFile)) {
IOUtils.copy(fis, zipOutputStream);
} catch (IOException e) {
logger.error("Exception writing zip entry", e);
}
for (File outputFile : outputFiles) { zipOutputStream.closeEntry();
ZipEntry entry = new ZipEntry(outputFile.getName()); }
zipOutputStream.putNextEntry(entry); } catch (IOException e) {
FileInputStream fis = new FileInputStream(outputFile); logger.error("Exception writing zip", e);
IOUtils.copy(fis, zipOutputStream);
fis.close();
zipOutputStream.closeEntry();
} }
zipOutputStream.close();
fileBytes = byteArrayOutputStream.toByteArray(); fileBytes = byteArrayOutputStream.toByteArray();
} }

View File

@ -125,7 +125,7 @@ public class ProcessExecutor {
logger.warn( logger.warn(
"Error reader thread was interrupted due to timeout."); "Error reader thread was interrupted due to timeout.");
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); logger.error("exception", e);
} }
}); });
@ -149,7 +149,7 @@ public class ProcessExecutor {
logger.warn( logger.warn(
"Error reader thread was interrupted due to timeout."); "Error reader thread was interrupted due to timeout.");
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); logger.error("exception", e);
} }
}); });

View File

@ -332,6 +332,10 @@ home.certSign.title=Sign with Certificate
home.certSign.desc=Signs a PDF with a Certificate/Key (PEM/P12) home.certSign.desc=Signs a PDF with a Certificate/Key (PEM/P12)
certSign.tags=authenticate,PEM,P12,official,encrypt certSign.tags=authenticate,PEM,P12,official,encrypt
home.removeCertSign.title=Remove Certificate Sign
home.removeCertSign.desc=Remove certificate signature from PDF
removeCertSign.tags=authenticate,PEM,P12,official,decrypt
home.pageLayout.title=Multi-Page Layout home.pageLayout.title=Multi-Page Layout
home.pageLayout.desc=Merge multiple pages of a PDF document into a single page home.pageLayout.desc=Merge multiple pages of a PDF document into a single page
pageLayout.tags=merge,composite,single-view,organize pageLayout.tags=merge,composite,single-view,organize
@ -457,6 +461,7 @@ login.oauth2InvalidUserInfoResponse=Invalid User Info Response
login.oauth2invalidRequest=Invalid Request login.oauth2invalidRequest=Invalid Request
login.oauth2AccessDenied=Access Denied login.oauth2AccessDenied=Access Denied
login.oauth2InvalidTokenResponse=Invalid Token Response login.oauth2InvalidTokenResponse=Invalid Token Response
login.oauth2InvalidIdToken=Invalid Id Token
#auto-redact #auto-redact
@ -654,6 +659,13 @@ certSign.name=الاسم
certSign.submit=تسجيل PDF certSign.submit=تسجيل PDF
#removeCertSign
removeCertSign.title=Remove Certificate Signature
removeCertSign.header=Remove the digital certificate from the PDF
removeCertSign.selectPDF=Select a PDF file:
removeCertSign.submit=Remove Signature
#removeBlanks #removeBlanks
removeBlanks.title=إزالة الفراغات removeBlanks.title=إزالة الفراغات
removeBlanks.header=إزالة الصفحات الفارغة removeBlanks.header=إزالة الصفحات الفارغة
@ -964,6 +976,7 @@ pdfToPDFA.credit=تستخدم هذه الخدمة OCRmyPDF لتحويل PDF / A.
pdfToPDFA.submit=تحويل pdfToPDFA.submit=تحويل
pdfToPDFA.tip=Currently does not work for multiple inputs at once pdfToPDFA.tip=Currently does not work for multiple inputs at once
pdfToPDFA.outputFormat=Output format pdfToPDFA.outputFormat=Output format
pdfToPDFA.pdfWithDigitalSignature=The PDF contains a digital signature. This will be removed in the next step.
#PDFToWord #PDFToWord

View File

@ -59,8 +59,8 @@ deleteCurrentUserMessage=Не може да се изтрие вписания
deleteUsernameExistsMessage=Потребителското име не съществува и не може да бъде изтрито. deleteUsernameExistsMessage=Потребителското име не съществува и не може да бъде изтрито.
downgradeCurrentUserMessage=Не може да се понижи ролята на текущия потребител downgradeCurrentUserMessage=Не може да се понижи ролята на текущия потребител
downgradeCurrentUserLongMessage=Не може да се понижи ролята на текущия потребител. Следователно текущият потребител няма да бъде показан. downgradeCurrentUserLongMessage=Не може да се понижи ролята на текущия потребител. Следователно текущият потребител няма да бъде показан.
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user. userAlreadyExistsOAuthMessage=Потребителят вече съществува като OAuth2 потребител.
userAlreadyExistsWebMessage=The user already exists as an web user. userAlreadyExistsWebMessage=Потребителят вече съществува като уеб-потребител.
error=Грешка error=Грешка
oops=Опаа! oops=Опаа!
help=Помощ help=Помощ
@ -105,18 +105,18 @@ pipelineOptions.validateButton=Валидирай
############# #############
# NAVBAR # # NAVBAR #
############# #############
navbar.favorite=Favorites navbar.favorite=Любими
navbar.darkmode=Тъмна тема navbar.darkmode=Тъмна тема
navbar.language=Languages navbar.language=Езици
navbar.settings=Настройки navbar.settings=Настройки
navbar.allTools=Tools navbar.allTools=Инструменти
navbar.multiTool=Multi Tools navbar.multiTool=Мулти инструменти
navbar.sections.organize=Organize navbar.sections.organize=Организирайте
navbar.sections.convertTo=Convert to PDF navbar.sections.convertTo=Преобразуване в PDF
navbar.sections.convertFrom=Convert from PDF navbar.sections.convertFrom=Преобразуване от PDF
navbar.sections.security=Sign & Security navbar.sections.security=Подписване и сигурност
navbar.sections.advance=Advanced navbar.sections.advance=Разширено
navbar.sections.edit=View & Edit navbar.sections.edit=Преглед и редактиране
############# #############
# SETTINGS # # SETTINGS #
@ -185,7 +185,7 @@ adminUserSettings.internalApiUser=Вътрешен API потребител
adminUserSettings.forceChange=Принудете потребителя да промени потребителското име/парола при влизане adminUserSettings.forceChange=Принудете потребителя да промени потребителското име/парола при влизане
adminUserSettings.submit=Съхранете потребителя adminUserSettings.submit=Съхранете потребителя
adminUserSettings.changeUserRole=Промяна на ролята на потребителя adminUserSettings.changeUserRole=Промяна на ролята на потребителя
adminUserSettings.authenticated=Authenticated adminUserSettings.authenticated=Удостоверен
############# #############
# HOME-PAGE # # HOME-PAGE #
@ -267,7 +267,7 @@ home.fileToPDF.desc=Преобразуване почти всеки файл к
fileToPDF.tags=трансформация,формат,документ,изображение,слайд,текст,преобразуване,офис,документи,word,excel,powerpoint fileToPDF.tags=трансформация,формат,документ,изображение,слайд,текст,преобразуване,офис,документи,word,excel,powerpoint
home.ocr.title=OCR / Почистващи сканирания home.ocr.title=OCR / Почистващи сканирания
home.ocr.desc=Почистване, сканира и открива текст от изображения към PDF и го добавя отново като текст. home.ocr.desc=Почиства, сканира и открива текст от изображения в PDF и го добавя отново като текст.
ocr.tags=разпознаване,текст,изображение,сканиране,четене,идентифициране,откриване,редактиране ocr.tags=разпознаване,текст,изображение,сканиране,четене,идентифициране,откриване,редактиране
@ -313,7 +313,7 @@ home.flatten.desc=Премахнете всички интерактивни е
flatten.tags=статичен,деактивиран,неинтерактивен,рационализиран flatten.tags=статичен,деактивиран,неинтерактивен,рационализиран
home.repair.title=Поправи home.repair.title=Поправи
home.repair.desc=Опитва се да поправи повреден/счупен PDF home.repair.desc=Опитва се да поправи повреден PDF
repair.tags=поправка,възстановяване,корекция,възстановяване repair.tags=поправка,възстановяване,корекция,възстановяване
home.removeBlanks.title=Премахване на празни страници home.removeBlanks.title=Премахване на празни страници
@ -332,6 +332,10 @@ home.certSign.title=Подпишете със сертификат
home.certSign.desc=Подписва PDF със сертификат/ключ (PEM/P12) home.certSign.desc=Подписва PDF със сертификат/ключ (PEM/P12)
certSign.tags=удостоверяване,PEM,P12,официален,шифроване certSign.tags=удостоверяване,PEM,P12,официален,шифроване
home.removeCertSign.title=Remove Certificate Sign
home.removeCertSign.desc=Remove certificate signature from PDF
removeCertSign.tags=authenticate,PEM,P12,official,decrypt
home.pageLayout.title=Оформление с няколко страници home.pageLayout.title=Оформление с няколко страници
home.pageLayout.desc=Слейте няколко страници от PDF документ в една страница home.pageLayout.desc=Слейте няколко страници от PDF документ в една страница
pageLayout.tags=сливане,комбиниран,единичен изглед,организиране pageLayout.tags=сливане,комбиниран,единичен изглед,организиране
@ -364,7 +368,7 @@ home.autoSplitPDF.title=Автоматично разделяне на стра
home.autoSplitPDF.desc=Автоматично разделяне на сканиран PDF файл с QR код за разделяне на физически сканирани страници home.autoSplitPDF.desc=Автоматично разделяне на сканиран PDF файл с QR код за разделяне на физически сканирани страници
autoSplitPDF.tags=QR-базиран,отделен,сканиране-сегмент,организиране autoSplitPDF.tags=QR-базиран,отделен,сканиране-сегмент,организиране
home.sanitizePdf.title=Дезинфенкцирам home.sanitizePdf.title=Обеззаразяване
home.sanitizePdf.desc=Премахване на скриптове и други елементи от PDF файлове home.sanitizePdf.desc=Премахване на скриптове и други елементи от PDF файлове
sanitizePdf.tags=чисти,сигурни,безопасни,премахване-заплахи sanitizePdf.tags=чисти,сигурни,безопасни,премахване-заплахи
@ -382,8 +386,8 @@ home.MarkdownToPDF.desc=Преобразува всеки Markdown файл къ
MarkdownToPDF.tags=маркиране,уеб-съдържание,трансформация,преобразуване MarkdownToPDF.tags=маркиране,уеб-съдържание,трансформация,преобразуване
home.getPdfInfo.title=Вземете ЦЯЛАТА информация към PDF home.getPdfInfo.title=Вземете ЦЯЛАТА информация от PDF
home.getPdfInfo.desc=Взема всяка възможна информация от PDF файлове home.getPdfInfo.desc=Взима всяка възможна информация от PDF файлове
getPdfInfo.tags=информация,данни,статистики,статистика getPdfInfo.tags=информация,данни,статистики,статистика
@ -405,7 +409,7 @@ home.autoRedact.title=Автоматично редактиране
home.autoRedact.desc=Автоматично редактира (зачернява) текст в PDF въз основа на въведен текст home.autoRedact.desc=Автоматично редактира (зачернява) текст в PDF въз основа на въведен текст
autoRedact.tags=Редактиране,Скриване,затъмняване,черен,маркер,скрит autoRedact.tags=Редактиране,Скриване,затъмняване,черен,маркер,скрит
home.tableExtraxt.title=PDF to CSV home.tableExtraxt.title=PDF в CSV
home.tableExtraxt.desc=Извлича таблици от PDF, като ги конвертира в CSV home.tableExtraxt.desc=Извлича таблици от PDF, като ги конвертира в CSV
tableExtraxt.tags=CSV,извличане на таблица,извличане,конвертиране tableExtraxt.tags=CSV,извличане на таблица,извличане,конвертиране
@ -452,11 +456,12 @@ login.locked=Вашият акаунт е заключен.
login.signinTitle=Моля впишете се login.signinTitle=Моля впишете се
login.ssoSignIn=Влизане чрез еднократно влизане login.ssoSignIn=Влизане чрез еднократно влизане
login.oauth2AutoCreateDisabled=OAUTH2 Автоматично създаване на потребител е деактивирано login.oauth2AutoCreateDisabled=OAUTH2 Автоматично създаване на потребител е деактивирано
login.oauth2RequestNotFound=Authorization request not found login.oauth2RequestNotFound=Заявката за оторизация не е намерена
login.oauth2InvalidUserInfoResponse=Invalid User Info Response login.oauth2InvalidUserInfoResponse=Невалидна информация за потребителя
login.oauth2invalidRequest=Invalid Request login.oauth2invalidRequest=Невалидна заявка
login.oauth2AccessDenied=Access Denied login.oauth2AccessDenied=Отказан достъп
login.oauth2InvalidTokenResponse=Invalid Token Response login.oauth2InvalidTokenResponse=Невалиден отговор на токена
login.oauth2InvalidIdToken=Невалиден токен за идентификатор
#auto-redact #auto-redact
@ -637,7 +642,7 @@ scalePages.submit=Подайте
#certSign #certSign
certSign.title=Подписване на сертификат certSign.title=Подписване със сертификат
certSign.header=Подпишете PDF с вашия сертификат (В процес на работа) certSign.header=Подпишете PDF с вашия сертификат (В процес на работа)
certSign.selectPDF=Изберете PDF файл за подписване: certSign.selectPDF=Изберете PDF файл за подписване:
certSign.jksNote=Забележка: Ако вашият тип сертификат не е в списъка по-долу, моля, конвертирайте го във файл на Java Keystore (.jks) с помощта на инструмента за команден ред keytool. След това изберете опцията за .jks файл по-долу. certSign.jksNote=Забележка: Ако вашият тип сертификат не е в списъка по-долу, моля, конвертирайте го във файл на Java Keystore (.jks) с помощта на инструмента за команден ред keytool. След това изберете опцията за .jks файл по-долу.
@ -654,6 +659,13 @@ certSign.name=Име
certSign.submit=Подпишете PDF certSign.submit=Подпишете PDF
#removeCertSign
removeCertSign.title=Remove Certificate Signature
removeCertSign.header=Remove the digital certificate from the PDF
removeCertSign.selectPDF=Select a PDF file:
removeCertSign.submit=Remove Signature
#removeBlanks #removeBlanks
removeBlanks.title=Премахване на празни места removeBlanks.title=Премахване на празни места
removeBlanks.header=Премахване на празни страници removeBlanks.header=Премахване на празни страници
@ -757,7 +769,7 @@ extractImages.submit=Извличане
fileToPDF.title=Файл към PDF fileToPDF.title=Файл към PDF
fileToPDF.header=Конвертирайте всеки файл към PDF fileToPDF.header=Конвертирайте всеки файл към PDF
fileToPDF.credit=Тази услуга използва LibreOffice и Unoconv за преобразуване на файлове. fileToPDF.credit=Тази услуга използва LibreOffice и Unoconv за преобразуване на файлове.
fileToPDF.supportedFileTypesInfo=Supported File types fileToPDF.supportedFileTypesInfo=Поддържание файлови типове
fileToPDF.supportedFileTypes=Поддържаните типове файлове трябва да включват по-долу, но за пълен актуализиран списък на поддържаните формати, моля, вижте документацията на LibreOffice fileToPDF.supportedFileTypes=Поддържаните типове файлове трябва да включват по-долу, но за пълен актуализиран списък на поддържаните формати, моля, вижте документацията на LibreOffice
fileToPDF.submit=Преобразуване към PDF fileToPDF.submit=Преобразуване към PDF
@ -766,10 +778,10 @@ fileToPDF.submit=Преобразуване към PDF
compress.title=Компресиране compress.title=Компресиране
compress.header=Компресиране на PDF compress.header=Компресиране на PDF
compress.credit=Тази услуга използва Ghostscript за PDF компресиране/оптимизиране. compress.credit=Тази услуга използва Ghostscript за PDF компресиране/оптимизиране.
compress.selectText.1=Ръчен режим - От 1 до 4 compress.selectText.1=Ръчен режим - от 1 до 4
compress.selectText.2=Ниво на оптимизация: compress.selectText.2=Ниво на оптимизация:
compress.selectText.3=4 (Ужасно за текстови изображения) compress.selectText.3=4 (Ужасно за текстови изображения)
compress.selectText.4=Автоматичен режим - Автоматично настройва качеството, за да получи PDF точен размер compress.selectText.4=Автоматичен режим - Автоматично настройва качеството, за да получи PDF с точен размер
compress.selectText.5=Очакван PDF размер (напр. 25МБ, 10.8МБ, 25КБ) compress.selectText.5=Очакван PDF размер (напр. 25МБ, 10.8МБ, 25КБ)
compress.submit=Компресиране compress.submit=Компресиране
@ -810,7 +822,7 @@ pdfOrganiser.placeholder=(напр. 1,3,2 или 4-8,2,10-12 или 2n-1)
#multiTool #multiTool
multiTool.title=PDF Мулти инструмент multiTool.title=PDF Мулти инструмент
multiTool.header=PDF Мулти инструмент multiTool.header=PDF Мулти инструмент
multiTool.uploadPrompts=File Name multiTool.uploadPrompts=Име на файл
#view pdf #view pdf
viewPdf.title=Преглед на PDF viewPdf.title=Преглед на PDF
@ -909,8 +921,8 @@ watermark.selectText.7=Непрозрачност (0% - 100%):
watermark.selectText.8=Тип воден знак: watermark.selectText.8=Тип воден знак:
watermark.selectText.9=Изображение за воден знак: watermark.selectText.9=Изображение за воден знак:
watermark.submit=Добавяне на воден знак watermark.submit=Добавяне на воден знак
watermark.type.1=Text watermark.type.1=Текст
watermark.type.2=Image watermark.type.2=Изображение
#Change permissions #Change permissions
@ -964,6 +976,7 @@ pdfToPDFA.credit=Тази услуга използва OCRmyPDF за PDF/A пр
pdfToPDFA.submit=Преобразуване pdfToPDFA.submit=Преобразуване
pdfToPDFA.tip=В момента не работи за няколко входа наведнъж pdfToPDFA.tip=В момента не работи за няколко входа наведнъж
pdfToPDFA.outputFormat=Изходен формат pdfToPDFA.outputFormat=Изходен формат
pdfToPDFA.pdfWithDigitalSignature=The PDF contains a digital signature. This will be removed in the next step.
#PDFToWord #PDFToWord

View File

@ -332,6 +332,10 @@ home.certSign.title=Signa amb Certificat
home.certSign.desc=Sign PDF amb Certificate/Clau (PEM/P12) home.certSign.desc=Sign PDF amb Certificate/Clau (PEM/P12)
certSign.tags=authentica,PEM,P12,official,encripta certSign.tags=authentica,PEM,P12,official,encripta
home.removeCertSign.title=Remove Certificate Sign
home.removeCertSign.desc=Remove certificate signature from PDF
removeCertSign.tags=authenticate,PEM,P12,official,decrypt
home.pageLayout.title=Multi-Page Layout home.pageLayout.title=Multi-Page Layout
home.pageLayout.desc=Merge multiple pages of a PDF document into a single page home.pageLayout.desc=Merge multiple pages of a PDF document into a single page
pageLayout.tags=merge,composite,single-view,organize pageLayout.tags=merge,composite,single-view,organize
@ -457,6 +461,7 @@ login.oauth2InvalidUserInfoResponse=Invalid User Info Response
login.oauth2invalidRequest=Invalid Request login.oauth2invalidRequest=Invalid Request
login.oauth2AccessDenied=Access Denied login.oauth2AccessDenied=Access Denied
login.oauth2InvalidTokenResponse=Invalid Token Response login.oauth2InvalidTokenResponse=Invalid Token Response
login.oauth2InvalidIdToken=Invalid Id Token
#auto-redact #auto-redact
@ -654,6 +659,13 @@ certSign.name=Nom
certSign.submit=Firma PDF certSign.submit=Firma PDF
#removeCertSign
removeCertSign.title=Remove Certificate Signature
removeCertSign.header=Remove the digital certificate from the PDF
removeCertSign.selectPDF=Select a PDF file:
removeCertSign.submit=Remove Signature
#removeBlanks #removeBlanks
removeBlanks.title=Elimina els espais en blanc removeBlanks.title=Elimina els espais en blanc
removeBlanks.header=Elimina les pàgines en blanc removeBlanks.header=Elimina les pàgines en blanc
@ -964,6 +976,7 @@ pdfToPDFA.credit=Utilitza OCRmyPDF per la conversió a PDF/A
pdfToPDFA.submit=Converteix pdfToPDFA.submit=Converteix
pdfToPDFA.tip=Currently does not work for multiple inputs at once pdfToPDFA.tip=Currently does not work for multiple inputs at once
pdfToPDFA.outputFormat=Output format pdfToPDFA.outputFormat=Output format
pdfToPDFA.pdfWithDigitalSignature=The PDF contains a digital signature. This will be removed in the next step.
#PDFToWord #PDFToWord

View File

@ -26,7 +26,7 @@ bored=Nudíte se při čekání?
alphabet=Abeceda alphabet=Abeceda
downloadPdf=Stáhnout PDF downloadPdf=Stáhnout PDF
text=Text text=Text
font=Font font=Písmo
selectFillter=-- Vyberte -- selectFillter=-- Vyberte --
pageNum=Číslo stránky pageNum=Číslo stránky
sizes.small=Malé sizes.small=Malé
@ -332,6 +332,10 @@ home.certSign.title=Podpis s certifikátem
home.certSign.desc=Podpis PDF s certifikátem/klíčem (PEM/P12) home.certSign.desc=Podpis PDF s certifikátem/klíčem (PEM/P12)
certSign.tags=autentizace,PEM,P12,oficiální,šifrování certSign.tags=autentizace,PEM,P12,oficiální,šifrování
home.removeCertSign.title=Remove Certificate Sign
home.removeCertSign.desc=Remove certificate signature from PDF
removeCertSign.tags=authenticate,PEM,P12,official,decrypt
home.pageLayout.title=Vícestránkové rozložení home.pageLayout.title=Vícestránkové rozložení
home.pageLayout.desc=Sloučení více stránek dokumentu PDF do jedné stránky home.pageLayout.desc=Sloučení více stránek dokumentu PDF do jedné stránky
pageLayout.tags=sloučit,kompozitní,jedno zobrazení,organizovat pageLayout.tags=sloučit,kompozitní,jedno zobrazení,organizovat
@ -457,6 +461,7 @@ login.oauth2InvalidUserInfoResponse=Invalid User Info Response
login.oauth2invalidRequest=Invalid Request login.oauth2invalidRequest=Invalid Request
login.oauth2AccessDenied=Access Denied login.oauth2AccessDenied=Access Denied
login.oauth2InvalidTokenResponse=Invalid Token Response login.oauth2InvalidTokenResponse=Invalid Token Response
login.oauth2InvalidIdToken=Invalid Id Token
#auto-redact #auto-redact
@ -654,6 +659,13 @@ certSign.name=Název
certSign.submit=Podepsat PDF certSign.submit=Podepsat PDF
#removeCertSign
removeCertSign.title=Remove Certificate Signature
removeCertSign.header=Remove the digital certificate from the PDF
removeCertSign.selectPDF=Select a PDF file:
removeCertSign.submit=Remove Signature
#removeBlanks #removeBlanks
removeBlanks.title=Odebrat prázdné stránky removeBlanks.title=Odebrat prázdné stránky
removeBlanks.header=Odebrat prázdné stránky removeBlanks.header=Odebrat prázdné stránky
@ -964,6 +976,7 @@ pdfToPDFA.credit=Tato služba používá OCRmyPDF pro konverzi do formátu PDF/A
pdfToPDFA.submit=Převést pdfToPDFA.submit=Převést
pdfToPDFA.tip=V současné době nepracuje pro více vstupů najednou pdfToPDFA.tip=V současné době nepracuje pro více vstupů najednou
pdfToPDFA.outputFormat=Výstupní formát pdfToPDFA.outputFormat=Výstupní formát
pdfToPDFA.pdfWithDigitalSignature=The PDF contains a digital signature. This will be removed in the next step.
#PDFToWord #PDFToWord

View File

@ -71,7 +71,7 @@ visitGithub=GitHub-Repository besuchen
donate=Spenden donate=Spenden
color=Farbe color=Farbe
sponsor=Sponsor sponsor=Sponsor
info=Die Info info=Informationen
@ -332,6 +332,10 @@ home.certSign.title=Mit Zertifikat signieren
home.certSign.desc=Ein PDF mit einem Zertifikat/Schlüssel (PEM/P12) signieren home.certSign.desc=Ein PDF mit einem Zertifikat/Schlüssel (PEM/P12) signieren
certSign.tags=authentifizieren,pem,p12,offiziell,verschlüsseln certSign.tags=authentifizieren,pem,p12,offiziell,verschlüsseln
home.removeCertSign.title=Remove Certificate Sign
home.removeCertSign.desc=Remove certificate signature from PDF
removeCertSign.tags=authenticate,PEM,P12,official,decrypt
home.pageLayout.title=Mehrseitiges Layout home.pageLayout.title=Mehrseitiges Layout
home.pageLayout.desc=Mehrere Seiten eines PDF zu einer Seite zusammenführen home.pageLayout.desc=Mehrere Seiten eines PDF zu einer Seite zusammenführen
pageLayout.tags=zusammenführen,zusammensetzen,einzelansicht,organisieren pageLayout.tags=zusammenführen,zusammensetzen,einzelansicht,organisieren
@ -457,6 +461,7 @@ login.oauth2InvalidUserInfoResponse=Ungültige Benutzerinformationsantwort
login.oauth2invalidRequest=ungültige Anfrage login.oauth2invalidRequest=ungültige Anfrage
login.oauth2AccessDenied=Zugriff abgelehnt login.oauth2AccessDenied=Zugriff abgelehnt
login.oauth2InvalidTokenResponse=Ungültige Token-Antwort login.oauth2InvalidTokenResponse=Ungültige Token-Antwort
login.oauth2InvalidIdToken=Ungültiges ID-Token
#auto-redact #auto-redact
@ -654,6 +659,13 @@ certSign.name=Name
certSign.submit=PDF signieren certSign.submit=PDF signieren
#removeCertSign
removeCertSign.title=Zertifikatsignatur entfernen
removeCertSign.header=Digitales Zertifikat aus dem PDF entfernen
removeCertSign.selectPDF=PDF-Datei auswählen:
removeCertSign.submit=Signatur entfernen
#removeBlanks #removeBlanks
removeBlanks.title=Leere Seiten entfernen removeBlanks.title=Leere Seiten entfernen
removeBlanks.header=Leere Seiten entfernen removeBlanks.header=Leere Seiten entfernen
@ -964,6 +976,7 @@ pdfToPDFA.credit=Dieser Dienst verwendet OCRmyPDF für die PDF/A-Konvertierung
pdfToPDFA.submit=Konvertieren pdfToPDFA.submit=Konvertieren
pdfToPDFA.tip=Dieser Dienst kann nur einzelne Eingangsdateien verarbeiten. pdfToPDFA.tip=Dieser Dienst kann nur einzelne Eingangsdateien verarbeiten.
pdfToPDFA.outputFormat=Ausgabeformat pdfToPDFA.outputFormat=Ausgabeformat
pdfToPDFA.pdfWithDigitalSignature=Das PDF enthält eine digitale Signatur. Sie wird im nächsten Schritt entfernt.
#PDFToWord #PDFToWord

View File

@ -332,6 +332,10 @@ home.certSign.title=Υπογραφή με Πιστοποιητικό
home.certSign.desc=Υπογραφή ενός PDF αρχείου με ένα Πιστοποιητικό/Κλειδί (PEM/P12) home.certSign.desc=Υπογραφή ενός PDF αρχείου με ένα Πιστοποιητικό/Κλειδί (PEM/P12)
certSign.tags=authenticate,PEM,P12,official,encrypt certSign.tags=authenticate,PEM,P12,official,encrypt
home.removeCertSign.title=Remove Certificate Sign
home.removeCertSign.desc=Remove certificate signature from PDF
removeCertSign.tags=authenticate,PEM,P12,official,decrypt
home.pageLayout.title=Διάταξη πολλών σελίδων home.pageLayout.title=Διάταξη πολλών σελίδων
home.pageLayout.desc=Συγχώνευση πολλαπλών σελίδων ενός εγγράφου PDF σε μία μόνο σελίδα home.pageLayout.desc=Συγχώνευση πολλαπλών σελίδων ενός εγγράφου PDF σε μία μόνο σελίδα
pageLayout.tags=merge,composite,single-view,organize pageLayout.tags=merge,composite,single-view,organize
@ -457,6 +461,7 @@ login.oauth2InvalidUserInfoResponse=Invalid User Info Response
login.oauth2invalidRequest=Invalid Request login.oauth2invalidRequest=Invalid Request
login.oauth2AccessDenied=Access Denied login.oauth2AccessDenied=Access Denied
login.oauth2InvalidTokenResponse=Invalid Token Response login.oauth2InvalidTokenResponse=Invalid Token Response
login.oauth2InvalidIdToken=Invalid Id Token
#auto-redact #auto-redact
@ -654,6 +659,13 @@ certSign.name=Όνομα
certSign.submit=Υπογραφή PDF certSign.submit=Υπογραφή PDF
#removeCertSign
removeCertSign.title=Remove Certificate Signature
removeCertSign.header=Remove the digital certificate from the PDF
removeCertSign.selectPDF=Select a PDF file:
removeCertSign.submit=Remove Signature
#removeBlanks #removeBlanks
removeBlanks.title=Αφαίρεση Κενών removeBlanks.title=Αφαίρεση Κενών
removeBlanks.header=Αφαίρεση Κενών Σελίδων removeBlanks.header=Αφαίρεση Κενών Σελίδων
@ -964,6 +976,7 @@ pdfToPDFA.credit=Αυτή η υπηρεσία χρησιμοποιεί OCRmyPDF
pdfToPDFA.submit=Μετατροπή pdfToPDFA.submit=Μετατροπή
pdfToPDFA.tip=Προς το παρόν δεν λειτουργεί για πολλαπλές εισόδους ταυτόχρονα pdfToPDFA.tip=Προς το παρόν δεν λειτουργεί για πολλαπλές εισόδους ταυτόχρονα
pdfToPDFA.outputFormat=Output format pdfToPDFA.outputFormat=Output format
pdfToPDFA.pdfWithDigitalSignature=The PDF contains a digital signature. This will be removed in the next step.
#PDFToWord #PDFToWord

View File

@ -332,6 +332,10 @@ home.certSign.title=Sign with Certificate
home.certSign.desc=Signs a PDF with a Certificate/Key (PEM/P12) home.certSign.desc=Signs a PDF with a Certificate/Key (PEM/P12)
certSign.tags=authenticate,PEM,P12,official,encrypt certSign.tags=authenticate,PEM,P12,official,encrypt
home.removeCertSign.title=Remove Certificate Sign
home.removeCertSign.desc=Remove certificate signature from PDF
removeCertSign.tags=authenticate,PEM,P12,official,decrypt
home.pageLayout.title=Multi-Page Layout home.pageLayout.title=Multi-Page Layout
home.pageLayout.desc=Merge multiple pages of a PDF document into a single page home.pageLayout.desc=Merge multiple pages of a PDF document into a single page
pageLayout.tags=merge,composite,single-view,organize pageLayout.tags=merge,composite,single-view,organize
@ -457,6 +461,7 @@ login.oauth2InvalidUserInfoResponse=Invalid User Info Response
login.oauth2invalidRequest=Invalid Request login.oauth2invalidRequest=Invalid Request
login.oauth2AccessDenied=Access Denied login.oauth2AccessDenied=Access Denied
login.oauth2InvalidTokenResponse=Invalid Token Response login.oauth2InvalidTokenResponse=Invalid Token Response
login.oauth2InvalidIdToken=Invalid Id Token
#auto-redact #auto-redact
@ -654,6 +659,13 @@ certSign.name=Name
certSign.submit=Sign PDF certSign.submit=Sign PDF
#removeCertSign
removeCertSign.title=Remove Certificate Signature
removeCertSign.header=Remove the digital certificate from the PDF
removeCertSign.selectPDF=Select a PDF file:
removeCertSign.submit=Remove Signature
#removeBlanks #removeBlanks
removeBlanks.title=Remove Blanks removeBlanks.title=Remove Blanks
removeBlanks.header=Remove Blank Pages removeBlanks.header=Remove Blank Pages
@ -964,6 +976,7 @@ pdfToPDFA.credit=This service uses OCRmyPDF for PDF/A conversion
pdfToPDFA.submit=Convert pdfToPDFA.submit=Convert
pdfToPDFA.tip=Currently does not work for multiple inputs at once pdfToPDFA.tip=Currently does not work for multiple inputs at once
pdfToPDFA.outputFormat=Output format pdfToPDFA.outputFormat=Output format
pdfToPDFA.pdfWithDigitalSignature=The PDF contains a digital signature. This will be removed in the next step.
#PDFToWord #PDFToWord

View File

@ -332,6 +332,10 @@ home.certSign.title=Sign with Certificate
home.certSign.desc=Signs a PDF with a Certificate/Key (PEM/P12) home.certSign.desc=Signs a PDF with a Certificate/Key (PEM/P12)
certSign.tags=authenticate,PEM,P12,official,encrypt certSign.tags=authenticate,PEM,P12,official,encrypt
home.removeCertSign.title=Remove Certificate Sign
home.removeCertSign.desc=Remove certificate signature from PDF
removeCertSign.tags=authenticate,PEM,P12,official,decrypt
home.pageLayout.title=Multi-Page Layout home.pageLayout.title=Multi-Page Layout
home.pageLayout.desc=Merge multiple pages of a PDF document into a single page home.pageLayout.desc=Merge multiple pages of a PDF document into a single page
pageLayout.tags=merge,composite,single-view,organize pageLayout.tags=merge,composite,single-view,organize
@ -457,6 +461,7 @@ login.oauth2InvalidUserInfoResponse=Invalid User Info Response
login.oauth2invalidRequest=Invalid Request login.oauth2invalidRequest=Invalid Request
login.oauth2AccessDenied=Access Denied login.oauth2AccessDenied=Access Denied
login.oauth2InvalidTokenResponse=Invalid Token Response login.oauth2InvalidTokenResponse=Invalid Token Response
login.oauth2InvalidIdToken=Invalid Id Token
#auto-redact #auto-redact
@ -654,6 +659,13 @@ certSign.name=Name
certSign.submit=Sign PDF certSign.submit=Sign PDF
#removeCertSign
removeCertSign.title=Remove Certificate Signature
removeCertSign.header=Remove the digital certificate from the PDF
removeCertSign.selectPDF=Select a PDF file:
removeCertSign.submit=Remove Signature
#removeBlanks #removeBlanks
removeBlanks.title=Remove Blanks removeBlanks.title=Remove Blanks
removeBlanks.header=Remove Blank Pages removeBlanks.header=Remove Blank Pages
@ -964,6 +976,7 @@ pdfToPDFA.credit=This service uses OCRmyPDF for PDF/A conversion
pdfToPDFA.submit=Convert pdfToPDFA.submit=Convert
pdfToPDFA.tip=Currently does not work for multiple inputs at once pdfToPDFA.tip=Currently does not work for multiple inputs at once
pdfToPDFA.outputFormat=Output format pdfToPDFA.outputFormat=Output format
pdfToPDFA.pdfWithDigitalSignature=The PDF contains a digital signature. This will be removed in the next step.
#PDFToWord #PDFToWord

View File

@ -332,6 +332,10 @@ home.certSign.title=Firmar con certificado
home.certSign.desc=Firmar un PDF con un Certificado/Clave (PEM/P12) home.certSign.desc=Firmar un PDF con un Certificado/Clave (PEM/P12)
certSign.tags=autentificar,PEM,P12,oficial,encriptar certSign.tags=autentificar,PEM,P12,oficial,encriptar
home.removeCertSign.title=Remove Certificate Sign
home.removeCertSign.desc=Remove certificate signature from PDF
removeCertSign.tags=authenticate,PEM,P12,official,decrypt
home.pageLayout.title=Diseño de varias páginas home.pageLayout.title=Diseño de varias páginas
home.pageLayout.desc=Unir varias páginas de un documento PDF en una sola página home.pageLayout.desc=Unir varias páginas de un documento PDF en una sola página
pageLayout.tags=unir,compuesto,vista única,organizar pageLayout.tags=unir,compuesto,vista única,organizar
@ -457,6 +461,7 @@ login.oauth2InvalidUserInfoResponse=Invalid User Info Response
login.oauth2invalidRequest=Invalid Request login.oauth2invalidRequest=Invalid Request
login.oauth2AccessDenied=Access Denied login.oauth2AccessDenied=Access Denied
login.oauth2InvalidTokenResponse=Invalid Token Response login.oauth2InvalidTokenResponse=Invalid Token Response
login.oauth2InvalidIdToken=Invalid Id Token
#auto-redact #auto-redact
@ -654,6 +659,13 @@ certSign.name=Nombre
certSign.submit=Firmar PDF certSign.submit=Firmar PDF
#removeCertSign
removeCertSign.title=Remove Certificate Signature
removeCertSign.header=Remove the digital certificate from the PDF
removeCertSign.selectPDF=Select a PDF file:
removeCertSign.submit=Remove Signature
#removeBlanks #removeBlanks
removeBlanks.title=Eliminar espacios en blanco removeBlanks.title=Eliminar espacios en blanco
removeBlanks.header=Eliminar páginas en blanco removeBlanks.header=Eliminar páginas en blanco
@ -964,6 +976,7 @@ pdfToPDFA.credit=Este servicio usa OCRmyPDF para la conversión a PDF/A
pdfToPDFA.submit=Convertir pdfToPDFA.submit=Convertir
pdfToPDFA.tip=Actualmente no funciona para múltiples entrada a la vez pdfToPDFA.tip=Actualmente no funciona para múltiples entrada a la vez
pdfToPDFA.outputFormat=Output format pdfToPDFA.outputFormat=Output format
pdfToPDFA.pdfWithDigitalSignature=The PDF contains a digital signature. This will be removed in the next step.
#PDFToWord #PDFToWord

View File

@ -332,6 +332,10 @@ home.certSign.title=Sinatu ziurtagiriarekin
home.certSign.desc=Sinatu PDF bat Ziurtagiri/Gako batekin (PEM/P12) home.certSign.desc=Sinatu PDF bat Ziurtagiri/Gako batekin (PEM/P12)
certSign.tags=authenticate,PEM,P12,official,encrypt certSign.tags=authenticate,PEM,P12,official,encrypt
home.removeCertSign.title=Remove Certificate Sign
home.removeCertSign.desc=Remove certificate signature from PDF
removeCertSign.tags=authenticate,PEM,P12,official,decrypt
home.pageLayout.title=Zenbait orrialderen diseinua home.pageLayout.title=Zenbait orrialderen diseinua
home.pageLayout.desc=Elkartu orri bakar batean PDF dokumentu baten zenbait orrialde home.pageLayout.desc=Elkartu orri bakar batean PDF dokumentu baten zenbait orrialde
pageLayout.tags=merge,composite,single-view,organize pageLayout.tags=merge,composite,single-view,organize
@ -457,6 +461,7 @@ login.oauth2InvalidUserInfoResponse=Invalid User Info Response
login.oauth2invalidRequest=Invalid Request login.oauth2invalidRequest=Invalid Request
login.oauth2AccessDenied=Access Denied login.oauth2AccessDenied=Access Denied
login.oauth2InvalidTokenResponse=Invalid Token Response login.oauth2InvalidTokenResponse=Invalid Token Response
login.oauth2InvalidIdToken=Invalid Id Token
#auto-redact #auto-redact
@ -654,6 +659,13 @@ certSign.name=Izena
certSign.submit=Sinatu PDFa certSign.submit=Sinatu PDFa
#removeCertSign
removeCertSign.title=Remove Certificate Signature
removeCertSign.header=Remove the digital certificate from the PDF
removeCertSign.selectPDF=Select a PDF file:
removeCertSign.submit=Remove Signature
#removeBlanks #removeBlanks
removeBlanks.title=Ezabatu zuriuneak removeBlanks.title=Ezabatu zuriuneak
removeBlanks.header=Ezabatu orrialde zuriak removeBlanks.header=Ezabatu orrialde zuriak
@ -964,6 +976,7 @@ pdfToPDFA.credit=Zerbitzu honek OCRmyPDF erabiltzen du PDFak PDF/A bihurtzeko
pdfToPDFA.submit=Bihurtu pdfToPDFA.submit=Bihurtu
pdfToPDFA.tip=Currently does not work for multiple inputs at once pdfToPDFA.tip=Currently does not work for multiple inputs at once
pdfToPDFA.outputFormat=Output format pdfToPDFA.outputFormat=Output format
pdfToPDFA.pdfWithDigitalSignature=The PDF contains a digital signature. This will be removed in the next step.
#PDFToWord #PDFToWord

View File

@ -332,6 +332,10 @@ home.certSign.title=Signer avec un certificat
home.certSign.desc=Signez un PDF avec un certificat ou une clé (PEM/P12). home.certSign.desc=Signez un PDF avec un certificat ou une clé (PEM/P12).
certSign.tags=signer,chiffrer,certificat,authenticate,PEM,P12,official,encrypt certSign.tags=signer,chiffrer,certificat,authenticate,PEM,P12,official,encrypt
home.removeCertSign.title=Remove Certificate Sign
home.removeCertSign.desc=Remove certificate signature from PDF
removeCertSign.tags=authenticate,PEM,P12,official,decrypt
home.pageLayout.title=Fusionner des pages home.pageLayout.title=Fusionner des pages
home.pageLayout.desc=Fusionnez plusieurs pages dun PDF en une seule. home.pageLayout.desc=Fusionnez plusieurs pages dun PDF en une seule.
pageLayout.tags=fusionner,merge,composite,single-view,organize pageLayout.tags=fusionner,merge,composite,single-view,organize
@ -457,6 +461,7 @@ login.oauth2InvalidUserInfoResponse=Invalid User Info Response
login.oauth2invalidRequest=Invalid Request login.oauth2invalidRequest=Invalid Request
login.oauth2AccessDenied=Access Denied login.oauth2AccessDenied=Access Denied
login.oauth2InvalidTokenResponse=Invalid Token Response login.oauth2InvalidTokenResponse=Invalid Token Response
login.oauth2InvalidIdToken=Invalid Id Token
#auto-redact #auto-redact
@ -654,6 +659,13 @@ certSign.name=Nom
certSign.submit=Signer certSign.submit=Signer
#removeCertSign
removeCertSign.title=Remove Certificate Signature
removeCertSign.header=Remove the digital certificate from the PDF
removeCertSign.selectPDF=Select a PDF file:
removeCertSign.submit=Remove Signature
#removeBlanks #removeBlanks
removeBlanks.title=Supprimer les pages vierges removeBlanks.title=Supprimer les pages vierges
removeBlanks.header=Supprimer les pages vierges removeBlanks.header=Supprimer les pages vierges
@ -964,6 +976,7 @@ pdfToPDFA.credit=Ce service utilise OCRmyPDF pour la conversion en PDF/A.
pdfToPDFA.submit=Convertir pdfToPDFA.submit=Convertir
pdfToPDFA.tip=Ne fonctionne actuellement pas pour plusieurs entrées à la fois pdfToPDFA.tip=Ne fonctionne actuellement pas pour plusieurs entrées à la fois
pdfToPDFA.outputFormat=Format de sortie pdfToPDFA.outputFormat=Format de sortie
pdfToPDFA.pdfWithDigitalSignature=The PDF contains a digital signature. This will be removed in the next step.
#PDFToWord #PDFToWord

View File

@ -332,6 +332,10 @@ home.certSign.title=प्रमाणपत्र के साथ हस्त
home.certSign.desc=पीडीएफ़ को प्रमाणपत्र/कुंजी (PEM/P12) के साथ हस्ताक्षरित करता है। home.certSign.desc=पीडीएफ़ को प्रमाणपत्र/कुंजी (PEM/P12) के साथ हस्ताक्षरित करता है।
certSign.tags=प्रमाणीकरण, PEM, P12, आधिकारिक, एन्क्रिप्ट certSign.tags=प्रमाणीकरण, PEM, P12, आधिकारिक, एन्क्रिप्ट
home.removeCertSign.title=Remove Certificate Sign
home.removeCertSign.desc=Remove certificate signature from PDF
removeCertSign.tags=authenticate,PEM,P12,official,decrypt
home.pageLayout.title=मल्टी-पेज लेआउट home.pageLayout.title=मल्टी-पेज लेआउट
home.pageLayout.desc=पीडीएफ़ दस्तावेज़ के कई पेजों को एक ही पेज में मर्ज करता है। home.pageLayout.desc=पीडीएफ़ दस्तावेज़ के कई पेजों को एक ही पेज में मर्ज करता है।
pageLayout.tags=मर्ज, संयोजित, एकल दृश्य, संगठित pageLayout.tags=मर्ज, संयोजित, एकल दृश्य, संगठित
@ -457,6 +461,7 @@ login.oauth2InvalidUserInfoResponse=Invalid User Info Response
login.oauth2invalidRequest=Invalid Request login.oauth2invalidRequest=Invalid Request
login.oauth2AccessDenied=Access Denied login.oauth2AccessDenied=Access Denied
login.oauth2InvalidTokenResponse=Invalid Token Response login.oauth2InvalidTokenResponse=Invalid Token Response
login.oauth2InvalidIdToken=Invalid Id Token
#auto-redact #auto-redact
@ -654,6 +659,13 @@ certSign.name=नाम
certSign.submit=पीडीएफ़ पर हस्ताक्षर करें certSign.submit=पीडीएफ़ पर हस्ताक्षर करें
#removeCertSign
removeCertSign.title=Remove Certificate Signature
removeCertSign.header=Remove the digital certificate from the PDF
removeCertSign.selectPDF=Select a PDF file:
removeCertSign.submit=Remove Signature
#removeBlanks #removeBlanks
removeBlanks.title=खाली पेज हटाएं removeBlanks.title=खाली पेज हटाएं
removeBlanks.header=खाली पेज हटाएं removeBlanks.header=खाली पेज हटाएं
@ -964,6 +976,7 @@ pdfToPDFA.credit=इस सेवा में PDF/A परिवर्तन
pdfToPDFA.submit=परिवर्तित करें pdfToPDFA.submit=परिवर्तित करें
pdfToPDFA.tip=Currently does not work for multiple inputs at once pdfToPDFA.tip=Currently does not work for multiple inputs at once
pdfToPDFA.outputFormat=Output format pdfToPDFA.outputFormat=Output format
pdfToPDFA.pdfWithDigitalSignature=The PDF contains a digital signature. This will be removed in the next step.
#PDFToWord #PDFToWord

File diff suppressed because it is too large Load Diff

View File

@ -332,6 +332,10 @@ home.certSign.title=Aláírás Tanúsítvánnyal
home.certSign.desc=PDF aláírása tanúsítvánnyal/kulccsal (PEM/P12) 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 certSign.tags=hitelesítés,PEM,P12,hivatalos,segitít,álca
home.removeCertSign.title=Remove Certificate Sign
home.removeCertSign.desc=Remove certificate signature from PDF
removeCertSign.tags=authenticate,PEM,P12,official,decrypt
home.pageLayout.title=Több oldal elrendezése home.pageLayout.title=Több oldal elrendezése
home.pageLayout.desc=Több oldal egyesítése egy PDF dokumentumban egyetlen oldallá home.pageLayout.desc=Több oldal egyesítése egy PDF dokumentumban egyetlen oldallá
pageLayout.tags=egyesítés,kompozit,egy oldal,megszervez pageLayout.tags=egyesítés,kompozit,egy oldal,megszervez
@ -457,6 +461,7 @@ login.oauth2InvalidUserInfoResponse=Invalid User Info Response
login.oauth2invalidRequest=Invalid Request login.oauth2invalidRequest=Invalid Request
login.oauth2AccessDenied=Access Denied login.oauth2AccessDenied=Access Denied
login.oauth2InvalidTokenResponse=Invalid Token Response login.oauth2InvalidTokenResponse=Invalid Token Response
login.oauth2InvalidIdToken=Invalid Id Token
#auto-redact #auto-redact
@ -654,6 +659,13 @@ certSign.name=Név
certSign.submit=PDF aláírása certSign.submit=PDF aláírása
#removeCertSign
removeCertSign.title=Remove Certificate Signature
removeCertSign.header=Remove the digital certificate from the PDF
removeCertSign.selectPDF=Select a PDF file:
removeCertSign.submit=Remove Signature
#removeBlanks #removeBlanks
removeBlanks.title=Üres oldalak eltávolítása removeBlanks.title=Üres oldalak eltávolítása
removeBlanks.header=Üres oldalak eltávolítása removeBlanks.header=Üres oldalak eltávolítása
@ -964,6 +976,7 @@ pdfToPDFA.credit=Ez a szolgáltatás az OCRmyPDF-t használja a PDF/A konverzió
pdfToPDFA.submit=Konvertálás pdfToPDFA.submit=Konvertálás
pdfToPDFA.tip=Currently does not work for multiple inputs at once pdfToPDFA.tip=Currently does not work for multiple inputs at once
pdfToPDFA.outputFormat=Output format pdfToPDFA.outputFormat=Output format
pdfToPDFA.pdfWithDigitalSignature=The PDF contains a digital signature. This will be removed in the next step.
#PDFToWord #PDFToWord

View File

@ -332,6 +332,10 @@ home.certSign.title=Tanda tangani dengan Sertifikat
home.certSign.desc=Menandatangani PDF dengan Certificate/Key (PEM/P12) home.certSign.desc=Menandatangani PDF dengan Certificate/Key (PEM/P12)
certSign.tags=mengotentikasi, PEM, P12, resmi, mengenkripsi certSign.tags=mengotentikasi, PEM, P12, resmi, mengenkripsi
home.removeCertSign.title=Remove Certificate Sign
home.removeCertSign.desc=Remove certificate signature from PDF
removeCertSign.tags=authenticate,PEM,P12,official,decrypt
home.pageLayout.title=Tata Letak Multi-Halaman home.pageLayout.title=Tata Letak Multi-Halaman
home.pageLayout.desc=Menggabungkan beberapa halaman dokumen PDF menjadi satu halaman home.pageLayout.desc=Menggabungkan beberapa halaman dokumen PDF menjadi satu halaman
pageLayout.tags=menggabungkan, komposit, tampilan tunggal, mengatur pageLayout.tags=menggabungkan, komposit, tampilan tunggal, mengatur
@ -457,6 +461,7 @@ login.oauth2InvalidUserInfoResponse=Invalid User Info Response
login.oauth2invalidRequest=Invalid Request login.oauth2invalidRequest=Invalid Request
login.oauth2AccessDenied=Access Denied login.oauth2AccessDenied=Access Denied
login.oauth2InvalidTokenResponse=Invalid Token Response login.oauth2InvalidTokenResponse=Invalid Token Response
login.oauth2InvalidIdToken=Invalid Id Token
#auto-redact #auto-redact
@ -654,6 +659,13 @@ certSign.name=Nama
certSign.submit=Tanda tangani PDF certSign.submit=Tanda tangani PDF
#removeCertSign
removeCertSign.title=Remove Certificate Signature
removeCertSign.header=Remove the digital certificate from the PDF
removeCertSign.selectPDF=Select a PDF file:
removeCertSign.submit=Remove Signature
#removeBlanks #removeBlanks
removeBlanks.title=Hapus Halaman Kosong removeBlanks.title=Hapus Halaman Kosong
removeBlanks.header=Remove Blank Pages removeBlanks.header=Remove Blank Pages
@ -964,6 +976,7 @@ pdfToPDFA.credit=Layanan ini menggunakan OCRmyPDF untuk konversi PDF/A.
pdfToPDFA.submit=Konversi pdfToPDFA.submit=Konversi
pdfToPDFA.tip=Currently does not work for multiple inputs at once pdfToPDFA.tip=Currently does not work for multiple inputs at once
pdfToPDFA.outputFormat=Output format pdfToPDFA.outputFormat=Output format
pdfToPDFA.pdfWithDigitalSignature=The PDF contains a digital signature. This will be removed in the next step.
#PDFToWord #PDFToWord

View File

@ -332,6 +332,10 @@ home.certSign.title=Firma con certificato
home.certSign.desc=Firma un PDF con un certificato/chiave (PEM/P12) home.certSign.desc=Firma un PDF con un certificato/chiave (PEM/P12)
certSign.tags=autenticare,PEM,P12,ufficiale,crittografare certSign.tags=autenticare,PEM,P12,ufficiale,crittografare
home.removeCertSign.title=Remove Certificate Sign
home.removeCertSign.desc=Remove certificate signature from PDF
removeCertSign.tags=authenticate,PEM,P12,official,decrypt
home.pageLayout.title=Layout multipagina home.pageLayout.title=Layout multipagina
home.pageLayout.desc=Unisci più pagine di un documento PDF in un'unica pagina home.pageLayout.desc=Unisci più pagine di un documento PDF in un'unica pagina
pageLayout.tags=unire,comporre,visualizzazione singola,organizzare pageLayout.tags=unire,comporre,visualizzazione singola,organizzare
@ -417,7 +421,7 @@ autoSizeSplitPDF.tags=pdf,diviso,documento,organizzazione
home.overlay-pdfs.title=Sovrapposizione di PDF home.overlay-pdfs.title=Sovrapposizione di PDF
home.overlay-pdfs.desc=Sovrappone i PDF sopra un altro PDF home.overlay-pdfs.desc=Sovrappone i PDF sopra un altro PDF
overlay-pdfs.tags=Svrapponi overlay-pdfs.tags=Sovrapponi
home.split-by-sections.title=Dividi PDF per sezioni 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 home.split-by-sections.desc=Dividi ciascuna pagina di un PDF in sezioni orizzontali e verticali più piccole
@ -457,6 +461,7 @@ login.oauth2InvalidUserInfoResponse=Risposta relativa alle informazioni utente n
login.oauth2invalidRequest=Richiesta non valida login.oauth2invalidRequest=Richiesta non valida
login.oauth2AccessDenied=Accesso negato login.oauth2AccessDenied=Accesso negato
login.oauth2InvalidTokenResponse=Risposta token non valida login.oauth2InvalidTokenResponse=Risposta token non valida
login.oauth2InvalidIdToken=Id Token non valido
#auto-redact #auto-redact
@ -654,6 +659,13 @@ certSign.name=Nome
certSign.submit=Firma PDF certSign.submit=Firma PDF
#removeCertSign
removeCertSign.title=Rimuovi certificato della firma
removeCertSign.header=Rimuovere il certificato digitale dal PDF
removeCertSign.selectPDF=Seleziona un file PDF:
removeCertSign.submit=Rimuovi firma
#removeBlanks #removeBlanks
removeBlanks.title=Rimuovi spazi vuoti removeBlanks.title=Rimuovi spazi vuoti
removeBlanks.header=Rimuovi pagine vuote removeBlanks.header=Rimuovi pagine vuote
@ -964,6 +976,7 @@ pdfToPDFA.credit=Questo servizio utilizza OCRmyPDF per la conversione in PDF/A.
pdfToPDFA.submit=Converti pdfToPDFA.submit=Converti
pdfToPDFA.tip=Attualmente non funziona per più input contemporaneamente pdfToPDFA.tip=Attualmente non funziona per più input contemporaneamente
pdfToPDFA.outputFormat=Formato di output pdfToPDFA.outputFormat=Formato di output
pdfToPDFA.pdfWithDigitalSignature=The PDF contains a digital signature. This will be removed in the next step.
#PDFToWord #PDFToWord

View File

@ -332,6 +332,10 @@ home.certSign.title=証明書による署名
home.certSign.desc=証明書/キーを使用してPDFに署名します。 (PEM/P12) home.certSign.desc=証明書/キーを使用してPDFに署名します。 (PEM/P12)
certSign.tags=authenticate,PEM,P12,official,encrypt certSign.tags=authenticate,PEM,P12,official,encrypt
home.removeCertSign.title=Remove Certificate Sign
home.removeCertSign.desc=Remove certificate signature from PDF
removeCertSign.tags=authenticate,PEM,P12,official,decrypt
home.pageLayout.title=マルチページレイアウト home.pageLayout.title=マルチページレイアウト
home.pageLayout.desc=PDFの複数のページを1ページに結合します。 home.pageLayout.desc=PDFの複数のページを1ページに結合します。
pageLayout.tags=merge,composite,single-view,organize pageLayout.tags=merge,composite,single-view,organize
@ -457,6 +461,7 @@ login.oauth2InvalidUserInfoResponse=Invalid User Info Response
login.oauth2invalidRequest=Invalid Request login.oauth2invalidRequest=Invalid Request
login.oauth2AccessDenied=Access Denied login.oauth2AccessDenied=Access Denied
login.oauth2InvalidTokenResponse=Invalid Token Response login.oauth2InvalidTokenResponse=Invalid Token Response
login.oauth2InvalidIdToken=Invalid Id Token
#auto-redact #auto-redact
@ -654,6 +659,13 @@ certSign.name=名前
certSign.submit=PDFに署名 certSign.submit=PDFに署名
#removeCertSign
removeCertSign.title=Remove Certificate Signature
removeCertSign.header=Remove the digital certificate from the PDF
removeCertSign.selectPDF=Select a PDF file:
removeCertSign.submit=Remove Signature
#removeBlanks #removeBlanks
removeBlanks.title=空白の削除 removeBlanks.title=空白の削除
removeBlanks.header=空白ページの削除 removeBlanks.header=空白ページの削除
@ -964,6 +976,7 @@ pdfToPDFA.credit=本サービスはPDF/Aの変換にOCRmyPDFを使用してい
pdfToPDFA.submit=変換 pdfToPDFA.submit=変換
pdfToPDFA.tip=現在、一度に複数の入力に対して機能しません pdfToPDFA.tip=現在、一度に複数の入力に対して機能しません
pdfToPDFA.outputFormat=Output format pdfToPDFA.outputFormat=Output format
pdfToPDFA.pdfWithDigitalSignature=The PDF contains a digital signature. This will be removed in the next step.
#PDFToWord #PDFToWord

View File

@ -332,6 +332,10 @@ home.certSign.title=인증서로 서명
home.certSign.desc=PDF 문서에 인증서 또는 키로 서명합니다. (PEM/P12) home.certSign.desc=PDF 문서에 인증서 또는 키로 서명합니다. (PEM/P12)
certSign.tags=authenticate,PEM,P12,official,encrypt certSign.tags=authenticate,PEM,P12,official,encrypt
home.removeCertSign.title=Remove Certificate Sign
home.removeCertSign.desc=Remove certificate signature from PDF
removeCertSign.tags=authenticate,PEM,P12,official,decrypt
home.pageLayout.title=다중 페이지 레이아웃 home.pageLayout.title=다중 페이지 레이아웃
home.pageLayout.desc=PDF 문서의 여러 페이지를 한 페이지로 합칩니다. home.pageLayout.desc=PDF 문서의 여러 페이지를 한 페이지로 합칩니다.
pageLayout.tags=merge,composite,single-view,organize pageLayout.tags=merge,composite,single-view,organize
@ -457,6 +461,7 @@ login.oauth2InvalidUserInfoResponse=Invalid User Info Response
login.oauth2invalidRequest=Invalid Request login.oauth2invalidRequest=Invalid Request
login.oauth2AccessDenied=Access Denied login.oauth2AccessDenied=Access Denied
login.oauth2InvalidTokenResponse=Invalid Token Response login.oauth2InvalidTokenResponse=Invalid Token Response
login.oauth2InvalidIdToken=Invalid Id Token
#auto-redact #auto-redact
@ -654,6 +659,13 @@ certSign.name=이름
certSign.submit=PDF 서명 certSign.submit=PDF 서명
#removeCertSign
removeCertSign.title=Remove Certificate Signature
removeCertSign.header=Remove the digital certificate from the PDF
removeCertSign.selectPDF=Select a PDF file:
removeCertSign.submit=Remove Signature
#removeBlanks #removeBlanks
removeBlanks.title=빈 페이지 제거 removeBlanks.title=빈 페이지 제거
removeBlanks.header=빈 페이지 제거 removeBlanks.header=빈 페이지 제거
@ -964,6 +976,7 @@ pdfToPDFA.credit=이 서비스는 PDF/A 변환을 위해 OCRmyPDF 문서를 사
pdfToPDFA.submit=변환 pdfToPDFA.submit=변환
pdfToPDFA.tip=현재 한 번에 여러 입력에 대해 작동하지 않습니다. pdfToPDFA.tip=현재 한 번에 여러 입력에 대해 작동하지 않습니다.
pdfToPDFA.outputFormat=Output format pdfToPDFA.outputFormat=Output format
pdfToPDFA.pdfWithDigitalSignature=The PDF contains a digital signature. This will be removed in the next step.
#PDFToWord #PDFToWord

View File

@ -332,6 +332,10 @@ home.certSign.title=Ondertekenen met certificaat
home.certSign.desc=Ondertekent een PDF met een certificaat/sleutel (PEM/P12) home.certSign.desc=Ondertekent een PDF met een certificaat/sleutel (PEM/P12)
certSign.tags=authenticeren,PEM,P12,officieel,versleutelen certSign.tags=authenticeren,PEM,P12,officieel,versleutelen
home.removeCertSign.title=Remove Certificate Sign
home.removeCertSign.desc=Remove certificate signature from PDF
removeCertSign.tags=authenticate,PEM,P12,official,decrypt
home.pageLayout.title=Multi-pagina indeling home.pageLayout.title=Multi-pagina indeling
home.pageLayout.desc=Voeg meerdere pagina's van een PDF-document samen op één pagina home.pageLayout.desc=Voeg meerdere pagina's van een PDF-document samen op één pagina
pageLayout.tags=samenvoegen,composiet,enkel-zicht,organiseren pageLayout.tags=samenvoegen,composiet,enkel-zicht,organiseren
@ -457,6 +461,7 @@ login.oauth2InvalidUserInfoResponse=Invalid User Info Response
login.oauth2invalidRequest=Invalid Request login.oauth2invalidRequest=Invalid Request
login.oauth2AccessDenied=Access Denied login.oauth2AccessDenied=Access Denied
login.oauth2InvalidTokenResponse=Invalid Token Response login.oauth2InvalidTokenResponse=Invalid Token Response
login.oauth2InvalidIdToken=Invalid Id Token
#auto-redact #auto-redact
@ -654,6 +659,13 @@ certSign.name=Naam
certSign.submit=PDF ondertekenen certSign.submit=PDF ondertekenen
#removeCertSign
removeCertSign.title=Remove Certificate Signature
removeCertSign.header=Remove the digital certificate from the PDF
removeCertSign.selectPDF=Select a PDF file:
removeCertSign.submit=Remove Signature
#removeBlanks #removeBlanks
removeBlanks.title=Verwijder blanco's removeBlanks.title=Verwijder blanco's
removeBlanks.header=Verwijder lege pagina's removeBlanks.header=Verwijder lege pagina's
@ -964,6 +976,7 @@ pdfToPDFA.credit=Deze service gebruikt OCRmyPDF voor PDF/A-conversie
pdfToPDFA.submit=Converteren pdfToPDFA.submit=Converteren
pdfToPDFA.tip=Currently does not work for multiple inputs at once pdfToPDFA.tip=Currently does not work for multiple inputs at once
pdfToPDFA.outputFormat=Output format pdfToPDFA.outputFormat=Output format
pdfToPDFA.pdfWithDigitalSignature=The PDF contains a digital signature. This will be removed in the next step.
#PDFToWord #PDFToWord

File diff suppressed because it is too large Load Diff

View File

@ -332,6 +332,10 @@ home.certSign.title=Podpisz certyfikatem
home.certSign.desc=Podpisz dokument PDF za pomocą certyfikatu/klucza prywatnego (PEM/P12) home.certSign.desc=Podpisz dokument PDF za pomocą certyfikatu/klucza prywatnego (PEM/P12)
certSign.tags=authenticate,PEM,P12,official,encrypt certSign.tags=authenticate,PEM,P12,official,encrypt
home.removeCertSign.title=Remove Certificate Sign
home.removeCertSign.desc=Remove certificate signature from PDF
removeCertSign.tags=authenticate,PEM,P12,official,decrypt
home.pageLayout.title=Układ wielu stron home.pageLayout.title=Układ wielu stron
home.pageLayout.desc=Scal wiele stron dokumentu PDF w jedną stronę home.pageLayout.desc=Scal wiele stron dokumentu PDF w jedną stronę
pageLayout.tags=merge,composite,single-view,organize pageLayout.tags=merge,composite,single-view,organize
@ -457,6 +461,7 @@ login.oauth2InvalidUserInfoResponse=Invalid User Info Response
login.oauth2invalidRequest=Invalid Request login.oauth2invalidRequest=Invalid Request
login.oauth2AccessDenied=Access Denied login.oauth2AccessDenied=Access Denied
login.oauth2InvalidTokenResponse=Invalid Token Response login.oauth2InvalidTokenResponse=Invalid Token Response
login.oauth2InvalidIdToken=Invalid Id Token
#auto-redact #auto-redact
@ -654,6 +659,13 @@ certSign.name=Nazwa
certSign.submit=Podpisz PDF certSign.submit=Podpisz PDF
#removeCertSign
removeCertSign.title=Remove Certificate Signature
removeCertSign.header=Remove the digital certificate from the PDF
removeCertSign.selectPDF=Select a PDF file:
removeCertSign.submit=Remove Signature
#removeBlanks #removeBlanks
removeBlanks.title=Usuń puste removeBlanks.title=Usuń puste
removeBlanks.header=Usuń puste strony removeBlanks.header=Usuń puste strony
@ -964,6 +976,7 @@ pdfToPDFA.credit=Ta usługa używa OCRmyPDF do konwersji PDF/A
pdfToPDFA.submit=Konwertuj pdfToPDFA.submit=Konwertuj
pdfToPDFA.tip=Currently does not work for multiple inputs at once pdfToPDFA.tip=Currently does not work for multiple inputs at once
pdfToPDFA.outputFormat=Output format pdfToPDFA.outputFormat=Output format
pdfToPDFA.pdfWithDigitalSignature=The PDF contains a digital signature. This will be removed in the next step.
#PDFToWord #PDFToWord

View File

@ -332,6 +332,10 @@ home.certSign.title=Assinar com Certificado
home.certSign.desc=Assinar um PDF com um Certificado/Chave (PEM/P12) home.certSign.desc=Assinar um PDF com um Certificado/Chave (PEM/P12)
certSign.tags=autenticar,PEM,P12,oficial,criptografar certSign.tags=autenticar,PEM,P12,oficial,criptografar
home.removeCertSign.title=Remove Certificate Sign
home.removeCertSign.desc=Remove certificate signature from PDF
removeCertSign.tags=authenticate,PEM,P12,official,decrypt
home.pageLayout.title=Layout de Múltiplas Páginas home.pageLayout.title=Layout de Múltiplas Páginas
home.pageLayout.desc=Mesclar várias páginas de um documento PDF em uma única página home.pageLayout.desc=Mesclar várias páginas de um documento PDF em uma única página
pageLayout.tags=mesclar,composto,vista-única,organizar pageLayout.tags=mesclar,composto,vista-única,organizar
@ -457,6 +461,7 @@ login.oauth2InvalidUserInfoResponse=Invalid User Info Response
login.oauth2invalidRequest=Invalid Request login.oauth2invalidRequest=Invalid Request
login.oauth2AccessDenied=Access Denied login.oauth2AccessDenied=Access Denied
login.oauth2InvalidTokenResponse=Invalid Token Response login.oauth2InvalidTokenResponse=Invalid Token Response
login.oauth2InvalidIdToken=Invalid Id Token
#auto-redact #auto-redact
@ -654,6 +659,13 @@ certSign.name=Nome
certSign.submit=Assinar PDF certSign.submit=Assinar PDF
#removeCertSign
removeCertSign.title=Remove Certificate Signature
removeCertSign.header=Remove the digital certificate from the PDF
removeCertSign.selectPDF=Select a PDF file:
removeCertSign.submit=Remove Signature
#removeBlanks #removeBlanks
removeBlanks.title=Remover Páginas em Branco removeBlanks.title=Remover Páginas em Branco
removeBlanks.header=Remover Páginas em Branco removeBlanks.header=Remover Páginas em Branco
@ -964,6 +976,7 @@ pdfToPDFA.credit=Este serviço usa OCRmyPDF para Conversão de PDF/A
pdfToPDFA.submit=Converter pdfToPDFA.submit=Converter
pdfToPDFA.tip=Currently does not work for multiple inputs at once pdfToPDFA.tip=Currently does not work for multiple inputs at once
pdfToPDFA.outputFormat=Output format pdfToPDFA.outputFormat=Output format
pdfToPDFA.pdfWithDigitalSignature=The PDF contains a digital signature. This will be removed in the next step.
#PDFToWord #PDFToWord

View File

@ -332,6 +332,10 @@ home.certSign.title=Assinar com Certificado
home.certSign.desc=Assinar um PDF com um Certificado/Chave (PEM/P12) home.certSign.desc=Assinar um PDF com um Certificado/Chave (PEM/P12)
certSign.tags=autenticar,PEM,P12,oficial,criptografar certSign.tags=autenticar,PEM,P12,oficial,criptografar
home.removeCertSign.title=Remove Certificate Sign
home.removeCertSign.desc=Remove certificate signature from PDF
removeCertSign.tags=authenticate,PEM,P12,official,decrypt
home.pageLayout.title=Layout de Múltiplas Páginas home.pageLayout.title=Layout de Múltiplas Páginas
home.pageLayout.desc=Juntar várias páginas de um documento PDF em uma única página home.pageLayout.desc=Juntar várias páginas de um documento PDF em uma única página
pageLayout.tags=juntar,composto,vista-única,organizar pageLayout.tags=juntar,composto,vista-única,organizar
@ -457,6 +461,7 @@ login.oauth2InvalidUserInfoResponse=Invalid User Info Response
login.oauth2invalidRequest=Invalid Request login.oauth2invalidRequest=Invalid Request
login.oauth2AccessDenied=Access Denied login.oauth2AccessDenied=Access Denied
login.oauth2InvalidTokenResponse=Invalid Token Response login.oauth2InvalidTokenResponse=Invalid Token Response
login.oauth2InvalidIdToken=Invalid Id Token
#auto-redact #auto-redact
@ -654,6 +659,13 @@ certSign.name=Nome
certSign.submit=Assinar PDF certSign.submit=Assinar PDF
#removeCertSign
removeCertSign.title=Remove Certificate Signature
removeCertSign.header=Remove the digital certificate from the PDF
removeCertSign.selectPDF=Select a PDF file:
removeCertSign.submit=Remove Signature
#removeBlanks #removeBlanks
removeBlanks.title=Remover Páginas em Branco removeBlanks.title=Remover Páginas em Branco
removeBlanks.header=Remover Páginas em Branco removeBlanks.header=Remover Páginas em Branco
@ -964,6 +976,7 @@ pdfToPDFA.credit=Este serviço usa OCRmyPDF para Conversão de PDF/A
pdfToPDFA.submit=Converter pdfToPDFA.submit=Converter
pdfToPDFA.tip=Currently does not work for multiple inputs at once pdfToPDFA.tip=Currently does not work for multiple inputs at once
pdfToPDFA.outputFormat=Output format pdfToPDFA.outputFormat=Output format
pdfToPDFA.pdfWithDigitalSignature=The PDF contains a digital signature. This will be removed in the next step.
#PDFToWord #PDFToWord

View File

@ -332,6 +332,10 @@ home.certSign.title=Semnare cu certificat
home.certSign.desc=Semnează un PDF cu un certificat/cheie (PEM/P12) home.certSign.desc=Semnează un PDF cu un certificat/cheie (PEM/P12)
certSign.tags=authenticate,PEM,P12,official,encrypt certSign.tags=authenticate,PEM,P12,official,encrypt
home.removeCertSign.title=Remove Certificate Sign
home.removeCertSign.desc=Remove certificate signature from PDF
removeCertSign.tags=authenticate,PEM,P12,official,decrypt
home.pageLayout.title=Multi-Page Layout home.pageLayout.title=Multi-Page Layout
home.pageLayout.desc=Merge multiple pages of a PDF document into a single page home.pageLayout.desc=Merge multiple pages of a PDF document into a single page
pageLayout.tags=merge,composite,single-view,organize pageLayout.tags=merge,composite,single-view,organize
@ -457,6 +461,7 @@ login.oauth2InvalidUserInfoResponse=Invalid User Info Response
login.oauth2invalidRequest=Invalid Request login.oauth2invalidRequest=Invalid Request
login.oauth2AccessDenied=Access Denied login.oauth2AccessDenied=Access Denied
login.oauth2InvalidTokenResponse=Invalid Token Response login.oauth2InvalidTokenResponse=Invalid Token Response
login.oauth2InvalidIdToken=Invalid Id Token
#auto-redact #auto-redact
@ -654,6 +659,13 @@ certSign.name=Numele
certSign.submit=Semnează PDF certSign.submit=Semnează PDF
#removeCertSign
removeCertSign.title=Remove Certificate Signature
removeCertSign.header=Remove the digital certificate from the PDF
removeCertSign.selectPDF=Select a PDF file:
removeCertSign.submit=Remove Signature
#removeBlanks #removeBlanks
removeBlanks.title=Elimină pagini goale removeBlanks.title=Elimină pagini goale
removeBlanks.header=Elimină pagini goale removeBlanks.header=Elimină pagini goale
@ -964,6 +976,7 @@ pdfToPDFA.credit=Acest serviciu utilizează OCRmyPDF pentru conversia în PDF/A
pdfToPDFA.submit=Convert pdfToPDFA.submit=Convert
pdfToPDFA.tip=Currently does not work for multiple inputs at once pdfToPDFA.tip=Currently does not work for multiple inputs at once
pdfToPDFA.outputFormat=Output format pdfToPDFA.outputFormat=Output format
pdfToPDFA.pdfWithDigitalSignature=The PDF contains a digital signature. This will be removed in the next step.
#PDFToWord #PDFToWord

View File

@ -332,6 +332,10 @@ home.certSign.title=Подписать сертификатом
home.certSign.desc=Подписать PDF сертификатом/ключом (PEM/P12) home.certSign.desc=Подписать PDF сертификатом/ключом (PEM/P12)
certSign.tags=authenticate,PEM,P12,official,encrypt certSign.tags=authenticate,PEM,P12,official,encrypt
home.removeCertSign.title=Remove Certificate Sign
home.removeCertSign.desc=Remove certificate signature from PDF
removeCertSign.tags=authenticate,PEM,P12,official,decrypt
home.pageLayout.title=Объединить страницы home.pageLayout.title=Объединить страницы
home.pageLayout.desc=Объединение нескольких страниц документа PDF в одну страницу home.pageLayout.desc=Объединение нескольких страниц документа PDF в одну страницу
pageLayout.tags=merge,composite,single-view,organize pageLayout.tags=merge,composite,single-view,organize
@ -457,6 +461,7 @@ login.oauth2InvalidUserInfoResponse=Invalid User Info Response
login.oauth2invalidRequest=Invalid Request login.oauth2invalidRequest=Invalid Request
login.oauth2AccessDenied=Access Denied login.oauth2AccessDenied=Access Denied
login.oauth2InvalidTokenResponse=Invalid Token Response login.oauth2InvalidTokenResponse=Invalid Token Response
login.oauth2InvalidIdToken=Invalid Id Token
#auto-redact #auto-redact
@ -654,6 +659,13 @@ certSign.name=Имя
certSign.submit=Подписать PDF certSign.submit=Подписать PDF
#removeCertSign
removeCertSign.title=Remove Certificate Signature
removeCertSign.header=Remove the digital certificate from the PDF
removeCertSign.selectPDF=Select a PDF file:
removeCertSign.submit=Remove Signature
#removeBlanks #removeBlanks
removeBlanks.title=Удалить Пустые removeBlanks.title=Удалить Пустые
removeBlanks.header=Удалить Пустые Страницы removeBlanks.header=Удалить Пустые Страницы
@ -964,6 +976,7 @@ pdfToPDFA.credit=Этот сервис использует OCRmyPDF для пр
pdfToPDFA.submit=Конвертировать pdfToPDFA.submit=Конвертировать
pdfToPDFA.tip=Currently does not work for multiple inputs at once pdfToPDFA.tip=Currently does not work for multiple inputs at once
pdfToPDFA.outputFormat=Output format pdfToPDFA.outputFormat=Output format
pdfToPDFA.pdfWithDigitalSignature=The PDF contains a digital signature. This will be removed in the next step.
#PDFToWord #PDFToWord

View File

@ -332,6 +332,10 @@ home.certSign.title=Podpísať s certifikátom
home.certSign.desc=Podpísať PDF s certifikátom/kľúčom (PEM/P12) home.certSign.desc=Podpísať PDF s certifikátom/kľúčom (PEM/P12)
certSign.tags=autentifikovať,PEM,P12,oficiálne,šifrovať certSign.tags=autentifikovať,PEM,P12,oficiálne,šifrovať
home.removeCertSign.title=Remove Certificate Sign
home.removeCertSign.desc=Remove certificate signature from PDF
removeCertSign.tags=authenticate,PEM,P12,official,decrypt
home.pageLayout.title=Viacstranové usporiadanie home.pageLayout.title=Viacstranové usporiadanie
home.pageLayout.desc=Zlúčte viacero stránok PDF dokumentu do jednej stránky home.pageLayout.desc=Zlúčte viacero stránok PDF dokumentu do jednej stránky
pageLayout.tags=zlúčiť,zjednotiť,jednostranový pohľad,organizovať pageLayout.tags=zlúčiť,zjednotiť,jednostranový pohľad,organizovať
@ -457,6 +461,7 @@ login.oauth2InvalidUserInfoResponse=Invalid User Info Response
login.oauth2invalidRequest=Invalid Request login.oauth2invalidRequest=Invalid Request
login.oauth2AccessDenied=Access Denied login.oauth2AccessDenied=Access Denied
login.oauth2InvalidTokenResponse=Invalid Token Response login.oauth2InvalidTokenResponse=Invalid Token Response
login.oauth2InvalidIdToken=Invalid Id Token
#auto-redact #auto-redact
@ -654,6 +659,13 @@ certSign.name=Meno
certSign.submit=Podpísať PDF certSign.submit=Podpísať PDF
#removeCertSign
removeCertSign.title=Remove Certificate Signature
removeCertSign.header=Remove the digital certificate from the PDF
removeCertSign.selectPDF=Select a PDF file:
removeCertSign.submit=Remove Signature
#removeBlanks #removeBlanks
removeBlanks.title=Odstrániť prázdne stránky removeBlanks.title=Odstrániť prázdne stránky
removeBlanks.header=Odstrániť prázdne stránky removeBlanks.header=Odstrániť prázdne stránky
@ -964,6 +976,7 @@ pdfToPDFA.credit=Táto služba používa OCRmyPDF na konverziu PDF/A
pdfToPDFA.submit=Konvertovať pdfToPDFA.submit=Konvertovať
pdfToPDFA.tip=Momentálne nefunguje pre viacero vstupov naraz pdfToPDFA.tip=Momentálne nefunguje pre viacero vstupov naraz
pdfToPDFA.outputFormat=Výstupný formát pdfToPDFA.outputFormat=Výstupný formát
pdfToPDFA.pdfWithDigitalSignature=The PDF contains a digital signature. This will be removed in the next step.
#PDFToWord #PDFToWord

View File

@ -332,6 +332,10 @@ home.certSign.title=Potpis sa sertifikatom
home.certSign.desc=Potpisuje PDF sa sertifikatom/ključem (PEM/P12) home.certSign.desc=Potpisuje PDF sa sertifikatom/ključem (PEM/P12)
certSign.tags=autentifikacija,PEM,P12,zvanično,šifrovanje certSign.tags=autentifikacija,PEM,P12,zvanično,šifrovanje
home.removeCertSign.title=Remove Certificate Sign
home.removeCertSign.desc=Remove certificate signature from PDF
removeCertSign.tags=authenticate,PEM,P12,official,decrypt
home.pageLayout.title=Višestruki prikaz stranica home.pageLayout.title=Višestruki prikaz stranica
home.pageLayout.desc=Spaja više stranica PDF dokumenta u jednu stranicu home.pageLayout.desc=Spaja više stranica PDF dokumenta u jednu stranicu
pageLayout.tags=spajanje,kompozit,pojedinačan-prikaz,organizacija pageLayout.tags=spajanje,kompozit,pojedinačan-prikaz,organizacija
@ -457,6 +461,7 @@ login.oauth2InvalidUserInfoResponse=Invalid User Info Response
login.oauth2invalidRequest=Invalid Request login.oauth2invalidRequest=Invalid Request
login.oauth2AccessDenied=Access Denied login.oauth2AccessDenied=Access Denied
login.oauth2InvalidTokenResponse=Invalid Token Response login.oauth2InvalidTokenResponse=Invalid Token Response
login.oauth2InvalidIdToken=Invalid Id Token
#auto-redact #auto-redact
@ -654,6 +659,13 @@ certSign.name=Ime
certSign.submit=Potpiši PDF certSign.submit=Potpiši PDF
#removeCertSign
removeCertSign.title=Remove Certificate Signature
removeCertSign.header=Remove the digital certificate from the PDF
removeCertSign.selectPDF=Select a PDF file:
removeCertSign.submit=Remove Signature
#removeBlanks #removeBlanks
removeBlanks.title=Ukloni prazne stranice removeBlanks.title=Ukloni prazne stranice
removeBlanks.header=Ukloni prazne stranice removeBlanks.header=Ukloni prazne stranice
@ -964,6 +976,7 @@ pdfToPDFA.credit=Ova usluga koristi OCRmyPDF za konverziju u PDF/A format
pdfToPDFA.submit=Konvertuj pdfToPDFA.submit=Konvertuj
pdfToPDFA.tip=Currently does not work for multiple inputs at once pdfToPDFA.tip=Currently does not work for multiple inputs at once
pdfToPDFA.outputFormat=Output format pdfToPDFA.outputFormat=Output format
pdfToPDFA.pdfWithDigitalSignature=The PDF contains a digital signature. This will be removed in the next step.
#PDFToWord #PDFToWord

View File

@ -332,6 +332,10 @@ home.certSign.title=Sign with Certificate
home.certSign.desc=Signs a PDF with a Certificate/Key (PEM/P12) home.certSign.desc=Signs a PDF with a Certificate/Key (PEM/P12)
certSign.tags=authenticate,PEM,P12,official,encrypt certSign.tags=authenticate,PEM,P12,official,encrypt
home.removeCertSign.title=Remove Certificate Sign
home.removeCertSign.desc=Remove certificate signature from PDF
removeCertSign.tags=authenticate,PEM,P12,official,decrypt
home.pageLayout.title=Multi-Page Layout home.pageLayout.title=Multi-Page Layout
home.pageLayout.desc=Merge multiple pages of a PDF document into a single page home.pageLayout.desc=Merge multiple pages of a PDF document into a single page
pageLayout.tags=merge,composite,single-view,organize pageLayout.tags=merge,composite,single-view,organize
@ -457,6 +461,7 @@ login.oauth2InvalidUserInfoResponse=Invalid User Info Response
login.oauth2invalidRequest=Invalid Request login.oauth2invalidRequest=Invalid Request
login.oauth2AccessDenied=Access Denied login.oauth2AccessDenied=Access Denied
login.oauth2InvalidTokenResponse=Invalid Token Response login.oauth2InvalidTokenResponse=Invalid Token Response
login.oauth2InvalidIdToken=Invalid Id Token
#auto-redact #auto-redact
@ -654,6 +659,13 @@ certSign.name=Namn
certSign.submit=Skriv under PDF certSign.submit=Skriv under PDF
#removeCertSign
removeCertSign.title=Remove Certificate Signature
removeCertSign.header=Remove the digital certificate from the PDF
removeCertSign.selectPDF=Select a PDF file:
removeCertSign.submit=Remove Signature
#removeBlanks #removeBlanks
removeBlanks.title=Ta bort tomrum removeBlanks.title=Ta bort tomrum
removeBlanks.header=Ta bort tomma sidor removeBlanks.header=Ta bort tomma sidor
@ -964,6 +976,7 @@ pdfToPDFA.credit=Denna tjänst använder OCRmyPDF för PDF/A-konvertering
pdfToPDFA.submit=Konvertera pdfToPDFA.submit=Konvertera
pdfToPDFA.tip=Currently does not work for multiple inputs at once pdfToPDFA.tip=Currently does not work for multiple inputs at once
pdfToPDFA.outputFormat=Output format pdfToPDFA.outputFormat=Output format
pdfToPDFA.pdfWithDigitalSignature=The PDF contains a digital signature. This will be removed in the next step.
#PDFToWord #PDFToWord

View File

@ -332,6 +332,10 @@ home.certSign.title=Sertifika ile İmzala
home.certSign.desc=Bir PDF'i Sertifika/Anahtar (PEM/P12) ile imzalar home.certSign.desc=Bir PDF'i Sertifika/Anahtar (PEM/P12) ile imzalar
certSign.tags=doğrula,PEM,P12,resmi,şifrele certSign.tags=doğrula,PEM,P12,resmi,şifrele
home.removeCertSign.title=Remove Certificate Sign
home.removeCertSign.desc=Remove certificate signature from PDF
removeCertSign.tags=authenticate,PEM,P12,official,decrypt
home.pageLayout.title=Çoklu-Sayfa Düzeni home.pageLayout.title=Çoklu-Sayfa Düzeni
home.pageLayout.desc=Bir PDF belgesinin çoklu sayfalarını tek bir sayfada birleştirir home.pageLayout.desc=Bir PDF belgesinin çoklu sayfalarını tek bir sayfada birleştirir
pageLayout.tags=birleştir,kompozit,tek-görünüm,düzenle pageLayout.tags=birleştir,kompozit,tek-görünüm,düzenle
@ -457,6 +461,7 @@ login.oauth2InvalidUserInfoResponse=Invalid User Info Response
login.oauth2invalidRequest=Invalid Request login.oauth2invalidRequest=Invalid Request
login.oauth2AccessDenied=Access Denied login.oauth2AccessDenied=Access Denied
login.oauth2InvalidTokenResponse=Invalid Token Response login.oauth2InvalidTokenResponse=Invalid Token Response
login.oauth2InvalidIdToken=Invalid Id Token
#auto-redact #auto-redact
@ -654,6 +659,13 @@ certSign.name=İsim
certSign.submit=PDF'i İmzala certSign.submit=PDF'i İmzala
#removeCertSign
removeCertSign.title=Remove Certificate Signature
removeCertSign.header=Remove the digital certificate from the PDF
removeCertSign.selectPDF=Select a PDF file:
removeCertSign.submit=Remove Signature
#removeBlanks #removeBlanks
removeBlanks.title=Boşları Kaldır removeBlanks.title=Boşları Kaldır
removeBlanks.header=Boş Sayfaları Kaldır removeBlanks.header=Boş Sayfaları Kaldır
@ -964,6 +976,7 @@ pdfToPDFA.credit=Bu hizmet PDF/A dönüşümü için OCRmyPDF kullanır
pdfToPDFA.submit=Dönüştür pdfToPDFA.submit=Dönüştür
pdfToPDFA.tip=Şu anda aynı anda birden fazla giriş için çalışmıyor pdfToPDFA.tip=Şu anda aynı anda birden fazla giriş için çalışmıyor
pdfToPDFA.outputFormat=Çıkış formatı pdfToPDFA.outputFormat=Çıkış formatı
pdfToPDFA.pdfWithDigitalSignature=The PDF contains a digital signature. This will be removed in the next step.
#PDFToWord #PDFToWord

View File

@ -332,6 +332,10 @@ home.certSign.title=Підписати сертифікатом
home.certSign.desc=Підписати PDF сертифікатом/ключем (PEM/P12) home.certSign.desc=Підписати PDF сертифікатом/ключем (PEM/P12)
certSign.tags=authenticate,PEM,P12,official,encrypt certSign.tags=authenticate,PEM,P12,official,encrypt
home.removeCertSign.title=Remove Certificate Sign
home.removeCertSign.desc=Remove certificate signature from PDF
removeCertSign.tags=authenticate,PEM,P12,official,decrypt
home.pageLayout.title=Об'єднати сторінки home.pageLayout.title=Об'єднати сторінки
home.pageLayout.desc=Об'єднання кількох сторінок документа PDF в одну сторінку home.pageLayout.desc=Об'єднання кількох сторінок документа PDF в одну сторінку
pageLayout.tags=merge,composite,single-view,organize pageLayout.tags=merge,composite,single-view,organize
@ -457,6 +461,7 @@ login.oauth2InvalidUserInfoResponse=Invalid User Info Response
login.oauth2invalidRequest=Invalid Request login.oauth2invalidRequest=Invalid Request
login.oauth2AccessDenied=Access Denied login.oauth2AccessDenied=Access Denied
login.oauth2InvalidTokenResponse=Invalid Token Response login.oauth2InvalidTokenResponse=Invalid Token Response
login.oauth2InvalidIdToken=Invalid Id Token
#auto-redact #auto-redact
@ -654,6 +659,13 @@ certSign.name=Ім'я
certSign.submit=Підписати PDF certSign.submit=Підписати PDF
#removeCertSign
removeCertSign.title=Remove Certificate Signature
removeCertSign.header=Remove the digital certificate from the PDF
removeCertSign.selectPDF=Select a PDF file:
removeCertSign.submit=Remove Signature
#removeBlanks #removeBlanks
removeBlanks.title=Видалити порожні removeBlanks.title=Видалити порожні
removeBlanks.header=Видалити порожні сторінки removeBlanks.header=Видалити порожні сторінки
@ -964,6 +976,7 @@ pdfToPDFA.credit=Цей сервіс використовує OCRmyPDF для п
pdfToPDFA.submit=Конвертувати pdfToPDFA.submit=Конвертувати
pdfToPDFA.tip=Наразі не працює для кількох вхідних файлів одночасно pdfToPDFA.tip=Наразі не працює для кількох вхідних файлів одночасно
pdfToPDFA.outputFormat=Output format pdfToPDFA.outputFormat=Output format
pdfToPDFA.pdfWithDigitalSignature=The PDF contains a digital signature. This will be removed in the next step.
#PDFToWord #PDFToWord

View File

@ -332,6 +332,10 @@ home.certSign.title=使用证书签署
home.certSign.desc=使用证书/密钥PEM/P12对PDF进行签署 home.certSign.desc=使用证书/密钥PEM/P12对PDF进行签署
certSign.tags=身份验证、PEM、P12、官方、加密 certSign.tags=身份验证、PEM、P12、官方、加密
home.removeCertSign.title=Remove Certificate Sign
home.removeCertSign.desc=Remove certificate signature from PDF
removeCertSign.tags=authenticate,PEM,P12,official,decrypt
home.pageLayout.title=多页布局 home.pageLayout.title=多页布局
home.pageLayout.desc=将PDF文档的多个页面合并成一页 home.pageLayout.desc=将PDF文档的多个页面合并成一页
pageLayout.tags=合并、组合、单视图、整理 pageLayout.tags=合并、组合、单视图、整理
@ -457,6 +461,7 @@ login.oauth2InvalidUserInfoResponse=Invalid User Info Response
login.oauth2invalidRequest=Invalid Request login.oauth2invalidRequest=Invalid Request
login.oauth2AccessDenied=Access Denied login.oauth2AccessDenied=Access Denied
login.oauth2InvalidTokenResponse=Invalid Token Response login.oauth2InvalidTokenResponse=Invalid Token Response
login.oauth2InvalidIdToken=Invalid Id Token
#auto-redact #auto-redact
@ -654,6 +659,13 @@ certSign.name=名称
certSign.submit=签署 PDF certSign.submit=签署 PDF
#removeCertSign
removeCertSign.title=Remove Certificate Signature
removeCertSign.header=Remove the digital certificate from the PDF
removeCertSign.selectPDF=Select a PDF file:
removeCertSign.submit=Remove Signature
#removeBlanks #removeBlanks
removeBlanks.title=删除空白 removeBlanks.title=删除空白
removeBlanks.header=删除空白页 removeBlanks.header=删除空白页
@ -964,6 +976,7 @@ pdfToPDFA.credit=此服务使用OCRmyPDF进行PDF/A转换
pdfToPDFA.submit=转换 pdfToPDFA.submit=转换
pdfToPDFA.tip=目前不支持上传多个 pdfToPDFA.tip=目前不支持上传多个
pdfToPDFA.outputFormat=Output format pdfToPDFA.outputFormat=Output format
pdfToPDFA.pdfWithDigitalSignature=The PDF contains a digital signature. This will be removed in the next step.
#PDFToWord #PDFToWord

View File

@ -332,6 +332,10 @@ home.certSign.title=使用憑證簽章
home.certSign.desc=使用憑證/金鑰PEM/P12簽章 PDF home.certSign.desc=使用憑證/金鑰PEM/P12簽章 PDF
certSign.tags=驗證,PEM,P12,官方,加密 certSign.tags=驗證,PEM,P12,官方,加密
home.removeCertSign.title=Remove Certificate Sign
home.removeCertSign.desc=Remove certificate signature from PDF
removeCertSign.tags=authenticate,PEM,P12,official,decrypt
home.pageLayout.title=多頁面版面配置 home.pageLayout.title=多頁面版面配置
home.pageLayout.desc=將 PDF 檔案的多個頁面合併到單一頁面 home.pageLayout.desc=將 PDF 檔案的多個頁面合併到單一頁面
pageLayout.tags=合併,複合,單一檢視,組織 pageLayout.tags=合併,複合,單一檢視,組織
@ -457,6 +461,7 @@ login.oauth2InvalidUserInfoResponse=Invalid User Info Response
login.oauth2invalidRequest=Invalid Request login.oauth2invalidRequest=Invalid Request
login.oauth2AccessDenied=Access Denied login.oauth2AccessDenied=Access Denied
login.oauth2InvalidTokenResponse=Invalid Token Response login.oauth2InvalidTokenResponse=Invalid Token Response
login.oauth2InvalidIdToken=Invalid Id Token
#auto-redact #auto-redact
@ -654,6 +659,13 @@ certSign.name=名稱
certSign.submit=簽章 PDF certSign.submit=簽章 PDF
#removeCertSign
removeCertSign.title=Remove Certificate Signature
removeCertSign.header=Remove the digital certificate from the PDF
removeCertSign.selectPDF=Select a PDF file:
removeCertSign.submit=Remove Signature
#removeBlanks #removeBlanks
removeBlanks.title=移除空白頁面 removeBlanks.title=移除空白頁面
removeBlanks.header=移除空白頁面 removeBlanks.header=移除空白頁面
@ -964,6 +976,7 @@ pdfToPDFA.credit=此服務使用 OCRmyPDF 進行 PDF/A 轉換
pdfToPDFA.submit=轉換 pdfToPDFA.submit=轉換
pdfToPDFA.tip=目前不支援上傳多個 pdfToPDFA.tip=目前不支援上傳多個
pdfToPDFA.outputFormat=Output format pdfToPDFA.outputFormat=Output format
pdfToPDFA.pdfWithDigitalSignature=The PDF contains a digital signature. This will be removed in the next step.
#PDFToWord #PDFToWord

View File

@ -5,7 +5,7 @@
font-size: 16px; font-size: 16px;
margin-bottom: 2rem; margin-bottom: 2rem;
padding: 0.75rem 3.5rem; padding: 0.75rem 3.5rem;
border: 0.1rem solid var(--md-sys-color-outline-variant); border: 1px solid var(--md-sys-color-outline-variant);
border-radius: 3rem; border-radius: 3rem;
outline-color: var(--md-sys-color-outline-variant); outline-color: var(--md-sys-color-outline-variant);
} }
@ -19,12 +19,12 @@
.features-container { .features-container {
display: grid; display: grid;
grid-template-columns: repeat(auto-fill, minmax(15rem, 3fr)); grid-template-columns: repeat(auto-fill, minmax(15rem, 3fr));
gap: 25px 30px; gap: 30px 30px;
} }
.feature-card { .feature-card {
border: 2px solid var(--md-sys-color-surface-5); border: 1px solid var(--md-sys-color-surface-5);
border-radius: 2rem; border-radius: 1.75rem;
padding: 1.25rem; padding: 1.25rem;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -47,13 +47,13 @@
} }
.feature-card .card-text { .feature-card .card-text {
flex: 1; font-size: .875rem;
} }
.feature-card:hover { .feature-card:hover {
cursor: pointer; cursor: pointer;
transform: scale(1.1); transform: scale(1.08);
box-shadow: var(--md-sys-elevation-3); box-shadow: var(--md-sys-elevation-2);
} }
.card-title.text-primary { .card-title.text-primary {
@ -79,11 +79,12 @@
} }
#tool-text { #tool-text {
margin: 0.5rem 0.5rem 0rem; margin: 0.0rem 0 0 1.25rem;
} }
.card-title { .card-title {
margin-bottom: 1rem; margin-bottom: 1rem;
font-size: 1.1rem;
} }
/* Only show the favorite icons when the parent card is being hovered over */ /* Only show the favorite icons when the parent card is being hovered over */

View File

@ -75,7 +75,7 @@
} }
.icon-text { .icon-text {
margin-left: 4px; margin-left: 8px;
margin-right: 4px; margin-right: 4px;
display: inline-flex; display: inline-flex;
flex-direction: column; flex-direction: column;

View File

@ -187,8 +187,8 @@ td {
.feature-card .nav-icon { .feature-card .nav-icon {
vertical-align: middle; vertical-align: middle;
font-size: 2rem !important; font-size: 2rem !important;
padding: 0.5rem; padding: 0.75rem;
border-radius: 1.25rem; border-radius: 0.9rem;
color: var(--md-sys-color-surface); color: var(--md-sys-color-surface);
} }
@ -798,6 +798,8 @@ textarea.form-control {
} }
.nav-link { .nav-link {
display: flex;
align-items: center;
transition: none !important; transition: none !important;
padding: 0.5rem 1rem !important; padding: 0.5rem 1rem !important;
border: 1px transparent; border: 1px transparent;

View File

@ -7,7 +7,7 @@
.material-symbols-rounded { .material-symbols-rounded {
font-family: 'Material Symbols Rounded'; font-family: 'Material Symbols Rounded';
font-weight: normal; font-weight: 300;
font-style: normal; font-style: normal;
font-size: 24px; font-size: 24px;
line-height: 1; line-height: 1;

View File

@ -30,5 +30,5 @@
} }
.no-fill { .no-fill {
font-variation-settings: var(--md-sys-icon-fill-0); /* font-variation-settings: var(--md-sys-icon-fill-0); */
} }

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 40 KiB

View File

@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-no" viewBox="0 0 640 480">
<path fill="#ed2939" d="M0 0h640v480H0z"/>
<path fill="#fff" d="M180 0h120v480H180z"/>
<path fill="#fff" d="M0 180h640v120H0z"/>
<path fill="#002664" d="M210 0h60v480h-60z"/>
<path fill="#002664" d="M0 210h640v60H0z"/>
</svg>

After

Width:  |  Height:  |  Size: 318 B

View File

@ -34,7 +34,7 @@
<td th:text="#{${user.roleName}}"></td> <td th:text="#{${user.roleName}}"></td>
<td> <td>
<form th:if="${user.username != currentUsername}" th:action="@{'/api/v1/user/admin/deleteUser/' + ${user.username}}" method="post"> <form th:if="${user.username != currentUsername}" th:action="@{'/api/v1/user/admin/deleteUser/' + ${user.username}}" method="post">
<button type="submit" th:text="#{delete}">Delete</button> <button class="btn btn-danger" type="submit" th:text="#{delete}">Delete</button>
</form> </form>
</td> </td>
<td th:text="${user.authenticationType}"></td> <td th:text="${user.authenticationType}"></td>
@ -47,7 +47,7 @@
<span th:text="#{${addMessage}}">Default message if not found</span> <span th:text="#{${addMessage}}">Default message if not found</span>
</div> </div>
<button class="btn btn-outline-info" data-toggle="tooltip" data-placement="auto" th:title="#{adminUserSettings.usernameInfo}" th:text="#{help}">Help</button> <button class="btn btn-outline-info" data-toggle="tooltip" data-placement="auto" th:title="#{adminUserSettings.usernameInfo}" th:text="#{help}">Help</button>
<form id="formsaveuser" action="/api/v1/user/admin/saveUser" method="post"> <form id="formsaveuser" th:action="@{/api/v1/user/admin/saveUser}" method="post">
<div class="mb-3"> <div class="mb-3">
<label for="username" th:text="#{username}">Username</label> <label for="username" th:text="#{username}">Username</label>
<input type="text" class="form-control" name="username" id="username" th:title="#{adminUserSettings.usernameInfo}" required> <input type="text" class="form-control" name="username" id="username" th:title="#{adminUserSettings.usernameInfo}" required>
@ -78,7 +78,7 @@
<div th:if="${changeMessage}" class="alert alert-danger"> <div th:if="${changeMessage}" class="alert alert-danger">
<span th:text="#{${changeMessage}}">Default message if not found</span> <span th:text="#{${changeMessage}}">Default message if not found</span>
</div> </div>
<form action="/api/v1/user/admin/changeRole" method="post"> <form th:action="@{/api/v1/user/admin/changeRole}" method="post">
<div class="mb-3"> <div class="mb-3">
<label for="username" th:text="#{username}">Username</label> <label for="username" th:text="#{username}">Username</label>
<select name="username" class="form-control" required> <select name="username" class="form-control" required>

View File

@ -21,15 +21,51 @@
<form method="post" enctype="multipart/form-data" th:action="@{api/v1/convert/pdf/pdfa}"> <form method="post" enctype="multipart/form-data" th:action="@{api/v1/convert/pdf/pdfa}">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div> <div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
<div class="mb-3"> <div class="mb-3">
<label th:text="#{pdfToPDFA.outputFormat}"></label> <label for="outputFormat" th:text="#{pdfToPDFA.outputFormat}"></label>
<select class="form-control" name="outputFormat"> <select class="form-control" name="outputFormat" id="outputFormat">
<option value="pdfa-1">PDF/A-1b</option> <option value="pdfa-1">PDF/A-1b</option>
<option value="pdfa">PDF/A-2b</option> <option value="pdfa">PDF/A-2b</option>
</select> </select>
</div> </div>
<br> <div id="result" class="alert-warning"></div>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{pdfToPDFA.submit}"></button> <button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{pdfToPDFA.submit}"></button>
</form> </form>
<script th:inline="javascript">
document.getElementById('fileInput-input').addEventListener('change', async () => {
pdfjsLib.GlobalWorkerOptions.workerSrc = './pdfjs/pdf.worker.mjs';
const fileInput = document.getElementById('fileInput-input');
const resultDiv = document.getElementById('result');
const file = fileInput.files[0];
const arrayBuffer = await file.arrayBuffer();
try {
const pdf = await pdfjsLib.getDocument({ data: arrayBuffer }).promise;
let hasSignature = false;
for (let i = 1; i <= pdf.numPages; i++) {
const page = await pdf.getPage(i);
const annotations = await page.getAnnotations({ intent: 'display' });
annotations.forEach(annotation => {
console.log(annotation)
if (annotation.subtype === 'Widget' && annotation.fieldType === 'Sig') {
hasSignature = true;
}
});
}
if (hasSignature) {
/*<![CDATA[*/
resultDiv.textContent = /*[[#{pdfToPDFA.pdfWithDigitalSignature}]]*/ "The PDF contains a digital signature. This will be removed in the next step.";
/*]]>*/
}
} catch (error) {
resultDiv.textContent = 'Error reading the PDF: ' + error.message;
}
});
</script>
<p class="mt-3" th:text="#{pdfToPDFA.credit}"></p> <p class="mt-3" th:text="#{pdfToPDFA.credit}"></p>
</div> </div>
</div> </div>

View File

@ -1,7 +1,7 @@
<!DOCTYPE html> <!DOCTYPE html>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}" xmlns:th="https://www.thymeleaf.org"> <html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}" xmlns:th="https://www.thymeleaf.org">
<head> <head>
<th:block th:insert="~{fragments/common :: head(title=#{crop.title}, header=#{crop.header})}"></th:block> <th:block th:insert="~{fragments/common :: head(title=#{crop.title}, header=#{crop.header})}"></th:block>
</head> </head>
<body> <body>
@ -16,7 +16,7 @@
<span class="material-symbols-rounded tool-header-icon organize">crop</span> <span class="material-symbols-rounded tool-header-icon organize">crop</span>
<span class="tool-header-text" th:text="#{crop.header}"></span> <span class="tool-header-text" th:text="#{crop.header}"></span>
</div> </div>
<form id="cropForm" action="/api/v1/general/crop" method="post" enctype="multipart/form-data"> <form id="cropForm" th:action="@{/api/v1/general/crop}" method="post" enctype="multipart/form-data">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div> <div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
<input id="x" type="hidden" name="x"> <input id="x" type="hidden" name="x">
<input id="y" type="hidden" name="y"> <input id="y" type="hidden" name="y">
@ -75,6 +75,16 @@
} }
}); });
cropForm.addEventListener('submit', function(e) {
if (xInput.value == "" && yInput.value == "" && widthInput.value == "" && heightInput.value == "") {
// Ορίστε συντεταγμένες για ολόκληρη την επιφάνεια του PDF
xInput.value = 0;
yInput.value = 0;
widthInput.value = pdfCanvas.width;
heightInput.value = pdfCanvas.height;
}
});
overlayCanvas.addEventListener('mousedown', function(e) { overlayCanvas.addEventListener('mousedown', function(e) {
// Clear previously drawn rectangle on the main canvas // Clear previously drawn rectangle on the main canvas
context.clearRect(0, 0, pdfCanvas.width, pdfCanvas.height); context.clearRect(0, 0, pdfCanvas.width, pdfCanvas.height);
@ -138,4 +148,4 @@
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block> <th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div> </div>
</body> </body>
</html> </html>

View File

@ -21,7 +21,7 @@
<a href="https://github.com/Stirling-Tools/Stirling-PDF/issues" id="github-button" class="btn btn-primary" target="_blank" th:text="#{error.github}"></a> <a href="https://github.com/Stirling-Tools/Stirling-PDF/issues" id="github-button" class="btn btn-primary" target="_blank" th:text="#{error.github}"></a>
<a href="https://discord.gg/Cn8pWhQRxZ" id="discord-button" class="btn btn-primary" target="_blank" th:text="#{joinDiscord}"></a> <a href="https://discord.gg/Cn8pWhQRxZ" id="discord-button" class="btn btn-primary" target="_blank" th:text="#{joinDiscord}"></a>
</div> </div>
<a href="/" id="home-button" class="home-button btn btn-primary" th:text="#{goHomepage}"></a> <a th:href="@{/}" id="home-button" class="home-button btn btn-primary" th:text="#{goHomepage}"></a>
</div> </div>
</div> </div>
</div> </div>

View File

@ -29,4 +29,6 @@
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="sr_LATN_RS"> <img src="images/flags/rs.svg" alt="icon" width="20" height="15"> Srpski</a> <a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="sr_LATN_RS"> <img src="images/flags/rs.svg" alt="icon" width="20" height="15"> Srpski</a>
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="uk_UA"> <img src="images/flags/ua.svg" alt="icon" width="20" height="15"> Українська</a> <a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="uk_UA"> <img src="images/flags/ua.svg" alt="icon" width="20" height="15"> Українська</a>
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="cs_CZ"> <img src="images/flags/cz.svg" alt="icon" width="20" height="15"> Česky</a> <a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="cs_CZ"> <img src="images/flags/cz.svg" alt="icon" width="20" height="15"> Česky</a>
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="hr_HR"> <img src="images/flags/hr.svg" alt="icon" width="20" height="15"> Hrvatski</a>
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="no_NB"> <img src="images/flags/no.svg" alt="icon" width="20" height="15"> Norsk</a>
</th:block> </th:block>

View File

@ -8,7 +8,7 @@
<script src="js/githubVersion.js"></script> <script src="js/githubVersion.js"></script>
<nav class="navbar navbar-expand-lg"> <nav class="navbar navbar-expand-lg">
<div class="container "> <div class="container ">
<a class="navbar-brand" href="/" style="display: flex;"> <a class="navbar-brand" th:href="@{/}" style="display: flex;">
<img class="main-icon" src="favicon.svg" alt="icon"> <img class="main-icon" src="favicon.svg" alt="icon">
<span class="icon-text" th:text="${@navBarText}"></span> <span class="icon-text" th:text="${@navBarText}"></span>
</a> </a>
@ -22,8 +22,7 @@
<ul class="navbar-nav me-auto flex-nowrap"> <ul class="navbar-nav me-auto flex-nowrap">
<!-- All Tools --> <!-- All Tools -->
<li class="nav-item dropdown dropdown-mega position-static" <li class="nav-item dropdown dropdown-mega position-static">
th:classappend="${currentPage}=='remove-pages' OR ${currentPage}=='merge-pdfs' OR ${currentPage}=='split-pdfs' OR ${currentPage}=='crop' OR ${currentPage}=='adjust-contrast' OR ${currentPage}=='pdf-organizer' OR ${currentPage}=='rotate-pdf' OR ${currentPage}=='multi-page-layout' OR ${currentPage}=='scale-pages' OR ${currentPage}=='auto-split-pdf' OR ${currentPage}=='extract-page' OR ${currentPage}=='pdf-to-single-page' OR ${currentPage}=='add-password' OR ${currentPage}=='remove-password' OR ${currentPage}=='add-watermark' OR ${currentPage}=='cert-sign' OR ${currentPage}=='sanitize-pdf' OR ${currentPage}=='img-to-pdf' OR ${currentPage}=='file-to-pdf' OR ${currentPage}=='html-to-pdf' OR ${currentPage}=='url-to-pdf' OR ${currentPage}=='pdf-to-img' OR ${currentPage}=='pdf-to-word' OR ${currentPage}=='pdf-to-presentation' OR ${currentPage}=='pdf-to-text' OR ${currentPage}=='pdf-to-html' OR ${currentPage}=='pdf-to-xml' OR ${currentPage}=='pdf-to-pdfa' OR ${currentPage}=='sign' OR ${currentPage}=='repair' OR ${currentPage}=='compare' OR ${currentPage}=='show-javascript' OR ${currentPage}=='flatten' OR ${currentPage}=='remove-blanks' OR ${currentPage}=='remove-annotations' OR ${currentPage}=='extract-image-scans' OR ${currentPage}=='change-metadata' OR ${currentPage}=='add-image' OR ${currentPage}=='ocr-pdf' OR ${currentPage}=='change-permissions' OR ${currentPage}=='extract-images' OR ${currentPage}=='compress-pdf' OR ${currentPage}=='add-page-numbers' OR ${currentPage}=='auto-rename' OR ${currentPage}=='get-info-on-pdf' ? 'active' : ''">
<a class="nav-link" id="navbarDropdown-1" href="#" role="button" data-bs-toggle="dropdown" <a class="nav-link" id="navbarDropdown-1" href="#" role="button" data-bs-toggle="dropdown"
aria-haspopup="true" aria-expanded="false"> aria-haspopup="true" aria-expanded="false">
<span class="material-symbols-rounded"> <span class="material-symbols-rounded">
@ -91,6 +90,9 @@
<div <div
th:replace="~{fragments/navbarEntry :: navbarEntry ('markdown-to-pdf', 'markdown', 'home.MarkdownToPDF.title', 'home.MarkdownToPDF.desc', 'MarkdownToPDF.tags', 'convert')}"> th:replace="~{fragments/navbarEntry :: navbarEntry ('markdown-to-pdf', 'markdown', 'home.MarkdownToPDF.title', 'home.MarkdownToPDF.desc', 'MarkdownToPDF.tags', 'convert')}">
</div> </div>
<div
th:replace="~{fragments/navbarEntry :: navbarEntry ('book-to-pdf', 'book', 'home.BookToPDF.title', 'home.BookToPDF.desc', 'BookToPDF.tags', 'convert')}">
</div>
</div> </div>
<!-- Convert from PDF menu items --> <!-- Convert from PDF menu items -->
<div class="col-lg-2 col-sm-6 py px-xl-1 px-2"> <div class="col-lg-2 col-sm-6 py px-xl-1 px-2">
@ -119,6 +121,9 @@
<div <div
th:replace="~{fragments/navbarEntry :: navbarEntry ('pdf-to-csv', 'csv', 'home.tableExtraxt.title', 'home.tableExtraxt.desc', 'pdfToPDFA.tags', 'convert')}"> th:replace="~{fragments/navbarEntry :: navbarEntry ('pdf-to-csv', 'csv', 'home.tableExtraxt.title', 'home.tableExtraxt.desc', 'pdfToPDFA.tags', 'convert')}">
</div> </div>
<div
th:replace="~{fragments/navbarEntry :: navbarEntry ('pdf-to-book', 'book', 'home.PDFToBook.title', 'home.PDFToBook.desc', 'PDFToBook.tags', 'convert')}">
</div>
</div> </div>
<!-- Security menu items --> <!-- Security menu items -->
<div class="col-lg-2 col-sm-6 py px-xl-1 px-2"> <div class="col-lg-2 col-sm-6 py px-xl-1 px-2">
@ -141,6 +146,9 @@
<div <div
th:replace="~{fragments/navbarEntry :: navbarEntry ('cert-sign', 'workspace_premium', 'home.certSign.title', 'home.certSign.desc', 'certSign.tags', 'security')}"> th:replace="~{fragments/navbarEntry :: navbarEntry ('cert-sign', 'workspace_premium', 'home.certSign.title', 'home.certSign.desc', 'certSign.tags', 'security')}">
</div> </div>
<div
th:replace="~{fragments/navbarEntry :: navbarEntry ('remove-cert-sign', 'remove_moderator', 'home.removeCertSign.title', 'home.removeCertSign.desc', 'removeCertSign.tags', 'security')}">
</div>
<div <div
th:replace="~{fragments/navbarEntry :: navbarEntry ('sanitize-pdf', 'sanitizer', 'home.sanitizePdf.title', 'home.sanitizePdf.desc', 'sanitizePdf.tags', 'security')}"> th:replace="~{fragments/navbarEntry :: navbarEntry ('sanitize-pdf', 'sanitizer', 'home.sanitizePdf.title', 'home.sanitizePdf.desc', 'sanitizePdf.tags', 'security')}">
</div> </div>
@ -233,7 +241,7 @@
</li> </li>
<li class="nav-item"> <li class="nav-item" th:if="${@endpointConfiguration.isEndpointEnabled('multi-tool')}">
<a class="nav-link" href="#" th:href="@{multi-tool}" <a class="nav-link" href="#" th:href="@{multi-tool}"
th:classappend="${currentPage}=='multi-tool' ? 'active' : ''" th:title="#{home.multiTool.desc}"> th:classappend="${currentPage}=='multi-tool' ? 'active' : ''" th:title="#{home.multiTool.desc}">
<span class="material-symbols-rounded"> <span class="material-symbols-rounded">
@ -243,7 +251,7 @@
</a> </a>
</li> </li>
<li class="nav-item"> <li class="nav-item" th:if="${@endpointConfiguration.isEndpointEnabled('pipeline')}" >
<a class="nav-link" href="#" th:href="@{pipeline}" <a class="nav-link" href="#" th:href="@{pipeline}"
th:classappend="${currentPage}=='pipeline' ? 'active' : ''" th:title="#{home.pipeline.desc}"> th:classappend="${currentPage}=='pipeline' ? 'active' : ''" th:title="#{home.pipeline.desc}">
<span class="material-symbols-rounded"> <span class="material-symbols-rounded">
@ -253,7 +261,7 @@
</a> </a>
</li> </li>
<li class="nav-item"> <li class="nav-item" th:if="${@endpointConfiguration.isEndpointEnabled('compress-pdf')}" >
<a class="nav-link" href="#" title="#{home.compressPdfs.title}" th:href="@{compress-pdf}" <a class="nav-link" href="#" title="#{home.compressPdfs.title}" th:href="@{compress-pdf}"
th:classappend="${currentPage}=='compress-pdf' ? 'active' : ''" th:title="#{home.compressPdfs.desc}"> th:classappend="${currentPage}=='compress-pdf' ? 'active' : ''" th:title="#{home.compressPdfs.desc}">
<span class="material-symbols-rounded"> <span class="material-symbols-rounded">

View File

@ -164,6 +164,9 @@
<div <div
th:replace="~{fragments/card :: card(id='cert-sign', cardTitle=#{home.certSign.title}, cardText=#{home.certSign.desc}, cardLink='cert-sign', toolIcon='workspace_premium', tags=#{certSign.tags}, toolGroup='security')}"> th:replace="~{fragments/card :: card(id='cert-sign', cardTitle=#{home.certSign.title}, cardText=#{home.certSign.desc}, cardLink='cert-sign', toolIcon='workspace_premium', tags=#{certSign.tags}, toolGroup='security')}">
</div> </div>
<div
th:replace="~{fragments/card :: card(id='remove-cert-sign', cardTitle=#{home.removeCertSign.title}, cardText=#{home.removeCertSign.desc}, cardLink='remove-cert-sign', toolIcon='remove_moderator', tags=#{removeCertSign.tags}, toolGroup='security')}">
</div>
<div <div
th:replace="~{fragments/card :: card(id='multi-page-layout', cardTitle=#{home.pageLayout.title}, cardText=#{home.pageLayout.desc}, cardLink='multi-page-layout', toolIcon='dashboard', tags=#{pageLayout.tags}, toolGroup='organize')}"> th:replace="~{fragments/card :: card(id='multi-page-layout', cardTitle=#{home.pageLayout.title}, cardText=#{home.pageLayout.desc}, cardLink='multi-page-layout', toolIcon='dashboard', tags=#{pageLayout.tags}, toolGroup='organize')}">
</div> </div>

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