Compare commits

..

13 Commits

Author SHA1 Message Date
Anthony Stirling
fbc6b3a70e fix 2024-11-29 15:05:10 +00:00
Anthony Stirling
329f755823 Merge branch 'main' into saml 2024-11-29 14:32:04 +00:00
Anthony Stirling
acc50e48c5 debugs 2024-11-29 14:31:15 +00:00
Anthony Stirling
92a571d31c saml stuff 2024-11-29 14:29:54 +00:00
Anthony Stirling
5976e69f54 debugs 2024-11-29 10:40:10 +00:00
Anthony Stirling
e588d8f99e spring dev fix for saml 2024-11-29 09:06:51 +00:00
Anthony Stirling
1c0f423510 remove unused repo 2024-11-29 08:59:29 +00:00
Anthony Stirling
2d6fe55985 info to debug 2024-11-29 08:53:54 +00:00
Anthony Stirling
5171088fca more fixes 2024-11-29 08:43:57 +00:00
Anthony Stirling
b4837df76c ee flag for saml 2024-11-28 19:41:39 +00:00
Anthony Stirling
d20e8f7d54 oauth to saml and compare fixes etc 2024-11-28 19:27:37 +00:00
Anthony Stirling
2885fac30d remove debugs 2024-11-28 15:43:24 +00:00
Anthony Stirling
2a4a19a80f backup 2024-11-28 15:00:37 +00:00
135 changed files with 2683 additions and 35055 deletions

View File

@@ -1,5 +1,5 @@
blank_issues_enabled: true blank_issues_enabled: true
contact_links: contact_links:
- name: 💬 Discord Server - name: 💬 Discord Server
url: https://discord.gg/HYmhKj45pU url: https://discord.gg/Cn8pWhQRxZ
about: You can join our Discord server for real time discussion and support about: You can join our Discord server for real time discussion and support

View File

@@ -76,7 +76,7 @@ jobs:
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v5 uses: actions/setup-python@v5
with: with:
python-version: "3.12" python-version: "3.7"
- name: Pip requirements - name: Pip requirements
run: | run: |

View File

@@ -1,96 +0,0 @@
name: Test Installers Build
on:
workflow_dispatch:
release:
types: [created]
permissions:
contents: write
packages: write
jobs:
build-installers:
strategy:
matrix:
include:
- os: windows-latest
platform: win
ext: exe
#- os: macos-latest
# platform: mac
# ext: dmg
#- os: ubuntu-latest
# platform: linux
# ext: deb
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
java-version: "21"
distribution: "temurin"
- uses: gradle/actions/setup-gradle@v4
with:
gradle-version: 8.7
# Install Windows dependencies
- name: Install WiX Toolset
if: matrix.os == 'windows-latest'
run: |
curl -L -o wix.exe https://github.com/wixtoolset/wix3/releases/download/wix3141rtm/wix314.exe
.\wix.exe /install /quiet
# Install Linux dependencies
- name: Install Linux Dependencies
if: matrix.os == 'ubuntu-latest'
run: |
sudo apt-get update
sudo apt-get install -y fakeroot rpm
# Get version number
- name: Get version number
id: versionNumber
run: echo "versionNumber=$(./gradlew printVersion --quiet | tail -1)" >> $GITHUB_OUTPUT
shell: bash
- name: Get version number mac
id: versionNumberMac
run: echo "versionNumberMac=$(./gradlew printMacVersion --quiet | tail -1)" >> $GITHUB_OUTPUT
shell: bash
# Build installer
- name: Build Installer
run: ./gradlew build jpackage -x test --info
env:
DOCKER_ENABLE_SECURITY: false
STIRLING_PDF_DESKTOP_UI: true
# Rename and collect artifacts based on OS
- name: Prepare artifacts
id: prepare
shell: bash
run: |
if [ "${{ matrix.os }}" = "windows-latest" ]; then
mv "build/jpackage/Stirling-PDF-${{ steps.versionNumber.outputs.versionNumber }}.exe" "Stirling-PDF-${{ matrix.platform }}-installer.${{ matrix.ext }}"
elif [ "${{ matrix.os }}" = "macos-latest" ]; then
mv "build/jpackage/Stirling-PDF-${{ steps.versionNumberMac.outputs.versionNumberMac }}.dmg" "Stirling-PDF-${{ steps.versionNumber.outputs.versionNumber }}-${{ matrix.platform }}.${{ matrix.ext }}"
else
mv "build/jpackage/stirling-pdf_${{ steps.versionNumber.outputs.versionNumber }}-1_amd64.deb" "Stirling-PDF-${{ steps.versionNumber.outputs.versionNumber }}-${{ matrix.platform }}.${{ matrix.ext }}"
fi
# Upload installer as artifact for testing
- name: Upload Installer Artifact
uses: actions/upload-artifact@v4
with:
name: Stirling-PDF-${{ matrix.platform }}-installer.${{ matrix.ext }}
path: Stirling-PDF-${{ matrix.platform }}-installer.${{ matrix.ext }}
retention-days: 1
if-no-files-found: error
- name: Upload binaries to release
uses: softprops/action-gh-release@v2
with:
files: ./Stirling-PDF-${{ matrix.platform }}-installer.${{ matrix.ext }}

View File

@@ -35,28 +35,27 @@ jobs:
run: ./gradlew clean createExe run: ./gradlew clean createExe
env: env:
DOCKER_ENABLE_SECURITY: ${{ matrix.enable_security }} DOCKER_ENABLE_SECURITY: ${{ matrix.enable_security }}
STIRLING_PDF_DESKTOP_UI: false
- name: Get version number - name: Get version number
id: versionNumber id: versionNumber
run: echo "versionNumber=$(./gradlew printVersion --quiet | tail -1)" >> $GITHUB_OUTPUT run: echo "versionNumber=$(./gradlew printVersion --quiet | tail -1)" >> $GITHUB_OUTPUT
- name: Rename binarie - name: Rename binarie
run: cp ./build/launch4j/Stirling-PDF.exe ./build/launch4j/Stirling-PDF-Server${{ matrix.file_suffix }}.exe if: matrix.file_suffix != ''
run: cp ./build/launch4j/Stirling-PDF.exe ./build/launch4j/Stirling-PDF${{ matrix.file_suffix }}.exe
- name: Upload Assets binarie - name: Upload Assets binarie
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
path: ./build/launch4j/Stirling-PDF-Server${{ matrix.file_suffix }}.exe path: ./build/launch4j/Stirling-PDF${{ matrix.file_suffix }}.exe
name: Stirling-PDF-Server${{ matrix.file_suffix }}.exe name: Stirling-PDF${{ matrix.file_suffix }}.exe
overwrite: true overwrite: true
retention-days: 1 retention-days: 1
if-no-files-found: error if-no-files-found: error
- name: Upload binaries to release - name: Upload binaries to release
uses: softprops/action-gh-release@v2 uses: softprops/action-gh-release@v2
with: with:
files: ./build/launch4j/Stirling-PDF-Server${{ matrix.file_suffix }}.exe files: ./build/launch4j/Stirling-PDF${{ matrix.file_suffix }}.exe
- name: Rename jar binaries - name: Rename jar binaries
run: cp ./build/libs/Stirling-PDF-${{ steps.versionNumber.outputs.versionNumber }}.jar ./build/libs/Stirling-PDF${{ matrix.file_suffix }}.jar run: cp ./build/libs/Stirling-PDF-${{ steps.versionNumber.outputs.versionNumber }}.jar ./build/libs/Stirling-PDF${{ matrix.file_suffix }}.jar
@@ -74,43 +73,3 @@ jobs:
uses: softprops/action-gh-release@v2 uses: softprops/action-gh-release@v2
with: with:
files: ./build/libs/Stirling-PDF${{ matrix.file_suffix }}.jar files: ./build/libs/Stirling-PDF${{ matrix.file_suffix }}.jar
push-ui:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: "17"
distribution: "temurin"
- uses: gradle/actions/setup-gradle@v4
with:
gradle-version: 8.7
- name: Generate exe
run: ./gradlew clean createExe
env:
DOCKER_ENABLE_SECURITY: false
STIRLING_PDF_DESKTOP_UI: true
- name: Get version number
id: versionNumber
run: echo "versionNumber=$(./gradlew printVersion --quiet | tail -1)" >> $GITHUB_OUTPUT
- name: Upload Assets binarie
uses: actions/upload-artifact@v4
with:
path: ./build/launch4j/Stirling-PDF.exe
name: Stirling-PDF.exe
overwrite: true
retention-days: 1
if-no-files-found: error
- name: Upload binaries to release
uses: softprops/action-gh-release@v2
with:
files: ./build/launch4j/Stirling-PDF.exe

1
.gitignore vendored
View File

@@ -161,4 +161,3 @@ out/
.pytest_cache .pytest_cache
.ipynb_checkpoints .ipynb_checkpoints
**/jcef-bundle/

View File

@@ -1,5 +1,5 @@
# use alpine # use alpine
FROM alpine:3.21.0 FROM alpine:3.20.3
ARG VERSION_TAG ARG VERSION_TAG

View File

@@ -2,7 +2,7 @@
<h1 align="center">Stirling-PDF</h1> <h1 align="center">Stirling-PDF</h1>
[![Docker Pulls](https://img.shields.io/docker/pulls/frooodle/s-pdf)](https://hub.docker.com/r/frooodle/s-pdf) [![Docker Pulls](https://img.shields.io/docker/pulls/frooodle/s-pdf)](https://hub.docker.com/r/frooodle/s-pdf)
[![Discord](https://img.shields.io/discord/1068636748814483718?label=Discord)](https://discord.gg/HYmhKj45pU) [![Discord](https://img.shields.io/discord/1068636748814483718?label=Discord)](https://discord.gg/Cn8pWhQRxZ)
[![Docker Image Version (tag latest semver)](https://img.shields.io/docker/v/frooodle/s-pdf/latest)](https://github.com/Stirling-Tools/Stirling-PDF/) [![Docker Image Version (tag latest semver)](https://img.shields.io/docker/v/frooodle/s-pdf/latest)](https://github.com/Stirling-Tools/Stirling-PDF/)
[![GitHub Repo stars](https://img.shields.io/github/stars/stirling-tools/stirling-pdf?style=social)](https://github.com/Stirling-Tools/stirling-pdf) [![GitHub Repo stars](https://img.shields.io/github/stars/stirling-tools/stirling-pdf?style=social)](https://github.com/Stirling-Tools/stirling-pdf)
@@ -187,48 +187,47 @@ Certain functionality like `Sign` supports pre-saved files stored at `/customFil
## Supported Languages ## Supported Languages
Stirling-PDF currently supports 38 languages! Stirling-PDF currently supports 37 languages!
| Language | Progress | | Language | Progress |
| -------------------------------------------- | -------------------------------------- | | -------------------------------------------- | -------------------------------------- |
| Arabic (العربية) (ar_AR) | ![94%](https://geps.dev/progress/94) | | Arabic (العربية) (ar_AR) | ![98%](https://geps.dev/progress/98) |
| Azerbaijani (Azərbaycan Dili) (az_AZ) | ![93%](https://geps.dev/progress/93) | | Azerbaijani (Azərbaycan Dili) (az_AZ) | ![97%](https://geps.dev/progress/97) |
| Basque (Euskara) (eu_ES) | ![53%](https://geps.dev/progress/53) | | Basque (Euskara) (eu_ES) | ![54%](https://geps.dev/progress/54) |
| Bulgarian (Български) (bg_BG) | ![90%](https://geps.dev/progress/90) | | Bulgarian (Български) (bg_BG) | ![94%](https://geps.dev/progress/94) |
| Catalan (Català) (ca_CA) | ![84%](https://geps.dev/progress/84) | | Catalan (Català) (ca_CA) | ![88%](https://geps.dev/progress/88) |
| Croatian (Hrvatski) (hr_HR) | ![91%](https://geps.dev/progress/91) | | Croatian (Hrvatski) (hr_HR) | ![96%](https://geps.dev/progress/96) |
| Czech (Česky) (cs_CZ) | ![91%](https://geps.dev/progress/91) | | Czech (Česky) (cs_CZ) | ![95%](https://geps.dev/progress/95) |
| Danish (Dansk) (da_DK) | ![90%](https://geps.dev/progress/90) | | Danish (Dansk) (da_DK) | ![94%](https://geps.dev/progress/94) |
| Dutch (Nederlands) (nl_NL) | ![89%](https://geps.dev/progress/89) | | Dutch (Nederlands) (nl_NL) | ![94%](https://geps.dev/progress/94) |
| 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) |
| French (Français) (fr_FR) | ![92%](https://geps.dev/progress/92) | | French (Français) (fr_FR) | ![97%](https://geps.dev/progress/97) |
| German (Deutsch) (de_DE) | ![99%](https://geps.dev/progress/99) | | German (Deutsch) (de_DE) | ![97%](https://geps.dev/progress/97) |
| Greek (Ελληνικά) (el_GR) | ![90%](https://geps.dev/progress/90) | | Greek (Ελληνικά) (el_GR) | ![95%](https://geps.dev/progress/95) |
| Hindi (हिंदी) (hi_IN) | ![88%](https://geps.dev/progress/88) | | Hindi (हिंदी) (hi_IN) | ![93%](https://geps.dev/progress/93) |
| Hungarian (Magyar) (hu_HU) | ![91%](https://geps.dev/progress/91) | | Hungarian (Magyar) (hu_HU) | ![95%](https://geps.dev/progress/95) |
| Indonesian (Bahasa Indonesia) (id_ID) | ![91%](https://geps.dev/progress/91) | | Indonesian (Bahasa Indonesia) (id_ID) | ![95%](https://geps.dev/progress/95) |
| Irish (Gaeilge) (ga_IE) | ![83%](https://geps.dev/progress/83) | | Irish (Gaeilge) (ga_IE) | ![86%](https://geps.dev/progress/86) |
| Italian (Italiano) (it_IT) | ![99%](https://geps.dev/progress/99) | | Italian (Italiano) (it_IT) | ![99%](https://geps.dev/progress/99) |
| Japanese (日本語) (ja_JP) | ![93%](https://geps.dev/progress/93) | | Japanese (日本語) (ja_JP) | ![83%](https://geps.dev/progress/83) |
| Korean (한국어) (ko_KR) | ![89%](https://geps.dev/progress/89) | | Korean (한국어) (ko_KR) | ![93%](https://geps.dev/progress/93) |
| Norwegian (Norsk) (no_NB) | ![82%](https://geps.dev/progress/82) | | Norwegian (Norsk) (no_NB) | ![85%](https://geps.dev/progress/85) |
| Persian (فارسی) (fa_IR) | ![99%](https://geps.dev/progress/99) | | Polish (Polski) (pl_PL) | ![94%](https://geps.dev/progress/94) |
| Polish (Polski) (pl_PL) | ![90%](https://geps.dev/progress/90) | | Portuguese (Português) (pt_PT) | ![95%](https://geps.dev/progress/95) |
| Portuguese (Português) (pt_PT) | ![91%](https://geps.dev/progress/91) | | Portuguese Brazilian (Português) (pt_BR) | ![96%](https://geps.dev/progress/96) |
| Portuguese Brazilian (Português) (pt_BR) | ![98%](https://geps.dev/progress/98) | | Romanian (Română) (ro_RO) | ![88%](https://geps.dev/progress/88) |
| Romanian (Română) (ro_RO) | ![85%](https://geps.dev/progress/85) | | Russian (Русский) (ru_RU) | ![95%](https://geps.dev/progress/95) |
| Russian (Русский) (ru_RU) | ![90%](https://geps.dev/progress/90) | | Serbian Latin alphabet (Srpski) (sr_LATN_RS) | ![68%](https://geps.dev/progress/68) |
| Serbian Latin alphabet (Srpski) (sr_LATN_RS) | ![67%](https://geps.dev/progress/67) | | Simplified Chinese (简体中文) (zh_CN) | ![89%](https://geps.dev/progress/89) |
| Simplified Chinese (简体中文) (zh_CN) | ![93%](https://geps.dev/progress/93) | | Slovakian (Slovensky) (sk_SK) | ![80%](https://geps.dev/progress/80) |
| Slovakian (Slovensky) (sk_SK) | ![78%](https://geps.dev/progress/78) | | Spanish (Español) (es_ES) | ![96%](https://geps.dev/progress/96) |
| Spanish (Español) (es_ES) | ![91%](https://geps.dev/progress/91) | | Swedish (Svenska) (sv_SE) | ![94%](https://geps.dev/progress/94) |
| Swedish (Svenska) (sv_SE) | ![90%](https://geps.dev/progress/90) | | Thai (ไทย) (th_TH) | ![94%](https://geps.dev/progress/94) |
| Thai (ไทย) (th_TH) | ![90%](https://geps.dev/progress/90) | | Traditional Chinese (繁體中文) (zh_TW) | ![96%](https://geps.dev/progress/96) |
| Traditional Chinese (繁體中文) (zh_TW) | ![91%](https://geps.dev/progress/91) | | Turkish (Türkçe) (tr_TR) | ![90%](https://geps.dev/progress/90) |
| Turkish (Türkçe) (tr_TR) | ![86%](https://geps.dev/progress/86) | | Ukrainian (Українська) (uk_UA) | ![78%](https://geps.dev/progress/78) |
| Ukrainian (Українська) (uk_UA) | ![76%](https://geps.dev/progress/76) | | Vietnamese (Tiếng Việt) (vi_VN) | ![86%](https://geps.dev/progress/86) |
| Vietnamese (Tiếng Việt) (vi_VN) | ![83%](https://geps.dev/progress/83) |
## Contributing (Creating Issues, Translations, Fixing Bugs, etc.) ## Contributing (Creating Issues, Translations, Fixing Bugs, etc.)
@@ -405,7 +404,7 @@ To access your account settings, go to Account Settings in the settings cog menu
To add new users, go to the bottom of Account Settings and hit 'Admin Settings'. Here you can add new users. The different roles mentioned within this are for rate limiting. This is a work in progress and will be expanded on more in the future. To add new users, go to the bottom of Account Settings and hit 'Admin Settings'. Here you can add new users. The different roles mentioned within this are for rate limiting. This is a work in progress and will be expanded on more in the future.
For API usage, you must provide a header with `X-API-KEY` and the associated API key for that user. For API usage, you must provide a header with `X-API-Key` and the associated API key for that user.
## FAQ ## FAQ

View File

@@ -8,7 +8,6 @@ plugins {
id "com.diffplug.spotless" version "6.25.0" id "com.diffplug.spotless" version "6.25.0"
id "com.github.jk1.dependency-license-report" version "2.9" id "com.github.jk1.dependency-license-report" version "2.9"
//id "nebula.lint" version "19.0.3" //id "nebula.lint" version "19.0.3"
id("org.panteleyev.jpackageplugin") version "1.6.0"
} }
@@ -27,7 +26,7 @@ ext {
} }
group = "stirling.software" group = "stirling.software"
version = "0.36.3" version = "0.35.0"
java { java {
@@ -42,9 +41,6 @@ repositories {
maven { maven {
url 'https://build.shibboleth.net/maven/releases' url 'https://build.shibboleth.net/maven/releases'
} }
maven { url "https://build.shibboleth.net/maven/releases" }
maven { url "https://maven.pkg.github.com/jcefmaven/jcefmaven" }
} }
licenseReport { licenseReport {
@@ -68,12 +64,6 @@ sourceSets {
exclude "stirling/software/SPDF/model/User.java" exclude "stirling/software/SPDF/model/User.java"
exclude "stirling/software/SPDF/repository/**" exclude "stirling/software/SPDF/repository/**"
} }
if (System.getenv("STIRLING_PDF_DESKTOP_UI") == "false") {
exclude "stirling/software/SPDF/UI/impl/**"
}
} }
} }
} }
@@ -84,153 +74,16 @@ openApi {
outputFileName = "SwaggerDoc.json" outputFileName = "SwaggerDoc.json"
} }
//0.11.5 to 2024.11.5
def getMacVersion(String version) {
def currentYear = java.time.Year.now().getValue()
def versionParts = version.split("\\.", 2)
return "${currentYear}.${versionParts.length > 1 ? versionParts[1] : versionParts[0]}"
}
jpackage {
input = "build/libs"
appName = "Stirling-PDF"
appVersion = project.version
vendor = "Stirling-Software"
appDescription = "Stirling PDF - Your Local PDF Editor"
mainJar = "Stirling-PDF-${project.version}.jar"
mainClass = "org.springframework.boot.loader.launch.JarLauncher"
icon = "src/main/resources/static/favicon.ico"
// JVM Options
javaOptions = [
"-DBROWSER_OPEN=true",
"-DSTIRLING_PDF_DESKTOP_UI=true",
"-Djava.awt.headless=false",
"-Dapple.awt.UIElement=true",
"--add-opens", "java.base/java.lang=ALL-UNNAMED",
"--add-opens", "java.desktop/java.awt.event=ALL-UNNAMED",
"--add-opens", "java.desktop/sun.awt=ALL-UNNAMED"
]
verbose = true
destination = "${projectDir}/build/jpackage"
// Windows-specific configuration
windows {
launcherAsService = false
appVersion = project.version
winConsole = false
winDirChooser = true
winMenu = true
winShortcut = true
winPerUserInstall = true
winMenuGroup = "Stirling Software"
winUpgradeUuid = "2a43ed0c-b8c2-40cf-89e1-751129b87641" // Unique identifier for updates
winHelpUrl = "https://github.com/Stirling-Tools/Stirling-PDF"
winUpdateUrl = "https://github.com/Stirling-Tools/Stirling-PDF/releases"
type = "exe"
installDir = "C:/Program Files/Stirling-PDF"
}
// macOS-specific configuration
mac {
appVersion = getMacVersion(project.version.toString())
icon = "src/main/resources/static/favicon.icns"
type = "dmg"
macPackageIdentifier = "com.stirling.software.pdf"
macPackageName = "Stirling-PDF"
macAppCategory = "public.app-category.productivity"
macSign = false // Enable signing
macAppStore = false // Not targeting App Store initially
//installDir = "Applications"
// Add license and other documentation to DMG
/*macDmgContent = [
"README.md",
"LICENSE",
"CHANGELOG.md"
]*/
// Enable Mac-specific entitlements
//macEntitlements = "entitlements.plist" // You'll need to create this file
}
// Linux-specific configuration
linux {
appVersion = project.version
icon = "src/main/resources/static/favicon.png"
type = "deb" // Can also use "rpm" for Red Hat-based systems
// Debian package configuration
//linuxPackageName = "stirlingpdf"
linuxDebMaintainer = "support@stirlingpdf.com"
linuxMenuGroup = "Office;PDF;Productivity"
linuxAppCategory = "Office"
linuxAppRelease = "1"
linuxPackageDeps = true
installDir = "/opt/Stirling-PDF"
// RPM-specific settings
//linuxRpmLicenseType = "MIT"
}
// Common additional options
//jLinkOptions = [
// "--strip-debug",
// "--compress=2",
// "--no-header-files",
// "--no-man-pages"
//]
// Add any additional modules required
/*addModules = [
"java.base",
"java.desktop",
"java.logging",
"java.sql",
"java.xml",
"jdk.crypto.ec"
]*/
// Add copyright and license information
copyright = "Copyright © 2024 Stirling Software"
licenseFile = "LICENSE"
}
launch4j { launch4j {
icon = "${projectDir}/src/main/resources/static/favicon.ico" icon = "${projectDir}/src/main/resources/static/favicon.ico"
outfile="Stirling-PDF.exe" outfile="Stirling-PDF.exe"
headerType="console"
if(System.getenv("STIRLING_PDF_DESKTOP_UI") == 'true') {
headerType = "gui"
} else {
headerType = "console"
}
jarTask = tasks.bootJar jarTask = tasks.bootJar
errTitle="Encountered error, Do you have Java 21?" errTitle="Encountered error, Do you have Java 21?"
downloadUrl="https://download.oracle.com/java/21/latest/jdk-21_windows-x64_bin.exe" downloadUrl="https://download.oracle.com/java/21/latest/jdk-21_windows-x64_bin.exe"
variables=["BROWSER_OPEN=true"]
if(System.getenv("STIRLING_PDF_DESKTOP_UI") == 'true') {
variables=["BROWSER_OPEN=true", "STIRLING_PDF_DESKTOP_UI=true"]
} else {
variables=["BROWSER_OPEN=true"]
}
jreMinVersion="17" jreMinVersion="17"
mutexName="Stirling-PDF" mutexName="Stirling-PDF"
@@ -270,13 +123,6 @@ configurations.all {
exclude group: "org.springframework.boot", module: "spring-boot-starter-tomcat" exclude group: "org.springframework.boot", module: "spring-boot-starter-tomcat"
} }
dependencies { dependencies {
if (System.getenv("STIRLING_PDF_DESKTOP_UI") != "false") {
implementation "me.friwi:jcefmaven:127.3.1"
implementation "org.openjfx:javafx-controls:21"
implementation "org.openjfx:javafx-swing:21"
}
//security updates //security updates
implementation "org.springframework:spring-webmvc:6.2.0" implementation "org.springframework:spring-webmvc:6.2.0"
@@ -296,7 +142,7 @@ dependencies {
if (System.getenv("DOCKER_ENABLE_SECURITY") != "false") { if (System.getenv("DOCKER_ENABLE_SECURITY") != "false") {
implementation "org.springframework.boot:spring-boot-starter-security:$springBootVersion" implementation "org.springframework.boot:spring-boot-starter-security:$springBootVersion"
implementation "org.thymeleaf.extras:thymeleaf-extras-springsecurity5:3.1.3.RELEASE" implementation "org.thymeleaf.extras:thymeleaf-extras-springsecurity5:3.1.2.RELEASE"
implementation "org.springframework.boot:spring-boot-starter-data-jpa:$springBootVersion" implementation "org.springframework.boot:spring-boot-starter-data-jpa:$springBootVersion"
implementation "org.springframework.boot:spring-boot-starter-oauth2-client:$springBootVersion" implementation "org.springframework.boot:spring-boot-starter-oauth2-client:$springBootVersion"
@@ -425,14 +271,7 @@ jar {
tasks.named("test") { tasks.named("test") {
useJUnitPlatform() useJUnitPlatform()
} }
task printVersion {
doLast {
println project.version
}
}
task printMacVersion { task printVersion {
doLast { println project.version
println getMacVersion(project.version.toString())
}
} }

View File

@@ -15,10 +15,6 @@ import shutil
import re import re
from PIL import Image, ImageDraw from PIL import Image, ImageDraw
API_HEADERS = {
'X-API-KEY': '123456789'
}
######### #########
# GIVEN # # GIVEN #
######### #########
@@ -231,7 +227,7 @@ def save_generated_pdf(context, filename):
def step_send_get_request(context, endpoint): def step_send_get_request(context, endpoint):
base_url = "http://localhost:8080" base_url = "http://localhost:8080"
full_url = f"{base_url}{endpoint}" full_url = f"{base_url}{endpoint}"
response = requests.get(full_url, headers=API_HEADERS) response = requests.get(full_url)
context.response = response context.response = response
@when('I send a GET request to "{endpoint}" with parameters') @when('I send a GET request to "{endpoint}" with parameters')
@@ -239,7 +235,7 @@ def step_send_get_request_with_params(context, endpoint):
base_url = "http://localhost:8080" base_url = "http://localhost:8080"
params = {row['parameter']: row['value'] for row in context.table} params = {row['parameter']: row['value'] for row in context.table}
full_url = f"{base_url}{endpoint}" full_url = f"{base_url}{endpoint}"
response = requests.get(full_url, params=params, headers=API_HEADERS) response = requests.get(full_url, params=params)
context.response = response context.response = response
@when('I send the API request to the endpoint "{endpoint}"') @when('I send the API request to the endpoint "{endpoint}"')
@@ -260,7 +256,7 @@ def step_send_api_request(context, endpoint):
print(f"form_data {file.name} with {mime_type}") print(f"form_data {file.name} with {mime_type}")
form_data.append((key, (file.name, file, mime_type))) form_data.append((key, (file.name, file, mime_type)))
response = requests.post(url, files=form_data, headers=API_HEADERS) response = requests.post(url, files=form_data)
context.response = response context.response = response
######## ########

View File

@@ -1,34 +0,0 @@
services:
stirling-pdf:
container_name: Stirling-PDF-Security-Fat
image: stirlingtools/stirling-pdf:latest-fat
deploy:
resources:
limits:
memory: 4G
healthcheck:
test: ["CMD-SHELL", "curl -f -H 'X-API-KEY: 123456789' http://localhost:8080/api/v1/info/status | grep -q 'UP'"]
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"
SECURITY_CUSTOMGLOBALAPIKEY: "123456789"
restart: on-failure:5

View File

@@ -42,19 +42,14 @@ ignore = [
'addPageNumbers.selectText.3', 'addPageNumbers.selectText.3',
'alphabet', 'alphabet',
'certSign.name', 'certSign.name',
'fileChooser.dragAndDrop',
'home.pipeline.title', 'home.pipeline.title',
'language.direction', 'language.direction',
'legal.impressum',
'licenses.version', 'licenses.version',
'pipeline.title', 'pipeline.title',
'pipelineOptions.pipelineHeader', 'pipelineOptions.pipelineHeader',
'pro', 'pro',
'sponsor', 'sponsor',
'text', 'text',
'validateSignature.cert.bits',
'validateSignature.cert.version',
'validateSignature.status',
'watermark.type.1', 'watermark.type.1',
] ]
@@ -66,6 +61,7 @@ ignore = [
[es_ES] [es_ES]
ignore = [ ignore = [
'adminUserSettings.roles', 'adminUserSettings.roles',
'color',
'error', 'error',
'language.direction', 'language.direction',
'no', 'no',
@@ -77,11 +73,6 @@ ignore = [
'language.direction', 'language.direction',
] ]
[fa_IR]
ignore = [
'language.direction',
]
[fr_FR] [fr_FR]
ignore = [ ignore = [
'AddStampRequest.alphabet', 'AddStampRequest.alphabet',

View File

@@ -28,7 +28,7 @@ public class LicenseKeyChecker {
this.checkLicense(); this.checkLicense();
} }
@Scheduled(initialDelay = 604800000, fixedRate = 604800000) // 7 days in milliseconds @Scheduled(fixedRate = 604800000) // 7 days in milliseconds
public void checkLicensePeriodically() { public void checkLicensePeriodically() {
checkLicense(); checkLicense();
} }

View File

@@ -1,6 +1,5 @@
package stirling.software.SPDF; package stirling.software.SPDF;
import java.awt.*;
import java.io.IOException; import java.io.IOException;
import java.net.ServerSocket; import java.net.ServerSocket;
import java.nio.file.Files; import java.nio.file.Files;
@@ -9,9 +8,6 @@ import java.nio.file.Paths;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Properties;
import javax.swing.*;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -23,16 +19,13 @@ import org.springframework.core.env.Environment;
import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.annotation.EnableScheduling;
import io.github.pixee.security.SystemCommand; import io.github.pixee.security.SystemCommand;
import jakarta.annotation.PostConstruct; import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.UI.WebBrowser;
import stirling.software.SPDF.config.ConfigInitializer; import stirling.software.SPDF.config.ConfigInitializer;
import stirling.software.SPDF.model.ApplicationProperties; import stirling.software.SPDF.model.ApplicationProperties;
@SpringBootApplication @SpringBootApplication
@EnableScheduling @EnableScheduling
@Slf4j
public class SPdfApplication { public class SPdfApplication {
private static final Logger logger = LoggerFactory.getLogger(SPdfApplication.class); private static final Logger logger = LoggerFactory.getLogger(SPdfApplication.class);
@@ -74,19 +67,36 @@ public class SPdfApplication {
} }
} }
@PostConstruct
public void init() {
baseUrlStatic = this.baseUrl;
// Check if the BROWSER_OPEN environment variable is set to true
String browserOpenEnv = env.getProperty("BROWSER_OPEN");
boolean browserOpen = browserOpenEnv != null && "true".equalsIgnoreCase(browserOpenEnv);
if (browserOpen) {
try {
String url = baseUrl + ":" + getStaticPort();
String os = System.getProperty("os.name").toLowerCase();
Runtime rt = Runtime.getRuntime();
if (os.contains("win")) {
// For Windows
SystemCommand.runCommand(rt, "rundll32 url.dll,FileProtocolHandler " + url);
} else if (os.contains("mac")) {
SystemCommand.runCommand(rt, "open " + url);
} else if (os.contains("nix") || os.contains("nux")) {
SystemCommand.runCommand(rt, "xdg-open " + url);
}
} catch (Exception e) {
logger.error("Error opening browser: {}", e.getMessage());
}
}
logger.info("Running configs {}", applicationProperties.toString());
}
public static void main(String[] args) throws IOException, InterruptedException { public static void main(String[] args) throws IOException, InterruptedException {
SpringApplication app = new SpringApplication(SPdfApplication.class); SpringApplication app = new SpringApplication(SPdfApplication.class);
Properties props = new Properties();
if (Boolean.parseBoolean(System.getProperty("STIRLING_PDF_DESKTOP_UI", "false"))) {
System.setProperty("java.awt.headless", "false");
app.setHeadless(false);
props.put("java.awt.headless", "false");
props.put("spring.main.web-application-type", "servlet");
}
app.setAdditionalProfiles("default"); app.setAdditionalProfiles("default");
app.addInitializers(new ConfigInitializer()); app.addInitializers(new ConfigInitializer());
Map<String, String> propertyFiles = new HashMap<>(); Map<String, String> propertyFiles = new HashMap<>();
@@ -110,20 +120,14 @@ public class SPdfApplication {
} else { } else {
logger.warn("Custom configuration file 'configs/custom_settings.yml' does not exist."); logger.warn("Custom configuration file 'configs/custom_settings.yml' does not exist.");
} }
Properties finalProps = new Properties();
if (!propertyFiles.isEmpty()) { if (!propertyFiles.isEmpty()) {
finalProps.putAll( app.setDefaultProperties(
Collections.singletonMap( Collections.singletonMap(
"spring.config.additional-location", "spring.config.additional-location",
propertyFiles.get("spring.config.additional-location"))); propertyFiles.get("spring.config.additional-location")));
} }
if (!props.isEmpty()) {
finalProps.putAll(props);
}
app.setDefaultProperties(finalProps);
app.run(args); app.run(args);
// Ensure directories are created // Ensure directories are created
@@ -143,46 +147,6 @@ public class SPdfApplication {
logger.info("Navigate to {}", url); logger.info("Navigate to {}", url);
} }
@Autowired(required = false)
private WebBrowser webBrowser;
@PostConstruct
public void init() {
baseUrlStatic = this.baseUrl;
String url = baseUrl + ":" + getStaticPort();
if (webBrowser != null
&& Boolean.parseBoolean(System.getProperty("STIRLING_PDF_DESKTOP_UI", "false"))) {
webBrowser.initWebUI(url);
} else {
String browserOpenEnv = env.getProperty("BROWSER_OPEN");
boolean browserOpen = browserOpenEnv != null && "true".equalsIgnoreCase(browserOpenEnv);
if (browserOpen) {
try {
String os = System.getProperty("os.name").toLowerCase();
Runtime rt = Runtime.getRuntime();
if (os.contains("win")) {
// For Windows
SystemCommand.runCommand(rt, "rundll32 url.dll,FileProtocolHandler " + url);
} else if (os.contains("mac")) {
SystemCommand.runCommand(rt, "open " + url);
} else if (os.contains("nix") || os.contains("nux")) {
SystemCommand.runCommand(rt, "xdg-open " + url);
}
} catch (Exception e) {
logger.error("Error opening browser: {}", e.getMessage());
}
}
}
logger.info("Running configs {}", applicationProperties.toString());
}
@PreDestroy
public void cleanup() {
if (webBrowser != null) {
webBrowser.cleanup();
}
}
public static String getStaticBaseUrl() { public static String getStaticBaseUrl() {
return baseUrlStatic; return baseUrlStatic;
} }

View File

@@ -1,7 +0,0 @@
package stirling.software.SPDF.UI;
public interface WebBrowser {
void initWebUI(String url);
void cleanup();
}

View File

@@ -1,354 +0,0 @@
package stirling.software.SPDF.UI.impl;
import java.awt.AWTException;
import java.awt.BorderLayout;
import java.awt.Frame;
import java.awt.Image;
import java.awt.MenuItem;
import java.awt.PopupMenu;
import java.awt.SystemTray;
import java.awt.TrayIcon;
import java.awt.event.WindowEvent;
import java.awt.event.WindowStateListener;
import java.io.File;
import java.io.InputStream;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import org.cef.CefApp;
import org.cef.CefClient;
import org.cef.CefSettings;
import org.cef.browser.CefBrowser;
import org.cef.callback.CefBeforeDownloadCallback;
import org.cef.callback.CefDownloadItem;
import org.cef.callback.CefDownloadItemCallback;
import org.cef.handler.CefDownloadHandlerAdapter;
import org.cef.handler.CefLoadHandlerAdapter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
import jakarta.annotation.PreDestroy;
import lombok.extern.slf4j.Slf4j;
import me.friwi.jcefmaven.CefAppBuilder;
import me.friwi.jcefmaven.EnumProgress;
import me.friwi.jcefmaven.MavenCefAppHandlerAdapter;
import me.friwi.jcefmaven.impl.progress.ConsoleProgressHandler;
import stirling.software.SPDF.UI.WebBrowser;
@Component
@Slf4j
@ConditionalOnProperty(
name = "STIRLING_PDF_DESKTOP_UI",
havingValue = "true",
matchIfMissing = false)
public class DesktopBrowser implements WebBrowser {
private static CefApp cefApp;
private static CefClient client;
private static CefBrowser browser;
private static JFrame frame;
private static LoadingWindow loadingWindow;
private static volatile boolean browserInitialized = false;
private static TrayIcon trayIcon;
private static SystemTray systemTray;
public DesktopBrowser() {
SwingUtilities.invokeLater(
() -> {
loadingWindow = new LoadingWindow(null, "Initializing...");
loadingWindow.setVisible(true);
});
}
public void initWebUI(String url) {
CompletableFuture.runAsync(
() -> {
try {
CefAppBuilder builder = new CefAppBuilder();
configureCefSettings(builder);
builder.setProgressHandler(createProgressHandler());
// Build and initialize CEF
cefApp = builder.build();
client = cefApp.createClient();
// Set up download handler
setupDownloadHandler();
// Create browser and frame on EDT
SwingUtilities.invokeAndWait(
() -> {
browser = client.createBrowser(url, false, false);
setupMainFrame();
setupLoadHandler();
// Show the frame immediately but transparent
frame.setVisible(true);
});
} catch (Exception e) {
log.error("Error initializing JCEF browser: ", e);
cleanup();
}
});
}
private void configureCefSettings(CefAppBuilder builder) {
CefSettings settings = builder.getCefSettings();
settings.cache_path = new File("jcef-bundle").getAbsolutePath();
settings.root_cache_path = new File("jcef-bundle").getAbsolutePath();
settings.persist_session_cookies = true;
settings.windowless_rendering_enabled = false;
settings.log_severity = CefSettings.LogSeverity.LOGSEVERITY_INFO;
builder.setAppHandler(
new MavenCefAppHandlerAdapter() {
@Override
public void stateHasChanged(org.cef.CefApp.CefAppState state) {
log.info("CEF state changed: " + state);
if (state == CefApp.CefAppState.TERMINATED) {
System.exit(0);
}
}
});
}
private void setupDownloadHandler() {
client.addDownloadHandler(
new CefDownloadHandlerAdapter() {
@Override
public boolean onBeforeDownload(
CefBrowser browser,
CefDownloadItem downloadItem,
String suggestedName,
CefBeforeDownloadCallback callback) {
callback.Continue("", true);
return true;
}
@Override
public void onDownloadUpdated(
CefBrowser browser,
CefDownloadItem downloadItem,
CefDownloadItemCallback callback) {
if (downloadItem.isComplete()) {
log.info("Download completed: " + downloadItem.getFullPath());
} else if (downloadItem.isCanceled()) {
log.info("Download canceled: " + downloadItem.getFullPath());
}
}
});
}
private ConsoleProgressHandler createProgressHandler() {
return new ConsoleProgressHandler() {
@Override
public void handleProgress(EnumProgress state, float percent) {
Objects.requireNonNull(state, "state cannot be null");
SwingUtilities.invokeLater(
() -> {
if (loadingWindow != null) {
switch (state) {
case LOCATING:
loadingWindow.setStatus("Locating Files...");
loadingWindow.setProgress(0);
break;
case DOWNLOADING:
if (percent >= 0) {
loadingWindow.setStatus(
String.format(
"Downloading additional files: %.0f%%",
percent));
loadingWindow.setProgress((int) percent);
}
break;
case EXTRACTING:
loadingWindow.setStatus("Extracting files...");
loadingWindow.setProgress(60);
break;
case INITIALIZING:
loadingWindow.setStatus("Initializing UI...");
loadingWindow.setProgress(80);
break;
case INITIALIZED:
loadingWindow.setStatus("Finalising startup...");
loadingWindow.setProgress(90);
break;
}
}
});
}
};
}
private void setupMainFrame() {
frame = new JFrame("Stirling-PDF");
frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
frame.setUndecorated(true);
frame.setOpacity(0.0f);
JPanel contentPane = new JPanel(new BorderLayout());
contentPane.setDoubleBuffered(true);
contentPane.add(browser.getUIComponent(), BorderLayout.CENTER);
frame.setContentPane(contentPane);
frame.addWindowListener(
new java.awt.event.WindowAdapter() {
@Override
public void windowClosing(java.awt.event.WindowEvent windowEvent) {
cleanup();
System.exit(0);
}
});
frame.setSize(1280, 768);
frame.setLocationRelativeTo(null);
loadIcon();
}
private void setupLoadHandler() {
client.addLoadHandler(
new CefLoadHandlerAdapter() {
@Override
public void onLoadingStateChange(
CefBrowser browser,
boolean isLoading,
boolean canGoBack,
boolean canGoForward) {
if (!isLoading && !browserInitialized) {
browserInitialized = true;
SwingUtilities.invokeLater(
() -> {
if (loadingWindow != null) {
Timer timer =
new Timer(
500,
e -> {
loadingWindow.dispose();
loadingWindow = null;
frame.dispose();
frame.setOpacity(1.0f);
frame.setUndecorated(false);
frame.pack();
frame.setSize(1280, 800);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
frame.requestFocus();
frame.toFront();
browser.getUIComponent()
.requestFocus();
});
timer.setRepeats(false);
timer.start();
}
});
}
}
});
}
private void setupTrayIcon(Image icon) {
if (!SystemTray.isSupported()) {
log.warn("System tray is not supported");
return;
}
try {
systemTray = SystemTray.getSystemTray();
// Create popup menu
PopupMenu popup = new PopupMenu();
// Create menu items
MenuItem showItem = new MenuItem("Show");
showItem.addActionListener(
e -> {
frame.setVisible(true);
frame.setState(Frame.NORMAL);
});
MenuItem exitItem = new MenuItem("Exit");
exitItem.addActionListener(
e -> {
cleanup();
System.exit(0);
});
// Add menu items to popup menu
popup.add(showItem);
popup.addSeparator();
popup.add(exitItem);
// Create tray icon
trayIcon = new TrayIcon(icon, "Stirling-PDF", popup);
trayIcon.setImageAutoSize(true);
// Add double-click behavior
trayIcon.addActionListener(
e -> {
frame.setVisible(true);
frame.setState(Frame.NORMAL);
});
// Add tray icon to system tray
systemTray.add(trayIcon);
// Modify frame behavior to minimize to tray
frame.addWindowStateListener(
new WindowStateListener() {
public void windowStateChanged(WindowEvent e) {
if (e.getNewState() == Frame.ICONIFIED) {
frame.setVisible(false);
}
}
});
} catch (AWTException e) {
log.error("Error setting up system tray icon", e);
}
}
private void loadIcon() {
try {
Image icon = null;
String[] iconPaths = {"/static/favicon.ico"};
for (String path : iconPaths) {
if (icon != null) break;
try {
try (InputStream is = getClass().getResourceAsStream(path)) {
if (is != null) {
icon = ImageIO.read(is);
break;
}
}
} catch (Exception e) {
log.debug("Could not load icon from " + path, e);
}
}
if (icon != null) {
frame.setIconImage(icon);
setupTrayIcon(icon);
} else {
log.warn("Could not load icon from any source");
}
} catch (Exception e) {
log.error("Error loading icon", e);
}
}
@PreDestroy
public void cleanup() {
if (browser != null) browser.close(true);
if (client != null) client.dispose();
if (cefApp != null) cefApp.dispose();
if (loadingWindow != null) loadingWindow.dispose();
}
}

View File

@@ -1,114 +0,0 @@
package stirling.software.SPDF.UI.impl;
import java.awt.*;
import java.io.InputStream;
import javax.imageio.ImageIO;
import javax.swing.*;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class LoadingWindow extends JDialog {
private final JProgressBar progressBar;
private final JLabel statusLabel;
private final JPanel mainPanel;
private final JLabel brandLabel;
public LoadingWindow(Frame parent, String initialUrl) {
super(parent, "Initializing Stirling-PDF", true);
// Initialize components
mainPanel = new JPanel();
mainPanel.setBackground(Color.WHITE);
mainPanel.setBorder(BorderFactory.createEmptyBorder(20, 30, 20, 30));
mainPanel.setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
// Configure GridBagConstraints
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.insets = new Insets(5, 5, 5, 5);
gbc.weightx = 1.0; // Add horizontal weight
gbc.weighty = 0.0; // Add vertical weight
// Add icon
try {
try (InputStream is = getClass().getResourceAsStream("/static/favicon.ico")) {
if (is != null) {
Image img = ImageIO.read(is);
if (img != null) {
Image scaledImg = img.getScaledInstance(48, 48, Image.SCALE_SMOOTH);
JLabel iconLabel = new JLabel(new ImageIcon(scaledImg));
iconLabel.setHorizontalAlignment(SwingConstants.CENTER);
gbc.gridy = 0;
mainPanel.add(iconLabel, gbc);
}
}
}
} catch (Exception e) {
log.error("Failed to load icon", e);
}
// URL Label with explicit size
brandLabel = new JLabel(initialUrl);
brandLabel.setHorizontalAlignment(SwingConstants.CENTER);
brandLabel.setPreferredSize(new Dimension(300, 25));
brandLabel.setText("Stirling-PDF");
gbc.gridy = 1;
mainPanel.add(brandLabel, gbc);
// Status label with explicit size
statusLabel = new JLabel("Initializing...");
statusLabel.setHorizontalAlignment(SwingConstants.CENTER);
statusLabel.setPreferredSize(new Dimension(300, 25));
gbc.gridy = 2;
mainPanel.add(statusLabel, gbc);
// Progress bar with explicit size
progressBar = new JProgressBar(0, 100);
progressBar.setStringPainted(true);
progressBar.setPreferredSize(new Dimension(300, 25));
gbc.gridy = 3;
mainPanel.add(progressBar, gbc);
// Set dialog properties
setContentPane(mainPanel);
setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
setResizable(false);
setUndecorated(false);
// Set size and position
setSize(400, 200);
setLocationRelativeTo(parent);
setAlwaysOnTop(true);
setProgress(0);
setStatus("Starting...");
}
public void setProgress(final int progress) {
SwingUtilities.invokeLater(
() -> {
try {
progressBar.setValue(Math.min(Math.max(progress, 0), 100));
progressBar.setString(progress + "%");
mainPanel.revalidate();
mainPanel.repaint();
} catch (Exception e) {
log.error("Error updating progress", e);
}
});
}
public void setStatus(final String status) {
log.info(status);
SwingUtilities.invokeLater(
() -> {
try {
statusLabel.setText(status != null ? status : "");
mainPanel.revalidate();
mainPanel.repaint();
} catch (Exception e) {
log.error("Error updating status", e);
}
});
}
}

View File

@@ -260,9 +260,6 @@ public class EndpointConfiguration {
// Pdftohtml dependent endpoints // Pdftohtml dependent endpoints
addEndpointToGroup("Pdftohtml", "pdf-to-html"); addEndpointToGroup("Pdftohtml", "pdf-to-html");
// disabled for now while we resolve issues
disableEndpoint("pdf-to-pdfa");
} }
private void processEnvironmentConfigs() { private void processEnvironmentConfigs() {

View File

@@ -1,14 +1,11 @@
package stirling.software.SPDF.config; package stirling.software.SPDF.config;
import java.io.IOException; import java.io.IOException;
import java.util.Properties;
import java.util.UUID; import java.util.UUID;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.Ordered; import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order; import org.springframework.core.annotation.Order;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import io.micrometer.common.util.StringUtils; import io.micrometer.common.util.StringUtils;
@@ -26,18 +23,6 @@ public class InitialSetup {
@Autowired private ApplicationProperties applicationProperties; @Autowired private ApplicationProperties applicationProperties;
@PostConstruct @PostConstruct
public void init() throws IOException {
initUUIDKey();
initSecretKey();
initEnableCSRFSecurity();
initLegalUrls();
initSetAppVersion();
}
public void initUUIDKey() throws IOException { public void initUUIDKey() throws IOException {
String uuid = applicationProperties.getAutomaticallyGenerated().getUUID(); String uuid = applicationProperties.getAutomaticallyGenerated().getUUID();
if (!GeneralUtils.isValidUUID(uuid)) { if (!GeneralUtils.isValidUUID(uuid)) {
@@ -47,6 +32,7 @@ public class InitialSetup {
} }
} }
@PostConstruct
public void initSecretKey() throws IOException { public void initSecretKey() throws IOException {
String secretKey = applicationProperties.getAutomaticallyGenerated().getKey(); String secretKey = applicationProperties.getAutomaticallyGenerated().getKey();
if (!GeneralUtils.isValidUUID(secretKey)) { if (!GeneralUtils.isValidUUID(secretKey)) {
@@ -56,24 +42,13 @@ public class InitialSetup {
} }
} }
public void initEnableCSRFSecurity() throws IOException { @PostConstruct
if (GeneralUtils.isVersionHigher(
"0.36.0", applicationProperties.getAutomaticallyGenerated().getAppVersion())) {
Boolean csrf = applicationProperties.getSecurity().getCsrfDisabled();
if (!csrf) {
GeneralUtils.saveKeyToConfig("security.csrfDisabled", false, false);
GeneralUtils.saveKeyToConfig("system.enableAnalytics", "true", false);
applicationProperties.getSecurity().setCsrfDisabled(false);
}
}
}
public void initLegalUrls() throws IOException { public void initLegalUrls() throws IOException {
// Initialize Terms and Conditions // Initialize Terms and Conditions
String termsUrl = applicationProperties.getLegal().getTermsAndConditions(); String termsUrl = applicationProperties.getLegal().getTermsAndConditions();
if (StringUtils.isEmpty(termsUrl)) { if (StringUtils.isEmpty(termsUrl)) {
String defaultTermsUrl = "https://www.stirlingpdf.com/terms-and-conditions"; String defaultTermsUrl = "https://www.stirlingpdf.com/terms-and-conditions";
GeneralUtils.saveKeyToConfig("legal.termsAndConditions", defaultTermsUrl, false); GeneralUtils.saveKeyToConfig("legal.termsAndConditions", defaultTermsUrl);
applicationProperties.getLegal().setTermsAndConditions(defaultTermsUrl); applicationProperties.getLegal().setTermsAndConditions(defaultTermsUrl);
} }
@@ -81,23 +56,8 @@ public class InitialSetup {
String privacyUrl = applicationProperties.getLegal().getPrivacyPolicy(); String privacyUrl = applicationProperties.getLegal().getPrivacyPolicy();
if (StringUtils.isEmpty(privacyUrl)) { if (StringUtils.isEmpty(privacyUrl)) {
String defaultPrivacyUrl = "https://www.stirlingpdf.com/privacy-policy"; String defaultPrivacyUrl = "https://www.stirlingpdf.com/privacy-policy";
GeneralUtils.saveKeyToConfig("legal.privacyPolicy", defaultPrivacyUrl, false); GeneralUtils.saveKeyToConfig("legal.privacyPolicy", defaultPrivacyUrl);
applicationProperties.getLegal().setPrivacyPolicy(defaultPrivacyUrl); applicationProperties.getLegal().setPrivacyPolicy(defaultPrivacyUrl);
} }
} }
public void initSetAppVersion() throws IOException {
String appVersion = "0.0.0";
Resource resource = new ClassPathResource("version.properties");
Properties props = new Properties();
try {
props.load(resource.getInputStream());
appVersion = props.getProperty("version");
} catch (Exception e) {
}
applicationProperties.getAutomaticallyGenerated().setAppVersion(appVersion);
GeneralUtils.saveKeyToConfig("AutomaticallyGenerated.appVersion", appVersion, false);
}
} }

View File

@@ -75,6 +75,5 @@ public class InitialSecuritySetup {
userService.addApiKeyToUser(Role.INTERNAL_API_USER.getRoleId()); userService.addApiKeyToUser(Role.INTERNAL_API_USER.getRoleId());
log.info("Internal API user created: " + Role.INTERNAL_API_USER.getRoleId()); log.info("Internal API user created: " + Role.INTERNAL_API_USER.getRoleId());
} }
userService.syncCustomApiUser(applicationProperties.getSecurity().getCustomGlobalAPIKey());
} }
} }

View File

@@ -1,5 +1,6 @@
package stirling.software.SPDF.config.security; package stirling.software.SPDF.config.security;
import java.io.IOException;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.util.*; import java.util.*;
@@ -12,6 +13,7 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn; import org.springframework.context.annotation.DependsOn;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.ProviderManager; import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider; import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
@@ -30,21 +32,35 @@ import org.springframework.security.oauth2.client.registration.InMemoryClientReg
import org.springframework.security.oauth2.core.user.OAuth2UserAuthority; import org.springframework.security.oauth2.core.user.OAuth2UserAuthority;
import org.springframework.security.saml2.core.Saml2X509Credential; import org.springframework.security.saml2.core.Saml2X509Credential;
import org.springframework.security.saml2.core.Saml2X509Credential.Saml2X509CredentialType; import org.springframework.security.saml2.core.Saml2X509Credential.Saml2X509CredentialType;
import org.springframework.security.saml2.provider.service.authentication.AbstractSaml2AuthenticationRequest;
import org.springframework.security.saml2.provider.service.authentication.OpenSaml4AuthenticationProvider; import org.springframework.security.saml2.provider.service.authentication.OpenSaml4AuthenticationProvider;
import org.springframework.security.saml2.provider.service.registration.InMemoryRelyingPartyRegistrationRepository; import org.springframework.security.saml2.provider.service.registration.InMemoryRelyingPartyRegistrationRepository;
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration; import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository; import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;
import org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding; import org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding;
import org.springframework.security.saml2.provider.service.web.HttpSessionSaml2AuthenticationRequestRepository;
import org.springframework.security.saml2.provider.service.web.Saml2AuthenticationRequestRepository;
import org.springframework.security.saml2.provider.service.web.authentication.OpenSaml4AuthenticationRequestResolver; import org.springframework.security.saml2.provider.service.web.authentication.OpenSaml4AuthenticationRequestResolver;
import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository; import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy;
import org.springframework.security.web.context.SecurityContextHolderFilter;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository; import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
import org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler; import org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler;
import org.springframework.security.web.savedrequest.NullRequestCache; import org.springframework.security.web.savedrequest.NullRequestCache;
import org.springframework.security.web.session.ForceEagerSessionCreationFilter;
import org.springframework.security.web.session.HttpSessionEventPublisher;
import org.springframework.security.web.session.SessionManagementFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.session.web.http.CookieSerializer;
import org.springframework.session.web.http.DefaultCookieSerializer;
import org.springframework.web.filter.OncePerRequestFilter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.config.security.oauth2.CustomOAuth2AuthenticationFailureHandler; import stirling.software.SPDF.config.security.oauth2.CustomOAuth2AuthenticationFailureHandler;
import stirling.software.SPDF.config.security.oauth2.CustomOAuth2AuthenticationSuccessHandler; import stirling.software.SPDF.config.security.oauth2.CustomOAuth2AuthenticationSuccessHandler;
@@ -99,7 +115,7 @@ public class SecurityConfiguration {
@Bean @Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
if (applicationProperties.getSecurity().getCsrfDisabled() || !loginEnabledValue) { if (applicationProperties.getSecurity().getCsrfDisabled()) {
http.csrf(csrf -> csrf.disable()); http.csrf(csrf -> csrf.disable());
} }
@@ -116,7 +132,7 @@ public class SecurityConfiguration {
csrf -> csrf ->
csrf.ignoringRequestMatchers( csrf.ignoringRequestMatchers(
request -> { request -> {
String apiKey = request.getHeader("X-API-KEY"); String apiKey = request.getHeader("X-API-Key");
// If there's no API key, don't ignore CSRF // If there's no API key, don't ignore CSRF
// (return false) // (return false)
@@ -147,7 +163,7 @@ public class SecurityConfiguration {
http.sessionManagement( http.sessionManagement(
sessionManagement -> sessionManagement ->
sessionManagement sessionManagement
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED) .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
.maximumSessions(10) .maximumSessions(10)
.maxSessionsPreventsLogin(false) .maxSessionsPreventsLogin(false)
.sessionRegistry(sessionRegistry) .sessionRegistry(sessionRegistry)
@@ -271,6 +287,7 @@ public class SecurityConfiguration {
relyingPartyRegistrations()) relyingPartyRegistrations())
.authenticationManager( .authenticationManager(
new ProviderManager(authenticationProvider)) new ProviderManager(authenticationProvider))
.successHandler( .successHandler(
new CustomSaml2AuthenticationSuccessHandler( new CustomSaml2AuthenticationSuccessHandler(
loginAttemptService, loginAttemptService,
@@ -289,17 +306,17 @@ public class SecurityConfiguration {
} }
} else { } else {
// if (!applicationProperties.getSecurity().getCsrfDisabled()) { if (!applicationProperties.getSecurity().getCsrfDisabled()) {
// CookieCsrfTokenRepository cookieRepo = CookieCsrfTokenRepository cookieRepo =
// CookieCsrfTokenRepository.withHttpOnlyFalse(); CookieCsrfTokenRepository.withHttpOnlyFalse();
// CsrfTokenRequestAttributeHandler requestHandler = CsrfTokenRequestAttributeHandler requestHandler =
// new CsrfTokenRequestAttributeHandler(); new CsrfTokenRequestAttributeHandler();
// requestHandler.setCsrfRequestAttributeName(null); requestHandler.setCsrfRequestAttributeName(null);
// http.csrf( http.csrf(
// csrf -> csrf ->
// csrf.csrfTokenRepository(cookieRepo) csrf.csrfTokenRepository(cookieRepo)
// .csrfTokenRequestHandler(requestHandler)); .csrfTokenRequestHandler(requestHandler));
// } }
http.authorizeHttpRequests(authz -> authz.anyRequest().permitAll()); http.authorizeHttpRequests(authz -> authz.anyRequest().permitAll());
} }

View File

@@ -71,7 +71,7 @@ public class UserAuthenticationFilter extends OncePerRequestFilter {
// Check for API key in the request headers if no authentication exists // Check for API key in the request headers if no authentication exists
if (authentication == null || !authentication.isAuthenticated()) { if (authentication == null || !authentication.isAuthenticated()) {
String apiKey = request.getHeader("X-API-KEY"); String apiKey = request.getHeader("X-API-Key");
if (apiKey != null && !apiKey.trim().isEmpty()) { if (apiKey != null && !apiKey.trim().isEmpty()) {
try { try {
// Use API key to authenticate. This requires you to have an authentication // Use API key to authenticate. This requires you to have an authentication

View File

@@ -59,7 +59,7 @@ public class UserBasedRateLimitingFilter extends OncePerRequestFilter {
String identifier = null; String identifier = null;
// Check for API key in the request headers // Check for API key in the request headers
String apiKey = request.getHeader("X-API-KEY"); String apiKey = request.getHeader("X-API-Key");
if (apiKey != null && !apiKey.trim().isEmpty()) { if (apiKey != null && !apiKey.trim().isEmpty()) {
identifier = identifier =
"API_KEY_" + apiKey; // Prefix to distinguish between API keys and usernames "API_KEY_" + apiKey; // Prefix to distinguish between API keys and usernames
@@ -79,7 +79,7 @@ public class UserBasedRateLimitingFilter extends OncePerRequestFilter {
Role userRole = Role userRole =
getRoleFromAuthentication(SecurityContextHolder.getContext().getAuthentication()); getRoleFromAuthentication(SecurityContextHolder.getContext().getAuthentication());
if (request.getHeader("X-API-KEY") != null) { if (request.getHeader("X-API-Key") != null) {
// It's an API call // It's an API call
processRequest( processRequest(
userRole.getApiCallsPerDay(), userRole.getApiCallsPerDay(),

View File

@@ -390,37 +390,6 @@ public class UserService implements UserServiceInterface {
} }
} }
@Transactional
public void syncCustomApiUser(String customApiKey) throws IOException {
if (customApiKey == null || customApiKey.trim().length() == 0) {
return;
}
String username = "CUSTOM_API_USER";
Optional<User> existingUser = findByUsernameIgnoreCase(username);
if (!existingUser.isPresent()) {
// Create new user with API role
User user = new User();
user.setUsername(username);
user.setPassword(UUID.randomUUID().toString());
user.setEnabled(true);
user.setFirstLogin(false);
user.setAuthenticationType(AuthenticationType.WEB);
user.setApiKey(customApiKey);
user.addAuthority(new Authority(Role.INTERNAL_API_USER.getRoleId(), user));
userRepository.save(user);
databaseBackupHelper.exportDatabase();
} else {
// Update API key if it has changed
User user = existingUser.get();
if (!customApiKey.equals(user.getApiKey())) {
user.setApiKey(customApiKey);
userRepository.save(user);
databaseBackupHelper.exportDatabase();
}
}
}
@Override @Override
public long getTotalUsersCount() { public long getTotalUsersCount() {
return userRepository.count(); return userRepository.count();

View File

@@ -52,115 +52,84 @@ public class SplitPDFController {
"This endpoint splits a given PDF file into separate documents based on the specified page numbers or ranges. Users can specify pages using individual numbers, ranges, or 'all' for every page. Input:PDF Output:PDF Type:SIMO") "This endpoint splits a given PDF file into separate documents based on the specified page numbers or ranges. Users can specify pages using individual numbers, ranges, or 'all' for every page. Input:PDF Output:PDF Type:SIMO")
public ResponseEntity<byte[]> splitPdf(@ModelAttribute PDFWithPageNums request) public ResponseEntity<byte[]> splitPdf(@ModelAttribute PDFWithPageNums request)
throws IOException { throws IOException {
MultipartFile file = request.getFileInput();
String pages = request.getPageNumbers();
// open the pdf document
PDDocument document = null; PDDocument document = Loader.loadPDF(file.getBytes());
Path zipFile = null; // PdfMetadata metadata = PdfMetadataService.extractMetadataFromPdf(document);
int totalPages = document.getNumberOfPages();
List<Integer> pageNumbers = request.getPageNumbersList(document, false);
if (!pageNumbers.contains(totalPages - 1)) {
// Create a mutable ArrayList so we can add to it
pageNumbers = new ArrayList<>(pageNumbers);
pageNumbers.add(totalPages - 1);
}
logger.info(
"Splitting PDF into pages: {}",
pageNumbers.stream().map(String::valueOf).collect(Collectors.joining(",")));
// split the document
List<ByteArrayOutputStream> splitDocumentsBoas = new ArrayList<>(); List<ByteArrayOutputStream> splitDocumentsBoas = new ArrayList<>();
int previousPageNumber = 0;
try { for (int splitPoint : pageNumbers) {
try (PDDocument splitDocument =
MultipartFile file = request.getFileInput(); pdfDocumentFactory.createNewDocumentBasedOnOldDocument(document)) {
String pages = request.getPageNumbers(); for (int i = previousPageNumber; i <= splitPoint; i++) {
// open the pdf document PDPage page = document.getPage(i);
splitDocument.addPage(page);
document = Loader.loadPDF(file.getBytes()); logger.info("Adding page {} to split document", i);
// PdfMetadata metadata = PdfMetadataService.extractMetadataFromPdf(document);
int totalPages = document.getNumberOfPages();
List<Integer> pageNumbers = request.getPageNumbersList(document, false);
if (!pageNumbers.contains(totalPages - 1)) {
// Create a mutable ArrayList so we can add to it
pageNumbers = new ArrayList<>(pageNumbers);
pageNumbers.add(totalPages - 1);
}
logger.info(
"Splitting PDF into pages: {}",
pageNumbers.stream().map(String::valueOf).collect(Collectors.joining(",")));
// split the document
splitDocumentsBoas = new ArrayList<>();
int previousPageNumber = 0;
for (int splitPoint : pageNumbers) {
try (PDDocument splitDocument =
pdfDocumentFactory.createNewDocumentBasedOnOldDocument(document)) {
for (int i = previousPageNumber; i <= splitPoint; i++) {
PDPage page = document.getPage(i);
splitDocument.addPage(page);
logger.info("Adding page {} to split document", i);
}
previousPageNumber = splitPoint + 1;
// Transfer metadata to split pdf
// PdfMetadataService.setMetadataToPdf(splitDocument, metadata);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
splitDocument.save(baos);
splitDocumentsBoas.add(baos);
} catch (Exception e) {
logger.error("Failed splitting documents and saving them", e);
throw e;
} }
} previousPageNumber = splitPoint + 1;
// closing the original document // Transfer metadata to split pdf
document.close(); // PdfMetadataService.setMetadataToPdf(splitDocument, metadata);
zipFile = Files.createTempFile("split_documents", ".zip"); ByteArrayOutputStream baos = new ByteArrayOutputStream();
splitDocument.save(baos);
String filename = splitDocumentsBoas.add(baos);
Filenames.toSimpleFileName(file.getOriginalFilename())
.replaceFirst("[.][^.]+$", "");
try (ZipOutputStream zipOut = new ZipOutputStream(Files.newOutputStream(zipFile))) {
// loop through the split documents and write them to the zip file
for (int i = 0; i < splitDocumentsBoas.size(); i++) {
String fileName = filename + "_" + (i + 1) + ".pdf";
ByteArrayOutputStream baos = splitDocumentsBoas.get(i);
byte[] pdf = baos.toByteArray();
// Add PDF file to the zip
ZipEntry pdfEntry = new ZipEntry(fileName);
zipOut.putNextEntry(pdfEntry);
zipOut.write(pdf);
zipOut.closeEntry();
logger.info("Wrote split document {} to zip file", fileName);
}
} catch (Exception e) { } catch (Exception e) {
logger.error("Failed writing to zip", e); logger.error("Failed splitting documents and saving them", e);
throw e; throw e;
} }
logger.info(
"Successfully created zip file with split documents: {}", zipFile.toString());
byte[] data = Files.readAllBytes(zipFile);
Files.deleteIfExists(zipFile);
// return the Resource in the response
return WebResponseUtils.bytesToWebResponse(
data, filename + ".zip", MediaType.APPLICATION_OCTET_STREAM);
} finally {
try {
// Close the main document
if (document != null) {
document.close();
}
// Close all ByteArrayOutputStreams
for (ByteArrayOutputStream baos : splitDocumentsBoas) {
if (baos != null) {
baos.close();
}
}
// Delete temporary zip file
if (zipFile != null) {
Files.deleteIfExists(zipFile);
}
} catch (Exception e) {
logger.error("Error while cleaning up resources", e);
}
} }
// closing the original document
document.close();
Path zipFile = Files.createTempFile("split_documents", ".zip");
String filename =
Filenames.toSimpleFileName(file.getOriginalFilename())
.replaceFirst("[.][^.]+$", "");
try (ZipOutputStream zipOut = new ZipOutputStream(Files.newOutputStream(zipFile))) {
// loop through the split documents and write them to the zip file
for (int i = 0; i < splitDocumentsBoas.size(); i++) {
String fileName = filename + "_" + (i + 1) + ".pdf";
ByteArrayOutputStream baos = splitDocumentsBoas.get(i);
byte[] pdf = baos.toByteArray();
// Add PDF file to the zip
ZipEntry pdfEntry = new ZipEntry(fileName);
zipOut.putNextEntry(pdfEntry);
zipOut.write(pdf);
zipOut.closeEntry();
logger.info("Wrote split document {} to zip file", fileName);
}
} catch (Exception e) {
logger.error("Failed writing to zip", e);
throw e;
}
logger.info("Successfully created zip file with split documents: {}", zipFile.toString());
byte[] data = Files.readAllBytes(zipFile);
Files.deleteIfExists(zipFile);
// return the Resource in the response
return WebResponseUtils.bytesToWebResponse(
data, filename + ".zip", MediaType.APPLICATION_OCTET_STREAM);
} }
} }

View File

@@ -59,86 +59,70 @@ public class SplitPdfByChaptersController {
public ResponseEntity<byte[]> splitPdf(@ModelAttribute SplitPdfByChaptersRequest request) public ResponseEntity<byte[]> splitPdf(@ModelAttribute SplitPdfByChaptersRequest request)
throws Exception { throws Exception {
MultipartFile file = request.getFileInput(); MultipartFile file = request.getFileInput();
PDDocument sourceDocument = null; boolean includeMetadata = request.getIncludeMetadata();
Path zipFile = null; Integer bookmarkLevel =
request.getBookmarkLevel(); // levels start from 0 (top most bookmarks)
try { if (bookmarkLevel < 0) {
boolean includeMetadata = request.getIncludeMetadata(); return ResponseEntity.badRequest().body("Invalid bookmark level".getBytes());
Integer bookmarkLevel =
request.getBookmarkLevel(); // levels start from 0 (top most bookmarks)
if (bookmarkLevel < 0) {
return ResponseEntity.badRequest().body("Invalid bookmark level".getBytes());
}
sourceDocument = Loader.loadPDF(file.getBytes());
PDDocumentOutline outline = sourceDocument.getDocumentCatalog().getDocumentOutline();
if (outline == null) {
logger.warn("No outline found for {}", file.getOriginalFilename());
return ResponseEntity.badRequest().body("No outline found".getBytes());
}
List<Bookmark> bookmarks = new ArrayList<>();
try {
bookmarks =
extractOutlineItems(
sourceDocument,
outline.getFirstChild(),
bookmarks,
outline.getFirstChild().getNextSibling(),
0,
bookmarkLevel);
// to handle last page edge case
bookmarks.get(bookmarks.size() - 1).setEndPage(sourceDocument.getNumberOfPages());
Bookmark lastBookmark = bookmarks.get(bookmarks.size() - 1);
} catch (Exception e) {
logger.error("Unable to extract outline items", e);
return ResponseEntity.internalServerError()
.body("Unable to extract outline items".getBytes());
}
boolean allowDuplicates = request.getAllowDuplicates();
if (!allowDuplicates) {
/*
duplicates are generated when multiple bookmarks correspond to the same page,
if the user doesn't want duplicates mergeBookmarksThatCorrespondToSamePage() method will merge the titles of all
the bookmarks that correspond to the same page, and treat them as a single bookmark
*/
bookmarks = mergeBookmarksThatCorrespondToSamePage(bookmarks);
}
for (Bookmark bookmark : bookmarks) {
logger.info(
"{}::::{} to {}",
bookmark.getTitle(),
bookmark.getStartPage(),
bookmark.getEndPage());
}
List<ByteArrayOutputStream> splitDocumentsBoas =
getSplitDocumentsBoas(sourceDocument, bookmarks, includeMetadata);
zipFile = createZipFile(bookmarks, splitDocumentsBoas);
byte[] data = Files.readAllBytes(zipFile);
Files.deleteIfExists(zipFile);
String filename =
Filenames.toSimpleFileName(file.getOriginalFilename())
.replaceFirst("[.][^.]+$", "");
sourceDocument.close();
return WebResponseUtils.bytesToWebResponse(
data, filename + ".zip", MediaType.APPLICATION_OCTET_STREAM);
} finally {
try {
if (sourceDocument != null) {
sourceDocument.close();
}
if (zipFile != null) {
Files.deleteIfExists(zipFile);
}
} catch (Exception e) {
logger.error("Error while cleaning up resources", e);
}
} }
PDDocument sourceDocument = Loader.loadPDF(file.getBytes());
PDDocumentOutline outline = sourceDocument.getDocumentCatalog().getDocumentOutline();
if (outline == null) {
logger.warn("No outline found for {}", file.getOriginalFilename());
return ResponseEntity.badRequest().body("No outline found".getBytes());
}
List<Bookmark> bookmarks = new ArrayList<>();
try {
bookmarks =
extractOutlineItems(
sourceDocument,
outline.getFirstChild(),
bookmarks,
outline.getFirstChild().getNextSibling(),
0,
bookmarkLevel);
// to handle last page edge case
bookmarks.get(bookmarks.size() - 1).setEndPage(sourceDocument.getNumberOfPages());
Bookmark lastBookmark = bookmarks.get(bookmarks.size() - 1);
} catch (Exception e) {
logger.error("Unable to extract outline items", e);
return ResponseEntity.internalServerError()
.body("Unable to extract outline items".getBytes());
}
boolean allowDuplicates = request.getAllowDuplicates();
if (!allowDuplicates) {
/*
duplicates are generated when multiple bookmarks correspond to the same page,
if the user doesn't want duplicates mergeBookmarksThatCorrespondToSamePage() method will merge the titles of all
the bookmarks that correspond to the same page, and treat them as a single bookmark
*/
bookmarks = mergeBookmarksThatCorrespondToSamePage(bookmarks);
}
for (Bookmark bookmark : bookmarks) {
logger.info(
"{}::::{} to {}",
bookmark.getTitle(),
bookmark.getStartPage(),
bookmark.getEndPage());
}
List<ByteArrayOutputStream> splitDocumentsBoas =
getSplitDocumentsBoas(sourceDocument, bookmarks, includeMetadata);
Path zipFile = createZipFile(bookmarks, splitDocumentsBoas);
byte[] data = Files.readAllBytes(zipFile);
Files.deleteIfExists(zipFile);
String filename =
Filenames.toSimpleFileName(file.getOriginalFilename())
.replaceFirst("[.][^.]+$", "");
sourceDocument.close();
return WebResponseUtils.bytesToWebResponse(
data, filename + ".zip", MediaType.APPLICATION_OCTET_STREAM);
} }
private List<Bookmark> mergeBookmarksThatCorrespondToSamePage(List<Bookmark> bookmarks) { private List<Bookmark> mergeBookmarksThatCorrespondToSamePage(List<Bookmark> bookmarks) {

View File

@@ -105,13 +105,15 @@ public class SplitPdfBySectionsController {
if (sectionNum == horiz * verti) pageNum++; if (sectionNum == horiz * verti) pageNum++;
} }
data = Files.readAllBytes(zipFile); } catch (Exception e) {
return WebResponseUtils.bytesToWebResponse( logger.error("exception", e);
data, filename + "_split.zip", MediaType.APPLICATION_OCTET_STREAM);
} finally { } finally {
data = Files.readAllBytes(zipFile);
Files.deleteIfExists(zipFile); Files.deleteIfExists(zipFile);
} }
return WebResponseUtils.bytesToWebResponse(
data, filename + "_split.zip", MediaType.APPLICATION_OCTET_STREAM);
} }
public List<PDDocument> splitPdfPages( public List<PDDocument> splitPdfPages(

View File

@@ -65,137 +65,112 @@ public class ConvertImgPDFController {
String colorType = request.getColorType(); String colorType = request.getColorType();
String dpi = request.getDpi(); String dpi = request.getDpi();
Path tempFile = null; byte[] pdfBytes = file.getBytes();
Path tempOutputDir = null; ImageType colorTypeResult = ImageType.RGB;
Path tempPdfPath = null; if ("greyscale".equals(colorType)) {
colorTypeResult = ImageType.GRAY;
} else if ("blackwhite".equals(colorType)) {
colorTypeResult = ImageType.BINARY;
}
// returns bytes for image
boolean singleImage = "single".equals(singleOrMultiple);
byte[] result = null; byte[] result = null;
String filename =
Filenames.toSimpleFileName(file.getOriginalFilename())
.replaceFirst("[.][^.]+$", "");
try { result =
byte[] pdfBytes = file.getBytes(); PdfUtils.convertFromPdf(
ImageType colorTypeResult = ImageType.RGB; pdfBytes,
if ("greyscale".equals(colorType)) { "webp".equalsIgnoreCase(imageFormat) ? "png" : imageFormat.toUpperCase(),
colorTypeResult = ImageType.GRAY; colorTypeResult,
} else if ("blackwhite".equals(colorType)) { singleImage,
colorTypeResult = ImageType.BINARY; Integer.valueOf(dpi),
} filename);
// returns bytes for image if (result == null || result.length == 0) {
boolean singleImage = "single".equals(singleOrMultiple); logger.error("resultant bytes for {} is null, error converting ", filename);
String filename = }
Filenames.toSimpleFileName(file.getOriginalFilename()) if ("webp".equalsIgnoreCase(imageFormat) && !CheckProgramInstall.isPythonAvailable()) {
.replaceFirst("[.][^.]+$", ""); throw new IOException("Python is not installed. Required for WebP conversion.");
} else if ("webp".equalsIgnoreCase(imageFormat)
result = && CheckProgramInstall.isPythonAvailable()) {
PdfUtils.convertFromPdf( // Write the output stream to a temp file
pdfBytes, Path tempFile = Files.createTempFile("temp_png", ".png");
"webp".equalsIgnoreCase(imageFormat) try (FileOutputStream fos = new FileOutputStream(tempFile.toFile())) {
? "png" fos.write(result);
: imageFormat.toUpperCase(), fos.flush();
colorTypeResult,
singleImage,
Integer.valueOf(dpi),
filename);
if (result == null || result.length == 0) {
logger.error("resultant bytes for {} is null, error converting ", filename);
}
if ("webp".equalsIgnoreCase(imageFormat) && !CheckProgramInstall.isPythonAvailable()) {
throw new IOException("Python is not installed. Required for WebP conversion.");
} else if ("webp".equalsIgnoreCase(imageFormat)
&& CheckProgramInstall.isPythonAvailable()) {
// Write the output stream to a temp file
tempFile = Files.createTempFile("temp_png", ".png");
try (FileOutputStream fos = new FileOutputStream(tempFile.toFile())) {
fos.write(result);
fos.flush();
}
String pythonVersion = CheckProgramInstall.getAvailablePythonCommand();
List<String> command = new ArrayList<>();
command.add(pythonVersion);
command.add("./scripts/png_to_webp.py"); // Python script to handle the conversion
// Create a temporary directory for the output WebP files
tempOutputDir = Files.createTempDirectory("webp_output");
if (singleImage) {
// Run the Python script to convert PNG to WebP
command.add(tempFile.toString());
command.add(tempOutputDir.toString());
command.add("--single");
} else {
// Save the uploaded PDF to a temporary file
tempPdfPath = Files.createTempFile("temp_pdf", ".pdf");
file.transferTo(tempPdfPath.toFile());
// Run the Python script to convert PDF to WebP
command.add(tempPdfPath.toString());
command.add(tempOutputDir.toString());
}
command.add("--dpi");
command.add(dpi);
ProcessExecutorResult resultProcess =
ProcessExecutor.getInstance(ProcessExecutor.Processes.PYTHON_OPENCV)
.runCommandWithOutputHandling(command);
// Find all WebP files in the output directory
List<Path> webpFiles =
Files.walk(tempOutputDir)
.filter(path -> path.toString().endsWith(".webp"))
.collect(Collectors.toList());
if (webpFiles.isEmpty()) {
logger.error("No WebP files were created in: {}", tempOutputDir.toString());
throw new IOException(
"No WebP files were created. " + resultProcess.getMessages());
}
byte[] bodyBytes = new byte[0];
if (webpFiles.size() == 1) {
// Return the single WebP file directly
Path webpFilePath = webpFiles.get(0);
bodyBytes = Files.readAllBytes(webpFilePath);
} else {
// Create a ZIP file containing all WebP images
ByteArrayOutputStream zipOutputStream = new ByteArrayOutputStream();
try (ZipOutputStream zos = new ZipOutputStream(zipOutputStream)) {
for (Path webpFile : webpFiles) {
zos.putNextEntry(new ZipEntry(webpFile.getFileName().toString()));
Files.copy(webpFile, zos);
zos.closeEntry();
}
}
bodyBytes = zipOutputStream.toByteArray();
}
// Clean up the temporary files
Files.deleteIfExists(tempFile);
if (tempOutputDir != null) FileUtils.deleteDirectory(tempOutputDir.toFile());
result = bodyBytes;
} }
String pythonVersion = CheckProgramInstall.getAvailablePythonCommand();
List<String> command = new ArrayList<>();
command.add(pythonVersion);
command.add("./scripts/png_to_webp.py"); // Python script to handle the conversion
// Create a temporary directory for the output WebP files
Path tempOutputDir = Files.createTempDirectory("webp_output");
if (singleImage) { if (singleImage) {
String docName = filename + "." + imageFormat; // Run the Python script to convert PNG to WebP
MediaType mediaType = MediaType.parseMediaType(getMediaType(imageFormat)); command.add(tempFile.toString());
return WebResponseUtils.bytesToWebResponse(result, docName, mediaType); command.add(tempOutputDir.toString());
command.add("--single");
} else { } else {
String zipFilename = filename + "_convertedToImages.zip"; // Save the uploaded PDF to a temporary file
return WebResponseUtils.bytesToWebResponse( Path tempPdfPath = Files.createTempFile("temp_pdf", ".pdf");
result, zipFilename, MediaType.APPLICATION_OCTET_STREAM); file.transferTo(tempPdfPath.toFile());
// Run the Python script to convert PDF to WebP
command.add(tempPdfPath.toString());
command.add(tempOutputDir.toString());
}
command.add("--dpi");
command.add(dpi);
ProcessExecutorResult resultProcess =
ProcessExecutor.getInstance(ProcessExecutor.Processes.PYTHON_OPENCV)
.runCommandWithOutputHandling(command);
// Find all WebP files in the output directory
List<Path> webpFiles =
Files.walk(tempOutputDir)
.filter(path -> path.toString().endsWith(".webp"))
.collect(Collectors.toList());
if (webpFiles.isEmpty()) {
logger.error("No WebP files were created in: {}", tempOutputDir.toString());
throw new IOException("No WebP files were created. " + resultProcess.getMessages());
} }
} finally { byte[] bodyBytes = new byte[0];
try {
// Clean up temporary files if (webpFiles.size() == 1) {
if (tempFile != null) { // Return the single WebP file directly
Files.deleteIfExists(tempFile); Path webpFilePath = webpFiles.get(0);
bodyBytes = Files.readAllBytes(webpFilePath);
} else {
// Create a ZIP file containing all WebP images
ByteArrayOutputStream zipOutputStream = new ByteArrayOutputStream();
try (ZipOutputStream zos = new ZipOutputStream(zipOutputStream)) {
for (Path webpFile : webpFiles) {
zos.putNextEntry(new ZipEntry(webpFile.getFileName().toString()));
Files.copy(webpFile, zos);
zos.closeEntry();
}
} }
if (tempPdfPath != null) { bodyBytes = zipOutputStream.toByteArray();
Files.deleteIfExists(tempPdfPath);
}
if (tempOutputDir != null) {
FileUtils.deleteDirectory(tempOutputDir.toFile());
}
} catch (Exception e) {
logger.error("Error cleaning up temporary files", e);
} }
// Clean up the temporary files
Files.deleteIfExists(tempFile);
if (tempOutputDir != null) FileUtils.deleteDirectory(tempOutputDir.toFile());
result = bodyBytes;
}
if (singleImage) {
String docName = filename + "." + imageFormat;
MediaType mediaType = MediaType.parseMediaType(getMediaType(imageFormat));
return WebResponseUtils.bytesToWebResponse(result, docName, mediaType);
} else {
String zipFilename = filename + "_convertedToImages.zip";
return WebResponseUtils.bytesToWebResponse(
result, zipFilename, MediaType.APPLICATION_OCTET_STREAM);
} }
} }

View File

@@ -14,8 +14,6 @@ import org.apache.pdfbox.pdmodel.PDDocumentInformation;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
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;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
@@ -28,7 +26,6 @@ import io.swagger.v3.oas.annotations.tags.Tag;
import stirling.software.SPDF.model.api.misc.MetadataRequest; import stirling.software.SPDF.model.api.misc.MetadataRequest;
import stirling.software.SPDF.utils.WebResponseUtils; import stirling.software.SPDF.utils.WebResponseUtils;
import stirling.software.SPDF.utils.propertyeditor.StringToMapPropertyEditor;
@RestController @RestController
@RequestMapping("/api/v1/misc") @RequestMapping("/api/v1/misc")
@@ -47,11 +44,6 @@ public class MetadataController {
return entry; return entry;
} }
@InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(Map.class, "allRequestParams", new StringToMapPropertyEditor());
}
@PostMapping(consumes = "multipart/form-data", value = "/update-metadata") @PostMapping(consumes = "multipart/form-data", value = "/update-metadata")
@Operation( @Operation(
summary = "Update metadata of a PDF file", summary = "Update metadata of a PDF file",

View File

@@ -87,7 +87,7 @@ public class OCRController {
Files.createDirectories(tempOutputDir); Files.createDirectories(tempOutputDir);
Files.createDirectories(tempImagesDir); Files.createDirectories(tempImagesDir);
Process process = null;
try { try {
// Save input file // Save input file
inputFile.transferTo(tempInputFile.toFile()); inputFile.transferTo(tempInputFile.toFile());
@@ -139,7 +139,7 @@ public class OCRController {
command.add("pdf"); // Always output PDF command.add("pdf"); // Always output PDF
ProcessBuilder pb = new ProcessBuilder(command); ProcessBuilder pb = new ProcessBuilder(command);
process = pb.start(); Process process = pb.start();
// Capture any error output // Capture any error output
try (BufferedReader reader = try (BufferedReader reader =
@@ -188,10 +188,6 @@ public class OCRController {
.body(pdfContent); .body(pdfContent);
} finally { } finally {
if (process != null) {
process.destroy();
}
// Clean up temporary files // Clean up temporary files
deleteDirectory(tempDir); deleteDirectory(tempDir);
} }

View File

@@ -229,22 +229,10 @@ public class StampController {
calculatePositionY( calculatePositionY(
pageSize, position, calculateTextCapHeight(font, fontSize), margin); pageSize, position, calculateTextCapHeight(font, fontSize), margin);
} }
// Split the stampText into multiple lines
String[] lines = stampText.split("\\\\n");
// Calculate dynamic line height based on font ascent and descent
float ascent = font.getFontDescriptor().getAscent();
float descent = font.getFontDescriptor().getDescent();
float lineHeight = ((ascent - descent) / 1000) * fontSize;
contentStream.beginText(); contentStream.beginText();
for (int i = 0; i < lines.length; i++) { contentStream.setTextMatrix(Matrix.getRotateInstance(Math.toRadians(rotation), x, y));
String line = lines[i]; contentStream.showText(stampText);
// Set the text matrix for each line with rotation
contentStream.setTextMatrix(
Matrix.getRotateInstance(Math.toRadians(rotation), x, y - (i * lineHeight)));
contentStream.showText(line);
}
contentStream.endText(); contentStream.endText();
} }

View File

@@ -221,7 +221,7 @@ public class PipelineProcessor {
HttpHeaders headers = new HttpHeaders(); HttpHeaders headers = new HttpHeaders();
String apiKey = getApiKeyForUser(); String apiKey = getApiKeyForUser();
headers.add("X-API-KEY", apiKey); headers.add("X-API-Key", apiKey);
headers.setContentType(MediaType.MULTIPART_FORM_DATA); headers.setContentType(MediaType.MULTIPART_FORM_DATA);
// Create HttpEntity with the body and headers // Create HttpEntity with the body and headers

View File

@@ -322,14 +322,27 @@ public class GetInfoOnPDF {
PDEncryption pdfEncryption = pdfBoxDoc.getEncryption(); PDEncryption pdfEncryption = pdfBoxDoc.getEncryption();
encryption.put("EncryptionAlgorithm", pdfEncryption.getFilter()); encryption.put("EncryptionAlgorithm", pdfEncryption.getFilter());
encryption.put("KeyLength", pdfEncryption.getLength()); encryption.put("KeyLength", pdfEncryption.getLength());
AccessPermission ap = pdfBoxDoc.getCurrentAccessPermission();
if (ap != null) {
ObjectNode permissionsNode = objectMapper.createObjectNode();
permissionsNode.put("CanAssembleDocument", ap.canAssembleDocument());
permissionsNode.put("CanExtractContent", ap.canExtractContent());
permissionsNode.put(
"CanExtractForAccessibility", ap.canExtractForAccessibility());
permissionsNode.put("CanFillInForm", ap.canFillInForm());
permissionsNode.put("CanModify", ap.canModify());
permissionsNode.put("CanModifyAnnotations", ap.canModifyAnnotations());
permissionsNode.put("CanPrint", ap.canPrint());
encryption.set(
"Permissions", permissionsNode); // set the node under "Permissions"
}
// Add other encryption-related properties as needed // Add other encryption-related properties as needed
} else { } else {
encryption.put("IsEncrypted", false); encryption.put("IsEncrypted", false);
} }
ObjectNode permissionsNode = objectMapper.createObjectNode();
setNodePermissions(pdfBoxDoc, permissionsNode);
ObjectNode pageInfoParent = objectMapper.createObjectNode(); ObjectNode pageInfoParent = objectMapper.createObjectNode();
for (int pageNum = 0; pageNum < pdfBoxDoc.getNumberOfPages(); pageNum++) { for (int pageNum = 0; pageNum < pdfBoxDoc.getNumberOfPages(); pageNum++) {
ObjectNode pageInfo = objectMapper.createObjectNode(); ObjectNode pageInfo = objectMapper.createObjectNode();
@@ -571,7 +584,6 @@ public class GetInfoOnPDF {
jsonOutput.set("DocumentInfo", docInfoNode); jsonOutput.set("DocumentInfo", docInfoNode);
jsonOutput.set("Compliancy", compliancy); jsonOutput.set("Compliancy", compliancy);
jsonOutput.set("Encryption", encryption); jsonOutput.set("Encryption", encryption);
jsonOutput.set("Permissions", permissionsNode); // set the node under "Permissions"
jsonOutput.set("Other", other); jsonOutput.set("Other", other);
jsonOutput.set("PerPageInfo", pageInfoParent); jsonOutput.set("PerPageInfo", pageInfoParent);
@@ -590,24 +602,6 @@ public class GetInfoOnPDF {
return null; return null;
} }
private void setNodePermissions(PDDocument pdfBoxDoc, ObjectNode permissionsNode) {
AccessPermission ap = pdfBoxDoc.getCurrentAccessPermission();
permissionsNode.put("Document Assembly", getPermissionState(ap.canAssembleDocument()));
permissionsNode.put("Extracting Content", getPermissionState(ap.canExtractContent()));
permissionsNode.put(
"Extracting for accessibility",
getPermissionState(ap.canExtractForAccessibility()));
permissionsNode.put("Form Filling", getPermissionState(ap.canFillInForm()));
permissionsNode.put("Modifying", getPermissionState(ap.canModify()));
permissionsNode.put("Modifying annotations", getPermissionState(ap.canModifyAnnotations()));
permissionsNode.put("Printing", getPermissionState(ap.canPrint()));
}
private String getPermissionState(boolean state) {
return state ? "Allowed" : "Not Allowed";
}
private static void addOutlinesToArray(PDOutlineItem outline, ArrayNode arrayNode) { private static void addOutlinesToArray(PDOutlineItem outline, ArrayNode arrayNode) {
if (outline == null) return; if (outline == null) return;

View File

@@ -39,7 +39,10 @@ public class PasswordController {
} }
@PostMapping(consumes = "multipart/form-data", value = "/remove-password") @PostMapping(consumes = "multipart/form-data", value = "/remove-password")
@Operation(summary = "Remove password from a PDF file", description = "This endpoint removes the password from a protected PDF file. Users need to provide the existing password. Input:PDF Output:PDF Type:SISO") @Operation(
summary = "Remove password from a PDF file",
description =
"This endpoint removes the password from a protected PDF file. Users need to provide the existing password. Input:PDF Output:PDF Type:SISO")
public ResponseEntity<byte[]> removePassword(@ModelAttribute PDFPasswordRequest request) public ResponseEntity<byte[]> removePassword(@ModelAttribute PDFPasswordRequest request)
throws IOException { throws IOException {
MultipartFile fileInput = request.getFileInput(); MultipartFile fileInput = request.getFileInput();
@@ -49,12 +52,15 @@ public class PasswordController {
return WebResponseUtils.pdfDocToWebResponse( return WebResponseUtils.pdfDocToWebResponse(
document, document,
Filenames.toSimpleFileName(fileInput.getOriginalFilename()) Filenames.toSimpleFileName(fileInput.getOriginalFilename())
.replaceFirst("[.][^.]+$", "") .replaceFirst("[.][^.]+$", "")
+ "_password_removed.pdf"); + "_password_removed.pdf");
} }
@PostMapping(consumes = "multipart/form-data", value = "/add-password") @PostMapping(consumes = "multipart/form-data", value = "/add-password")
@Operation(summary = "Add password to a PDF file", description = "This endpoint adds password protection to a PDF file. Users can specify a set of permissions that should be applied to the file. Input:PDF Output:PDF") @Operation(
summary = "Add password to a PDF file",
description =
"This endpoint adds password protection to a PDF file. Users can specify a set of permissions that should be applied to the file. Input:PDF Output:PDF")
public ResponseEntity<byte[]> addPassword(@ModelAttribute AddPasswordRequest request) public ResponseEntity<byte[]> addPassword(@ModelAttribute AddPasswordRequest request)
throws IOException { throws IOException {
MultipartFile fileInput = request.getFileInput(); MultipartFile fileInput = request.getFileInput();
@@ -92,12 +98,12 @@ public class PasswordController {
return WebResponseUtils.pdfDocToWebResponse( return WebResponseUtils.pdfDocToWebResponse(
document, document,
Filenames.toSimpleFileName(fileInput.getOriginalFilename()) Filenames.toSimpleFileName(fileInput.getOriginalFilename())
.replaceFirst("[.][^.]+$", "") .replaceFirst("[.][^.]+$", "")
+ "_permissions.pdf"); + "_permissions.pdf");
return WebResponseUtils.pdfDocToWebResponse( return WebResponseUtils.pdfDocToWebResponse(
document, document,
Filenames.toSimpleFileName(fileInput.getOriginalFilename()) Filenames.toSimpleFileName(fileInput.getOriginalFilename())
.replaceFirst("[.][^.]+$", "") .replaceFirst("[.][^.]+$", "")
+ "_passworded.pdf"); + "_passworded.pdf");
} }
} }

View File

@@ -1,180 +0,0 @@
package stirling.software.SPDF.controller.api.security;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPublicKey;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cms.CMSProcessable;
import org.bouncycastle.cms.CMSProcessableByteArray;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.SignerInformation;
import org.bouncycastle.cms.SignerInformationStore;
import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder;
import org.bouncycastle.util.Store;
import org.springframework.beans.factory.annotation.Autowired;
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.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import stirling.software.SPDF.model.api.security.SignatureValidationRequest;
import stirling.software.SPDF.model.api.security.SignatureValidationResult;
import stirling.software.SPDF.service.CertificateValidationService;
import stirling.software.SPDF.service.CustomPDDocumentFactory;
@RestController
@RequestMapping("/api/v1/security")
@Tag(name = "Security", description = "Security APIs")
public class ValidateSignatureController {
private final CustomPDDocumentFactory pdfDocumentFactory;
private final CertificateValidationService certValidationService;
@Autowired
public ValidateSignatureController(
CustomPDDocumentFactory pdfDocumentFactory,
CertificateValidationService certValidationService) {
this.pdfDocumentFactory = pdfDocumentFactory;
this.certValidationService = certValidationService;
}
@Operation(
summary = "Validate PDF Digital Signature",
description =
"Validates the digital signatures in a PDF file against default or custom certificates. Input:PDF Output:JSON Type:SISO")
@PostMapping(value = "/validate-signature")
public ResponseEntity<List<SignatureValidationResult>> validateSignature(
@ModelAttribute SignatureValidationRequest request) throws IOException {
List<SignatureValidationResult> results = new ArrayList<>();
MultipartFile file = request.getFileInput();
// Load custom certificate if provided
X509Certificate customCert = null;
if (request.getCertFile() != null && !request.getCertFile().isEmpty()) {
try (ByteArrayInputStream certStream =
new ByteArrayInputStream(request.getCertFile().getBytes())) {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
customCert = (X509Certificate) cf.generateCertificate(certStream);
} catch (CertificateException e) {
throw new RuntimeException("Invalid certificate file: " + e.getMessage());
}
}
try (PDDocument document = pdfDocumentFactory.load(file.getInputStream())) {
List<PDSignature> signatures = document.getSignatureDictionaries();
for (PDSignature sig : signatures) {
SignatureValidationResult result = new SignatureValidationResult();
try {
byte[] signedContent = sig.getSignedContent(file.getInputStream());
byte[] signatureBytes = sig.getContents(file.getInputStream());
CMSProcessable content = new CMSProcessableByteArray(signedContent);
CMSSignedData signedData = new CMSSignedData(content, signatureBytes);
Store<X509CertificateHolder> certStore = signedData.getCertificates();
SignerInformationStore signerStore = signedData.getSignerInfos();
for (SignerInformation signer : signerStore.getSigners()) {
X509CertificateHolder certHolder =
(X509CertificateHolder)
certStore.getMatches(signer.getSID()).iterator().next();
X509Certificate cert =
new JcaX509CertificateConverter().getCertificate(certHolder);
boolean isValid =
signer.verify(new JcaSimpleSignerInfoVerifierBuilder().build(cert));
result.setValid(isValid);
// Additional validations
result.setChainValid(
customCert != null
? certValidationService
.validateCertificateChainWithCustomCert(
cert, customCert)
: certValidationService.validateCertificateChain(cert));
result.setTrustValid(
customCert != null
? certValidationService.validateTrustWithCustomCert(
cert, customCert)
: certValidationService.validateTrustStore(cert));
result.setNotRevoked(!certValidationService.isRevoked(cert));
result.setNotExpired(!cert.getNotAfter().before(new Date()));
// Set basic signature info
result.setSignerName(sig.getName());
result.setSignatureDate(sig.getSignDate().getTime().toString());
result.setReason(sig.getReason());
result.setLocation(sig.getLocation());
// Set new certificate details
result.setIssuerDN(cert.getIssuerX500Principal().getName());
result.setSubjectDN(cert.getSubjectX500Principal().getName());
result.setSerialNumber(cert.getSerialNumber().toString(16)); // Hex format
result.setValidFrom(cert.getNotBefore().toString());
result.setValidUntil(cert.getNotAfter().toString());
result.setSignatureAlgorithm(cert.getSigAlgName());
// Get key size (if possible)
try {
result.setKeySize(
((RSAPublicKey) cert.getPublicKey()).getModulus().bitLength());
} catch (Exception e) {
// If not RSA or error, set to 0
result.setKeySize(0);
}
result.setVersion(String.valueOf(cert.getVersion()));
// Set key usage
List<String> keyUsages = new ArrayList<>();
boolean[] keyUsageFlags = cert.getKeyUsage();
if (keyUsageFlags != null) {
String[] keyUsageLabels = {
"Digital Signature", "Non-Repudiation", "Key Encipherment",
"Data Encipherment", "Key Agreement", "Certificate Signing",
"CRL Signing", "Encipher Only", "Decipher Only"
};
for (int i = 0; i < keyUsageFlags.length; i++) {
if (keyUsageFlags[i]) {
keyUsages.add(keyUsageLabels[i]);
}
}
}
result.setKeyUsages(keyUsages);
// Check if self-signed
result.setSelfSigned(
cert.getSubjectX500Principal()
.equals(cert.getIssuerX500Principal()));
}
} catch (Exception e) {
result.setValid(false);
result.setErrorMessage("Signature validation failed: " + e.getMessage());
}
results.add(result);
}
}
return ResponseEntity.ok(results);
}
}

View File

@@ -69,7 +69,6 @@ public class WatermarkController {
float opacity = request.getOpacity(); float opacity = request.getOpacity();
int widthSpacer = request.getWidthSpacer(); int widthSpacer = request.getWidthSpacer();
int heightSpacer = request.getHeightSpacer(); int heightSpacer = request.getHeightSpacer();
String customColor = request.getCustomColor();
boolean convertPdfToImage = request.isConvertPDFToImage(); boolean convertPdfToImage = request.isConvertPDFToImage();
// Load the input PDF // Load the input PDF
@@ -98,8 +97,7 @@ public class WatermarkController {
widthSpacer, widthSpacer,
heightSpacer, heightSpacer,
fontSize, fontSize,
alphabet, alphabet);
customColor);
} else if ("image".equalsIgnoreCase(watermarkType)) { } else if ("image".equalsIgnoreCase(watermarkType)) {
addImageWatermark( addImageWatermark(
contentStream, contentStream,
@@ -138,8 +136,7 @@ public class WatermarkController {
int widthSpacer, int widthSpacer,
int heightSpacer, int heightSpacer,
float fontSize, float fontSize,
String alphabet, String alphabet)
String colorString)
throws IOException { throws IOException {
String resourceDir = ""; String resourceDir = "";
PDFont font = new PDType1Font(Standard14Fonts.FontName.HELVETICA); PDFont font = new PDType1Font(Standard14Fonts.FontName.HELVETICA);
@@ -176,18 +173,7 @@ public class WatermarkController {
} }
contentStream.setFont(font, fontSize); contentStream.setFont(font, fontSize);
contentStream.setNonStrokingColor(Color.LIGHT_GRAY);
Color redactColor;
try {
if (!colorString.startsWith("#")) {
colorString = "#" + colorString;
}
redactColor = Color.decode(colorString);
} catch (NumberFormatException e) {
redactColor = Color.LIGHT_GRAY;
}
contentStream.setNonStrokingColor(redactColor);
String[] textLines = watermarkText.split("\\\\n"); String[] textLines = watermarkText.split("\\\\n");
float maxLineWidth = 0; float maxLineWidth = 0;

View File

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

View File

@@ -73,7 +73,6 @@ public class ApplicationProperties {
private int loginAttemptCount; private int loginAttemptCount;
private long loginResetTimeMinutes; private long loginResetTimeMinutes;
private String loginMethod = "all"; private String loginMethod = "all";
private String customGlobalAPIKey;
public Boolean isAltLogin() { public Boolean isAltLogin() {
return saml2.getEnabled() || oauth2.getEnabled(); return saml2.getEnabled() || oauth2.getEnabled();
@@ -286,7 +285,6 @@ public class ApplicationProperties {
public static class AutomaticallyGenerated { public static class AutomaticallyGenerated {
@ToString.Exclude private String key; @ToString.Exclude private String key;
private String UUID; private String UUID;
private String appVersion;
} }
@Data @Data

View File

@@ -45,9 +45,6 @@ public class AddWatermarkRequest extends PDFFile {
@Schema(description = "The height spacer between watermark elements", example = "50") @Schema(description = "The height spacer between watermark elements", example = "50")
private int heightSpacer; private int heightSpacer;
@Schema(description = "The color for watermark", defaultValue = "#d3d3d3")
private String customColor = "#d3d3d3";
@Schema(description = "Convert the redacted PDF to an image", defaultValue = "false") @Schema(description = "Convert the redacted PDF to an image", defaultValue = "false")
private boolean convertPDFToImage; private boolean convertPDFToImage;
} }

View File

@@ -1,17 +0,0 @@
package stirling.software.SPDF.model.api.security;
import org.springframework.web.multipart.MultipartFile;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import stirling.software.SPDF.model.api.PDFFile;
@Data
@EqualsAndHashCode(callSuper = true)
public class SignatureValidationRequest extends PDFFile {
@Schema(description = "(Optional) file to compare PDF cert signatures against x.509 format")
private MultipartFile certFile;
}

View File

@@ -1,30 +0,0 @@
package stirling.software.SPDF.model.api.security;
import java.util.List;
import lombok.Data;
@Data
public class SignatureValidationResult {
private boolean valid;
private String signerName;
private String signatureDate;
private String reason;
private String location;
private String errorMessage;
private boolean chainValid;
private boolean trustValid;
private boolean notExpired;
private boolean notRevoked;
private String issuerDN; // Certificate issuer's Distinguished Name
private String subjectDN; // Certificate subject's Distinguished Name
private String serialNumber; // Certificate serial number
private String validFrom; // Certificate validity start date
private String validUntil; // Certificate validity end date
private String signatureAlgorithm; // Algorithm used for signing
private int keySize; // Key size in bits
private String version; // Certificate version
private List<String> keyUsages; // List of key usage purposes
private boolean isSelfSigned; // Whether the certificate is self-signed
}

View File

@@ -1,158 +0,0 @@
package stirling.software.SPDF.service;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.cert.CertPath;
import java.security.cert.CertPathValidator;
import java.security.cert.CertificateExpiredException;
import java.security.cert.CertificateFactory;
import java.security.cert.CertificateNotYetValidException;
import java.security.cert.PKIXParameters;
import java.security.cert.TrustAnchor;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.springframework.stereotype.Service;
import io.github.pixee.security.BoundedLineReader;
import jakarta.annotation.PostConstruct;
@Service
public class CertificateValidationService {
private KeyStore trustStore;
@PostConstruct
private void initializeTrustStore() throws Exception {
trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
trustStore.load(null, null);
loadMozillaCertificates();
}
private void loadMozillaCertificates() throws Exception {
try (InputStream is = getClass().getResourceAsStream("/certdata.txt")) {
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
String line;
StringBuilder certData = new StringBuilder();
boolean inCert = false;
int certCount = 0;
while ((line = BoundedLineReader.readLine(reader, 5_000_000)) != null) {
if (line.startsWith("CKA_VALUE MULTILINE_OCTAL")) {
inCert = true;
certData = new StringBuilder();
continue;
}
if (inCert) {
if ("END".equals(line)) {
inCert = false;
byte[] certBytes = parseOctalData(certData.toString());
if (certBytes != null) {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate cert =
(X509Certificate)
cf.generateCertificate(
new ByteArrayInputStream(certBytes));
trustStore.setCertificateEntry("mozilla-cert-" + certCount++, cert);
}
} else {
certData.append(line).append("\n");
}
}
}
}
}
private byte[] parseOctalData(String data) {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
String[] tokens = data.split("\\\\");
for (String token : tokens) {
token = token.trim();
if (!token.isEmpty()) {
baos.write(Integer.parseInt(token, 8));
}
}
return baos.toByteArray();
} catch (Exception e) {
return null;
}
}
public boolean validateCertificateChain(X509Certificate cert) {
try {
CertPathValidator validator = CertPathValidator.getInstance("PKIX");
CertificateFactory cf = CertificateFactory.getInstance("X.509");
List<X509Certificate> certList = Arrays.asList(cert);
CertPath certPath = cf.generateCertPath(certList);
Set<TrustAnchor> anchors = new HashSet<>();
Enumeration<String> aliases = trustStore.aliases();
while (aliases.hasMoreElements()) {
Object trustCert = trustStore.getCertificate(aliases.nextElement());
if (trustCert instanceof X509Certificate) {
anchors.add(new TrustAnchor((X509Certificate) trustCert, null));
}
}
PKIXParameters params = new PKIXParameters(anchors);
params.setRevocationEnabled(false);
validator.validate(certPath, params);
return true;
} catch (Exception e) {
return false;
}
}
public boolean validateTrustStore(X509Certificate cert) {
try {
Enumeration<String> aliases = trustStore.aliases();
while (aliases.hasMoreElements()) {
Object trustCert = trustStore.getCertificate(aliases.nextElement());
if (trustCert instanceof X509Certificate && cert.equals(trustCert)) {
return true;
}
}
return false;
} catch (KeyStoreException e) {
return false;
}
}
public boolean isRevoked(X509Certificate cert) {
try {
cert.checkValidity();
return false;
} catch (CertificateExpiredException | CertificateNotYetValidException e) {
return true;
}
}
public boolean validateCertificateChainWithCustomCert(
X509Certificate cert, X509Certificate customCert) {
try {
cert.verify(customCert.getPublicKey());
return true;
} catch (Exception e) {
return false;
}
}
public boolean validateTrustWithCustomCert(X509Certificate cert, X509Certificate customCert) {
try {
// Compare the issuer of the signature certificate with the custom certificate
return cert.getIssuerX500Principal().equals(customCert.getSubjectX500Principal());
} catch (Exception e) {
return false;
}
}
}

View File

@@ -24,7 +24,7 @@ public class MetricsAggregatorService {
this.postHogService = postHogService; this.postHogService = postHogService;
} }
@Scheduled(fixedRate = 7200000) // Run every 2 hours @Scheduled(fixedRate = 1800000) // Run every 30 minutes
public void aggregateAndSendMetrics() { public void aggregateAndSendMetrics() {
Map<String, Object> metrics = new HashMap<>(); Map<String, Object> metrics = new HashMap<>();
Search.in(meterRegistry) Search.in(meterRegistry)

View File

@@ -88,45 +88,15 @@ public class GeneralUtils {
public static boolean isURLReachable(String urlStr) { public static boolean isURLReachable(String urlStr) {
try { try {
// Parse the URL
URL url = URI.create(urlStr).toURL(); URL url = URI.create(urlStr).toURL();
// Allow only http and https protocols
String protocol = url.getProtocol();
if (!protocol.equals("http") && !protocol.equals("https")) {
return false; // Disallow other protocols
}
// Check if the host is a local address
String host = url.getHost();
if (isLocalAddress(host)) {
return false; // Exclude local addresses
}
// Check if the URL is reachable
HttpURLConnection connection = (HttpURLConnection) url.openConnection(); HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("HEAD"); connection.setRequestMethod("HEAD");
// connection.setConnectTimeout(5000); // Set connection timeout
// connection.setReadTimeout(5000); // Set read timeout
int responseCode = connection.getResponseCode(); int responseCode = connection.getResponseCode();
return (200 <= responseCode && responseCode <= 399); return (200 <= responseCode && responseCode <= 399);
} catch (Exception e) { } catch (MalformedURLException e) {
return false; // Return false in case of any exception return false;
} } catch (IOException e) {
} return false;
private static boolean isLocalAddress(String host) {
try {
// Resolve DNS to IP address
InetAddress address = InetAddress.getByName(host);
// Check for local addresses
return address.isAnyLocalAddress() || // Matches 0.0.0.0 or similar
address.isLoopbackAddress() || // Matches 127.0.0.1 or ::1
address.isSiteLocalAddress() || // Matches private IPv4 ranges: 192.168.x.x, 10.x.x.x, 172.16.x.x to 172.31.x.x
address.getHostAddress().startsWith("fe80:"); // Matches link-local IPv6 addresses
} catch (Exception e) {
return false; // Return false for invalid or unresolved addresses
} }
} }
@@ -319,10 +289,6 @@ public class GeneralUtils {
saveKeyToConfig(id, key, true); saveKeyToConfig(id, key, true);
} }
public static void saveKeyToConfig(String id, boolean key) throws IOException {
saveKeyToConfig(id, key, true);
}
public static void saveKeyToConfig(String id, String key, boolean autoGenerated) public static void saveKeyToConfig(String id, String key, boolean autoGenerated)
throws IOException { throws IOException {
Path path = Paths.get("configs", "settings.yml"); // Target the configs/settings.yml Path path = Paths.get("configs", "settings.yml"); // Target the configs/settings.yml
@@ -341,24 +307,6 @@ public class GeneralUtils {
settingsYml.save(); settingsYml.save();
} }
public static void saveKeyToConfig(String id, boolean key, boolean autoGenerated)
throws IOException {
Path path = Paths.get("configs", "settings.yml");
final YamlFile settingsYml = new YamlFile(path.toFile());
DumperOptions yamlOptionssettingsYml =
((SimpleYamlImplementation) settingsYml.getImplementation()).getDumperOptions();
yamlOptionssettingsYml.setSplitLines(false);
settingsYml.loadWithComments();
YamlFileWrapper writer = settingsYml.path(id).set(key);
if (autoGenerated) {
writer.comment("# Automatically Generated Settings (Do Not Edit Directly)");
}
settingsYml.save();
}
public static String generateMachineFingerprint() { public static String generateMachineFingerprint() {
try { try {
// Get the MAC address // Get the MAC address
@@ -401,33 +349,4 @@ public class GeneralUtils {
return "GenericID"; return "GenericID";
} }
} }
public static boolean isVersionHigher(String currentVersion, String compareVersion) {
if (currentVersion == null || compareVersion == null) {
return false;
}
// Split versions into components
String[] current = currentVersion.split("\\.");
String[] compare = compareVersion.split("\\.");
// Get the length of the shorter version array
int length = Math.min(current.length, compare.length);
// Compare each component
for (int i = 0; i < length; i++) {
int currentPart = Integer.parseInt(current[i]);
int comparePart = Integer.parseInt(compare[i]);
if (currentPart > comparePart) {
return true;
}
if (currentPart < comparePart) {
return false;
}
}
// If all components so far are equal, the longer version is considered higher
return current.length > compare.length;
}
} }

View File

@@ -1,26 +0,0 @@
package stirling.software.SPDF.utils.propertyeditor;
import java.beans.PropertyEditorSupport;
import java.util.HashMap;
import java.util.Map;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
public class StringToMapPropertyEditor extends PropertyEditorSupport {
private final ObjectMapper objectMapper = new ObjectMapper();
@Override
public void setAsText(String text) throws IllegalArgumentException {
try {
TypeReference<HashMap<String, String>> typeRef =
new TypeReference<HashMap<String, String>>() {};
Map<String, String> map = objectMapper.readValue(text, typeRef);
setValue(map);
} catch (Exception e) {
throw new IllegalArgumentException(
"Failed to convert java.lang.String to java.util.Map");
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -512,10 +512,6 @@ home.splitPdfByChapters.title=تجزئة المستندات PDF حسب الفص
home.splitPdfByChapters.desc=قسم مستند PDF إلى ملفات متعددة بناءً على هيكل فصوله. home.splitPdfByChapters.desc=قسم مستند PDF إلى ملفات متعددة بناءً على هيكل فصوله.
splitPdfByChapters.tags=تجزئة، فصول، علامات تبويب، تنظيم splitPdfByChapters.tags=تجزئة، فصول، علامات تبويب، تنظيم
home.validateSignature.title=Validate PDF Signature
home.validateSignature.desc=Verify digital signatures and certificates in PDF documents
validateSignature.tags=signature,verify,validate,pdf,certificate,digital signature,Validate Signature,Validate certificate
#replace-invert-color #replace-invert-color
replace-color.title=إستبدال-عكس اللون replace-color.title=إستبدال-عكس اللون
replace-color.header=استبدال-عكس لون PDF replace-color.header=استبدال-عكس لون PDF
@@ -965,16 +961,6 @@ multiTool.dragDropMessage=الصفحات المحددة
multiTool.undo=تراجع multiTool.undo=تراجع
multiTool.redo=إعادة إجراء multiTool.redo=إعادة إجراء
#decrypt
decrypt.passwordPrompt=This file is password-protected. Please enter the password:
decrypt.cancelled=Operation cancelled for PDF: {0}
decrypt.noPassword=No password provided for encrypted PDF: {0}
decrypt.invalidPassword=Please try again with the correct password.
decrypt.invalidPasswordHeader=Incorrect password or unsupported encryption for PDF: {0}
decrypt.unexpectedError=There was an error processing the file. Please try again.
decrypt.serverError=Server error while decrypting: {0}
decrypt.success=File decrypted successfully.
#multiTool-advert #multiTool-advert
multiTool-advert.message=هذه الميزة متوفرة في <a href="{0}">صفحة الأدوات المتعددة</a> لدينا. اطلع عليها للحصول على واجهة مستخدم محسّنة لكل صفحة وميزات إضافية! multiTool-advert.message=هذه الميزة متوفرة في <a href="{0}">صفحة الأدوات المتعددة</a> لدينا. اطلع عليها للحصول على واجهة مستخدم محسّنة لكل صفحة وميزات إضافية!
@@ -1066,7 +1052,6 @@ addPassword.submit=تشفير
#watermark #watermark
watermark.title=إضافة علامة مائية watermark.title=إضافة علامة مائية
watermark.header=إضافة علامة مائية watermark.header=إضافة علامة مائية
watermark.customColor=لون نص مخصص
watermark.selectText.1=حدد PDF لإضافة العلامة المائية إليه: watermark.selectText.1=حدد PDF لإضافة العلامة المائية إليه:
watermark.selectText.2=نص العلامة المائية: watermark.selectText.2=نص العلامة المائية:
watermark.selectText.3=حجم الخط: watermark.selectText.3=حجم الخط:
@@ -1278,11 +1263,6 @@ splitByChapters.desc.3=تمثيل البيانات الأصلية: إذا تم
splitByChapters.desc.4=سماح بالتكرار: إذا تم اختياره، يسمح بوجود معاينات متعددة في الصفحة نفسها لخلق ملفات PDF منفصلة. splitByChapters.desc.4=سماح بالتكرار: إذا تم اختياره، يسمح بوجود معاينات متعددة في الصفحة نفسها لخلق ملفات PDF منفصلة.
splitByChapters.submit=تقطيع ملف PDF splitByChapters.submit=تقطيع ملف PDF
#File Chooser
fileChooser.click=انقر هنا
fileChooser.or=أو
fileChooser.dragAndDrop=قم بسحب الملفات وإفلاتها
fileChooser.hoveredDragAndDrop=قم بسحب المفات وإفلاتها هنا
#release notes #release notes
releases.footer=Releases releases.footer=Releases
@@ -1290,38 +1270,3 @@ releases.title=Release Notes
releases.header=Release Notes releases.header=Release Notes
releases.current.version=Current Release releases.current.version=Current Release
releases.note=Release notes are only available in English releases.note=Release notes are only available in English
#Validate Signature
validateSignature.title=Validate PDF Signatures
validateSignature.header=Validate Digital Signatures
validateSignature.selectPDF=Select signed PDF file
validateSignature.submit=Validate Signatures
validateSignature.results=Validation Results
validateSignature.status=Status
validateSignature.signer=Signer
validateSignature.date=Date
validateSignature.reason=Reason
validateSignature.location=Location
validateSignature.noSignatures=No digital signatures found in this document
validateSignature.status.valid=Valid
validateSignature.status.invalid=Invalid
validateSignature.chain.invalid=Certificate chain validation failed - cannot verify signer's identity
validateSignature.trust.invalid=Certificate not in trust store - source cannot be verified
validateSignature.cert.expired=Certificate has expired
validateSignature.cert.revoked=Certificate has been revoked
validateSignature.signature.info=Signature Information
validateSignature.signature=Signature
validateSignature.signature.mathValid=Signature is mathematically valid BUT:
validateSignature.selectCustomCert=Custom Certificate File X.509 (Optional)
validateSignature.cert.info=Certificate Details
validateSignature.cert.issuer=Issuer
validateSignature.cert.subject=Subject
validateSignature.cert.serialNumber=Serial Number
validateSignature.cert.validFrom=Valid From
validateSignature.cert.validUntil=Valid Until
validateSignature.cert.algorithm=Algorithm
validateSignature.cert.keySize=Key Size
validateSignature.cert.version=Version
validateSignature.cert.keyUsage=Key Usage
validateSignature.cert.selfSigned=Self-Signed
validateSignature.cert.bits=bits

View File

@@ -512,10 +512,6 @@ home.splitPdfByChapters.title=PDF-i Fəsillərə Əsasən Böl
home.splitPdfByChapters.desc=Fəsil strukturuna əsasən PDF-i bir neçə fayla böl. home.splitPdfByChapters.desc=Fəsil strukturuna əsasən PDF-i bir neçə fayla böl.
splitPdfByChapters.tags=böl,fəsillər,əlfəcinlər,nizamla splitPdfByChapters.tags=böl,fəsillər,əlfəcinlər,nizamla
home.validateSignature.title=Validate PDF Signature
home.validateSignature.desc=Verify digital signatures and certificates in PDF documents
validateSignature.tags=signature,verify,validate,pdf,certificate,digital signature,Validate Signature,Validate certificate
#replace-invert-color #replace-invert-color
replace-color.title=Qabaqcıl Rəng Seçimləri replace-color.title=Qabaqcıl Rəng Seçimləri
replace-color.header=PDF-də Rəngləri Dəyiş-Tərsinə Çevir replace-color.header=PDF-də Rəngləri Dəyiş-Tərsinə Çevir
@@ -965,16 +961,6 @@ multiTool.dragDropMessage=Seçilmiş Səhifə(lər)
multiTool.undo=Undo multiTool.undo=Undo
multiTool.redo=Redo multiTool.redo=Redo
#decrypt
decrypt.passwordPrompt=This file is password-protected. Please enter the password:
decrypt.cancelled=Operation cancelled for PDF: {0}
decrypt.noPassword=No password provided for encrypted PDF: {0}
decrypt.invalidPassword=Please try again with the correct password.
decrypt.invalidPasswordHeader=Incorrect password or unsupported encryption for PDF: {0}
decrypt.unexpectedError=There was an error processing the file. Please try again.
decrypt.serverError=Server error while decrypting: {0}
decrypt.success=File decrypted successfully.
#multiTool-advert #multiTool-advert
multiTool-advert.message=Bu xüsusiyyət bizim <a href="{0}">multi-alət səhifə</a>mizdə də mövcuddur. Əlavə xüsusiyyətlər və səhifə-səhifə interfeys üçün sınaqdan keçirin! multiTool-advert.message=Bu xüsusiyyət bizim <a href="{0}">multi-alət səhifə</a>mizdə də mövcuddur. Əlavə xüsusiyyətlər və səhifə-səhifə interfeys üçün sınaqdan keçirin!
@@ -1066,7 +1052,6 @@ addPassword.submit=Şifrlə
#watermark #watermark
watermark.title=Watermark Əlavə Et watermark.title=Watermark Əlavə Et
watermark.header=Watermark Əlavə Et watermark.header=Watermark Əlavə Et
watermark.customColor=Fərdi Mətn Rəngi
watermark.selectText.1=Watermark əlavə olunacaq PDF-i seç watermark.selectText.1=Watermark əlavə olunacaq PDF-i seç
watermark.selectText.2=Watermark Mətni: watermark.selectText.2=Watermark Mətni:
watermark.selectText.3=Şrift Ölçüsü: watermark.selectText.3=Şrift Ölçüsü:
@@ -1278,11 +1263,6 @@ splitByChapters.desc.3=Metadatanı daxil edin: Əgər yoxlanılıbsa, orijinal P
splitByChapters.desc.4=Allow Duplicates: Dublikatlara icazə verin: Əgər işarələnərsə, eyni səhifədə birdən çox bookmarka ayrı-ayrı PDF sənədləri yaratmağa icazə verin. splitByChapters.desc.4=Allow Duplicates: Dublikatlara icazə verin: Əgər işarələnərsə, eyni səhifədə birdən çox bookmarka ayrı-ayrı PDF sənədləri yaratmağa icazə verin.
splitByChapters.submit=PDF-i Ayır splitByChapters.submit=PDF-i Ayır
#File Chooser
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes #release notes
releases.footer=Buraxılışlar releases.footer=Buraxılışlar
@@ -1290,38 +1270,3 @@ releases.title=Buraxılış Qeydləri
releases.header=Buraxılış Qeydləri releases.header=Buraxılış Qeydləri
releases.current.version=Hazırki Buraxılış releases.current.version=Hazırki Buraxılış
releases.note=Buraxılış Qeydləri yalnız ingiliscə mövcuddur releases.note=Buraxılış Qeydləri yalnız ingiliscə mövcuddur
#Validate Signature
validateSignature.title=Validate PDF Signatures
validateSignature.header=Validate Digital Signatures
validateSignature.selectPDF=Select signed PDF file
validateSignature.submit=Validate Signatures
validateSignature.results=Validation Results
validateSignature.status=Status
validateSignature.signer=Signer
validateSignature.date=Date
validateSignature.reason=Reason
validateSignature.location=Location
validateSignature.noSignatures=No digital signatures found in this document
validateSignature.status.valid=Valid
validateSignature.status.invalid=Invalid
validateSignature.chain.invalid=Certificate chain validation failed - cannot verify signer's identity
validateSignature.trust.invalid=Certificate not in trust store - source cannot be verified
validateSignature.cert.expired=Certificate has expired
validateSignature.cert.revoked=Certificate has been revoked
validateSignature.signature.info=Signature Information
validateSignature.signature=Signature
validateSignature.signature.mathValid=Signature is mathematically valid BUT:
validateSignature.selectCustomCert=Custom Certificate File X.509 (Optional)
validateSignature.cert.info=Certificate Details
validateSignature.cert.issuer=Issuer
validateSignature.cert.subject=Subject
validateSignature.cert.serialNumber=Serial Number
validateSignature.cert.validFrom=Valid From
validateSignature.cert.validUntil=Valid Until
validateSignature.cert.algorithm=Algorithm
validateSignature.cert.keySize=Key Size
validateSignature.cert.version=Version
validateSignature.cert.keyUsage=Key Usage
validateSignature.cert.selfSigned=Self-Signed
validateSignature.cert.bits=bits

View File

@@ -512,10 +512,6 @@ home.splitPdfByChapters.title=Разделете PDF по глави
home.splitPdfByChapters.desc=Разделете PDF на множество файлове въз основа на неговата структура на глави. home.splitPdfByChapters.desc=Разделете PDF на множество файлове въз основа на неговата структура на глави.
splitPdfByChapters.tags=разделяне, глави, отметки, организиране splitPdfByChapters.tags=разделяне, глави, отметки, организиране
home.validateSignature.title=Validate PDF Signature
home.validateSignature.desc=Verify digital signatures and certificates in PDF documents
validateSignature.tags=signature,verify,validate,pdf,certificate,digital signature,Validate Signature,Validate certificate
#replace-invert-color #replace-invert-color
replace-color.title=Замени-инвертиране-на-цвят replace-color.title=Замени-инвертиране-на-цвят
replace-color.header=Замяна-инвертиране на цвят PDF replace-color.header=Замяна-инвертиране на цвят PDF
@@ -965,16 +961,6 @@ multiTool.dragDropMessage=Page(s) Selected
multiTool.undo=Undo multiTool.undo=Undo
multiTool.redo=Redo multiTool.redo=Redo
#decrypt
decrypt.passwordPrompt=This file is password-protected. Please enter the password:
decrypt.cancelled=Operation cancelled for PDF: {0}
decrypt.noPassword=No password provided for encrypted PDF: {0}
decrypt.invalidPassword=Please try again with the correct password.
decrypt.invalidPasswordHeader=Incorrect password or unsupported encryption for PDF: {0}
decrypt.unexpectedError=There was an error processing the file. Please try again.
decrypt.serverError=Server error while decrypting: {0}
decrypt.success=File decrypted successfully.
#multiTool-advert #multiTool-advert
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features! multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
@@ -1066,7 +1052,6 @@ addPassword.submit=Шифроване
#watermark #watermark
watermark.title=Добавяне на воден знак watermark.title=Добавяне на воден знак
watermark.header=Добавяне на воден знак watermark.header=Добавяне на воден знак
watermark.customColor=Персонализиран цвят на текста
watermark.selectText.1=Изберете PDF, към който да добавите воден знак: watermark.selectText.1=Изберете PDF, към който да добавите воден знак:
watermark.selectText.2=Текст на воден знак: watermark.selectText.2=Текст на воден знак:
watermark.selectText.3=Размер на шрифта: watermark.selectText.3=Размер на шрифта:
@@ -1278,11 +1263,6 @@ splitByChapters.desc.3=Включване на метаданни: Ако е о
splitByChapters.desc.4=Разрешаване на дубликати: Ако е отметнато, позволява множество отметки на една и съща страница за създаване на отделни PDF файлове. splitByChapters.desc.4=Разрешаване на дубликати: Ако е отметнато, позволява множество отметки на една и съща страница за създаване на отделни PDF файлове.
splitByChapters.submit=Разделяне на PDF splitByChapters.submit=Разделяне на PDF
#File Chooser
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes #release notes
releases.footer=Releases releases.footer=Releases
@@ -1290,38 +1270,3 @@ releases.title=Release Notes
releases.header=Release Notes releases.header=Release Notes
releases.current.version=Current Release releases.current.version=Current Release
releases.note=Release notes are only available in English releases.note=Release notes are only available in English
#Validate Signature
validateSignature.title=Validate PDF Signatures
validateSignature.header=Validate Digital Signatures
validateSignature.selectPDF=Select signed PDF file
validateSignature.submit=Validate Signatures
validateSignature.results=Validation Results
validateSignature.status=Status
validateSignature.signer=Signer
validateSignature.date=Date
validateSignature.reason=Reason
validateSignature.location=Location
validateSignature.noSignatures=No digital signatures found in this document
validateSignature.status.valid=Valid
validateSignature.status.invalid=Invalid
validateSignature.chain.invalid=Certificate chain validation failed - cannot verify signer's identity
validateSignature.trust.invalid=Certificate not in trust store - source cannot be verified
validateSignature.cert.expired=Certificate has expired
validateSignature.cert.revoked=Certificate has been revoked
validateSignature.signature.info=Signature Information
validateSignature.signature=Signature
validateSignature.signature.mathValid=Signature is mathematically valid BUT:
validateSignature.selectCustomCert=Custom Certificate File X.509 (Optional)
validateSignature.cert.info=Certificate Details
validateSignature.cert.issuer=Issuer
validateSignature.cert.subject=Subject
validateSignature.cert.serialNumber=Serial Number
validateSignature.cert.validFrom=Valid From
validateSignature.cert.validUntil=Valid Until
validateSignature.cert.algorithm=Algorithm
validateSignature.cert.keySize=Key Size
validateSignature.cert.version=Version
validateSignature.cert.keyUsage=Key Usage
validateSignature.cert.selfSigned=Self-Signed
validateSignature.cert.bits=bits

View File

@@ -512,10 +512,6 @@ home.splitPdfByChapters.title=Divideix PDF per Capítols
home.splitPdfByChapters.desc=Divideix un PDF en múltiples fitxers segons la seva estructura de capítols. home.splitPdfByChapters.desc=Divideix un PDF en múltiples fitxers segons la seva estructura de capítols.
splitPdfByChapters.tags=split,chapters,bookmarks,organize splitPdfByChapters.tags=split,chapters,bookmarks,organize
home.validateSignature.title=Validate PDF Signature
home.validateSignature.desc=Verify digital signatures and certificates in PDF documents
validateSignature.tags=signature,verify,validate,pdf,certificate,digital signature,Validate Signature,Validate certificate
#replace-invert-color #replace-invert-color
replace-color.title=Reemplaça-Inverteix-Color replace-color.title=Reemplaça-Inverteix-Color
replace-color.header=Reemplaça-Inverteix Color en PDF replace-color.header=Reemplaça-Inverteix Color en PDF
@@ -965,16 +961,6 @@ multiTool.dragDropMessage=Page(s) Selected
multiTool.undo=Undo multiTool.undo=Undo
multiTool.redo=Redo multiTool.redo=Redo
#decrypt
decrypt.passwordPrompt=This file is password-protected. Please enter the password:
decrypt.cancelled=Operation cancelled for PDF: {0}
decrypt.noPassword=No password provided for encrypted PDF: {0}
decrypt.invalidPassword=Please try again with the correct password.
decrypt.invalidPasswordHeader=Incorrect password or unsupported encryption for PDF: {0}
decrypt.unexpectedError=There was an error processing the file. Please try again.
decrypt.serverError=Server error while decrypting: {0}
decrypt.success=File decrypted successfully.
#multiTool-advert #multiTool-advert
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features! multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
@@ -1066,7 +1052,6 @@ addPassword.submit=Encripta
#watermark #watermark
watermark.title=Afegir Marca d'Aigua watermark.title=Afegir Marca d'Aigua
watermark.header=Afegir Marca d'Aigua watermark.header=Afegir Marca d'Aigua
watermark.customColor=Color de Text Personalitzat
watermark.selectText.1=Selecciona el PDF per afegir la Marca d'Aigua: watermark.selectText.1=Selecciona el PDF per afegir la Marca d'Aigua:
watermark.selectText.2=Text de la Marca d'Aigua watermark.selectText.2=Text de la Marca d'Aigua
watermark.selectText.3=Mida de la Font: watermark.selectText.3=Mida de la Font:
@@ -1278,11 +1263,6 @@ splitByChapters.desc.3=Incloure Metadades: Si està marcat, les metadades del PD
splitByChapters.desc.4=Permetre Duplicats: Si està marcat, permet diversos marcadors a la mateixa pàgina per crear PDFs separats. splitByChapters.desc.4=Permetre Duplicats: Si està marcat, permet diversos marcadors a la mateixa pàgina per crear PDFs separats.
splitByChapters.submit=Divideix PDF splitByChapters.submit=Divideix PDF
#File Chooser
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes #release notes
releases.footer=Releases releases.footer=Releases
@@ -1290,38 +1270,3 @@ releases.title=Release Notes
releases.header=Release Notes releases.header=Release Notes
releases.current.version=Current Release releases.current.version=Current Release
releases.note=Release notes are only available in English releases.note=Release notes are only available in English
#Validate Signature
validateSignature.title=Validate PDF Signatures
validateSignature.header=Validate Digital Signatures
validateSignature.selectPDF=Select signed PDF file
validateSignature.submit=Validate Signatures
validateSignature.results=Validation Results
validateSignature.status=Status
validateSignature.signer=Signer
validateSignature.date=Date
validateSignature.reason=Reason
validateSignature.location=Location
validateSignature.noSignatures=No digital signatures found in this document
validateSignature.status.valid=Valid
validateSignature.status.invalid=Invalid
validateSignature.chain.invalid=Certificate chain validation failed - cannot verify signer's identity
validateSignature.trust.invalid=Certificate not in trust store - source cannot be verified
validateSignature.cert.expired=Certificate has expired
validateSignature.cert.revoked=Certificate has been revoked
validateSignature.signature.info=Signature Information
validateSignature.signature=Signature
validateSignature.signature.mathValid=Signature is mathematically valid BUT:
validateSignature.selectCustomCert=Custom Certificate File X.509 (Optional)
validateSignature.cert.info=Certificate Details
validateSignature.cert.issuer=Issuer
validateSignature.cert.subject=Subject
validateSignature.cert.serialNumber=Serial Number
validateSignature.cert.validFrom=Valid From
validateSignature.cert.validUntil=Valid Until
validateSignature.cert.algorithm=Algorithm
validateSignature.cert.keySize=Key Size
validateSignature.cert.version=Version
validateSignature.cert.keyUsage=Key Usage
validateSignature.cert.selfSigned=Self-Signed
validateSignature.cert.bits=bits

View File

@@ -512,10 +512,6 @@ home.splitPdfByChapters.title=Rozdělit PDF podle kapitol
home.splitPdfByChapters.desc=Rozdělit PDF do více souborů na základě jeho struktury kapitol. home.splitPdfByChapters.desc=Rozdělit PDF do více souborů na základě jeho struktury kapitol.
splitPdfByChapters.tags=rozdělení, kapitoly, zápisky, organizace splitPdfByChapters.tags=rozdělení, kapitoly, zápisky, organizace
home.validateSignature.title=Validate PDF Signature
home.validateSignature.desc=Verify digital signatures and certificates in PDF documents
validateSignature.tags=signature,verify,validate,pdf,certificate,digital signature,Validate Signature,Validate certificate
#replace-invert-color #replace-invert-color
replace-color.title=Replace-Invert-Color replace-color.title=Replace-Invert-Color
replace-color.header=Nahradit inverzní barvu PDF replace-color.header=Nahradit inverzní barvu PDF
@@ -965,16 +961,6 @@ multiTool.dragDropMessage=Page(s) Selected
multiTool.undo=Undo multiTool.undo=Undo
multiTool.redo=Redo multiTool.redo=Redo
#decrypt
decrypt.passwordPrompt=This file is password-protected. Please enter the password:
decrypt.cancelled=Operation cancelled for PDF: {0}
decrypt.noPassword=No password provided for encrypted PDF: {0}
decrypt.invalidPassword=Please try again with the correct password.
decrypt.invalidPasswordHeader=Incorrect password or unsupported encryption for PDF: {0}
decrypt.unexpectedError=There was an error processing the file. Please try again.
decrypt.serverError=Server error while decrypting: {0}
decrypt.success=File decrypted successfully.
#multiTool-advert #multiTool-advert
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features! multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
@@ -1066,7 +1052,6 @@ addPassword.submit=Šifrovat
#watermark #watermark
watermark.title=Přidat vodoznak watermark.title=Přidat vodoznak
watermark.header=Přidat vodoznak watermark.header=Přidat vodoznak
watermark.customColor=Vlastní barva textu
watermark.selectText.1=Vyberte PDF, ke kterému chcete přidat vodoznak: watermark.selectText.1=Vyberte PDF, ke kterému chcete přidat vodoznak:
watermark.selectText.2=Text vodoznaku: watermark.selectText.2=Text vodoznaku:
watermark.selectText.3=Velikost písma: watermark.selectText.3=Velikost písma:
@@ -1278,11 +1263,6 @@ splitByChapters.desc.3=Zahrnout metadatů: Pokud je zaškrtnuto, původní metad
splitByChapters.desc.4=Povolit duplicitní záznamy: Pokud je zaškrtnuto, návštěvníci mohou vytvořit samostatné PDF soubory z více záhlaví na stejné straně. splitByChapters.desc.4=Povolit duplicitní záznamy: Pokud je zaškrtnuto, návštěvníci mohou vytvořit samostatné PDF soubory z více záhlaví na stejné straně.
splitByChapters.submit=Podělit se PDF splitByChapters.submit=Podělit se PDF
#File Chooser
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes #release notes
releases.footer=Releases releases.footer=Releases
@@ -1290,38 +1270,3 @@ releases.title=Release Notes
releases.header=Release Notes releases.header=Release Notes
releases.current.version=Current Release releases.current.version=Current Release
releases.note=Release notes are only available in English releases.note=Release notes are only available in English
#Validate Signature
validateSignature.title=Validate PDF Signatures
validateSignature.header=Validate Digital Signatures
validateSignature.selectPDF=Select signed PDF file
validateSignature.submit=Validate Signatures
validateSignature.results=Validation Results
validateSignature.status=Status
validateSignature.signer=Signer
validateSignature.date=Date
validateSignature.reason=Reason
validateSignature.location=Location
validateSignature.noSignatures=No digital signatures found in this document
validateSignature.status.valid=Valid
validateSignature.status.invalid=Invalid
validateSignature.chain.invalid=Certificate chain validation failed - cannot verify signer's identity
validateSignature.trust.invalid=Certificate not in trust store - source cannot be verified
validateSignature.cert.expired=Certificate has expired
validateSignature.cert.revoked=Certificate has been revoked
validateSignature.signature.info=Signature Information
validateSignature.signature=Signature
validateSignature.signature.mathValid=Signature is mathematically valid BUT:
validateSignature.selectCustomCert=Custom Certificate File X.509 (Optional)
validateSignature.cert.info=Certificate Details
validateSignature.cert.issuer=Issuer
validateSignature.cert.subject=Subject
validateSignature.cert.serialNumber=Serial Number
validateSignature.cert.validFrom=Valid From
validateSignature.cert.validUntil=Valid Until
validateSignature.cert.algorithm=Algorithm
validateSignature.cert.keySize=Key Size
validateSignature.cert.version=Version
validateSignature.cert.keyUsage=Key Usage
validateSignature.cert.selfSigned=Self-Signed
validateSignature.cert.bits=bits

View File

@@ -512,10 +512,6 @@ home.splitPdfByChapters.title=Partitioner PDF efter kapitler
home.splitPdfByChapters.desc=Partitioner en PDF i flere filer baseret på dens kapitelstruktur. home.splitPdfByChapters.desc=Partitioner en PDF i flere filer baseret på dens kapitelstruktur.
splitPdfByChapters.tags=partitionering,kapitler,merker,organisering splitPdfByChapters.tags=partitionering,kapitler,merker,organisering
home.validateSignature.title=Validate PDF Signature
home.validateSignature.desc=Verify digital signatures and certificates in PDF documents
validateSignature.tags=signature,verify,validate,pdf,certificate,digital signature,Validate Signature,Validate certificate
#replace-invert-color #replace-invert-color
replace-color.title=Replace-Invert-Color replace-color.title=Replace-Invert-Color
replace-color.header=Erstat-omgivende Farve PDF replace-color.header=Erstat-omgivende Farve PDF
@@ -965,16 +961,6 @@ multiTool.dragDropMessage=Page(s) Selected
multiTool.undo=Undo multiTool.undo=Undo
multiTool.redo=Redo multiTool.redo=Redo
#decrypt
decrypt.passwordPrompt=This file is password-protected. Please enter the password:
decrypt.cancelled=Operation cancelled for PDF: {0}
decrypt.noPassword=No password provided for encrypted PDF: {0}
decrypt.invalidPassword=Please try again with the correct password.
decrypt.invalidPasswordHeader=Incorrect password or unsupported encryption for PDF: {0}
decrypt.unexpectedError=There was an error processing the file. Please try again.
decrypt.serverError=Server error while decrypting: {0}
decrypt.success=File decrypted successfully.
#multiTool-advert #multiTool-advert
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features! multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
@@ -1066,7 +1052,6 @@ addPassword.submit=Kryptér
#watermark #watermark
watermark.title=Tilføj Vandmærke watermark.title=Tilføj Vandmærke
watermark.header=Tilføj Vandmærke watermark.header=Tilføj Vandmærke
watermark.customColor=Brugerdefineret Tekstfarve
watermark.selectText.1=Vælg PDF til at tilføje vandmærke: watermark.selectText.1=Vælg PDF til at tilføje vandmærke:
watermark.selectText.2=Vandmærketekst: watermark.selectText.2=Vandmærketekst:
watermark.selectText.3=Skriftstørrelse: watermark.selectText.3=Skriftstørrelse:
@@ -1278,11 +1263,6 @@ splitByChapters.desc.3=Inkluder metadata: Hvis markeret, vil den originale PDF's
splitByChapters.desc.4=Tillad duplikater: Hvis markeret, tillader det flere bogmærker på samme side til at oprette separate PDF'er. splitByChapters.desc.4=Tillad duplikater: Hvis markeret, tillader det flere bogmærker på samme side til at oprette separate PDF'er.
splitByChapters.submit=Splitter PDF splitByChapters.submit=Splitter PDF
#File Chooser
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes #release notes
releases.footer=Releases releases.footer=Releases
@@ -1290,38 +1270,3 @@ releases.title=Release Notes
releases.header=Release Notes releases.header=Release Notes
releases.current.version=Current Release releases.current.version=Current Release
releases.note=Release notes are only available in English releases.note=Release notes are only available in English
#Validate Signature
validateSignature.title=Validate PDF Signatures
validateSignature.header=Validate Digital Signatures
validateSignature.selectPDF=Select signed PDF file
validateSignature.submit=Validate Signatures
validateSignature.results=Validation Results
validateSignature.status=Status
validateSignature.signer=Signer
validateSignature.date=Date
validateSignature.reason=Reason
validateSignature.location=Location
validateSignature.noSignatures=No digital signatures found in this document
validateSignature.status.valid=Valid
validateSignature.status.invalid=Invalid
validateSignature.chain.invalid=Certificate chain validation failed - cannot verify signer's identity
validateSignature.trust.invalid=Certificate not in trust store - source cannot be verified
validateSignature.cert.expired=Certificate has expired
validateSignature.cert.revoked=Certificate has been revoked
validateSignature.signature.info=Signature Information
validateSignature.signature=Signature
validateSignature.signature.mathValid=Signature is mathematically valid BUT:
validateSignature.selectCustomCert=Custom Certificate File X.509 (Optional)
validateSignature.cert.info=Certificate Details
validateSignature.cert.issuer=Issuer
validateSignature.cert.subject=Subject
validateSignature.cert.serialNumber=Serial Number
validateSignature.cert.validFrom=Valid From
validateSignature.cert.validUntil=Valid Until
validateSignature.cert.algorithm=Algorithm
validateSignature.cert.keySize=Key Size
validateSignature.cert.version=Version
validateSignature.cert.keyUsage=Key Usage
validateSignature.cert.selfSigned=Self-Signed
validateSignature.cert.bits=bits

View File

@@ -146,8 +146,8 @@ navbar.search=Suche
navbar.sections.organize=Organisieren navbar.sections.organize=Organisieren
navbar.sections.convertTo=In PDF konvertieren navbar.sections.convertTo=In PDF konvertieren
navbar.sections.convertFrom=Konvertieren von PDF navbar.sections.convertFrom=Konvertieren von PDF
navbar.sections.security=Signieren und Sicherheit navbar.sections.security=Zeichen und Sicherheit
navbar.sections.advance=Erweiterte Funktionen navbar.sections.advance=Fortschrittlich
navbar.sections.edit=Anzeigen und Bearbeiten navbar.sections.edit=Anzeigen und Bearbeiten
navbar.sections.popular=Beliebt navbar.sections.popular=Beliebt
@@ -248,7 +248,7 @@ database.fileNullOrEmpty=Datei darf nicht null oder leer sein
database.failedImportFile=Dateiimport fehlgeschlagen database.failedImportFile=Dateiimport fehlgeschlagen
session.expired=Ihre Sitzung ist abgelaufen. Bitte laden Sie die Seite neu und versuchen Sie es erneut. session.expired=Ihre Sitzung ist abgelaufen. Bitte laden Sie die Seite neu und versuchen Sie es erneut.
session.refreshPage=Seite aktualisieren session.refreshPage=Refresh Page
############# #############
# HOME-PAGE # # HOME-PAGE #
@@ -512,10 +512,6 @@ home.splitPdfByChapters.title=PDF-Datei nach Kapiteln aufteilen
home.splitPdfByChapters.desc=Aufteilung einer PDF-Datei in mehrere Dateien auf Basis der Kapitelstruktur. home.splitPdfByChapters.desc=Aufteilung einer PDF-Datei in mehrere Dateien auf Basis der Kapitelstruktur.
splitPdfByChapters.tags=aufteilen,kapitel,lesezeichen,organisieren splitPdfByChapters.tags=aufteilen,kapitel,lesezeichen,organisieren
home.validateSignature.title=PDF-Signatur überprüfen
home.validateSignature.desc=Digitale Signaturen und Zertifikate in PDF-Dokumenten überprüfen
validateSignature.tags=signature,verify,validate,pdf,digitale signatur,signatur validieren,überprüfen,Zertifikat,cert
#replace-invert-color #replace-invert-color
replace-color.title=Farbe Ersetzen-Invertieren replace-color.title=Farbe Ersetzen-Invertieren
replace-color.header=Farb-PDF Ersetzen-Invertieren replace-color.header=Farb-PDF Ersetzen-Invertieren
@@ -822,12 +818,12 @@ sign.save=Signature speichern
sign.personalSigs=Persönliche Signaturen sign.personalSigs=Persönliche Signaturen
sign.sharedSigs=Geteilte Signaturen sign.sharedSigs=Geteilte Signaturen
sign.noSavedSigs=Es wurden keine gespeicherten Signaturen gefunden sign.noSavedSigs=Es wurden keine gespeicherten Signaturen gefunden
sign.addToAll=Zu allen Seiten hinzufügen sign.addToAll=Add to all pages
sign.delete=Löschen sign.delete=Delete
sign.first=Erste Seite sign.first=First page
sign.last=Letzte Seite sign.last=Last page
sign.next=Nächste Seite sign.next=Next page
sign.previous=Vorherige Seite sign.previous=Previous page
#repair #repair
repair.title=Reparieren repair.title=Reparieren
@@ -953,27 +949,17 @@ multiTool.deleteSelected=Auswahl löschen
multiTool.downloadAll=Downloaden multiTool.downloadAll=Downloaden
multiTool.downloadSelected=Auswahl downloaden multiTool.downloadSelected=Auswahl downloaden
multiTool.insertPageBreak=Seitenumbruch einfügen multiTool.insertPageBreak=Insert Page Break
multiTool.addFile=Datei hinzufügen multiTool.addFile=Add File
multiTool.rotateLeft=Nach links drehen multiTool.rotateLeft=Rotate Left
multiTool.rotateRight=Nach rechts drehen multiTool.rotateRight=Rotate Right
multiTool.split=Teilen multiTool.split=Split
multiTool.moveLeft=Nach links verschieben multiTool.moveLeft=Move Left
multiTool.moveRight=Nach rechts verschieben multiTool.moveRight=Move Right
multiTool.delete=Löschen multiTool.delete=Delete
multiTool.dragDropMessage=Ausgewählte Seite(n) multiTool.dragDropMessage=Page(s) Selected
multiTool.undo=Rückgängig machen multiTool.undo=Undo
multiTool.redo=Wiederherstellen multiTool.redo=Redo
#decrypt
decrypt.passwordPrompt=This file is password-protected. Please enter the password:
decrypt.cancelled=Operation cancelled for PDF: {0}
decrypt.noPassword=No password provided for encrypted PDF: {0}
decrypt.invalidPassword=Please try again with the correct password.
decrypt.invalidPasswordHeader=Incorrect password or unsupported encryption for PDF: {0}
decrypt.unexpectedError=There was an error processing the file. Please try again.
decrypt.serverError=Server error while decrypting: {0}
decrypt.success=File decrypted successfully.
#multiTool-advert #multiTool-advert
multiTool-advert.message=Diese Funktion ist auch auf unserer <a href="{0}">PDF-Multitool-Seite</a> verfügbar. Probieren Sie sie aus, denn sie bietet eine verbesserte Benutzeroberfläche und zusätzliche Funktionen! multiTool-advert.message=Diese Funktion ist auch auf unserer <a href="{0}">PDF-Multitool-Seite</a> verfügbar. Probieren Sie sie aus, denn sie bietet eine verbesserte Benutzeroberfläche und zusätzliche Funktionen!
@@ -1066,7 +1052,6 @@ addPassword.submit=Verschlüsseln
#watermark #watermark
watermark.title=Wasserzeichen hinzufügen watermark.title=Wasserzeichen hinzufügen
watermark.header=Wasserzeichen hinzufügen watermark.header=Wasserzeichen hinzufügen
watermark.customColor=Benutzerdefinierte Textfarbe
watermark.selectText.1=PDF auswählen, dem ein Wasserzeichen hinzugefügt werden soll: watermark.selectText.1=PDF auswählen, dem ein Wasserzeichen hinzugefügt werden soll:
watermark.selectText.2=Wasserzeichen Text: watermark.selectText.2=Wasserzeichen Text:
watermark.selectText.3=Schriftgröße: watermark.selectText.3=Schriftgröße:
@@ -1278,50 +1263,10 @@ splitByChapters.desc.3=Metadaten einschließen: Wenn diese Option aktiviert ist,
splitByChapters.desc.4=Duplikate erlauben: Wenn diese Option aktiviert ist, können mehrere Lesezeichen auf derselben Seite separate PDF Dateien erstellen. splitByChapters.desc.4=Duplikate erlauben: Wenn diese Option aktiviert ist, können mehrere Lesezeichen auf derselben Seite separate PDF Dateien erstellen.
splitByChapters.submit=PDF teilen splitByChapters.submit=PDF teilen
#File Chooser
fileChooser.click=Klicken
fileChooser.or=oder
fileChooser.dragAndDrop=Drag & Drop
fileChooser.hoveredDragAndDrop=Datei(en) hierhin Ziehen & Fallenlassen
#release notes #release notes
releases.footer=Veröffentlichungen releases.footer=Releases
releases.title=Versionshinweise releases.title=Release Notes
releases.header=Versionshinweise releases.header=Release Notes
releases.current.version=Aktuelle Version releases.current.version=Current Release
releases.note=Versionshinweise sind nur auf Englisch verfügbar releases.note=Release notes are only available in English
#Validate Signature
validateSignature.title=PDF-Signaturen überprüfen
validateSignature.header=Digitale Signaturen überprüfen
validateSignature.selectPDF=Signierte PDF-Datei auswählen
validateSignature.submit=Signaturen überprüfen
validateSignature.results=Gültigkeitsprüfungsergebnisse
validateSignature.status=Status
validateSignature.signer=Unterzeichner
validateSignature.date=Datum
validateSignature.reason=Grund
validateSignature.location=Ort
validateSignature.noSignatures=Keine digitalen Signaturen in diesem Dokument gefunden
validateSignature.status.valid=Gültig
validateSignature.status.invalid=Ungültig
validateSignature.chain.invalid=Zertifikatskettenprüfung fehlgeschlagen - kann die Identität des Unterzeichners nicht verifizieren
validateSignature.trust.invalid=Zertifikat nicht im Truststore - Quelle kann nicht verifiziert werden
validateSignature.cert.expired=Zertifikat ist abgelaufen
validateSignature.cert.revoked=Zertifikat wurde widerrufen
validateSignature.signature.info=Signaturinformationen
validateSignature.signature=Signatur
validateSignature.signature.mathValid=Signatur ist mathematisch gültig ABER:
validateSignature.selectCustomCert=Benutzerdefinierte Zertifikatsdatei X.509 (Optional)
validateSignature.cert.info=Zertifikat Details
validateSignature.cert.issuer=Aussteller
validateSignature.cert.subject=Betreff
validateSignature.cert.serialNumber=Seriennummer
validateSignature.cert.validFrom=Gültig von
validateSignature.cert.validUntil=Gültig bis
validateSignature.cert.algorithm=Algorithmus
validateSignature.cert.keySize=Schlüsselgröße
validateSignature.cert.version=Version
validateSignature.cert.keyUsage=Schlüsselverwendung
validateSignature.cert.selfSigned=Selbstsigniert
validateSignature.cert.bits=bits

View File

@@ -512,10 +512,6 @@ home.splitPdfByChapters.title=Διχοτομία PDF ανά Περιγραφές
home.splitPdfByChapters.desc=Split a PDF into multiple files based on its chapter structure. home.splitPdfByChapters.desc=Split a PDF into multiple files based on its chapter structure.
splitPdfByChapters.tags=διχοτομία,περιγραφές,κεφάλαια,συνορία splitPdfByChapters.tags=διχοτομία,περιγραφές,κεφάλαια,συνορία
home.validateSignature.title=Validate PDF Signature
home.validateSignature.desc=Verify digital signatures and certificates in PDF documents
validateSignature.tags=signature,verify,validate,pdf,certificate,digital signature,Validate Signature,Validate certificate
#replace-invert-color #replace-invert-color
replace-color.title=Replace-Invert-Color replace-color.title=Replace-Invert-Color
replace-color.header=Αντικατάσταση-Αντίστροφη Παθωμένη Πίντσουρ replace-color.header=Αντικατάσταση-Αντίστροφη Παθωμένη Πίντσουρ
@@ -965,16 +961,6 @@ multiTool.dragDropMessage=Page(s) Selected
multiTool.undo=Undo multiTool.undo=Undo
multiTool.redo=Redo multiTool.redo=Redo
#decrypt
decrypt.passwordPrompt=This file is password-protected. Please enter the password:
decrypt.cancelled=Operation cancelled for PDF: {0}
decrypt.noPassword=No password provided for encrypted PDF: {0}
decrypt.invalidPassword=Please try again with the correct password.
decrypt.invalidPasswordHeader=Incorrect password or unsupported encryption for PDF: {0}
decrypt.unexpectedError=There was an error processing the file. Please try again.
decrypt.serverError=Server error while decrypting: {0}
decrypt.success=File decrypted successfully.
#multiTool-advert #multiTool-advert
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features! multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
@@ -1066,13 +1052,12 @@ addPassword.submit=Κρυπτογράφηση
#watermark #watermark
watermark.title=Προσθήκη Υδατογραφήματος watermark.title=Προσθήκη Υδατογραφήματος
watermark.header=Προσθήκη Υδατογραφήματος watermark.header=Προσθήκη Υδατογραφήματος
watermark.customColor=Προσαρμοσμένο χρώμα κειμένου
watermark.selectText.1=Επιλέξτε PDF για την προσθήκη του υδατογραφήματος: watermark.selectText.1=Επιλέξτε PDF για την προσθήκη του υδατογραφήματος:
watermark.selectText.2=Κείμενο Υδατογραφήματος: watermark.selectText.2=Κείμενο Υδατογραφήματος:
watermark.selectText.3=Μέγεθος Κειμένου: watermark.selectText.3=Μέγεθος Κειμένου:
watermark.selectText.4=Περιστροφή (0-360): watermark.selectText.4=Περιστροφή (0-360):
watermark.selectText.5=Width Spacer (Κενό μεταξύ κάθε υδατογραφήματος οριζόντια): watermark.selectText.5=widthSpacer (Κενό μεταξύ κάθε υδατογραφήματος οριζόντια):
watermark.selectText.6=Height Spacer (Κενό μεταξύ κάθε υδατογραφήματος κάθετα): watermark.selectText.6=heightSpacer (Κενό μεταξύ κάθε υδατογραφήματος κάθετα):
watermark.selectText.7=Αδιαφάνεια (Opacity) (0% - 100%): watermark.selectText.7=Αδιαφάνεια (Opacity) (0% - 100%):
watermark.selectText.8=Τύπος Υδατογραφήματος: watermark.selectText.8=Τύπος Υδατογραφήματος:
watermark.selectText.9=Εικόνα Υδατογραφήματος: watermark.selectText.9=Εικόνα Υδατογραφήματος:
@@ -1278,11 +1263,6 @@ splitByChapters.desc.3=Πρόσθεση Metadata: Αν επεξεργαστεί
splitByChapters.desc.4=Διάλυση Παρόντων Τίτλων Επιπέδου: Αν επεξεργαστείται, επιτρέπει τη δημιουργία αποκοπών PDF με βάση πλήρως καθορισμένους σήμαντες έδρας. splitByChapters.desc.4=Διάλυση Παρόντων Τίτλων Επιπέδου: Αν επεξεργαστείται, επιτρέπει τη δημιουργία αποκοπών PDF με βάση πλήρως καθορισμένους σήμαντες έδρας.
splitByChapters.submit=Διαλύστε το PDF splitByChapters.submit=Διαλύστε το PDF
#File Chooser
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes #release notes
releases.footer=Releases releases.footer=Releases
@@ -1290,38 +1270,3 @@ releases.title=Release Notes
releases.header=Release Notes releases.header=Release Notes
releases.current.version=Current Release releases.current.version=Current Release
releases.note=Release notes are only available in English releases.note=Release notes are only available in English
#Validate Signature
validateSignature.title=Validate PDF Signatures
validateSignature.header=Validate Digital Signatures
validateSignature.selectPDF=Select signed PDF file
validateSignature.submit=Validate Signatures
validateSignature.results=Validation Results
validateSignature.status=Status
validateSignature.signer=Signer
validateSignature.date=Date
validateSignature.reason=Reason
validateSignature.location=Location
validateSignature.noSignatures=No digital signatures found in this document
validateSignature.status.valid=Valid
validateSignature.status.invalid=Invalid
validateSignature.chain.invalid=Certificate chain validation failed - cannot verify signer's identity
validateSignature.trust.invalid=Certificate not in trust store - source cannot be verified
validateSignature.cert.expired=Certificate has expired
validateSignature.cert.revoked=Certificate has been revoked
validateSignature.signature.info=Signature Information
validateSignature.signature=Signature
validateSignature.signature.mathValid=Signature is mathematically valid BUT:
validateSignature.selectCustomCert=Custom Certificate File X.509 (Optional)
validateSignature.cert.info=Certificate Details
validateSignature.cert.issuer=Issuer
validateSignature.cert.subject=Subject
validateSignature.cert.serialNumber=Serial Number
validateSignature.cert.validFrom=Valid From
validateSignature.cert.validUntil=Valid Until
validateSignature.cert.algorithm=Algorithm
validateSignature.cert.keySize=Key Size
validateSignature.cert.version=Version
validateSignature.cert.keyUsage=Key Usage
validateSignature.cert.selfSigned=Self-Signed
validateSignature.cert.bits=bits

View File

@@ -73,7 +73,7 @@ joinDiscord=Join our Discord server
seeDockerHub=See Docker Hub seeDockerHub=See Docker Hub
visitGithub=Visit Github Repository visitGithub=Visit Github Repository
donate=Donate donate=Donate
color=Colour color=Color
sponsor=Sponsor sponsor=Sponsor
info=Info info=Info
pro=Pro pro=Pro
@@ -419,9 +419,9 @@ home.auto-rename.title=Auto Rename PDF File
home.auto-rename.desc=Auto renames a PDF file based on its detected header home.auto-rename.desc=Auto renames a PDF file based on its detected header
auto-rename.tags=auto-detect,header-based,organize,relabel auto-rename.tags=auto-detect,header-based,organize,relabel
home.adjust-contrast.title=Adjust Colours/Contrast home.adjust-contrast.title=Adjust Colors/Contrast
home.adjust-contrast.desc=Adjust Contrast, Saturation and Brightness of a PDF home.adjust-contrast.desc=Adjust Contrast, Saturation and Brightness of a PDF
adjust-contrast.tags=color-correction,tune,modify,enhance,colour-correction adjust-contrast.tags=color-correction,tune,modify,enhance
home.crop.title=Crop PDF home.crop.title=Crop PDF
home.crop.desc=Crop a PDF to reduce its size (maintains text!) home.crop.desc=Crop a PDF to reduce its size (maintains text!)
@@ -488,11 +488,11 @@ overlay-pdfs.tags=Overlay
home.split-by-sections.title=Split PDF by Sections home.split-by-sections.title=Split PDF by Sections
home.split-by-sections.desc=Divide each page of a PDF into smaller horizontal and vertical sections home.split-by-sections.desc=Divide each page of a PDF into smaller horizontal and vertical sections
split-by-sections.tags=Section Split, Divide, Customize,Customise split-by-sections.tags=Section Split, Divide, Customize
home.AddStampRequest.title=Add Stamp to PDF home.AddStampRequest.title=Add Stamp to PDF
home.AddStampRequest.desc=Add text or add image stamps at set locations home.AddStampRequest.desc=Add text or add image stamps at set locations
AddStampRequest.tags=Stamp, Add image, center image, Watermark, PDF, Embed, Customize,Customise AddStampRequest.tags=Stamp, Add image, center image, Watermark, PDF, Embed, Customize
home.PDFToBook.title=PDF to Book home.PDFToBook.title=PDF to Book
@@ -512,27 +512,23 @@ home.splitPdfByChapters.title=Split PDF by Chapters
home.splitPdfByChapters.desc=Split a PDF into multiple files based on its chapter structure. home.splitPdfByChapters.desc=Split a PDF into multiple files based on its chapter structure.
splitPdfByChapters.tags=split,chapters,bookmarks,organize splitPdfByChapters.tags=split,chapters,bookmarks,organize
home.validateSignature.title=Validate PDF Signature
home.validateSignature.desc=Verify digital signatures and certificates in PDF documents
validateSignature.tags=signature,verify,validate,pdf,certificate,digital signature,Validate Signature,Validate certificate
#replace-invert-color #replace-invert-color
replace-color.title=Advanced Colour options replace-color.title=Advanced Colour options
replace-color.header=Replace-Invert Colour PDF replace-color.header=Replace-Invert Color PDF
home.replaceColorPdf.title=Advanced Colour options home.replaceColorPdf.title=Advanced Colour options
home.replaceColorPdf.desc=Replace colour for text and background in PDF and invert full colour of pdf to reduce file size home.replaceColorPdf.desc=Replace color for text and background in PDF and invert full color of pdf to reduce file size
replaceColorPdf.tags=Replace Colour,Page operations,Back end,server side replaceColorPdf.tags=Replace Color,Page operations,Back end,server side
replace-color.selectText.1=Replace or Invert colour Options replace-color.selectText.1=Replace or Invert color Options
replace-color.selectText.2=Default(Default high contrast colours) replace-color.selectText.2=Default(Default high contrast colors)
replace-color.selectText.3=Custom(Customised colours) replace-color.selectText.3=Custom(Customized colors)
replace-color.selectText.4=Full-Invert(Invert all colours) replace-color.selectText.4=Full-Invert(Invert all colors)
replace-color.selectText.5=High contrast colour options replace-color.selectText.5=High contrast color options
replace-color.selectText.6=white text on black background replace-color.selectText.6=white text on black background
replace-color.selectText.7=Black text on white background replace-color.selectText.7=Black text on white background
replace-color.selectText.8=Yellow text on black background replace-color.selectText.8=Yellow text on black background
replace-color.selectText.9=Green text on black background replace-color.selectText.9=Green text on black background
replace-color.selectText.10=Choose text Colour replace-color.selectText.10=Choose text Color
replace-color.selectText.11=Choose background Colour replace-color.selectText.11=Choose background Color
replace-color.submit=Replace replace-color.submit=Replace
@@ -655,7 +651,7 @@ AddStampRequest.position=Position
AddStampRequest.overrideX=Override X Coordinate AddStampRequest.overrideX=Override X Coordinate
AddStampRequest.overrideY=Override Y Coordinate AddStampRequest.overrideY=Override Y Coordinate
AddStampRequest.customMargin=Custom Margin AddStampRequest.customMargin=Custom Margin
AddStampRequest.customColor=Custom Text Colour AddStampRequest.customColor=Custom Text Color
AddStampRequest.submit=Submit AddStampRequest.submit=Submit
@@ -787,8 +783,8 @@ removeAnnotations.submit=Remove
#compare #compare
compare.title=Compare compare.title=Compare
compare.header=Compare PDFs compare.header=Compare PDFs
compare.highlightColor.1=Highlight Colour 1: compare.highlightColor.1=Highlight Color 1:
compare.highlightColor.2=Highlight Colour 2: compare.highlightColor.2=Highlight Color 2:
compare.document.1=Document 1 compare.document.1=Document 1
compare.document.2=Document 2 compare.document.2=Document 2
compare.submit=Compare compare.submit=Compare
@@ -846,7 +842,7 @@ flatten.submit=Flatten
ScannerImageSplit.selectText.1=Angle Threshold: ScannerImageSplit.selectText.1=Angle Threshold:
ScannerImageSplit.selectText.2=Sets the minimum absolute angle required for the image to be rotated (default: 10). ScannerImageSplit.selectText.2=Sets the minimum absolute angle required for the image to be rotated (default: 10).
ScannerImageSplit.selectText.3=Tolerance: ScannerImageSplit.selectText.3=Tolerance:
ScannerImageSplit.selectText.4=Determines the range of colour variation around the estimated background colour (default: 30). ScannerImageSplit.selectText.4=Determines the range of color variation around the estimated background color (default: 30).
ScannerImageSplit.selectText.5=Minimum Area: ScannerImageSplit.selectText.5=Minimum Area:
ScannerImageSplit.selectText.6=Sets the minimum area threshold for a photo (default: 10000). ScannerImageSplit.selectText.6=Sets the minimum area threshold for a photo (default: 10000).
ScannerImageSplit.selectText.7=Minimum Contour Area: ScannerImageSplit.selectText.7=Minimum Contour Area:
@@ -898,7 +894,7 @@ compress.title=Compress
compress.header=Compress PDF compress.header=Compress PDF
compress.credit=This service uses qpdf for PDF Compress/Optimisation. compress.credit=This service uses qpdf for PDF Compress/Optimisation.
compress.selectText.1=Manual Mode - From 1 to 4 compress.selectText.1=Manual Mode - From 1 to 4
compress.selectText.2=Optimisation level: compress.selectText.2=Optimization level:
compress.selectText.3=4 (Terrible for text images) compress.selectText.3=4 (Terrible for text images)
compress.selectText.4=Auto mode - Auto adjusts quality to get PDF to exact size compress.selectText.4=Auto mode - Auto adjusts quality to get PDF to exact size
compress.selectText.5=Expected PDF Size (e.g. 25MB, 10.8MB, 25KB) compress.selectText.5=Expected PDF Size (e.g. 25MB, 10.8MB, 25KB)
@@ -965,16 +961,6 @@ multiTool.dragDropMessage=Page(s) Selected
multiTool.undo=Undo multiTool.undo=Undo
multiTool.redo=Redo multiTool.redo=Redo
#decrypt
decrypt.passwordPrompt=This file is password-protected. Please enter the password:
decrypt.cancelled=Operation cancelled for PDF: {0}
decrypt.noPassword=No password provided for encrypted PDF: {0}
decrypt.invalidPassword=Please try again with the correct password.
decrypt.invalidPasswordHeader=Incorrect password or unsupported encryption for PDF: {0}
decrypt.unexpectedError=There was an error processing the file. Please try again.
decrypt.serverError=Server error while decrypting: {0}
decrypt.success=File decrypted successfully.
#multiTool-advert #multiTool-advert
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features! multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
@@ -1066,13 +1052,12 @@ addPassword.submit=Encrypt
#watermark #watermark
watermark.title=Add Watermark watermark.title=Add Watermark
watermark.header=Add Watermark watermark.header=Add Watermark
watermark.customColor=Custom Text Colour
watermark.selectText.1=Select PDF to add watermark to: watermark.selectText.1=Select PDF to add watermark to:
watermark.selectText.2=Watermark Text: watermark.selectText.2=Watermark Text:
watermark.selectText.3=Font Size: watermark.selectText.3=Font Size:
watermark.selectText.4=Rotation (0-360): watermark.selectText.4=Rotation (0-360):
watermark.selectText.5=Width Spacer (Space between each watermark horizontally): watermark.selectText.5=widthSpacer (Space between each watermark horizontally):
watermark.selectText.6=Height Spacer (Space between each watermark vertically): watermark.selectText.6=heightSpacer (Space between each watermark vertically):
watermark.selectText.7=Opacity (0% - 100%): watermark.selectText.7=Opacity (0% - 100%):
watermark.selectText.8=Watermark Type: watermark.selectText.8=Watermark Type:
watermark.selectText.9=Watermark Image: watermark.selectText.9=Watermark Image:
@@ -1278,11 +1263,6 @@ splitByChapters.desc.3=Include Metadata: If checked, the original PDF's metadata
splitByChapters.desc.4=Allow Duplicates: If checked, allows multiple bookmarks on the same page to create separate PDFs. splitByChapters.desc.4=Allow Duplicates: If checked, allows multiple bookmarks on the same page to create separate PDFs.
splitByChapters.submit=Split PDF splitByChapters.submit=Split PDF
#File Chooser
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes #release notes
releases.footer=Releases releases.footer=Releases
@@ -1290,38 +1270,3 @@ releases.title=Release Notes
releases.header=Release Notes releases.header=Release Notes
releases.current.version=Current Release releases.current.version=Current Release
releases.note=Release notes are only available in English releases.note=Release notes are only available in English
#Validate Signature
validateSignature.title=Validate PDF Signatures
validateSignature.header=Validate Digital Signatures
validateSignature.selectPDF=Select signed PDF file
validateSignature.submit=Validate Signatures
validateSignature.results=Validation Results
validateSignature.status=Status
validateSignature.signer=Signer
validateSignature.date=Date
validateSignature.reason=Reason
validateSignature.location=Location
validateSignature.noSignatures=No digital signatures found in this document
validateSignature.status.valid=Valid
validateSignature.status.invalid=Invalid
validateSignature.chain.invalid=Certificate chain validation failed - cannot verify signer's identity
validateSignature.trust.invalid=Certificate not in trust store - source cannot be verified
validateSignature.cert.expired=Certificate has expired
validateSignature.cert.revoked=Certificate has been revoked
validateSignature.signature.info=Signature Information
validateSignature.signature=Signature
validateSignature.signature.mathValid=Signature is mathematically valid BUT:
validateSignature.selectCustomCert=Custom Certificate File X.509 (Optional)
validateSignature.cert.info=Certificate Details
validateSignature.cert.issuer=Issuer
validateSignature.cert.subject=Subject
validateSignature.cert.serialNumber=Serial Number
validateSignature.cert.validFrom=Valid From
validateSignature.cert.validUntil=Valid Until
validateSignature.cert.algorithm=Algorithm
validateSignature.cert.keySize=Key Size
validateSignature.cert.version=Version
validateSignature.cert.keyUsage=Key Usage
validateSignature.cert.selfSigned=Self-Signed
validateSignature.cert.bits=bits

View File

@@ -512,10 +512,6 @@ home.splitPdfByChapters.title=Split PDF by Chapters
home.splitPdfByChapters.desc=Split a PDF into multiple files based on its chapter structure. home.splitPdfByChapters.desc=Split a PDF into multiple files based on its chapter structure.
splitPdfByChapters.tags=split,chapters,bookmarks,organize splitPdfByChapters.tags=split,chapters,bookmarks,organize
home.validateSignature.title=Validate PDF Signature
home.validateSignature.desc=Verify digital signatures and certificates in PDF documents
validateSignature.tags=signature,verify,validate,pdf,certificate,digital signature,Validate Signature,Validate certificate
#replace-invert-color #replace-invert-color
replace-color.title=Replace-Invert-Color replace-color.title=Replace-Invert-Color
replace-color.header=Replace-Invert Color PDF replace-color.header=Replace-Invert Color PDF
@@ -965,16 +961,6 @@ multiTool.dragDropMessage=Page(s) Selected
multiTool.undo=Undo multiTool.undo=Undo
multiTool.redo=Redo multiTool.redo=Redo
#decrypt
decrypt.passwordPrompt=This file is password-protected. Please enter the password:
decrypt.cancelled=Operation cancelled for PDF: {0}
decrypt.noPassword=No password provided for encrypted PDF: {0}
decrypt.invalidPassword=Please try again with the correct password.
decrypt.invalidPasswordHeader=Incorrect password or unsupported encryption for PDF: {0}
decrypt.unexpectedError=There was an error processing the file. Please try again.
decrypt.serverError=Server error while decrypting: {0}
decrypt.success=File decrypted successfully.
#multiTool-advert #multiTool-advert
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features! multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
@@ -1066,13 +1052,12 @@ addPassword.submit=Encrypt
#watermark #watermark
watermark.title=Add Watermark watermark.title=Add Watermark
watermark.header=Add Watermark watermark.header=Add Watermark
watermark.customColor=Custom Text Color
watermark.selectText.1=Select PDF to add watermark to: watermark.selectText.1=Select PDF to add watermark to:
watermark.selectText.2=Watermark Text: watermark.selectText.2=Watermark Text:
watermark.selectText.3=Font Size: watermark.selectText.3=Font Size:
watermark.selectText.4=Rotation (0-360): watermark.selectText.4=Rotation (0-360):
watermark.selectText.5=Width Spacer (Space between each watermark horizontally): watermark.selectText.5=widthSpacer (Space between each watermark horizontally):
watermark.selectText.6=Height Spacer (Space between each watermark vertically): watermark.selectText.6=heightSpacer (Space between each watermark vertically):
watermark.selectText.7=Opacity (0% - 100%): watermark.selectText.7=Opacity (0% - 100%):
watermark.selectText.8=Watermark Type: watermark.selectText.8=Watermark Type:
watermark.selectText.9=Watermark Image: watermark.selectText.9=Watermark Image:
@@ -1278,11 +1263,6 @@ splitByChapters.desc.3=Include Metadata: If checked, the original PDF's metadata
splitByChapters.desc.4=Allow Duplicates: If checked, allows multiple bookmarks on the same page to create separate PDFs. splitByChapters.desc.4=Allow Duplicates: If checked, allows multiple bookmarks on the same page to create separate PDFs.
splitByChapters.submit=Split PDF splitByChapters.submit=Split PDF
#File Chooser
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes #release notes
releases.footer=Releases releases.footer=Releases
@@ -1290,38 +1270,3 @@ releases.title=Release Notes
releases.header=Release Notes releases.header=Release Notes
releases.current.version=Current Release releases.current.version=Current Release
releases.note=Release notes are only available in English releases.note=Release notes are only available in English
#Validate Signature
validateSignature.title=Validate PDF Signatures
validateSignature.header=Validate Digital Signatures
validateSignature.selectPDF=Select signed PDF file
validateSignature.submit=Validate Signatures
validateSignature.results=Validation Results
validateSignature.status=Status
validateSignature.signer=Signer
validateSignature.date=Date
validateSignature.reason=Reason
validateSignature.location=Location
validateSignature.noSignatures=No digital signatures found in this document
validateSignature.status.valid=Valid
validateSignature.status.invalid=Invalid
validateSignature.chain.invalid=Certificate chain validation failed - cannot verify signer's identity
validateSignature.trust.invalid=Certificate not in trust store - source cannot be verified
validateSignature.cert.expired=Certificate has expired
validateSignature.cert.revoked=Certificate has been revoked
validateSignature.signature.info=Signature Information
validateSignature.signature=Signature
validateSignature.signature.mathValid=Signature is mathematically valid BUT:
validateSignature.selectCustomCert=Custom Certificate File X.509 (Optional)
validateSignature.cert.info=Certificate Details
validateSignature.cert.issuer=Issuer
validateSignature.cert.subject=Subject
validateSignature.cert.serialNumber=Serial Number
validateSignature.cert.validFrom=Valid From
validateSignature.cert.validUntil=Valid Until
validateSignature.cert.algorithm=Algorithm
validateSignature.cert.keySize=Key Size
validateSignature.cert.version=Version
validateSignature.cert.keyUsage=Key Usage
validateSignature.cert.selfSigned=Self-Signed
validateSignature.cert.bits=bits

View File

@@ -512,10 +512,6 @@ home.splitPdfByChapters.title=Dividir PDF por capítulos
home.splitPdfByChapters.desc=Divida un PDF en varios archivos según su estructura de capítulos. home.splitPdfByChapters.desc=Divida un PDF en varios archivos según su estructura de capítulos.
splitPdfByChapters.tags=dividir,capítulos,marcadores,organizar splitPdfByChapters.tags=dividir,capítulos,marcadores,organizar
home.validateSignature.title=Validate PDF Signature
home.validateSignature.desc=Verify digital signatures and certificates in PDF documents
validateSignature.tags=signature,verify,validate,pdf,certificate,digital signature,Validate Signature,Validate certificate
#replace-invert-color #replace-invert-color
replace-color.title=Reemplazar-Invertir-Color replace-color.title=Reemplazar-Invertir-Color
replace-color.header=Reemplazar-Invertir Color en PDF replace-color.header=Reemplazar-Invertir Color en PDF
@@ -965,16 +961,6 @@ multiTool.dragDropMessage=Page(s) Selected
multiTool.undo=Undo multiTool.undo=Undo
multiTool.redo=Redo multiTool.redo=Redo
#decrypt
decrypt.passwordPrompt=This file is password-protected. Please enter the password:
decrypt.cancelled=Operation cancelled for PDF: {0}
decrypt.noPassword=No password provided for encrypted PDF: {0}
decrypt.invalidPassword=Please try again with the correct password.
decrypt.invalidPasswordHeader=Incorrect password or unsupported encryption for PDF: {0}
decrypt.unexpectedError=There was an error processing the file. Please try again.
decrypt.serverError=Server error while decrypting: {0}
decrypt.success=File decrypted successfully.
#multiTool-advert #multiTool-advert
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features! multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
@@ -1066,7 +1052,6 @@ addPassword.submit=Encriptar
#watermark #watermark
watermark.title=Añadir marca de agua watermark.title=Añadir marca de agua
watermark.header=Añadir marca de agua watermark.header=Añadir marca de agua
watermark.customColor=Personalizar color de texto
watermark.selectText.1=Seleccionar PDF para añadir marca de agua: watermark.selectText.1=Seleccionar PDF para añadir marca de agua:
watermark.selectText.2=Texto de la marca de agua: watermark.selectText.2=Texto de la marca de agua:
watermark.selectText.3=Tamaño de la Fuente: watermark.selectText.3=Tamaño de la Fuente:
@@ -1278,11 +1263,6 @@ splitByChapters.desc.3=Incluir Metadatos: Si está seleccionado, los metadatos d
splitByChapters.desc.4=Permitir Duplicados: Si está seleccionado, permite que múltiples marcadores en la misma página creen archivos PDF separados. splitByChapters.desc.4=Permitir Duplicados: Si está seleccionado, permite que múltiples marcadores en la misma página creen archivos PDF separados.
splitByChapters.submit=Dividir PDF splitByChapters.submit=Dividir PDF
#File Chooser
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes #release notes
releases.footer=Releases releases.footer=Releases
@@ -1290,38 +1270,3 @@ releases.title=Release Notes
releases.header=Release Notes releases.header=Release Notes
releases.current.version=Current Release releases.current.version=Current Release
releases.note=Release notes are only available in English releases.note=Release notes are only available in English
#Validate Signature
validateSignature.title=Validate PDF Signatures
validateSignature.header=Validate Digital Signatures
validateSignature.selectPDF=Select signed PDF file
validateSignature.submit=Validate Signatures
validateSignature.results=Validation Results
validateSignature.status=Status
validateSignature.signer=Signer
validateSignature.date=Date
validateSignature.reason=Reason
validateSignature.location=Location
validateSignature.noSignatures=No digital signatures found in this document
validateSignature.status.valid=Valid
validateSignature.status.invalid=Invalid
validateSignature.chain.invalid=Certificate chain validation failed - cannot verify signer's identity
validateSignature.trust.invalid=Certificate not in trust store - source cannot be verified
validateSignature.cert.expired=Certificate has expired
validateSignature.cert.revoked=Certificate has been revoked
validateSignature.signature.info=Signature Information
validateSignature.signature=Signature
validateSignature.signature.mathValid=Signature is mathematically valid BUT:
validateSignature.selectCustomCert=Custom Certificate File X.509 (Optional)
validateSignature.cert.info=Certificate Details
validateSignature.cert.issuer=Issuer
validateSignature.cert.subject=Subject
validateSignature.cert.serialNumber=Serial Number
validateSignature.cert.validFrom=Valid From
validateSignature.cert.validUntil=Valid Until
validateSignature.cert.algorithm=Algorithm
validateSignature.cert.keySize=Key Size
validateSignature.cert.version=Version
validateSignature.cert.keyUsage=Key Usage
validateSignature.cert.selfSigned=Self-Signed
validateSignature.cert.bits=bits

View File

@@ -512,10 +512,6 @@ home.splitPdfByChapters.title=Split PDF by Chapters
home.splitPdfByChapters.desc=Split a PDF into multiple files based on its chapter structure. home.splitPdfByChapters.desc=Split a PDF into multiple files based on its chapter structure.
splitPdfByChapters.tags=split,chapters,bookmarks,organize splitPdfByChapters.tags=split,chapters,bookmarks,organize
home.validateSignature.title=Validate PDF Signature
home.validateSignature.desc=Verify digital signatures and certificates in PDF documents
validateSignature.tags=signature,verify,validate,pdf,certificate,digital signature,Validate Signature,Validate certificate
#replace-invert-color #replace-invert-color
replace-color.title=Replace-Invert-Color replace-color.title=Replace-Invert-Color
replace-color.header=Replace-Invert Color PDF replace-color.header=Replace-Invert Color PDF
@@ -965,16 +961,6 @@ multiTool.dragDropMessage=Page(s) Selected
multiTool.undo=Undo multiTool.undo=Undo
multiTool.redo=Redo multiTool.redo=Redo
#decrypt
decrypt.passwordPrompt=This file is password-protected. Please enter the password:
decrypt.cancelled=Operation cancelled for PDF: {0}
decrypt.noPassword=No password provided for encrypted PDF: {0}
decrypt.invalidPassword=Please try again with the correct password.
decrypt.invalidPasswordHeader=Incorrect password or unsupported encryption for PDF: {0}
decrypt.unexpectedError=There was an error processing the file. Please try again.
decrypt.serverError=Server error while decrypting: {0}
decrypt.success=File decrypted successfully.
#multiTool-advert #multiTool-advert
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features! multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
@@ -1066,7 +1052,6 @@ addPassword.submit=Enkriptatu
#watermark #watermark
watermark.title=Gehitu ur-marka watermark.title=Gehitu ur-marka
watermark.header=Gehitu ur-marka watermark.header=Gehitu ur-marka
watermark.customColor=Custom Text Color
watermark.selectText.1=Hautatu PDFa ur-marka gehitzeko: watermark.selectText.1=Hautatu PDFa ur-marka gehitzeko:
watermark.selectText.2=Ur-markaren testua: watermark.selectText.2=Ur-markaren testua:
watermark.selectText.3=Letra-tipoaren tamaina: watermark.selectText.3=Letra-tipoaren tamaina:
@@ -1278,11 +1263,6 @@ splitByChapters.desc.3=Include Metadata: If checked, the original PDF's metadata
splitByChapters.desc.4=Allow Duplicates: If checked, allows multiple bookmarks on the same page to create separate PDFs. splitByChapters.desc.4=Allow Duplicates: If checked, allows multiple bookmarks on the same page to create separate PDFs.
splitByChapters.submit=Split PDF splitByChapters.submit=Split PDF
#File Chooser
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes #release notes
releases.footer=Releases releases.footer=Releases
@@ -1290,38 +1270,3 @@ releases.title=Release Notes
releases.header=Release Notes releases.header=Release Notes
releases.current.version=Current Release releases.current.version=Current Release
releases.note=Release notes are only available in English releases.note=Release notes are only available in English
#Validate Signature
validateSignature.title=Validate PDF Signatures
validateSignature.header=Validate Digital Signatures
validateSignature.selectPDF=Select signed PDF file
validateSignature.submit=Validate Signatures
validateSignature.results=Validation Results
validateSignature.status=Status
validateSignature.signer=Signer
validateSignature.date=Date
validateSignature.reason=Reason
validateSignature.location=Location
validateSignature.noSignatures=No digital signatures found in this document
validateSignature.status.valid=Valid
validateSignature.status.invalid=Invalid
validateSignature.chain.invalid=Certificate chain validation failed - cannot verify signer's identity
validateSignature.trust.invalid=Certificate not in trust store - source cannot be verified
validateSignature.cert.expired=Certificate has expired
validateSignature.cert.revoked=Certificate has been revoked
validateSignature.signature.info=Signature Information
validateSignature.signature=Signature
validateSignature.signature.mathValid=Signature is mathematically valid BUT:
validateSignature.selectCustomCert=Custom Certificate File X.509 (Optional)
validateSignature.cert.info=Certificate Details
validateSignature.cert.issuer=Issuer
validateSignature.cert.subject=Subject
validateSignature.cert.serialNumber=Serial Number
validateSignature.cert.validFrom=Valid From
validateSignature.cert.validUntil=Valid Until
validateSignature.cert.algorithm=Algorithm
validateSignature.cert.keySize=Key Size
validateSignature.cert.version=Version
validateSignature.cert.keyUsage=Key Usage
validateSignature.cert.selfSigned=Self-Signed
validateSignature.cert.bits=bits

File diff suppressed because it is too large Load Diff

View File

@@ -512,10 +512,6 @@ home.splitPdfByChapters.title=Séparer un PDF par chapitres
home.splitPdfByChapters.desc=Séparez un PDF en fichiers multiples en fonction de sa structure par chapitres. home.splitPdfByChapters.desc=Séparez un PDF en fichiers multiples en fonction de sa structure par chapitres.
splitPdfByChapters.tags=séparer,chapitres,split,chapters,bookmarks,organize splitPdfByChapters.tags=séparer,chapitres,split,chapters,bookmarks,organize
home.validateSignature.title=Validate PDF Signature
home.validateSignature.desc=Verify digital signatures and certificates in PDF documents
validateSignature.tags=signature,verify,validate,pdf,certificate,digital signature,Validate Signature,Validate certificate
#replace-invert-color #replace-invert-color
replace-color.title=Remplacer-Inverser-Couleur replace-color.title=Remplacer-Inverser-Couleur
replace-color.header=Remplacer-Inverser Couleur PDF replace-color.header=Remplacer-Inverser Couleur PDF
@@ -965,16 +961,6 @@ multiTool.dragDropMessage=Page(s) sélectionnées
multiTool.undo=Undo multiTool.undo=Undo
multiTool.redo=Redo multiTool.redo=Redo
#decrypt
decrypt.passwordPrompt=This file is password-protected. Please enter the password:
decrypt.cancelled=Operation cancelled for PDF: {0}
decrypt.noPassword=No password provided for encrypted PDF: {0}
decrypt.invalidPassword=Please try again with the correct password.
decrypt.invalidPasswordHeader=Incorrect password or unsupported encryption for PDF: {0}
decrypt.unexpectedError=There was an error processing the file. Please try again.
decrypt.serverError=Server error while decrypting: {0}
decrypt.success=File decrypted successfully.
#multiTool-advert #multiTool-advert
multiTool-advert.message=Cette fonctionnalité est aussi disponible dans la <a href="{0}">page de l'outil multifonction</a>. Allez-y pour une interface page par page améliorée et des fonctionnalités additionnelles ! multiTool-advert.message=Cette fonctionnalité est aussi disponible dans la <a href="{0}">page de l'outil multifonction</a>. Allez-y pour une interface page par page améliorée et des fonctionnalités additionnelles !
@@ -1066,13 +1052,12 @@ addPassword.submit=Chiffrer
#watermark #watermark
watermark.title=Ajouter un filigrane watermark.title=Ajouter un filigrane
watermark.header=Ajouter un filigrane watermark.header=Ajouter un filigrane
watermark.customColor=Couleur de texte personnalisée
watermark.selectText.1=PDF auquel ajouter un filigrane watermark.selectText.1=PDF auquel ajouter un filigrane
watermark.selectText.2=Texte du filigrane watermark.selectText.2=Texte du filigrane
watermark.selectText.3=Taille de police watermark.selectText.3=Taille de police
watermark.selectText.4=Rotation (de 0 à 360 degrés) watermark.selectText.4=Rotation (de 0 à 360 degrés)
watermark.selectText.5=Width Spacer (espace entre chaque filigrane horizontalement) watermark.selectText.5=widthSpacer (espace entre chaque filigrane horizontalement)
watermark.selectText.6=Height Spacer (espace entre chaque filigrane verticalement) watermark.selectText.6=heightSpacer (espace entre chaque filigrane verticalement)
watermark.selectText.7=Opacité (de 0% à 100%) watermark.selectText.7=Opacité (de 0% à 100%)
watermark.selectText.8=Type de filigrane watermark.selectText.8=Type de filigrane
watermark.selectText.9=Image du filigrane watermark.selectText.9=Image du filigrane
@@ -1278,11 +1263,6 @@ splitByChapters.desc.3=Inclure les Métadonnées : Si coché, les métadonnées
splitByChapters.desc.4=Autoriser les Doublons : Si coché, permet à plusieurs signets sur la même page de créer des PDF séparés. splitByChapters.desc.4=Autoriser les Doublons : Si coché, permet à plusieurs signets sur la même page de créer des PDF séparés.
splitByChapters.submit=Diviser le PDF splitByChapters.submit=Diviser le PDF
#File Chooser
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes #release notes
releases.footer=Releases releases.footer=Releases
@@ -1290,38 +1270,3 @@ releases.title=Release Notes
releases.header=Release Notes releases.header=Release Notes
releases.current.version=Current Release releases.current.version=Current Release
releases.note=Release notes are only available in English releases.note=Release notes are only available in English
#Validate Signature
validateSignature.title=Validate PDF Signatures
validateSignature.header=Validate Digital Signatures
validateSignature.selectPDF=Select signed PDF file
validateSignature.submit=Validate Signatures
validateSignature.results=Validation Results
validateSignature.status=Status
validateSignature.signer=Signer
validateSignature.date=Date
validateSignature.reason=Reason
validateSignature.location=Location
validateSignature.noSignatures=No digital signatures found in this document
validateSignature.status.valid=Valid
validateSignature.status.invalid=Invalid
validateSignature.chain.invalid=Certificate chain validation failed - cannot verify signer's identity
validateSignature.trust.invalid=Certificate not in trust store - source cannot be verified
validateSignature.cert.expired=Certificate has expired
validateSignature.cert.revoked=Certificate has been revoked
validateSignature.signature.info=Signature Information
validateSignature.signature=Signature
validateSignature.signature.mathValid=Signature is mathematically valid BUT:
validateSignature.selectCustomCert=Custom Certificate File X.509 (Optional)
validateSignature.cert.info=Certificate Details
validateSignature.cert.issuer=Issuer
validateSignature.cert.subject=Subject
validateSignature.cert.serialNumber=Serial Number
validateSignature.cert.validFrom=Valid From
validateSignature.cert.validUntil=Valid Until
validateSignature.cert.algorithm=Algorithm
validateSignature.cert.keySize=Key Size
validateSignature.cert.version=Version
validateSignature.cert.keyUsage=Key Usage
validateSignature.cert.selfSigned=Self-Signed
validateSignature.cert.bits=bits

View File

@@ -512,10 +512,6 @@ home.splitPdfByChapters.title=Split PDF by Chapters
home.splitPdfByChapters.desc=Split a PDF into multiple files based on its chapter structure. home.splitPdfByChapters.desc=Split a PDF into multiple files based on its chapter structure.
splitPdfByChapters.tags=split,chapters,bookmarks,organize splitPdfByChapters.tags=split,chapters,bookmarks,organize
home.validateSignature.title=Validate PDF Signature
home.validateSignature.desc=Verify digital signatures and certificates in PDF documents
validateSignature.tags=signature,verify,validate,pdf,certificate,digital signature,Validate Signature,Validate certificate
#replace-invert-color #replace-invert-color
replace-color.title=Replace-Invert-Color replace-color.title=Replace-Invert-Color
replace-color.header=Replace-Invert Color PDF replace-color.header=Replace-Invert Color PDF
@@ -965,16 +961,6 @@ multiTool.dragDropMessage=Page(s) Selected
multiTool.undo=Undo multiTool.undo=Undo
multiTool.redo=Redo multiTool.redo=Redo
#decrypt
decrypt.passwordPrompt=This file is password-protected. Please enter the password:
decrypt.cancelled=Operation cancelled for PDF: {0}
decrypt.noPassword=No password provided for encrypted PDF: {0}
decrypt.invalidPassword=Please try again with the correct password.
decrypt.invalidPasswordHeader=Incorrect password or unsupported encryption for PDF: {0}
decrypt.unexpectedError=There was an error processing the file. Please try again.
decrypt.serverError=Server error while decrypting: {0}
decrypt.success=File decrypted successfully.
#multiTool-advert #multiTool-advert
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features! multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
@@ -1066,12 +1052,11 @@ addPassword.submit=Criptigh
#watermark #watermark
watermark.title=Cuir Uisce leis watermark.title=Cuir Uisce leis
watermark.header=Cuir Uisce leis watermark.header=Cuir Uisce leis
watermark.customColor=Dath Téacs Saincheaptha
watermark.selectText.1=Roghnaigh PDF chun comhartha uisce a chur leis: watermark.selectText.1=Roghnaigh PDF chun comhartha uisce a chur leis:
watermark.selectText.2=Téacs Comhartha Uisce: watermark.selectText.2=Téacs Comhartha Uisce:
watermark.selectText.3=Méid cló: watermark.selectText.3=Méid cló:
watermark.selectText.4=Rothlú (0-360): watermark.selectText.4=Rothlú (0-360):
watermark.selectText.5=Width Spacer (Spás idir gach comhartha uisce go cothrománach): watermark.selectText.5=widthSpacer (Spás idir gach comhartha uisce go cothrománach):
watermark.selectText.6=spásaire airde (Spás idir gach comhartha uisce go hingearach): watermark.selectText.6=spásaire airde (Spás idir gach comhartha uisce go hingearach):
watermark.selectText.7=Teimhneacht (0% - 100%): watermark.selectText.7=Teimhneacht (0% - 100%):
watermark.selectText.8=Cineál Comhartha Uisce: watermark.selectText.8=Cineál Comhartha Uisce:
@@ -1278,11 +1263,6 @@ splitByChapters.desc.3=Include Metadata: If checked, the original PDF's metadata
splitByChapters.desc.4=Allow Duplicates: If checked, allows multiple bookmarks on the same page to create separate PDFs. splitByChapters.desc.4=Allow Duplicates: If checked, allows multiple bookmarks on the same page to create separate PDFs.
splitByChapters.submit=Split PDF splitByChapters.submit=Split PDF
#File Chooser
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes #release notes
releases.footer=Releases releases.footer=Releases
@@ -1290,38 +1270,3 @@ releases.title=Release Notes
releases.header=Release Notes releases.header=Release Notes
releases.current.version=Current Release releases.current.version=Current Release
releases.note=Release notes are only available in English releases.note=Release notes are only available in English
#Validate Signature
validateSignature.title=Validate PDF Signatures
validateSignature.header=Validate Digital Signatures
validateSignature.selectPDF=Select signed PDF file
validateSignature.submit=Validate Signatures
validateSignature.results=Validation Results
validateSignature.status=Status
validateSignature.signer=Signer
validateSignature.date=Date
validateSignature.reason=Reason
validateSignature.location=Location
validateSignature.noSignatures=No digital signatures found in this document
validateSignature.status.valid=Valid
validateSignature.status.invalid=Invalid
validateSignature.chain.invalid=Certificate chain validation failed - cannot verify signer's identity
validateSignature.trust.invalid=Certificate not in trust store - source cannot be verified
validateSignature.cert.expired=Certificate has expired
validateSignature.cert.revoked=Certificate has been revoked
validateSignature.signature.info=Signature Information
validateSignature.signature=Signature
validateSignature.signature.mathValid=Signature is mathematically valid BUT:
validateSignature.selectCustomCert=Custom Certificate File X.509 (Optional)
validateSignature.cert.info=Certificate Details
validateSignature.cert.issuer=Issuer
validateSignature.cert.subject=Subject
validateSignature.cert.serialNumber=Serial Number
validateSignature.cert.validFrom=Valid From
validateSignature.cert.validUntil=Valid Until
validateSignature.cert.algorithm=Algorithm
validateSignature.cert.keySize=Key Size
validateSignature.cert.version=Version
validateSignature.cert.keyUsage=Key Usage
validateSignature.cert.selfSigned=Self-Signed
validateSignature.cert.bits=bits

View File

@@ -512,10 +512,6 @@ home.splitPdfByChapters.title=अध्यायों पर अलग-कर
home.splitPdfByChapters.desc=पुस्तक के अध्याय की संरचना पर आधारित एक PDF को बहिन-भागों में विभाजित करें home.splitPdfByChapters.desc=पुस्तक के अध्याय की संरचना पर आधारित एक PDF को बहिन-भागों में विभाजित करें
splitPdfByChapters.tags=विभाजन,अध्याय,पसंदीदा,रजैत splitPdfByChapters.tags=विभाजन,अध्याय,पसंदीदा,रजैत
home.validateSignature.title=Validate PDF Signature
home.validateSignature.desc=Verify digital signatures and certificates in PDF documents
validateSignature.tags=signature,verify,validate,pdf,certificate,digital signature,Validate Signature,Validate certificate
#replace-invert-color #replace-invert-color
replace-color.title=Replace-Invert-Color replace-color.title=Replace-Invert-Color
replace-color.header=चित्र रंग परिवर्तन/उलटकर परिवर्तन PDF replace-color.header=चित्र रंग परिवर्तन/उलटकर परिवर्तन PDF
@@ -965,16 +961,6 @@ multiTool.dragDropMessage=Page(s) Selected
multiTool.undo=Undo multiTool.undo=Undo
multiTool.redo=Redo multiTool.redo=Redo
#decrypt
decrypt.passwordPrompt=This file is password-protected. Please enter the password:
decrypt.cancelled=Operation cancelled for PDF: {0}
decrypt.noPassword=No password provided for encrypted PDF: {0}
decrypt.invalidPassword=Please try again with the correct password.
decrypt.invalidPasswordHeader=Incorrect password or unsupported encryption for PDF: {0}
decrypt.unexpectedError=There was an error processing the file. Please try again.
decrypt.serverError=Server error while decrypting: {0}
decrypt.success=File decrypted successfully.
#multiTool-advert #multiTool-advert
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features! multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
@@ -1066,7 +1052,6 @@ addPassword.submit=एन्क्रिप्ट करें
#watermark #watermark
watermark.title=वॉटरमार्क जोड़ें watermark.title=वॉटरमार्क जोड़ें
watermark.header=वॉटरमार्क जोड़ें watermark.header=वॉटरमार्क जोड़ें
watermark.customColor=संवैधित टेक्स्ट रंग
watermark.selectText.1=वॉटरमार्क जोड़ने के लिए पीडीएफ चुनें: watermark.selectText.1=वॉटरमार्क जोड़ने के लिए पीडीएफ चुनें:
watermark.selectText.2=वॉटरमार्क टेक्स्ट: watermark.selectText.2=वॉटरमार्क टेक्स्ट:
watermark.selectText.3=फ़ॉन्ट साइज़: watermark.selectText.3=फ़ॉन्ट साइज़:
@@ -1278,11 +1263,6 @@ splitByChapters.desc.3=मॉडेटरेट का शामिल करे
splitByChapters.desc.4=यादृच्छिक पुनरावृत्ति अनुमोदित: यदि सत्यापित किया जाता है, एक ही पेज पर दोहरे मूल्यांकन पब्लिक पीड़एफ बनाने की संभावना देता है। splitByChapters.desc.4=यादृच्छिक पुनरावृत्ति अनुमोदित: यदि सत्यापित किया जाता है, एक ही पेज पर दोहरे मूल्यांकन पब्लिक पीड़एफ बनाने की संभावना देता है।
splitByChapters.submit=PDF विभाजित splitByChapters.submit=PDF विभाजित
#File Chooser
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes #release notes
releases.footer=Releases releases.footer=Releases
@@ -1290,38 +1270,3 @@ releases.title=Release Notes
releases.header=Release Notes releases.header=Release Notes
releases.current.version=Current Release releases.current.version=Current Release
releases.note=Release notes are only available in English releases.note=Release notes are only available in English
#Validate Signature
validateSignature.title=Validate PDF Signatures
validateSignature.header=Validate Digital Signatures
validateSignature.selectPDF=Select signed PDF file
validateSignature.submit=Validate Signatures
validateSignature.results=Validation Results
validateSignature.status=Status
validateSignature.signer=Signer
validateSignature.date=Date
validateSignature.reason=Reason
validateSignature.location=Location
validateSignature.noSignatures=No digital signatures found in this document
validateSignature.status.valid=Valid
validateSignature.status.invalid=Invalid
validateSignature.chain.invalid=Certificate chain validation failed - cannot verify signer's identity
validateSignature.trust.invalid=Certificate not in trust store - source cannot be verified
validateSignature.cert.expired=Certificate has expired
validateSignature.cert.revoked=Certificate has been revoked
validateSignature.signature.info=Signature Information
validateSignature.signature=Signature
validateSignature.signature.mathValid=Signature is mathematically valid BUT:
validateSignature.selectCustomCert=Custom Certificate File X.509 (Optional)
validateSignature.cert.info=Certificate Details
validateSignature.cert.issuer=Issuer
validateSignature.cert.subject=Subject
validateSignature.cert.serialNumber=Serial Number
validateSignature.cert.validFrom=Valid From
validateSignature.cert.validUntil=Valid Until
validateSignature.cert.algorithm=Algorithm
validateSignature.cert.keySize=Key Size
validateSignature.cert.version=Version
validateSignature.cert.keyUsage=Key Usage
validateSignature.cert.selfSigned=Self-Signed
validateSignature.cert.bits=bits

View File

@@ -512,10 +512,6 @@ home.splitPdfByChapters.title=Podijeli PDF prema glavama
home.splitPdfByChapters.desc=Podijeli PDF na više datoteka prema njegovom strukturnom obliku glava. home.splitPdfByChapters.desc=Podijeli PDF na više datoteka prema njegovom strukturnom obliku glava.
splitPdfByChapters.tags=podjela, glave, markere, organizacija splitPdfByChapters.tags=podjela, glave, markere, organizacija
home.validateSignature.title=Validate PDF Signature
home.validateSignature.desc=Verify digital signatures and certificates in PDF documents
validateSignature.tags=signature,verify,validate,pdf,certificate,digital signature,Validate Signature,Validate certificate
#replace-invert-color #replace-invert-color
replace-color.title=Replace-Invert-Color replace-color.title=Replace-Invert-Color
replace-color.header=Zameni-inverziranje boja u PDF-u replace-color.header=Zameni-inverziranje boja u PDF-u
@@ -965,16 +961,6 @@ multiTool.dragDropMessage=Page(s) Selected
multiTool.undo=Undo multiTool.undo=Undo
multiTool.redo=Redo multiTool.redo=Redo
#decrypt
decrypt.passwordPrompt=This file is password-protected. Please enter the password:
decrypt.cancelled=Operation cancelled for PDF: {0}
decrypt.noPassword=No password provided for encrypted PDF: {0}
decrypt.invalidPassword=Please try again with the correct password.
decrypt.invalidPasswordHeader=Incorrect password or unsupported encryption for PDF: {0}
decrypt.unexpectedError=There was an error processing the file. Please try again.
decrypt.serverError=Server error while decrypting: {0}
decrypt.success=File decrypted successfully.
#multiTool-advert #multiTool-advert
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features! multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
@@ -1066,7 +1052,6 @@ addPassword.submit=Šifriraj
#watermark #watermark
watermark.title=Dodaj vodeni žig watermark.title=Dodaj vodeni žig
watermark.header=Dodaj vodeni žig watermark.header=Dodaj vodeni žig
watermark.customColor=Prilagođena boja teksta
watermark.selectText.1=Izaberite PDF za dodavanje vodenog žiga: watermark.selectText.1=Izaberite PDF za dodavanje vodenog žiga:
watermark.selectText.2=Tekst vodenog žiga: watermark.selectText.2=Tekst vodenog žiga:
watermark.selectText.3=Veličina fonta: watermark.selectText.3=Veličina fonta:
@@ -1278,11 +1263,6 @@ splitByChapters.desc.3=Uključi metapodatke: Ako je pokušano, metapodaci iz ori
splitByChapters.desc.4=Dopuštaj duplikate: Ako je ova opcija zaštićena, dozvoljava se da se na istoj strani mogu stvoriti posebne PDF datoteke s više oznaka. splitByChapters.desc.4=Dopuštaj duplikate: Ako je ova opcija zaštićena, dozvoljava se da se na istoj strani mogu stvoriti posebne PDF datoteke s više oznaka.
splitByChapters.submit=Podijeli PDF splitByChapters.submit=Podijeli PDF
#File Chooser
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes #release notes
releases.footer=Releases releases.footer=Releases
@@ -1290,38 +1270,3 @@ releases.title=Release Notes
releases.header=Release Notes releases.header=Release Notes
releases.current.version=Current Release releases.current.version=Current Release
releases.note=Release notes are only available in English releases.note=Release notes are only available in English
#Validate Signature
validateSignature.title=Validate PDF Signatures
validateSignature.header=Validate Digital Signatures
validateSignature.selectPDF=Select signed PDF file
validateSignature.submit=Validate Signatures
validateSignature.results=Validation Results
validateSignature.status=Status
validateSignature.signer=Signer
validateSignature.date=Date
validateSignature.reason=Reason
validateSignature.location=Location
validateSignature.noSignatures=No digital signatures found in this document
validateSignature.status.valid=Valid
validateSignature.status.invalid=Invalid
validateSignature.chain.invalid=Certificate chain validation failed - cannot verify signer's identity
validateSignature.trust.invalid=Certificate not in trust store - source cannot be verified
validateSignature.cert.expired=Certificate has expired
validateSignature.cert.revoked=Certificate has been revoked
validateSignature.signature.info=Signature Information
validateSignature.signature=Signature
validateSignature.signature.mathValid=Signature is mathematically valid BUT:
validateSignature.selectCustomCert=Custom Certificate File X.509 (Optional)
validateSignature.cert.info=Certificate Details
validateSignature.cert.issuer=Issuer
validateSignature.cert.subject=Subject
validateSignature.cert.serialNumber=Serial Number
validateSignature.cert.validFrom=Valid From
validateSignature.cert.validUntil=Valid Until
validateSignature.cert.algorithm=Algorithm
validateSignature.cert.keySize=Key Size
validateSignature.cert.version=Version
validateSignature.cert.keyUsage=Key Usage
validateSignature.cert.selfSigned=Self-Signed
validateSignature.cert.bits=bits

View File

@@ -512,10 +512,6 @@ home.splitPdfByChapters.title=PDF felosztása fejezetek szerint
home.splitPdfByChapters.desc=Fejezetei alapján egy PDF fájl több dokumentumba osztás. home.splitPdfByChapters.desc=Fejezetei alapján egy PDF fájl több dokumentumba osztás.
splitPdfByChapters.tags=Osztás, fejezetek, jelezes, organizálás splitPdfByChapters.tags=Osztás, fejezetek, jelezes, organizálás
home.validateSignature.title=Validate PDF Signature
home.validateSignature.desc=Verify digital signatures and certificates in PDF documents
validateSignature.tags=signature,verify,validate,pdf,certificate,digital signature,Validate Signature,Validate certificate
#replace-invert-color #replace-invert-color
replace-color.title=Replace-Invert-Color replace-color.title=Replace-Invert-Color
replace-color.header=Visszaalakítás-összevétel a színekkel PDF-ben replace-color.header=Visszaalakítás-összevétel a színekkel PDF-ben
@@ -965,16 +961,6 @@ multiTool.dragDropMessage=Page(s) Selected
multiTool.undo=Undo multiTool.undo=Undo
multiTool.redo=Redo multiTool.redo=Redo
#decrypt
decrypt.passwordPrompt=This file is password-protected. Please enter the password:
decrypt.cancelled=Operation cancelled for PDF: {0}
decrypt.noPassword=No password provided for encrypted PDF: {0}
decrypt.invalidPassword=Please try again with the correct password.
decrypt.invalidPasswordHeader=Incorrect password or unsupported encryption for PDF: {0}
decrypt.unexpectedError=There was an error processing the file. Please try again.
decrypt.serverError=Server error while decrypting: {0}
decrypt.success=File decrypted successfully.
#multiTool-advert #multiTool-advert
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features! multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
@@ -1066,13 +1052,12 @@ addPassword.submit=Titkosítás
#watermark #watermark
watermark.title=Vízjel hozzáadása watermark.title=Vízjel hozzáadása
watermark.header=Vízjel hozzáadása watermark.header=Vízjel hozzáadása
watermark.customColor=Egyéni szövegszín
watermark.selectText.1=Válassza ki a PDF-t, amelyhez vízjelet kíván hozzáadni: watermark.selectText.1=Válassza ki a PDF-t, amelyhez vízjelet kíván hozzáadni:
watermark.selectText.2=Vízjel szövege: watermark.selectText.2=Vízjel szövege:
watermark.selectText.3=Betűméret: watermark.selectText.3=Betűméret:
watermark.selectText.4=Forgatás (0-360): watermark.selectText.4=Forgatás (0-360):
watermark.selectText.5=Width Spacer (Hely a vízjelek között vízszintesen): watermark.selectText.5=widthSpacer (Hely a vízjelek között vízszintesen):
watermark.selectText.6=Height Spacer (Hely a vízjelek között függőlegesen): watermark.selectText.6=heightSpacer (Hely a vízjelek között függőlegesen):
watermark.selectText.7=Átlátszóság (0% - 100%): watermark.selectText.7=Átlátszóság (0% - 100%):
watermark.selectText.8=Vízjel típusa: watermark.selectText.8=Vízjel típusa:
watermark.selectText.9=Vízjel képe: watermark.selectText.9=Vízjel képe:
@@ -1278,11 +1263,6 @@ splitByChapters.desc.3=Metaadatok belefoglalása: Ha bevanítva van, az eredeti
splitByChapters.desc.4=Duplikációk engedélyezése: Ha bevanítva van, lehetővé teszi a megadott oldalon lévő több kijelzőszint alapján új PDF-ek létrehozása. splitByChapters.desc.4=Duplikációk engedélyezése: Ha bevanítva van, lehetővé teszi a megadott oldalon lévő több kijelzőszint alapján új PDF-ek létrehozása.
splitByChapters.submit=PDF osztás splitByChapters.submit=PDF osztás
#File Chooser
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes #release notes
releases.footer=Releases releases.footer=Releases
@@ -1290,38 +1270,3 @@ releases.title=Release Notes
releases.header=Release Notes releases.header=Release Notes
releases.current.version=Current Release releases.current.version=Current Release
releases.note=Release notes are only available in English releases.note=Release notes are only available in English
#Validate Signature
validateSignature.title=Validate PDF Signatures
validateSignature.header=Validate Digital Signatures
validateSignature.selectPDF=Select signed PDF file
validateSignature.submit=Validate Signatures
validateSignature.results=Validation Results
validateSignature.status=Status
validateSignature.signer=Signer
validateSignature.date=Date
validateSignature.reason=Reason
validateSignature.location=Location
validateSignature.noSignatures=No digital signatures found in this document
validateSignature.status.valid=Valid
validateSignature.status.invalid=Invalid
validateSignature.chain.invalid=Certificate chain validation failed - cannot verify signer's identity
validateSignature.trust.invalid=Certificate not in trust store - source cannot be verified
validateSignature.cert.expired=Certificate has expired
validateSignature.cert.revoked=Certificate has been revoked
validateSignature.signature.info=Signature Information
validateSignature.signature=Signature
validateSignature.signature.mathValid=Signature is mathematically valid BUT:
validateSignature.selectCustomCert=Custom Certificate File X.509 (Optional)
validateSignature.cert.info=Certificate Details
validateSignature.cert.issuer=Issuer
validateSignature.cert.subject=Subject
validateSignature.cert.serialNumber=Serial Number
validateSignature.cert.validFrom=Valid From
validateSignature.cert.validUntil=Valid Until
validateSignature.cert.algorithm=Algorithm
validateSignature.cert.keySize=Key Size
validateSignature.cert.version=Version
validateSignature.cert.keyUsage=Key Usage
validateSignature.cert.selfSigned=Self-Signed
validateSignature.cert.bits=bits

View File

@@ -512,10 +512,6 @@ home.splitPdfByChapters.title=Pisahkan PDF berdasarkan Bab
home.splitPdfByChapters.desc=Memisahkan PDF menjadi beberapa file berdasarkan struktur babnya. home.splitPdfByChapters.desc=Memisahkan PDF menjadi beberapa file berdasarkan struktur babnya.
splitPdfByChapters.tags=pemisahan,bab,bookmark,atur splitPdfByChapters.tags=pemisahan,bab,bookmark,atur
home.validateSignature.title=Validate PDF Signature
home.validateSignature.desc=Verify digital signatures and certificates in PDF documents
validateSignature.tags=signature,verify,validate,pdf,certificate,digital signature,Validate Signature,Validate certificate
#replace-invert-color #replace-invert-color
replace-color.title=Ganti-Inversi-Warna replace-color.title=Ganti-Inversi-Warna
replace-color.header=Ganti-Inversi Warna PDF replace-color.header=Ganti-Inversi Warna PDF
@@ -965,16 +961,6 @@ multiTool.dragDropMessage=Page(s) Selected
multiTool.undo=Undo multiTool.undo=Undo
multiTool.redo=Redo multiTool.redo=Redo
#decrypt
decrypt.passwordPrompt=This file is password-protected. Please enter the password:
decrypt.cancelled=Operation cancelled for PDF: {0}
decrypt.noPassword=No password provided for encrypted PDF: {0}
decrypt.invalidPassword=Please try again with the correct password.
decrypt.invalidPasswordHeader=Incorrect password or unsupported encryption for PDF: {0}
decrypt.unexpectedError=There was an error processing the file. Please try again.
decrypt.serverError=Server error while decrypting: {0}
decrypt.success=File decrypted successfully.
#multiTool-advert #multiTool-advert
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features! multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
@@ -1066,13 +1052,12 @@ addPassword.submit=Enkripsi
#watermark #watermark
watermark.title=Tambahkan Watermark watermark.title=Tambahkan Watermark
watermark.header=Tambahkan Watermark watermark.header=Tambahkan Watermark
watermark.customColor=Warna Teks Kustom
watermark.selectText.1=Pilih PDF untuk menambahkan watermark: watermark.selectText.1=Pilih PDF untuk menambahkan watermark:
watermark.selectText.2=Text Watermark: watermark.selectText.2=Text Watermark:
watermark.selectText.3=Ukuran Huruf: watermark.selectText.3=Ukuran Huruf:
watermark.selectText.4=Rotasi (0-360): watermark.selectText.4=Rotasi (0-360):
watermark.selectText.5=Width Spacer (Spasi diantara setiap watermark horisontal): watermark.selectText.5=widthSpacer (Spasi diantara setiap watermark horisontal):
watermark.selectText.6=Height Spacer (Spasi diantara setiap watermark vertikal): watermark.selectText.6=heightSpacer (Spasi diantara setiap watermark vertikal):
watermark.selectText.7=Kejernihan (0% - 100%): watermark.selectText.7=Kejernihan (0% - 100%):
watermark.selectText.8=Tipe Watermark: watermark.selectText.8=Tipe Watermark:
watermark.selectText.9=Gambar Watermark: watermark.selectText.9=Gambar Watermark:
@@ -1278,11 +1263,6 @@ splitByChapters.desc.3=Termasuk Metadata: Jika dicentang, metadata asli PDF akan
splitByChapters.desc.4=Izinkan Duplikat: Jika dicentang, mengizinkan beberapa markah pada halaman yang sama untuk membuat PDF terpisah. splitByChapters.desc.4=Izinkan Duplikat: Jika dicentang, mengizinkan beberapa markah pada halaman yang sama untuk membuat PDF terpisah.
splitByChapters.submit=Pecah PDF splitByChapters.submit=Pecah PDF
#File Chooser
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes #release notes
releases.footer=Releases releases.footer=Releases
@@ -1290,38 +1270,3 @@ releases.title=Release Notes
releases.header=Release Notes releases.header=Release Notes
releases.current.version=Current Release releases.current.version=Current Release
releases.note=Release notes are only available in English releases.note=Release notes are only available in English
#Validate Signature
validateSignature.title=Validate PDF Signatures
validateSignature.header=Validate Digital Signatures
validateSignature.selectPDF=Select signed PDF file
validateSignature.submit=Validate Signatures
validateSignature.results=Validation Results
validateSignature.status=Status
validateSignature.signer=Signer
validateSignature.date=Date
validateSignature.reason=Reason
validateSignature.location=Location
validateSignature.noSignatures=No digital signatures found in this document
validateSignature.status.valid=Valid
validateSignature.status.invalid=Invalid
validateSignature.chain.invalid=Certificate chain validation failed - cannot verify signer's identity
validateSignature.trust.invalid=Certificate not in trust store - source cannot be verified
validateSignature.cert.expired=Certificate has expired
validateSignature.cert.revoked=Certificate has been revoked
validateSignature.signature.info=Signature Information
validateSignature.signature=Signature
validateSignature.signature.mathValid=Signature is mathematically valid BUT:
validateSignature.selectCustomCert=Custom Certificate File X.509 (Optional)
validateSignature.cert.info=Certificate Details
validateSignature.cert.issuer=Issuer
validateSignature.cert.subject=Subject
validateSignature.cert.serialNumber=Serial Number
validateSignature.cert.validFrom=Valid From
validateSignature.cert.validUntil=Valid Until
validateSignature.cert.algorithm=Algorithm
validateSignature.cert.keySize=Key Size
validateSignature.cert.version=Version
validateSignature.cert.keyUsage=Key Usage
validateSignature.cert.selfSigned=Self-Signed
validateSignature.cert.bits=bits

View File

@@ -512,10 +512,6 @@ home.splitPdfByChapters.title=Dividi PDF per capitoli
home.splitPdfByChapters.desc=Dividi un PDF in più file in base alla struttura dei capitoli. home.splitPdfByChapters.desc=Dividi un PDF in più file in base alla struttura dei capitoli.
splitPdfByChapters.tags=dividi, capitoli, segnalibri, organizza splitPdfByChapters.tags=dividi, capitoli, segnalibri, organizza
home.validateSignature.title=Convalida la firma PDF
home.validateSignature.desc=Verificare le firme digitali e i certificati nei documenti PDF
validateSignature.tags=firma,verifica,convalida,pdf,certificato,firma digitale,convalida firma,convalida certificato
#replace-invert-color #replace-invert-color
replace-color.title=Sostituisci-Inverti-Colore replace-color.title=Sostituisci-Inverti-Colore
replace-color.header=Sostituisci-Inverti colore PDF replace-color.header=Sostituisci-Inverti colore PDF
@@ -962,18 +958,8 @@ multiTool.moveLeft=Sposta a sinistra
multiTool.moveRight=Sposta a destra multiTool.moveRight=Sposta a destra
multiTool.delete=Elimina multiTool.delete=Elimina
multiTool.dragDropMessage=Pagina(e) selezionata(e) multiTool.dragDropMessage=Pagina(e) selezionata(e)
multiTool.undo=Annulla multiTool.undo=Undo
multiTool.redo=Rifai multiTool.redo=Redo
#decrypt
decrypt.passwordPrompt=Questo file è protetto da password. Inserisci la password:
decrypt.cancelled=Operazione annullata per il PDF: {0}
decrypt.noPassword=Nessuna password fornita per il PDF crittografato: {0}
decrypt.invalidPassword=Riprova con la password corretta.
decrypt.invalidPasswordHeader=Password errata o crittografia non supportata per il PDF: {0}
decrypt.unexpectedError=Si è verificato un errore durante l'elaborazione del file. Riprova..
decrypt.serverError=Errore del server durante la decrittazione: {0}
decrypt.success=File decrittografato con successo.
#multiTool-advert #multiTool-advert
multiTool-advert.message=Questa funzione è disponibile anche nella nostra <a href="{0}">pagina multi-strumento</a>. Scoprila per un'interfaccia utente pagina per pagina migliorata e funzionalità aggiuntive! multiTool-advert.message=Questa funzione è disponibile anche nella nostra <a href="{0}">pagina multi-strumento</a>. Scoprila per un'interfaccia utente pagina per pagina migliorata e funzionalità aggiuntive!
@@ -1066,7 +1052,6 @@ addPassword.submit=Crittografa
#watermark #watermark
watermark.title=Aggiungi Filigrana watermark.title=Aggiungi Filigrana
watermark.header=Aggiungi filigrana watermark.header=Aggiungi filigrana
watermark.customColor=Colore testo personalizzato
watermark.selectText.1=Seleziona PDF a cui aggiungere la filigrana: watermark.selectText.1=Seleziona PDF a cui aggiungere la filigrana:
watermark.selectText.2=Testo: watermark.selectText.2=Testo:
watermark.selectText.3=Dimensione carattere: watermark.selectText.3=Dimensione carattere:
@@ -1278,11 +1263,6 @@ splitByChapters.desc.3=Includi metadati: se selezionato, i metadati del PDF orig
splitByChapters.desc.4=Consenti duplicati: se selezionata, consente più segnalibri sulla stessa pagina per creare PDF separati. splitByChapters.desc.4=Consenti duplicati: se selezionata, consente più segnalibri sulla stessa pagina per creare PDF separati.
splitByChapters.submit=Dividi PDF splitByChapters.submit=Dividi PDF
#File Chooser
fileChooser.click=Clicca
fileChooser.or=o
fileChooser.dragAndDrop=Trascina & Rilascia
fileChooser.hoveredDragAndDrop=Trascina & rilascia i file qui
#release notes #release notes
releases.footer=Rilasci releases.footer=Rilasci
@@ -1290,38 +1270,3 @@ releases.title=Note di rilascio
releases.header=Note di rilascio releases.header=Note di rilascio
releases.current.version=Rilascio corrente releases.current.version=Rilascio corrente
releases.note=Le note di rilascio sono disponibili solo in inglese releases.note=Le note di rilascio sono disponibili solo in inglese
#Validate Signature
validateSignature.title=Validare le firme PDF
validateSignature.header=Convalidare le firme digitali
validateSignature.selectPDF=Seleziona il file PDF firmato
validateSignature.submit=Convalida firme
validateSignature.results=Risultati di convalida
validateSignature.status=Stato
validateSignature.signer=Firmatario
validateSignature.date=Data
validateSignature.reason=Ragione
validateSignature.location=Posizione
validateSignature.noSignatures=Nessuna firma digitale trovata in questo documento
validateSignature.status.valid=Valida
validateSignature.status.invalid=Invalida
validateSignature.chain.invalid=Convalida della catena di certificati non riuscita: impossibile verificare l'identità del firmatario
validateSignature.trust.invalid=Certificato non presente nell'archivio attendibile: la fonte non può essere verificata
validateSignature.cert.expired=Il certificato è scaduto
validateSignature.cert.revoked=Il certificato è stato revocato
validateSignature.signature.info=Informazioni sulla firma
validateSignature.signature=Firma
validateSignature.signature.mathValid=La firma è matematicamente valida MA:
validateSignature.selectCustomCert=File di certificato personalizzato X.509 (opzionale)
validateSignature.cert.info=Dettagli del certificato
validateSignature.cert.issuer=Emittente
validateSignature.cert.subject=Soggetto
validateSignature.cert.serialNumber=Numero di serie
validateSignature.cert.validFrom=Valido da
validateSignature.cert.validUntil=Valido fino a
validateSignature.cert.algorithm=Algoritmo
validateSignature.cert.keySize=Dimensione chiave
validateSignature.cert.version=Versione
validateSignature.cert.keyUsage=Utilizzo della chiave
validateSignature.cert.selfSigned=Autofirmato
validateSignature.cert.bits=bit

View File

@@ -56,12 +56,12 @@ userNotFoundMessage=ユーザーが見つかりません。
incorrectPasswordMessage=現在のパスワードが正しくありません。 incorrectPasswordMessage=現在のパスワードが正しくありません。
usernameExistsMessage=新しいユーザー名はすでに存在します。 usernameExistsMessage=新しいユーザー名はすでに存在します。
invalidUsernameMessage=ユーザー名が無効です。ユーザー名には文字、数字、およびそれに続く特殊文字 @._+- のみを含めることができます。または、有効な電子メール アドレスである必要があります。 invalidUsernameMessage=ユーザー名が無効です。ユーザー名には文字、数字、およびそれに続く特殊文字 @._+- のみを含めることができます。または、有効な電子メール アドレスである必要があります。
invalidPasswordMessage=パスワードは空にすることはできません。また、先頭・末尾にスペースを含めることもできません。 invalidPasswordMessage=The password must not be empty and must not have spaces at the beginning or end.
confirmPasswordErrorMessage=新しいパスワードと新しいパスワードの確認は一致する必要があります。 confirmPasswordErrorMessage=新しいパスワードと新しいパスワードの確認は一致する必要があります。
deleteCurrentUserMessage=現在ログインしているユーザーは削除できません。 deleteCurrentUserMessage=現在ログインしているユーザーは削除できません。
deleteUsernameExistsMessage=そのユーザー名は存在しないため削除できません。 deleteUsernameExistsMessage=そのユーザー名は存在しないため削除できません。
downgradeCurrentUserMessage=現在のユーザーの役割をダウングレードできません downgradeCurrentUserMessage=現在のユーザーの役割をダウングレードできません
disabledCurrentUserMessage=現在のユーザーを無効にすることはできません disabledCurrentUserMessage=The current user cannot be disabled
downgradeCurrentUserLongMessage=現在のユーザーの役割をダウングレードできません。したがって、現在のユーザーは表示されません。 downgradeCurrentUserLongMessage=現在のユーザーの役割をダウングレードできません。したがって、現在のユーザーは表示されません。
userAlreadyExistsOAuthMessage=ユーザーは既にOAuth2ユーザーとして存在します。 userAlreadyExistsOAuthMessage=ユーザーは既にOAuth2ユーザーとして存在します。
userAlreadyExistsWebMessage=ユーザーは既にWebユーザーとして存在します。 userAlreadyExistsWebMessage=ユーザーは既にWebユーザーとして存在します。
@@ -76,12 +76,12 @@ donate=寄付する
color= color=
sponsor=スポンサー sponsor=スポンサー
info=Info info=Info
pro=pro pro=Pro
page=ページ page=Page
pages=ページ pages=Pages
loading=読込中... loading=Loading...
addToDoc=ドキュメントに追加 addToDoc=Add to Document
reset=リセット reset=Reset
legal.privacy=プライバシーポリシー legal.privacy=プライバシーポリシー
legal.terms=利用規約 legal.terms=利用規約
@@ -92,7 +92,7 @@ legal.impressum=著作権利者情報
############### ###############
# Pipeline # # Pipeline #
############### ###############
pipeline.header=パイプラインメニュー (Beta) pipeline.header=パイプラインメニュー (Alpha)
pipeline.uploadButton=カスタムのアップロード pipeline.uploadButton=カスタムのアップロード
pipeline.configureButton=設定 pipeline.configureButton=設定
pipeline.defaultOption=カスタム pipeline.defaultOption=カスタム
@@ -117,21 +117,21 @@ pipelineOptions.validateButton=検証
######################## ########################
# ENTERPRISE EDITION # # ENTERPRISE EDITION #
######################## ########################
enterpriseEdition.button=Proにアップグレード enterpriseEdition.button=Upgrade to Pro
enterpriseEdition.warning=この機能はProユーザーのみが利用できます。 enterpriseEdition.warning=This feature is only available to Pro users.
enterpriseEdition.yamlAdvert=Stirling PDF Proは、YAML構成ファイルやその他のSSO機能をサポートしています。 enterpriseEdition.yamlAdvert=Stirling PDF Pro supports YAML configuration files and other SSO features.
enterpriseEdition.ssoAdvert=より多くのユーザー管理機能をお探しですか? Stirling PDF Proをご覧ください enterpriseEdition.ssoAdvert=Looking for more user management features? Check out Stirling PDF Pro
################# #################
# Analytics # # Analytics #
################# #################
analytics.title=Stirling PDFをもっと良くしたいですか analytics.title=Do you want make Stirling PDF better?
analytics.paragraph1=Stirling PDFでは、製品の改善に役立つ分析機能をオプトインしています。個人情報やファイルの内容を追跡することはありません。 analytics.paragraph1=Stirling PDF has opt in analytics to help us improve the product. We do not track any personal information or file contents.
analytics.paragraph2=Stirling-PDFの成長を支援しユーザーをより深く理解できるように分析を有効にすることを検討してください。 analytics.paragraph2=Please consider enabling analytics to help Stirling-PDF grow and to allow us to understand our users better.
analytics.enable=分析を有効にする analytics.enable=Enable analytics
analytics.disable=分析を無効にする analytics.disable=Disable analytics
analytics.settings=config/settings.ymlファイルでアナリティクスの設定を変更できます。 analytics.settings=You can change the settings for analytics in the config/settings.yml file
############# #############
# NAVBAR # # NAVBAR #
@@ -142,14 +142,14 @@ navbar.language=言語
navbar.settings=設定 navbar.settings=設定
navbar.allTools=ツール navbar.allTools=ツール
navbar.multiTool=マルチツール navbar.multiTool=マルチツール
navbar.search=検索 navbar.search=Search
navbar.sections.organize=整理 navbar.sections.organize=整理
navbar.sections.convertTo=PDFへ変換 navbar.sections.convertTo=PDFへ変換
navbar.sections.convertFrom=PDFから変換 navbar.sections.convertFrom=PDFから変換
navbar.sections.security=署名とセキュリティ navbar.sections.security=署名とセキュリティ
navbar.sections.advance=アドバンスド navbar.sections.advance=アドバンスド
navbar.sections.edit=閲覧と編集 navbar.sections.edit=閲覧と編集
navbar.sections.popular=人気 navbar.sections.popular=Popular
############# #############
# SETTINGS # # SETTINGS #
@@ -208,7 +208,7 @@ adminUserSettings.user=ユーザー
adminUserSettings.addUser=新しいユーザを追加 adminUserSettings.addUser=新しいユーザを追加
adminUserSettings.deleteUser=ユーザの削除 adminUserSettings.deleteUser=ユーザの削除
adminUserSettings.confirmDeleteUser=ユーザを本当に削除しますか? adminUserSettings.confirmDeleteUser=ユーザを本当に削除しますか?
adminUserSettings.confirmChangeUserStatus=ユーザーを無効/有効にする必要がありますか? adminUserSettings.confirmChangeUserStatus=Should the user be disabled/enabled?
adminUserSettings.usernameInfo=ユーザー名には、文字、数字、および次の特殊文字 @._+- のみを含めることができます。または、有効な電子メール アドレスである必要があります。 adminUserSettings.usernameInfo=ユーザー名には、文字、数字、および次の特殊文字 @._+- のみを含めることができます。または、有効な電子メール アドレスである必要があります。
adminUserSettings.roles=役割 adminUserSettings.roles=役割
adminUserSettings.role=役割 adminUserSettings.role=役割
@@ -247,8 +247,8 @@ database.fileNotFound=ファイルが見つかりません
database.fileNullOrEmpty=ファイルは null または空であってはなりません database.fileNullOrEmpty=ファイルは null または空であってはなりません
database.failedImportFile=ファイルのインポートに失敗 database.failedImportFile=ファイルのインポートに失敗
session.expired=セッションが期限切れです。ページを更新してもう一度お試しください。 session.expired=Your session has expired. Please refresh the page and try again.
session.refreshPage=ページを更新 session.refreshPage=Refresh Page
############# #############
# HOME-PAGE # # HOME-PAGE #
@@ -488,52 +488,48 @@ overlay-pdfs.tags=Overlay
home.split-by-sections.title=PDFをセクションで分割 home.split-by-sections.title=PDFをセクションで分割
home.split-by-sections.desc=PDFの各ページを縦横に分割します。 home.split-by-sections.desc=PDFの各ページを縦横に分割します。
split-by-sections.tags=Section Split, Divide, Customize,Customise split-by-sections.tags=Section Split, Divide, Customize
home.AddStampRequest.title=PDFにスタンプを追加 home.AddStampRequest.title=PDFにスタンプを追加
home.AddStampRequest.desc=設定した位置にテキストや画像のスタンプを追加できます home.AddStampRequest.desc=設定した位置にテキストや画像のスタンプを追加できます
AddStampRequest.tags=Stamp, Add image, center image, Watermark, PDF, Embed, Customize,Customise AddStampRequest.tags=Stamp, Add image, center image, Watermark, PDF, Embed, Customize
home.PDFToBook.title=PDFを書籍に変換 home.PDFToBook.title=PDFを書籍に変換
home.PDFToBook.desc=calibreを使用してPDFを書籍/コミック形式に変換します home.PDFToBook.desc=calibreを使用してPDFを書籍/コミック形式に変換します
PDFToBook.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle,epub,mobi,azw3,docx,rtf,txt,html,lit,fb2,pdb,lrf PDFToBook.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle
home.BookToPDF.title=PDFを書籍に変換 home.BookToPDF.title=PDFを書籍に変換
home.BookToPDF.desc=calibreを使用してPDFを書籍/コミック形式に変換します home.BookToPDF.desc=calibreを使用してPDFを書籍/コミック形式に変換します
BookToPDF.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle,epub,mobi,azw3,docx,rtf,txt,html,lit,fb2,pdb,lrf BookToPDF.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle
home.removeImagePdf.title=画像の削除 home.removeImagePdf.title=画像の削除
home.removeImagePdf.desc=PDFから画像を削除してファイルサイズを小さくします home.removeImagePdf.desc=PDFから画像を削除してファイルサイズを小さくします
removeImagePdf.tags=Remove Image,Page operations,Back end,server side removeImagePdf.tags=Remove Image,Page operations,Back end,server side
home.splitPdfByChapters.title=PDFをチャプターごとに分割 home.splitPdfByChapters.title=Split PDF by Chapters
home.splitPdfByChapters.desc=チャプターの構造に基づいてPDFを複数のファイルに分割します home.splitPdfByChapters.desc=Split a PDF into multiple files based on its chapter structure.
splitPdfByChapters.tags=split,chapters,bookmarks,organize splitPdfByChapters.tags=split,chapters,bookmarks,organize
home.validateSignature.title=PDF署名の検証
home.validateSignature.desc=PDF文書のデジタル署名と証明書を検証します
validateSignature.tags=signature,verify,validate,pdf,certificate,digital signature,Validate Signature,Validate certificate
#replace-invert-color #replace-invert-color
replace-color.title=色の置換・反転 replace-color.title=Replace-Invert-Color
replace-color.header=PDFの色の置換・反転 replace-color.header=Replace-Invert Color PDF
home.replaceColorPdf.title=色の置換と反転 home.replaceColorPdf.title=Replace and Invert Color
home.replaceColorPdf.desc=PDF内のテキストと背景の色を置き換え、PDFのフルカラーを反転してファイルサイズを縮小します。 home.replaceColorPdf.desc=Replace color for text and background in PDF and invert full color of pdf to reduce file size
replaceColorPdf.tags=Replace Color,Page operations,Back end,server side replaceColorPdf.tags=Replace Color,Page operations,Back end,server side
replace-color.selectText.1=色の置換または反転オプション replace-color.selectText.1=Replace or Invert color Options
replace-color.selectText.2=デフォルト(デフォルトの高コントラスト色) replace-color.selectText.2=Default(Default high contrast colors)
replace-color.selectText.3=カスタム(カスタマイズされた色) replace-color.selectText.3=Custom(Customized colors)
replace-color.selectText.4=フル反転(すべての色を反転) replace-color.selectText.4=Full-Invert(Invert all colors)
replace-color.selectText.5=高コントラストカラーオプション replace-color.selectText.5=High contrast color options
replace-color.selectText.6=黒背景に白文字 replace-color.selectText.6=white text on black background
replace-color.selectText.7=白背景に黒文字 replace-color.selectText.7=Black text on white background
replace-color.selectText.8=黒背景に黄色文字 replace-color.selectText.8=Yellow text on black background
replace-color.selectText.9=黒背景に緑文字 replace-color.selectText.9=Green text on black background
replace-color.selectText.10=テキストの色を選択 replace-color.selectText.10=Choose text Color
replace-color.selectText.11=背景色を選択 replace-color.selectText.11=Choose background Color
replace-color.submit=置換 replace-color.submit=Replace
@@ -560,9 +556,9 @@ login.oauth2AccessDenied=アクセス拒否
login.oauth2InvalidTokenResponse=無効なトークン応答 login.oauth2InvalidTokenResponse=無効なトークン応答
login.oauth2InvalidIdToken=無効なIDトークン login.oauth2InvalidIdToken=無効なIDトークン
login.userIsDisabled=ユーザーは非アクティブ化されており、現在このユーザー名でのログインはブロックされています。管理者に連絡してください。 login.userIsDisabled=ユーザーは非アクティブ化されており、現在このユーザー名でのログインはブロックされています。管理者に連絡してください。
login.alreadyLoggedIn=すでにログインしています login.alreadyLoggedIn=You are already logged in to
login.alreadyLoggedIn2=デバイスからログアウトしてもう一度お試しください。 login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
login.toManySessions=アクティブなセッションが多すぎます login.toManySessions=You have too many active sessions
#auto-redact #auto-redact
autoRedact.title=自動塗りつぶし autoRedact.title=自動塗りつぶし
@@ -578,8 +574,8 @@ autoRedact.submitButton=送信
#showJS #showJS
showJS.title=Javascriptを表示 showJS.title=JavaScriptを表示
showJS.header=Javascriptを表示 showJS.header=JavaScriptを表示
showJS.downloadJS=Javascriptをダウンロード showJS.downloadJS=Javascriptをダウンロード
showJS.submit=表示 showJS.submit=表示
@@ -757,7 +753,7 @@ certSign.showSig=署名を表示
certSign.reason=理由 certSign.reason=理由
certSign.location=場所 certSign.location=場所
certSign.name=名前 certSign.name=名前
certSign.showLogo=ロゴを表示 certSign.showLogo=Show Logo
certSign.submit=PDFに署名 certSign.submit=PDFに署名
@@ -792,9 +788,9 @@ compare.highlightColor.2=ハイライトカラー 2:
compare.document.1=ドキュメント 1 compare.document.1=ドキュメント 1
compare.document.2=ドキュメント 2 compare.document.2=ドキュメント 2
compare.submit=比較 compare.submit=比較
compare.complex.message=提供された文書の一方または両方が大きなファイルであるため、比較の精度が低下する可能性があります。 compare.complex.message=One or both of the provided documents are large files, accuracy of comparison may be reduced
compare.large.file.message=提供された文書の1つまたは両方が大きすぎて処理できません compare.large.file.message=One or Both of the provided documents are too large to process
compare.no.text.message=選択したPDFの1つまたは両方にテキストコンテンツがありません。比較するには、テキストを含むPDFを選択してください。 compare.no.text.message=One or both of the selected PDFs have no text content. Please choose PDFs with text for comparison.
#BookToPDF #BookToPDF
BookToPDF.title=書籍やコミックをPDFに変換 BookToPDF.title=書籍やコミックをPDFに変換
@@ -803,8 +799,8 @@ BookToPDF.credit=calibreを使用
BookToPDF.submit=変換 BookToPDF.submit=変換
#PDFToBook #PDFToBook
PDFToBook.title=PDFを書籍に変換 PDFToBook.title=書籍をPDFに変換
PDFToBook.header=PDFを書籍に変換 PDFToBook.header=書籍をPDFに変換
PDFToBook.selectText.1=フォーマット PDFToBook.selectText.1=フォーマット
PDFToBook.credit=calibreを使用 PDFToBook.credit=calibreを使用
PDFToBook.submit=変換 PDFToBook.submit=変換
@@ -817,17 +813,17 @@ sign.draw=署名を書く
sign.text=テキスト入力 sign.text=テキスト入力
sign.clear=クリア sign.clear=クリア
sign.add=追加 sign.add=追加
sign.saved=保存された署名 sign.saved=Saved Signatures
sign.save=署名を保存 sign.save=Save Signature
sign.personalSigs=個人署名 sign.personalSigs=Personal Signatures
sign.sharedSigs=共有署名 sign.sharedSigs=Shared Signatures
sign.noSavedSigs=保存された署名が見つかりません sign.noSavedSigs=No saved signatures found
sign.addToAll=すべてのページに追加 sign.addToAll=Add to all pages
sign.delete=削除 sign.delete=Delete
sign.first=最初のページ sign.first=First page
sign.last=最後のページ sign.last=Last page
sign.next=次のページ sign.next=Next page
sign.previous=前のページ sign.previous=Previous page
#repair #repair
repair.title=修復 repair.title=修復
@@ -944,39 +940,29 @@ pdfOrganiser.placeholder=(例:1,3,2または4-8,2,10-12または2n-1)
multiTool.title=PDFマルチツール multiTool.title=PDFマルチツール
multiTool.header=PDFマルチツール multiTool.header=PDFマルチツール
multiTool.uploadPrompts=ファイル名 multiTool.uploadPrompts=ファイル名
multiTool.selectAll=すべて選択 multiTool.selectAll=Select All
multiTool.deselectAll=選択を解除 multiTool.deselectAll=Deselect All
multiTool.selectPages=ページ選択 multiTool.selectPages=Page Select
multiTool.selectedPages=選択したページ multiTool.selectedPages=Selected Pages
multiTool.page=ページ multiTool.page=Page
multiTool.deleteSelected=選択項目を削除 multiTool.deleteSelected=Delete Selected
multiTool.downloadAll=エクスポート multiTool.downloadAll=Export
multiTool.downloadSelected=選択項目をエクスポート multiTool.downloadSelected=Export Selected
multiTool.insertPageBreak=改ページを挿入 multiTool.insertPageBreak=Insert Page Break
multiTool.addFile=ファイルを追加 multiTool.addFile=Add File
multiTool.rotateLeft=左回転 multiTool.rotateLeft=Rotate Left
multiTool.rotateRight=右回転 multiTool.rotateRight=Rotate Right
multiTool.split=分割 multiTool.split=Split
multiTool.moveLeft=左に移動 multiTool.moveLeft=Move Left
multiTool.moveRight=右に移動 multiTool.moveRight=Move Right
multiTool.delete=削除 multiTool.delete=Delete
multiTool.dragDropMessage=選択されたページ multiTool.dragDropMessage=Page(s) Selected
multiTool.undo=元に戻す multiTool.undo=Undo
multiTool.redo=やり直す multiTool.redo=Redo
#decrypt
decrypt.passwordPrompt=このファイルはパスワードで保護されています。パスワードを入力してください:
decrypt.cancelled=PDFの操作がキャンセルされました: {0}
decrypt.noPassword=暗号化されたPDFにパスワードが指定されていません: {0}
decrypt.invalidPassword=正しいパスワードでもう一度お試しください。
decrypt.invalidPasswordHeader=PDFのパスワードが正しくないか、暗号化がサポートされていません: {0}
decrypt.unexpectedError=ファイルの処理中にエラーが発生しました。もう一度お試しください。
decrypt.serverError=復号化中にサーバーエラーが発生しました: {0}
decrypt.success=ファイルの暗号化が正常に完了しました。
#multiTool-advert #multiTool-advert
multiTool-advert.message=この機能は、<a href="{0}">マルチツール</a>でもご利用いただけます。強化されたページごとのUIと追加機能についてはこちらをご覧ください。 multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
#view pdf #view pdf
viewPdf.title=PDFを表示 viewPdf.title=PDFを表示
@@ -1066,7 +1052,6 @@ addPassword.submit=暗号化
#watermark #watermark
watermark.title=透かしの追加 watermark.title=透かしの追加
watermark.header=透かしの追加 watermark.header=透かしの追加
watermark.customColor=文字色のカスタム
watermark.selectText.1=透かしを追加するPDFを選択: watermark.selectText.1=透かしを追加するPDFを選択:
watermark.selectText.2=透かしのテキスト: watermark.selectText.2=透かしのテキスト:
watermark.selectText.3=文字サイズ: watermark.selectText.3=文字サイズ:
@@ -1132,8 +1117,8 @@ pdfToPDFA.header=PDFをPDF/Aに変換
pdfToPDFA.credit=本サービスはPDF/Aの変換にqpdfを使用しています。 pdfToPDFA.credit=本サービスはPDF/Aの変換にqpdfを使用しています。
pdfToPDFA.submit=変換 pdfToPDFA.submit=変換
pdfToPDFA.tip=現在、一度に複数の入力に対して機能しません pdfToPDFA.tip=現在、一度に複数の入力に対して機能しません
pdfToPDFA.outputFormat=出力形式 pdfToPDFA.outputFormat=Output format
pdfToPDFA.pdfWithDigitalSignature=PDFにはデジタル署名が含まれています。これは次の手順で削除されます。 pdfToPDFA.pdfWithDigitalSignature=PDF にはデジタル署名が含まれています。これは次の手順で削除されます。
#PDFToWord #PDFToWord
@@ -1238,8 +1223,8 @@ licenses.license=ライセンス
survey.nav=アンケート survey.nav=アンケート
survey.title=Stirling-PDFのアンケート survey.title=Stirling-PDFのアンケート
survey.description=Stirling-PDFには追跡機能がないため、Stirling-PDFをより良くするために皆様の意見を聞かせてください survey.description=Stirling-PDFには追跡機能がないため、Stirling-PDFをより良くするために皆様の意見を聞かせてください
survey.changes=Stirling-PDFは前回の調査から変更されました。詳細についてはこちらのブログ投稿をご覧ください。 survey.changes=Stirling-PDF has changed since the last survey! To find out more please check our blog post here:
survey.changes2=これらの変更により私たちは有償のビジネスサポートと資金援助を受けています survey.changes2=With these changes we are getting paid business support and funding
survey.please=アンケートにご協力ください! survey.please=アンケートにご協力ください!
survey.disabled=(アンケートのポップアップは、次の更新では無効になりますが、ページの下部に表示されます。) survey.disabled=(アンケートのポップアップは、次の更新では無効になりますが、ページの下部に表示されます。)
survey.button=アンケートに答える survey.button=アンケートに答える
@@ -1267,61 +1252,21 @@ removeImage.removeImage=画像の削除
removeImage.submit=画像を削除 removeImage.submit=画像を削除
splitByChapters.title=PDFをチャプターごとに分割 splitByChapters.title=Split PDF by Chapters
splitByChapters.header=PDFをチャプターごとに分割 splitByChapters.header=Split PDF by Chapters
splitByChapters.bookmarkLevel=ブックマークレベル splitByChapters.bookmarkLevel=Bookmark Level
splitByChapters.includeMetadata=メタデータを含める splitByChapters.includeMetadata=Include Metadata
splitByChapters.allowDuplicates=重複を許可する splitByChapters.allowDuplicates=Allow Duplicates
splitByChapters.desc.1=このツールは、チャプター構造に基づいてPDFファイルを複数のPDFに分割します。 splitByChapters.desc.1=This tool splits a PDF file into multiple PDFs based on its chapter structure.
splitByChapters.desc.2=ブックマークレベル:分割に使用するブックマークのレベルを選択します最上位レベルの場合は0、第2レベルの場合は1など splitByChapters.desc.2=Bookmark Level: Choose the level of bookmarks to use for splitting (0 for top-level, 1 for second-level, etc.).
splitByChapters.desc.3=メタデータを含める:チェックすると、元のPDFのメタデータが各分割PDFに含まれます。 splitByChapters.desc.3=Include Metadata: If checked, the original PDF's metadata will be included in each split PDF.
splitByChapters.desc.4=重複を許可:チェックすると同じページ上の複数のブックマークから個別のPDFを作成できます。 splitByChapters.desc.4=Allow Duplicates: If checked, allows multiple bookmarks on the same page to create separate PDFs.
splitByChapters.submit=PDFを分割 splitByChapters.submit=Split PDF
#File Chooser
fileChooser.click=クリック
fileChooser.or=または
fileChooser.dragAndDrop=ドラッグ&ドロップ
fileChooser.hoveredDragAndDrop=ファイルをここにドラッグ&ドロップ
#release notes #release notes
releases.footer=リリース releases.footer=Releases
releases.title=リリースノート releases.title=Release Notes
releases.header=リリースノート releases.header=Release Notes
releases.current.version=現在のリリース releases.current.version=Current Release
releases.note=リリースノートは英語でのみで提供されています releases.note=Release notes are only available in English
#Validate Signature
validateSignature.title=PDF署名の検証
validateSignature.header=デジタル署名の検証
validateSignature.selectPDF=署名済みPDFファイルを選択
validateSignature.submit=署名の検証
validateSignature.results=検証結果
validateSignature.status=状態
validateSignature.signer=署名者
validateSignature.date=日付
validateSignature.reason=理由
validateSignature.location=場所
validateSignature.noSignatures=この文書にはデジタル署名が見つかりません
validateSignature.status.valid=有効
validateSignature.status.invalid=無効
validateSignature.chain.invalid=証明書チェーンの検証に失敗しました - 署名者の身元を確認できません
validateSignature.trust.invalid=証明書が信頼ストアにありません - ソースを検証できません
validateSignature.cert.expired=証明書の有効期限が切れています
validateSignature.cert.revoked=証明書は取り消されました
validateSignature.signature.info=署名情報
validateSignature.signature=署名
validateSignature.signature.mathValid=署名は数学的には有効ですが:
validateSignature.selectCustomCert=カスタム証明書ファイル X.509 (オプション)
validateSignature.cert.info=証明書の詳細
validateSignature.cert.issuer=発行者
validateSignature.cert.subject=主題
validateSignature.cert.serialNumber=シリアルナンバー
validateSignature.cert.validFrom=有効開始日
validateSignature.cert.validUntil=有効期限
validateSignature.cert.algorithm=アルゴリズム
validateSignature.cert.keySize=キーサイズ
validateSignature.cert.version=バージョン
validateSignature.cert.keyUsage=キーの使用法
validateSignature.cert.selfSigned=自己署名
validateSignature.cert.bits=ビット

View File

@@ -512,10 +512,6 @@ home.splitPdfByChapters.title=챕터별로 PDF 분할
home.splitPdfByChapters.desc=PDF를 여러 파일로 나눕니다. 각 장의 구조에 따라. home.splitPdfByChapters.desc=PDF를 여러 파일로 나눕니다. 각 장의 구조에 따라.
splitPdfByChapters.tags=분할, 챕터, 북마크, 조직화 splitPdfByChapters.tags=분할, 챕터, 북마크, 조직화
home.validateSignature.title=Validate PDF Signature
home.validateSignature.desc=Verify digital signatures and certificates in PDF documents
validateSignature.tags=signature,verify,validate,pdf,certificate,digital signature,Validate Signature,Validate certificate
#replace-invert-color #replace-invert-color
replace-color.title=Replace-Invert-Color replace-color.title=Replace-Invert-Color
replace-color.header=색상 교체/반전 PDF replace-color.header=색상 교체/반전 PDF
@@ -965,16 +961,6 @@ multiTool.dragDropMessage=Page(s) Selected
multiTool.undo=Undo multiTool.undo=Undo
multiTool.redo=Redo multiTool.redo=Redo
#decrypt
decrypt.passwordPrompt=This file is password-protected. Please enter the password:
decrypt.cancelled=Operation cancelled for PDF: {0}
decrypt.noPassword=No password provided for encrypted PDF: {0}
decrypt.invalidPassword=Please try again with the correct password.
decrypt.invalidPasswordHeader=Incorrect password or unsupported encryption for PDF: {0}
decrypt.unexpectedError=There was an error processing the file. Please try again.
decrypt.serverError=Server error while decrypting: {0}
decrypt.success=File decrypted successfully.
#multiTool-advert #multiTool-advert
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features! multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
@@ -1066,7 +1052,6 @@ addPassword.submit=암호화
#watermark #watermark
watermark.title=워터마크 추가 watermark.title=워터마크 추가
watermark.header=워터마크 추가 watermark.header=워터마크 추가
watermark.customColor=사용자 정의 텍스트 색상
watermark.selectText.1=워터마크를 추가할 PDF 선택: watermark.selectText.1=워터마크를 추가할 PDF 선택:
watermark.selectText.2=워터마크 텍스트: watermark.selectText.2=워터마크 텍스트:
watermark.selectText.3=폰트 크기: watermark.selectText.3=폰트 크기:
@@ -1278,11 +1263,6 @@ splitByChapters.desc.3=메타데이터 포함: 체크하면 각 분할된 PDF에
splitByChapters.desc.4=중복 허용: 중복 북마크가 있는 같은 페이지에 여러 번 분할 PDF를 생성합니다. splitByChapters.desc.4=중복 허용: 중복 북마크가 있는 같은 페이지에 여러 번 분할 PDF를 생성합니다.
splitByChapters.submit=PDF 분할 splitByChapters.submit=PDF 분할
#File Chooser
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes #release notes
releases.footer=Releases releases.footer=Releases
@@ -1290,38 +1270,3 @@ releases.title=Release Notes
releases.header=Release Notes releases.header=Release Notes
releases.current.version=Current Release releases.current.version=Current Release
releases.note=Release notes are only available in English releases.note=Release notes are only available in English
#Validate Signature
validateSignature.title=Validate PDF Signatures
validateSignature.header=Validate Digital Signatures
validateSignature.selectPDF=Select signed PDF file
validateSignature.submit=Validate Signatures
validateSignature.results=Validation Results
validateSignature.status=Status
validateSignature.signer=Signer
validateSignature.date=Date
validateSignature.reason=Reason
validateSignature.location=Location
validateSignature.noSignatures=No digital signatures found in this document
validateSignature.status.valid=Valid
validateSignature.status.invalid=Invalid
validateSignature.chain.invalid=Certificate chain validation failed - cannot verify signer's identity
validateSignature.trust.invalid=Certificate not in trust store - source cannot be verified
validateSignature.cert.expired=Certificate has expired
validateSignature.cert.revoked=Certificate has been revoked
validateSignature.signature.info=Signature Information
validateSignature.signature=Signature
validateSignature.signature.mathValid=Signature is mathematically valid BUT:
validateSignature.selectCustomCert=Custom Certificate File X.509 (Optional)
validateSignature.cert.info=Certificate Details
validateSignature.cert.issuer=Issuer
validateSignature.cert.subject=Subject
validateSignature.cert.serialNumber=Serial Number
validateSignature.cert.validFrom=Valid From
validateSignature.cert.validUntil=Valid Until
validateSignature.cert.algorithm=Algorithm
validateSignature.cert.keySize=Key Size
validateSignature.cert.version=Version
validateSignature.cert.keyUsage=Key Usage
validateSignature.cert.selfSigned=Self-Signed
validateSignature.cert.bits=bits

View File

@@ -512,10 +512,6 @@ home.splitPdfByChapters.title=PDF op hoofdstukken splitsen
home.splitPdfByChapters.desc=Splits een PDF op basis van zijn hoofdstukstructuur in meerdere bestanden. home.splitPdfByChapters.desc=Splits een PDF op basis van zijn hoofdstukstructuur in meerdere bestanden.
splitPdfByChapters.tags=splitsen, hoofdstukken, bookmarks, organiseren splitPdfByChapters.tags=splitsen, hoofdstukken, bookmarks, organiseren
home.validateSignature.title=Validate PDF Signature
home.validateSignature.desc=Verify digital signatures and certificates in PDF documents
validateSignature.tags=signature,verify,validate,pdf,certificate,digital signature,Validate Signature,Validate certificate
#replace-invert-color #replace-invert-color
replace-color.title=Replace-Invert-Color replace-color.title=Replace-Invert-Color
replace-color.header=Kleur-instellingen voor PDF's replace-color.header=Kleur-instellingen voor PDF's
@@ -965,16 +961,6 @@ multiTool.dragDropMessage=Page(s) Selected
multiTool.undo=Undo multiTool.undo=Undo
multiTool.redo=Redo multiTool.redo=Redo
#decrypt
decrypt.passwordPrompt=This file is password-protected. Please enter the password:
decrypt.cancelled=Operation cancelled for PDF: {0}
decrypt.noPassword=No password provided for encrypted PDF: {0}
decrypt.invalidPassword=Please try again with the correct password.
decrypt.invalidPasswordHeader=Incorrect password or unsupported encryption for PDF: {0}
decrypt.unexpectedError=There was an error processing the file. Please try again.
decrypt.serverError=Server error while decrypting: {0}
decrypt.success=File decrypted successfully.
#multiTool-advert #multiTool-advert
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features! multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
@@ -1066,7 +1052,6 @@ addPassword.submit=Versleutelen
#watermark #watermark
watermark.title=Watermerk toevoegen watermark.title=Watermerk toevoegen
watermark.header=Watermerk toevoegen watermark.header=Watermerk toevoegen
watermark.customColor=Aangepaste tekstkleur
watermark.selectText.1=Selecteer PDF om watermerk toe te voegen: watermark.selectText.1=Selecteer PDF om watermerk toe te voegen:
watermark.selectText.2=Watermerk tekst: watermark.selectText.2=Watermerk tekst:
watermark.selectText.3=Tekengrootte: watermark.selectText.3=Tekengrootte:
@@ -1278,11 +1263,6 @@ splitByChapters.desc.3=Metadata inclusief: Als gecijfeld, de originele PDF's met
splitByChapters.desc.4=Dubbele items toestaan: Als gecijfeld, zorgen multiple boekmarkeersymboolen op dezelfde pagina voor het maken van aparte PDF-bestanden. splitByChapters.desc.4=Dubbele items toestaan: Als gecijfeld, zorgen multiple boekmarkeersymboolen op dezelfde pagina voor het maken van aparte PDF-bestanden.
splitByChapters.submit=PDF splitsen splitByChapters.submit=PDF splitsen
#File Chooser
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes #release notes
releases.footer=Releases releases.footer=Releases
@@ -1290,38 +1270,3 @@ releases.title=Release Notes
releases.header=Release Notes releases.header=Release Notes
releases.current.version=Current Release releases.current.version=Current Release
releases.note=Release notes are only available in English releases.note=Release notes are only available in English
#Validate Signature
validateSignature.title=Validate PDF Signatures
validateSignature.header=Validate Digital Signatures
validateSignature.selectPDF=Select signed PDF file
validateSignature.submit=Validate Signatures
validateSignature.results=Validation Results
validateSignature.status=Status
validateSignature.signer=Signer
validateSignature.date=Date
validateSignature.reason=Reason
validateSignature.location=Location
validateSignature.noSignatures=No digital signatures found in this document
validateSignature.status.valid=Valid
validateSignature.status.invalid=Invalid
validateSignature.chain.invalid=Certificate chain validation failed - cannot verify signer's identity
validateSignature.trust.invalid=Certificate not in trust store - source cannot be verified
validateSignature.cert.expired=Certificate has expired
validateSignature.cert.revoked=Certificate has been revoked
validateSignature.signature.info=Signature Information
validateSignature.signature=Signature
validateSignature.signature.mathValid=Signature is mathematically valid BUT:
validateSignature.selectCustomCert=Custom Certificate File X.509 (Optional)
validateSignature.cert.info=Certificate Details
validateSignature.cert.issuer=Issuer
validateSignature.cert.subject=Subject
validateSignature.cert.serialNumber=Serial Number
validateSignature.cert.validFrom=Valid From
validateSignature.cert.validUntil=Valid Until
validateSignature.cert.algorithm=Algorithm
validateSignature.cert.keySize=Key Size
validateSignature.cert.version=Version
validateSignature.cert.keyUsage=Key Usage
validateSignature.cert.selfSigned=Self-Signed
validateSignature.cert.bits=bits

View File

@@ -512,10 +512,6 @@ home.splitPdfByChapters.title=Split PDF by Chapters
home.splitPdfByChapters.desc=Split a PDF into multiple files based on its chapter structure. home.splitPdfByChapters.desc=Split a PDF into multiple files based on its chapter structure.
splitPdfByChapters.tags=split,chapters,bookmarks,organize splitPdfByChapters.tags=split,chapters,bookmarks,organize
home.validateSignature.title=Validate PDF Signature
home.validateSignature.desc=Verify digital signatures and certificates in PDF documents
validateSignature.tags=signature,verify,validate,pdf,certificate,digital signature,Validate Signature,Validate certificate
#replace-invert-color #replace-invert-color
replace-color.title=Replace-Invert-Color replace-color.title=Replace-Invert-Color
replace-color.header=Replace-Invert Color PDF replace-color.header=Replace-Invert Color PDF
@@ -965,16 +961,6 @@ multiTool.dragDropMessage=Page(s) Selected
multiTool.undo=Undo multiTool.undo=Undo
multiTool.redo=Redo multiTool.redo=Redo
#decrypt
decrypt.passwordPrompt=This file is password-protected. Please enter the password:
decrypt.cancelled=Operation cancelled for PDF: {0}
decrypt.noPassword=No password provided for encrypted PDF: {0}
decrypt.invalidPassword=Please try again with the correct password.
decrypt.invalidPasswordHeader=Incorrect password or unsupported encryption for PDF: {0}
decrypt.unexpectedError=There was an error processing the file. Please try again.
decrypt.serverError=Server error while decrypting: {0}
decrypt.success=File decrypted successfully.
#multiTool-advert #multiTool-advert
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features! multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
@@ -1066,7 +1052,6 @@ addPassword.submit=Krypter
#watermark #watermark
watermark.title=Legg til vannmerke watermark.title=Legg til vannmerke
watermark.header=Legg til vannmerke watermark.header=Legg til vannmerke
watermark.customColor=Tilpasset Tekstfarge
watermark.selectText.1=Velg PDF-fil å legge til vannmerke på: watermark.selectText.1=Velg PDF-fil å legge til vannmerke på:
watermark.selectText.2=Vannmerketekst: watermark.selectText.2=Vannmerketekst:
watermark.selectText.3=Skriftstørrelse: watermark.selectText.3=Skriftstørrelse:
@@ -1278,11 +1263,6 @@ splitByChapters.desc.3=Include Metadata: If checked, the original PDF's metadata
splitByChapters.desc.4=Allow Duplicates: If checked, allows multiple bookmarks on the same page to create separate PDFs. splitByChapters.desc.4=Allow Duplicates: If checked, allows multiple bookmarks on the same page to create separate PDFs.
splitByChapters.submit=Split PDF splitByChapters.submit=Split PDF
#File Chooser
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes #release notes
releases.footer=Releases releases.footer=Releases
@@ -1290,38 +1270,3 @@ releases.title=Release Notes
releases.header=Release Notes releases.header=Release Notes
releases.current.version=Current Release releases.current.version=Current Release
releases.note=Release notes are only available in English releases.note=Release notes are only available in English
#Validate Signature
validateSignature.title=Validate PDF Signatures
validateSignature.header=Validate Digital Signatures
validateSignature.selectPDF=Select signed PDF file
validateSignature.submit=Validate Signatures
validateSignature.results=Validation Results
validateSignature.status=Status
validateSignature.signer=Signer
validateSignature.date=Date
validateSignature.reason=Reason
validateSignature.location=Location
validateSignature.noSignatures=No digital signatures found in this document
validateSignature.status.valid=Valid
validateSignature.status.invalid=Invalid
validateSignature.chain.invalid=Certificate chain validation failed - cannot verify signer's identity
validateSignature.trust.invalid=Certificate not in trust store - source cannot be verified
validateSignature.cert.expired=Certificate has expired
validateSignature.cert.revoked=Certificate has been revoked
validateSignature.signature.info=Signature Information
validateSignature.signature=Signature
validateSignature.signature.mathValid=Signature is mathematically valid BUT:
validateSignature.selectCustomCert=Custom Certificate File X.509 (Optional)
validateSignature.cert.info=Certificate Details
validateSignature.cert.issuer=Issuer
validateSignature.cert.subject=Subject
validateSignature.cert.serialNumber=Serial Number
validateSignature.cert.validFrom=Valid From
validateSignature.cert.validUntil=Valid Until
validateSignature.cert.algorithm=Algorithm
validateSignature.cert.keySize=Key Size
validateSignature.cert.version=Version
validateSignature.cert.keyUsage=Key Usage
validateSignature.cert.selfSigned=Self-Signed
validateSignature.cert.bits=bits

55
src/main/resources/messages_pl_PL.properties Normal file → Executable file
View File

@@ -512,10 +512,6 @@ home.splitPdfByChapters.title=Podziel PDF według rozdziałów
home.splitPdfByChapters.desc=Podział pliku PDF na wiele plików na podstawie struktury rozdziałów. home.splitPdfByChapters.desc=Podział pliku PDF na wiele plików na podstawie struktury rozdziałów.
splitPdfByChapters.tags=podział, rozdziały, zakładki, porządkowanie, organizacja splitPdfByChapters.tags=podział, rozdziały, zakładki, porządkowanie, organizacja
home.validateSignature.title=Validate PDF Signature
home.validateSignature.desc=Verify digital signatures and certificates in PDF documents
validateSignature.tags=signature,verify,validate,pdf,certificate,digital signature,Validate Signature,Validate certificate
#replace-invert-color #replace-invert-color
replace-color.title=Zamień-Odwróć-Kolor replace-color.title=Zamień-Odwróć-Kolor
replace-color.header=Zamień-Odwróć kolor PDF replace-color.header=Zamień-Odwróć kolor PDF
@@ -965,16 +961,6 @@ multiTool.dragDropMessage=Page(s) Selected
multiTool.undo=Undo multiTool.undo=Undo
multiTool.redo=Redo multiTool.redo=Redo
#decrypt
decrypt.passwordPrompt=This file is password-protected. Please enter the password:
decrypt.cancelled=Operation cancelled for PDF: {0}
decrypt.noPassword=No password provided for encrypted PDF: {0}
decrypt.invalidPassword=Please try again with the correct password.
decrypt.invalidPasswordHeader=Incorrect password or unsupported encryption for PDF: {0}
decrypt.unexpectedError=There was an error processing the file. Please try again.
decrypt.serverError=Server error while decrypting: {0}
decrypt.success=File decrypted successfully.
#multiTool-advert #multiTool-advert
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features! multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
@@ -1066,7 +1052,6 @@ addPassword.submit=Zablokuj
#watermark #watermark
watermark.title=Dodaj znak wodny watermark.title=Dodaj znak wodny
watermark.header=Dodaj znak wodny watermark.header=Dodaj znak wodny
watermark.customColor=Własny kolor tekstu
watermark.selectText.1=Wybierz dokument PDF, do którego chcesz dodać znak wodny: watermark.selectText.1=Wybierz dokument PDF, do którego chcesz dodać znak wodny:
watermark.selectText.2=Treść znaku wodnego: watermark.selectText.2=Treść znaku wodnego:
watermark.selectText.3=Rozmiar czcionki: watermark.selectText.3=Rozmiar czcionki:
@@ -1278,11 +1263,6 @@ splitByChapters.desc.3=Dołącz Metadane: Jeśli opcja ta jest zaznaczona, metad
splitByChapters.desc.4=Zezwól na Duplikaty: Jeśli ta opcja jest zaznaczona, pozwala na tworzenie oddzielnych plików PDF przez wiele zakładek na tej samej stronie. splitByChapters.desc.4=Zezwól na Duplikaty: Jeśli ta opcja jest zaznaczona, pozwala na tworzenie oddzielnych plików PDF przez wiele zakładek na tej samej stronie.
splitByChapters.submit=Podziel PDF splitByChapters.submit=Podziel PDF
#File Chooser
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes #release notes
releases.footer=Releases releases.footer=Releases
@@ -1290,38 +1270,3 @@ releases.title=Release Notes
releases.header=Release Notes releases.header=Release Notes
releases.current.version=Current Release releases.current.version=Current Release
releases.note=Release notes are only available in English releases.note=Release notes are only available in English
#Validate Signature
validateSignature.title=Validate PDF Signatures
validateSignature.header=Validate Digital Signatures
validateSignature.selectPDF=Select signed PDF file
validateSignature.submit=Validate Signatures
validateSignature.results=Validation Results
validateSignature.status=Status
validateSignature.signer=Signer
validateSignature.date=Date
validateSignature.reason=Reason
validateSignature.location=Location
validateSignature.noSignatures=No digital signatures found in this document
validateSignature.status.valid=Valid
validateSignature.status.invalid=Invalid
validateSignature.chain.invalid=Certificate chain validation failed - cannot verify signer's identity
validateSignature.trust.invalid=Certificate not in trust store - source cannot be verified
validateSignature.cert.expired=Certificate has expired
validateSignature.cert.revoked=Certificate has been revoked
validateSignature.signature.info=Signature Information
validateSignature.signature=Signature
validateSignature.signature.mathValid=Signature is mathematically valid BUT:
validateSignature.selectCustomCert=Custom Certificate File X.509 (Optional)
validateSignature.cert.info=Certificate Details
validateSignature.cert.issuer=Issuer
validateSignature.cert.subject=Subject
validateSignature.cert.serialNumber=Serial Number
validateSignature.cert.validFrom=Valid From
validateSignature.cert.validUntil=Valid Until
validateSignature.cert.algorithm=Algorithm
validateSignature.cert.keySize=Key Size
validateSignature.cert.version=Version
validateSignature.cert.keyUsage=Key Usage
validateSignature.cert.selfSigned=Self-Signed
validateSignature.cert.bits=bits

File diff suppressed because it is too large Load Diff

View File

@@ -512,10 +512,6 @@ home.splitPdfByChapters.title=Dividir PDF por Capítulos
home.splitPdfByChapters.desc=Divida um PDF em vários arquivos com base na estrutura dos capítulos. home.splitPdfByChapters.desc=Divida um PDF em vários arquivos com base na estrutura dos capítulos.
splitPdfByChapters.tags=dividir,capítulos,marcadores,organizar splitPdfByChapters.tags=dividir,capítulos,marcadores,organizar
home.validateSignature.title=Validate PDF Signature
home.validateSignature.desc=Verify digital signatures and certificates in PDF documents
validateSignature.tags=signature,verify,validate,pdf,certificate,digital signature,Validate Signature,Validate certificate
#replace-invert-color #replace-invert-color
replace-color.title=Replace-Invert-Color replace-color.title=Replace-Invert-Color
replace-color.header=Substituir-Inverter Cor do PDF replace-color.header=Substituir-Inverter Cor do PDF
@@ -965,16 +961,6 @@ multiTool.dragDropMessage=Page(s) Selected
multiTool.undo=Undo multiTool.undo=Undo
multiTool.redo=Redo multiTool.redo=Redo
#decrypt
decrypt.passwordPrompt=This file is password-protected. Please enter the password:
decrypt.cancelled=Operation cancelled for PDF: {0}
decrypt.noPassword=No password provided for encrypted PDF: {0}
decrypt.invalidPassword=Please try again with the correct password.
decrypt.invalidPasswordHeader=Incorrect password or unsupported encryption for PDF: {0}
decrypt.unexpectedError=There was an error processing the file. Please try again.
decrypt.serverError=Server error while decrypting: {0}
decrypt.success=File decrypted successfully.
#multiTool-advert #multiTool-advert
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features! multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
@@ -1066,13 +1052,12 @@ addPassword.submit=Proteger
#watermark #watermark
watermark.title=Adicionar Marca d'Água watermark.title=Adicionar Marca d'Água
watermark.header=Adicionar Marca d'Água watermark.header=Adicionar Marca d'Água
watermark.customColor=Personalizar a cor do texto
watermark.selectText.1=Seleccione o PDF para Adicionar a Marca d'Água watermark.selectText.1=Seleccione o PDF para Adicionar a Marca d'Água
watermark.selectText.2=Texto da Marca d'Água watermark.selectText.2=Texto da Marca d'Água
watermark.selectText.3=Tamanho da Fonte watermark.selectText.3=Tamanho da Fonte
watermark.selectText.4=Rotação (0-360) watermark.selectText.4=Rotação (0-360)
watermark.selectText.5=Espaçamento Horizontal (Width Spacer) watermark.selectText.5=Espaçamento Horizontal (widthSpacer)
watermark.selectText.6=Espaçamento Vertical (Height Spacer) watermark.selectText.6=Espaçamento Vertical (heightSpacer)
watermark.selectText.7=Opacidade (0% - 100%) watermark.selectText.7=Opacidade (0% - 100%)
watermark.selectText.8=Tipo de Marca d'Água watermark.selectText.8=Tipo de Marca d'Água
watermark.selectText.9=Imagem da Marca d'Água watermark.selectText.9=Imagem da Marca d'Água
@@ -1278,11 +1263,6 @@ splitByChapters.desc.3=Inclua Metadados: Se marcado, os metadados originais do P
splitByChapters.desc.4=Permitir Duplicatas: Se marcado, permite a criação de vários bookmarks na mesma página para criar separadamente vários PDFs. splitByChapters.desc.4=Permitir Duplicatas: Se marcado, permite a criação de vários bookmarks na mesma página para criar separadamente vários PDFs.
splitByChapters.submit=Dividir o PDF splitByChapters.submit=Dividir o PDF
#File Chooser
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes #release notes
releases.footer=Releases releases.footer=Releases
@@ -1290,38 +1270,3 @@ releases.title=Release Notes
releases.header=Release Notes releases.header=Release Notes
releases.current.version=Current Release releases.current.version=Current Release
releases.note=Release notes are only available in English releases.note=Release notes are only available in English
#Validate Signature
validateSignature.title=Validate PDF Signatures
validateSignature.header=Validate Digital Signatures
validateSignature.selectPDF=Select signed PDF file
validateSignature.submit=Validate Signatures
validateSignature.results=Validation Results
validateSignature.status=Status
validateSignature.signer=Signer
validateSignature.date=Date
validateSignature.reason=Reason
validateSignature.location=Location
validateSignature.noSignatures=No digital signatures found in this document
validateSignature.status.valid=Valid
validateSignature.status.invalid=Invalid
validateSignature.chain.invalid=Certificate chain validation failed - cannot verify signer's identity
validateSignature.trust.invalid=Certificate not in trust store - source cannot be verified
validateSignature.cert.expired=Certificate has expired
validateSignature.cert.revoked=Certificate has been revoked
validateSignature.signature.info=Signature Information
validateSignature.signature=Signature
validateSignature.signature.mathValid=Signature is mathematically valid BUT:
validateSignature.selectCustomCert=Custom Certificate File X.509 (Optional)
validateSignature.cert.info=Certificate Details
validateSignature.cert.issuer=Issuer
validateSignature.cert.subject=Subject
validateSignature.cert.serialNumber=Serial Number
validateSignature.cert.validFrom=Valid From
validateSignature.cert.validUntil=Valid Until
validateSignature.cert.algorithm=Algorithm
validateSignature.cert.keySize=Key Size
validateSignature.cert.version=Version
validateSignature.cert.keyUsage=Key Usage
validateSignature.cert.selfSigned=Self-Signed
validateSignature.cert.bits=bits

View File

@@ -512,10 +512,6 @@ home.splitPdfByChapters.title=Split PDF by Chapters
home.splitPdfByChapters.desc=Split a PDF into multiple files based on its chapter structure. home.splitPdfByChapters.desc=Split a PDF into multiple files based on its chapter structure.
splitPdfByChapters.tags=split,chapters,bookmarks,organize splitPdfByChapters.tags=split,chapters,bookmarks,organize
home.validateSignature.title=Validate PDF Signature
home.validateSignature.desc=Verify digital signatures and certificates in PDF documents
validateSignature.tags=signature,verify,validate,pdf,certificate,digital signature,Validate Signature,Validate certificate
#replace-invert-color #replace-invert-color
replace-color.title=Replace-Invert-Color replace-color.title=Replace-Invert-Color
replace-color.header=Replace-Invert Color PDF replace-color.header=Replace-Invert Color PDF
@@ -965,16 +961,6 @@ multiTool.dragDropMessage=Page(s) Selected
multiTool.undo=Undo multiTool.undo=Undo
multiTool.redo=Redo multiTool.redo=Redo
#decrypt
decrypt.passwordPrompt=This file is password-protected. Please enter the password:
decrypt.cancelled=Operation cancelled for PDF: {0}
decrypt.noPassword=No password provided for encrypted PDF: {0}
decrypt.invalidPassword=Please try again with the correct password.
decrypt.invalidPasswordHeader=Incorrect password or unsupported encryption for PDF: {0}
decrypt.unexpectedError=There was an error processing the file. Please try again.
decrypt.serverError=Server error while decrypting: {0}
decrypt.success=File decrypted successfully.
#multiTool-advert #multiTool-advert
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features! multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
@@ -1066,7 +1052,6 @@ addPassword.submit=Criptează
#watermark #watermark
watermark.title=Adaugă Filigran watermark.title=Adaugă Filigran
watermark.header=Adaugă Filigran watermark.header=Adaugă Filigran
watermark.customColor=Culoare Text Personalizată
watermark.selectText.1=Selectează PDF-ul la care să adaugi filigranul: watermark.selectText.1=Selectează PDF-ul la care să adaugi filigranul:
watermark.selectText.2=Textul Filigranului: watermark.selectText.2=Textul Filigranului:
watermark.selectText.3=Mărimea fontului: watermark.selectText.3=Mărimea fontului:
@@ -1278,11 +1263,6 @@ splitByChapters.desc.3=Include Metadata: If checked, the original PDF's metadata
splitByChapters.desc.4=Allow Duplicates: If checked, allows multiple bookmarks on the same page to create separate PDFs. splitByChapters.desc.4=Allow Duplicates: If checked, allows multiple bookmarks on the same page to create separate PDFs.
splitByChapters.submit=Split PDF splitByChapters.submit=Split PDF
#File Chooser
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes #release notes
releases.footer=Releases releases.footer=Releases
@@ -1290,38 +1270,3 @@ releases.title=Release Notes
releases.header=Release Notes releases.header=Release Notes
releases.current.version=Current Release releases.current.version=Current Release
releases.note=Release notes are only available in English releases.note=Release notes are only available in English
#Validate Signature
validateSignature.title=Validate PDF Signatures
validateSignature.header=Validate Digital Signatures
validateSignature.selectPDF=Select signed PDF file
validateSignature.submit=Validate Signatures
validateSignature.results=Validation Results
validateSignature.status=Status
validateSignature.signer=Signer
validateSignature.date=Date
validateSignature.reason=Reason
validateSignature.location=Location
validateSignature.noSignatures=No digital signatures found in this document
validateSignature.status.valid=Valid
validateSignature.status.invalid=Invalid
validateSignature.chain.invalid=Certificate chain validation failed - cannot verify signer's identity
validateSignature.trust.invalid=Certificate not in trust store - source cannot be verified
validateSignature.cert.expired=Certificate has expired
validateSignature.cert.revoked=Certificate has been revoked
validateSignature.signature.info=Signature Information
validateSignature.signature=Signature
validateSignature.signature.mathValid=Signature is mathematically valid BUT:
validateSignature.selectCustomCert=Custom Certificate File X.509 (Optional)
validateSignature.cert.info=Certificate Details
validateSignature.cert.issuer=Issuer
validateSignature.cert.subject=Subject
validateSignature.cert.serialNumber=Serial Number
validateSignature.cert.validFrom=Valid From
validateSignature.cert.validUntil=Valid Until
validateSignature.cert.algorithm=Algorithm
validateSignature.cert.keySize=Key Size
validateSignature.cert.version=Version
validateSignature.cert.keyUsage=Key Usage
validateSignature.cert.selfSigned=Self-Signed
validateSignature.cert.bits=bits

View File

@@ -512,10 +512,6 @@ home.splitPdfByChapters.title=Разделить PDF по разделам
home.splitPdfByChapters.desc=Разделите PDF на несколько файлов на основе структуры его разделов home.splitPdfByChapters.desc=Разделите PDF на несколько файлов на основе структуры его разделов
splitPdfByChapters.tags=разделение, разделы, закладки, организация splitPdfByChapters.tags=разделение, разделы, закладки, организация
home.validateSignature.title=Validate PDF Signature
home.validateSignature.desc=Verify digital signatures and certificates in PDF documents
validateSignature.tags=signature,verify,validate,pdf,certificate,digital signature,Validate Signature,Validate certificate
#replace-invert-color #replace-invert-color
replace-color.title=Replace-Invert-Color replace-color.title=Replace-Invert-Color
replace-color.header=Заменить-Обратное изменение цвета PDF replace-color.header=Заменить-Обратное изменение цвета PDF
@@ -965,16 +961,6 @@ multiTool.dragDropMessage=Page(s) Selected
multiTool.undo=Undo multiTool.undo=Undo
multiTool.redo=Redo multiTool.redo=Redo
#decrypt
decrypt.passwordPrompt=This file is password-protected. Please enter the password:
decrypt.cancelled=Operation cancelled for PDF: {0}
decrypt.noPassword=No password provided for encrypted PDF: {0}
decrypt.invalidPassword=Please try again with the correct password.
decrypt.invalidPasswordHeader=Incorrect password or unsupported encryption for PDF: {0}
decrypt.unexpectedError=There was an error processing the file. Please try again.
decrypt.serverError=Server error while decrypting: {0}
decrypt.success=File decrypted successfully.
#multiTool-advert #multiTool-advert
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features! multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
@@ -1066,13 +1052,12 @@ addPassword.submit=Шифровать
#watermark #watermark
watermark.title=Добавить водяной знак watermark.title=Добавить водяной знак
watermark.header=Добавить водяной знак watermark.header=Добавить водяной знак
watermark.customColor=Настроенный цвет текста
watermark.selectText.1=Выберите PDF, чтобы добавить водяной знак: watermark.selectText.1=Выберите PDF, чтобы добавить водяной знак:
watermark.selectText.2=Текст водяного знака: watermark.selectText.2=Текст водяного знака:
watermark.selectText.3=Размер шрифта: watermark.selectText.3=Размер шрифта:
watermark.selectText.4=Поворот (0-360): watermark.selectText.4=Поворот (0-360):
watermark.selectText.5=Width Spacer (пробел между каждым водяным знаком по горизонтали): watermark.selectText.5=widthSpacer (пробел между каждым водяным знаком по горизонтали):
watermark.selectText.6=Height Spacer (пробел между каждым водяным знаком по вертикали): watermark.selectText.6=heightSpacer (пробел между каждым водяным знаком по вертикали):
watermark.selectText.7=Непрозрачность (0% - 100%): watermark.selectText.7=Непрозрачность (0% - 100%):
watermark.selectText.8=Тип водяного знака: watermark.selectText.8=Тип водяного знака:
watermark.selectText.9=Изображение водяного знака: watermark.selectText.9=Изображение водяного знака:
@@ -1278,11 +1263,6 @@ splitByChapters.desc.3=Включить метаданные: если эта о
splitByChapters.desc.4=Позволять дубликаты: если эта опция отмечена, на одной странице могут быть созданы несколько PDF из-за нескольких одинаковых закладок. splitByChapters.desc.4=Позволять дубликаты: если эта опция отмечена, на одной странице могут быть созданы несколько PDF из-за нескольких одинаковых закладок.
splitByChapters.submit=Разделить PDF splitByChapters.submit=Разделить PDF
#File Chooser
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes #release notes
releases.footer=Releases releases.footer=Releases
@@ -1290,38 +1270,3 @@ releases.title=Release Notes
releases.header=Release Notes releases.header=Release Notes
releases.current.version=Current Release releases.current.version=Current Release
releases.note=Release notes are only available in English releases.note=Release notes are only available in English
#Validate Signature
validateSignature.title=Validate PDF Signatures
validateSignature.header=Validate Digital Signatures
validateSignature.selectPDF=Select signed PDF file
validateSignature.submit=Validate Signatures
validateSignature.results=Validation Results
validateSignature.status=Status
validateSignature.signer=Signer
validateSignature.date=Date
validateSignature.reason=Reason
validateSignature.location=Location
validateSignature.noSignatures=No digital signatures found in this document
validateSignature.status.valid=Valid
validateSignature.status.invalid=Invalid
validateSignature.chain.invalid=Certificate chain validation failed - cannot verify signer's identity
validateSignature.trust.invalid=Certificate not in trust store - source cannot be verified
validateSignature.cert.expired=Certificate has expired
validateSignature.cert.revoked=Certificate has been revoked
validateSignature.signature.info=Signature Information
validateSignature.signature=Signature
validateSignature.signature.mathValid=Signature is mathematically valid BUT:
validateSignature.selectCustomCert=Custom Certificate File X.509 (Optional)
validateSignature.cert.info=Certificate Details
validateSignature.cert.issuer=Issuer
validateSignature.cert.subject=Subject
validateSignature.cert.serialNumber=Serial Number
validateSignature.cert.validFrom=Valid From
validateSignature.cert.validUntil=Valid Until
validateSignature.cert.algorithm=Algorithm
validateSignature.cert.keySize=Key Size
validateSignature.cert.version=Version
validateSignature.cert.keyUsage=Key Usage
validateSignature.cert.selfSigned=Self-Signed
validateSignature.cert.bits=bits

View File

@@ -512,10 +512,6 @@ home.splitPdfByChapters.title=Split PDF by Chapters
home.splitPdfByChapters.desc=Split a PDF into multiple files based on its chapter structure. home.splitPdfByChapters.desc=Split a PDF into multiple files based on its chapter structure.
splitPdfByChapters.tags=split,chapters,bookmarks,organize splitPdfByChapters.tags=split,chapters,bookmarks,organize
home.validateSignature.title=Validate PDF Signature
home.validateSignature.desc=Verify digital signatures and certificates in PDF documents
validateSignature.tags=signature,verify,validate,pdf,certificate,digital signature,Validate Signature,Validate certificate
#replace-invert-color #replace-invert-color
replace-color.title=Replace-Invert-Color replace-color.title=Replace-Invert-Color
replace-color.header=Replace-Invert Color PDF replace-color.header=Replace-Invert Color PDF
@@ -965,16 +961,6 @@ multiTool.dragDropMessage=Page(s) Selected
multiTool.undo=Undo multiTool.undo=Undo
multiTool.redo=Redo multiTool.redo=Redo
#decrypt
decrypt.passwordPrompt=This file is password-protected. Please enter the password:
decrypt.cancelled=Operation cancelled for PDF: {0}
decrypt.noPassword=No password provided for encrypted PDF: {0}
decrypt.invalidPassword=Please try again with the correct password.
decrypt.invalidPasswordHeader=Incorrect password or unsupported encryption for PDF: {0}
decrypt.unexpectedError=There was an error processing the file. Please try again.
decrypt.serverError=Server error while decrypting: {0}
decrypt.success=File decrypted successfully.
#multiTool-advert #multiTool-advert
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features! multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
@@ -1066,7 +1052,6 @@ addPassword.submit=Zašifrovať
#watermark #watermark
watermark.title=Pridať vodotlač watermark.title=Pridať vodotlač
watermark.header=Pridať vodotlač watermark.header=Pridať vodotlač
watermark.customColor=Vlastná farba textu
watermark.selectText.1=Vyberte PDF, do ktorého chcete pridať vodotlač: watermark.selectText.1=Vyberte PDF, do ktorého chcete pridať vodotlač:
watermark.selectText.2=Text vodotlače: watermark.selectText.2=Text vodotlače:
watermark.selectText.3=Veľkosť písma: watermark.selectText.3=Veľkosť písma:
@@ -1278,11 +1263,6 @@ splitByChapters.desc.3=Include Metadata: If checked, the original PDF's metadata
splitByChapters.desc.4=Allow Duplicates: If checked, allows multiple bookmarks on the same page to create separate PDFs. splitByChapters.desc.4=Allow Duplicates: If checked, allows multiple bookmarks on the same page to create separate PDFs.
splitByChapters.submit=Split PDF splitByChapters.submit=Split PDF
#File Chooser
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes #release notes
releases.footer=Releases releases.footer=Releases
@@ -1290,38 +1270,3 @@ releases.title=Release Notes
releases.header=Release Notes releases.header=Release Notes
releases.current.version=Current Release releases.current.version=Current Release
releases.note=Release notes are only available in English releases.note=Release notes are only available in English
#Validate Signature
validateSignature.title=Validate PDF Signatures
validateSignature.header=Validate Digital Signatures
validateSignature.selectPDF=Select signed PDF file
validateSignature.submit=Validate Signatures
validateSignature.results=Validation Results
validateSignature.status=Status
validateSignature.signer=Signer
validateSignature.date=Date
validateSignature.reason=Reason
validateSignature.location=Location
validateSignature.noSignatures=No digital signatures found in this document
validateSignature.status.valid=Valid
validateSignature.status.invalid=Invalid
validateSignature.chain.invalid=Certificate chain validation failed - cannot verify signer's identity
validateSignature.trust.invalid=Certificate not in trust store - source cannot be verified
validateSignature.cert.expired=Certificate has expired
validateSignature.cert.revoked=Certificate has been revoked
validateSignature.signature.info=Signature Information
validateSignature.signature=Signature
validateSignature.signature.mathValid=Signature is mathematically valid BUT:
validateSignature.selectCustomCert=Custom Certificate File X.509 (Optional)
validateSignature.cert.info=Certificate Details
validateSignature.cert.issuer=Issuer
validateSignature.cert.subject=Subject
validateSignature.cert.serialNumber=Serial Number
validateSignature.cert.validFrom=Valid From
validateSignature.cert.validUntil=Valid Until
validateSignature.cert.algorithm=Algorithm
validateSignature.cert.keySize=Key Size
validateSignature.cert.version=Version
validateSignature.cert.keyUsage=Key Usage
validateSignature.cert.selfSigned=Self-Signed
validateSignature.cert.bits=bits

View File

@@ -512,10 +512,6 @@ home.splitPdfByChapters.title=Split PDF by Chapters
home.splitPdfByChapters.desc=Split a PDF into multiple files based on its chapter structure. home.splitPdfByChapters.desc=Split a PDF into multiple files based on its chapter structure.
splitPdfByChapters.tags=split,chapters,bookmarks,organize splitPdfByChapters.tags=split,chapters,bookmarks,organize
home.validateSignature.title=Validate PDF Signature
home.validateSignature.desc=Verify digital signatures and certificates in PDF documents
validateSignature.tags=signature,verify,validate,pdf,certificate,digital signature,Validate Signature,Validate certificate
#replace-invert-color #replace-invert-color
replace-color.title=Replace-Invert-Color replace-color.title=Replace-Invert-Color
replace-color.header=Replace-Invert Color PDF replace-color.header=Replace-Invert Color PDF
@@ -965,16 +961,6 @@ multiTool.dragDropMessage=Page(s) Selected
multiTool.undo=Undo multiTool.undo=Undo
multiTool.redo=Redo multiTool.redo=Redo
#decrypt
decrypt.passwordPrompt=This file is password-protected. Please enter the password:
decrypt.cancelled=Operation cancelled for PDF: {0}
decrypt.noPassword=No password provided for encrypted PDF: {0}
decrypt.invalidPassword=Please try again with the correct password.
decrypt.invalidPasswordHeader=Incorrect password or unsupported encryption for PDF: {0}
decrypt.unexpectedError=There was an error processing the file. Please try again.
decrypt.serverError=Server error while decrypting: {0}
decrypt.success=File decrypted successfully.
#multiTool-advert #multiTool-advert
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features! multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
@@ -1066,7 +1052,6 @@ addPassword.submit=Enkriptuj
#watermark #watermark
watermark.title=Dodaj vodeni žig watermark.title=Dodaj vodeni žig
watermark.header=Dodaj vodeni žig watermark.header=Dodaj vodeni žig
watermark.customColor=Custom Text Color
watermark.selectText.1=Izaberite PDF za dodavanje vodenog žiga: watermark.selectText.1=Izaberite PDF za dodavanje vodenog žiga:
watermark.selectText.2=Tekst vodenog žiga: watermark.selectText.2=Tekst vodenog žiga:
watermark.selectText.3=Veličina fonta: watermark.selectText.3=Veličina fonta:
@@ -1278,11 +1263,6 @@ splitByChapters.desc.3=Include Metadata: If checked, the original PDF's metadata
splitByChapters.desc.4=Allow Duplicates: If checked, allows multiple bookmarks on the same page to create separate PDFs. splitByChapters.desc.4=Allow Duplicates: If checked, allows multiple bookmarks on the same page to create separate PDFs.
splitByChapters.submit=Split PDF splitByChapters.submit=Split PDF
#File Chooser
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes #release notes
releases.footer=Releases releases.footer=Releases
@@ -1290,38 +1270,3 @@ releases.title=Release Notes
releases.header=Release Notes releases.header=Release Notes
releases.current.version=Current Release releases.current.version=Current Release
releases.note=Release notes are only available in English releases.note=Release notes are only available in English
#Validate Signature
validateSignature.title=Validate PDF Signatures
validateSignature.header=Validate Digital Signatures
validateSignature.selectPDF=Select signed PDF file
validateSignature.submit=Validate Signatures
validateSignature.results=Validation Results
validateSignature.status=Status
validateSignature.signer=Signer
validateSignature.date=Date
validateSignature.reason=Reason
validateSignature.location=Location
validateSignature.noSignatures=No digital signatures found in this document
validateSignature.status.valid=Valid
validateSignature.status.invalid=Invalid
validateSignature.chain.invalid=Certificate chain validation failed - cannot verify signer's identity
validateSignature.trust.invalid=Certificate not in trust store - source cannot be verified
validateSignature.cert.expired=Certificate has expired
validateSignature.cert.revoked=Certificate has been revoked
validateSignature.signature.info=Signature Information
validateSignature.signature=Signature
validateSignature.signature.mathValid=Signature is mathematically valid BUT:
validateSignature.selectCustomCert=Custom Certificate File X.509 (Optional)
validateSignature.cert.info=Certificate Details
validateSignature.cert.issuer=Issuer
validateSignature.cert.subject=Subject
validateSignature.cert.serialNumber=Serial Number
validateSignature.cert.validFrom=Valid From
validateSignature.cert.validUntil=Valid Until
validateSignature.cert.algorithm=Algorithm
validateSignature.cert.keySize=Key Size
validateSignature.cert.version=Version
validateSignature.cert.keyUsage=Key Usage
validateSignature.cert.selfSigned=Self-Signed
validateSignature.cert.bits=bits

View File

@@ -512,10 +512,6 @@ home.splitPdfByChapters.title=Dela upp PDF efter kapitel
home.splitPdfByChapters.desc=Dela upp en PDF till flera filer baserat på dess kapitelstruktur. home.splitPdfByChapters.desc=Dela upp en PDF till flera filer baserat på dess kapitelstruktur.
splitPdfByChapters.tags=dela,kapitel,bokmärken,organisera splitPdfByChapters.tags=dela,kapitel,bokmärken,organisera
home.validateSignature.title=Validate PDF Signature
home.validateSignature.desc=Verify digital signatures and certificates in PDF documents
validateSignature.tags=signature,verify,validate,pdf,certificate,digital signature,Validate Signature,Validate certificate
#replace-invert-color #replace-invert-color
replace-color.title=Replace-Invert-Color replace-color.title=Replace-Invert-Color
replace-color.header=Ersätt-Invertera färg på PDF replace-color.header=Ersätt-Invertera färg på PDF
@@ -965,16 +961,6 @@ multiTool.dragDropMessage=Page(s) Selected
multiTool.undo=Undo multiTool.undo=Undo
multiTool.redo=Redo multiTool.redo=Redo
#decrypt
decrypt.passwordPrompt=This file is password-protected. Please enter the password:
decrypt.cancelled=Operation cancelled for PDF: {0}
decrypt.noPassword=No password provided for encrypted PDF: {0}
decrypt.invalidPassword=Please try again with the correct password.
decrypt.invalidPasswordHeader=Incorrect password or unsupported encryption for PDF: {0}
decrypt.unexpectedError=There was an error processing the file. Please try again.
decrypt.serverError=Server error while decrypting: {0}
decrypt.success=File decrypted successfully.
#multiTool-advert #multiTool-advert
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features! multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
@@ -1066,13 +1052,12 @@ addPassword.submit=Kryptera
#watermark #watermark
watermark.title=Lägg till vattenstämpel watermark.title=Lägg till vattenstämpel
watermark.header=Lägg till vattenstämpel watermark.header=Lägg till vattenstämpel
watermark.customColor=Anpassad textfärg
watermark.selectText.1=Välj PDF för att lägga till vattenstämpel till: watermark.selectText.1=Välj PDF för att lägga till vattenstämpel till:
watermark.selectText.2=Vattenmärkestext: watermark.selectText.2=Vattenmärkestext:
watermark.selectText.3=Teckenstorlek: watermark.selectText.3=Teckenstorlek:
watermark.selectText.4=Vändning (0-360): watermark.selectText.4=Vändning (0-360):
watermark.selectText.5=Width Spacer (mellanrum mellan varje vattenstämpel horisontellt): watermark.selectText.5=widthSpacer (mellanrum mellan varje vattenstämpel horisontellt):
watermark.selectText.6=Height Spacer (mellanrum mellan varje vattenstämpel vertikalt): watermark.selectText.6=heightSpacer (mellanrum mellan varje vattenstämpel vertikalt):
watermark.selectText.7=Opacitet (0% - 100%): watermark.selectText.7=Opacitet (0% - 100%):
watermark.selectText.8=Vattenstämpeltyp: watermark.selectText.8=Vattenstämpeltyp:
watermark.selectText.9=Vattenstämpelbild: watermark.selectText.9=Vattenstämpelbild:
@@ -1278,11 +1263,6 @@ splitByChapters.desc.3=Include Metadata: If checked, the original PDF's metadata
splitByChapters.desc.4=Tillåt duplicieringar: Om kryssrutan är markerad tillåts flera bokmärken på samma sida skapa individuella PDF:er. splitByChapters.desc.4=Tillåt duplicieringar: Om kryssrutan är markerad tillåts flera bokmärken på samma sida skapa individuella PDF:er.
splitByChapters.submit=Dela upp PDF splitByChapters.submit=Dela upp PDF
#File Chooser
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes #release notes
releases.footer=Releases releases.footer=Releases
@@ -1290,38 +1270,3 @@ releases.title=Release Notes
releases.header=Release Notes releases.header=Release Notes
releases.current.version=Current Release releases.current.version=Current Release
releases.note=Release notes are only available in English releases.note=Release notes are only available in English
#Validate Signature
validateSignature.title=Validate PDF Signatures
validateSignature.header=Validate Digital Signatures
validateSignature.selectPDF=Select signed PDF file
validateSignature.submit=Validate Signatures
validateSignature.results=Validation Results
validateSignature.status=Status
validateSignature.signer=Signer
validateSignature.date=Date
validateSignature.reason=Reason
validateSignature.location=Location
validateSignature.noSignatures=No digital signatures found in this document
validateSignature.status.valid=Valid
validateSignature.status.invalid=Invalid
validateSignature.chain.invalid=Certificate chain validation failed - cannot verify signer's identity
validateSignature.trust.invalid=Certificate not in trust store - source cannot be verified
validateSignature.cert.expired=Certificate has expired
validateSignature.cert.revoked=Certificate has been revoked
validateSignature.signature.info=Signature Information
validateSignature.signature=Signature
validateSignature.signature.mathValid=Signature is mathematically valid BUT:
validateSignature.selectCustomCert=Custom Certificate File X.509 (Optional)
validateSignature.cert.info=Certificate Details
validateSignature.cert.issuer=Issuer
validateSignature.cert.subject=Subject
validateSignature.cert.serialNumber=Serial Number
validateSignature.cert.validFrom=Valid From
validateSignature.cert.validUntil=Valid Until
validateSignature.cert.algorithm=Algorithm
validateSignature.cert.keySize=Key Size
validateSignature.cert.version=Version
validateSignature.cert.keyUsage=Key Usage
validateSignature.cert.selfSigned=Self-Signed
validateSignature.cert.bits=bits

View File

@@ -512,10 +512,6 @@ home.splitPdfByChapters.title=Split PDF by Chapters
home.splitPdfByChapters.desc=Split a PDF into multiple files based on its chapter structure. home.splitPdfByChapters.desc=Split a PDF into multiple files based on its chapter structure.
splitPdfByChapters.tags=split,chapters,bookmarks,organize splitPdfByChapters.tags=split,chapters,bookmarks,organize
home.validateSignature.title=Validate PDF Signature
home.validateSignature.desc=Verify digital signatures and certificates in PDF documents
validateSignature.tags=signature,verify,validate,pdf,certificate,digital signature,Validate Signature,Validate certificate
#replace-invert-color #replace-invert-color
replace-color.title=Replace-Invert-Color replace-color.title=Replace-Invert-Color
replace-color.header=Replace-Invert Color PDF replace-color.header=Replace-Invert Color PDF
@@ -965,16 +961,6 @@ multiTool.dragDropMessage=Page(s) Selected
multiTool.undo=Undo multiTool.undo=Undo
multiTool.redo=Redo multiTool.redo=Redo
#decrypt
decrypt.passwordPrompt=This file is password-protected. Please enter the password:
decrypt.cancelled=Operation cancelled for PDF: {0}
decrypt.noPassword=No password provided for encrypted PDF: {0}
decrypt.invalidPassword=Please try again with the correct password.
decrypt.invalidPasswordHeader=Incorrect password or unsupported encryption for PDF: {0}
decrypt.unexpectedError=There was an error processing the file. Please try again.
decrypt.serverError=Server error while decrypting: {0}
decrypt.success=File decrypted successfully.
#multiTool-advert #multiTool-advert
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features! multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
@@ -1066,7 +1052,6 @@ addPassword.submit=เข้ารหัส
#watermark #watermark
watermark.title=เพิ่มลายน้ำ watermark.title=เพิ่มลายน้ำ
watermark.header=เพิ่มลายน้ำ watermark.header=เพิ่มลายน้ำ
watermark.customColor=สีข้อความที่กำหนดเอง
watermark.selectText.1=เลือก PDF เพื่อเพิ่มลายน้ำ: watermark.selectText.1=เลือก PDF เพื่อเพิ่มลายน้ำ:
watermark.selectText.2=ข้อความลายน้ำ: watermark.selectText.2=ข้อความลายน้ำ:
watermark.selectText.3=ขนาดฟอนต์: watermark.selectText.3=ขนาดฟอนต์:
@@ -1278,11 +1263,6 @@ splitByChapters.desc.3=รวมข้อมูลเสริม: หากถ
splitByChapters.desc.4=อนุญาตให้มีการซ้ำ: หากถูกเลือก จะทำให้สามารถสร้างไฟล์ PDF แยกออกมาจากหน้าเดียวกันได้หลายรายการ splitByChapters.desc.4=อนุญาตให้มีการซ้ำ: หากถูกเลือก จะทำให้สามารถสร้างไฟล์ PDF แยกออกมาจากหน้าเดียวกันได้หลายรายการ
splitByChapters.submit=แบ่งไฟล์ PDF splitByChapters.submit=แบ่งไฟล์ PDF
#File Chooser
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes #release notes
releases.footer=Releases releases.footer=Releases
@@ -1290,38 +1270,3 @@ releases.title=Release Notes
releases.header=Release Notes releases.header=Release Notes
releases.current.version=Current Release releases.current.version=Current Release
releases.note=Release notes are only available in English releases.note=Release notes are only available in English
#Validate Signature
validateSignature.title=Validate PDF Signatures
validateSignature.header=Validate Digital Signatures
validateSignature.selectPDF=Select signed PDF file
validateSignature.submit=Validate Signatures
validateSignature.results=Validation Results
validateSignature.status=Status
validateSignature.signer=Signer
validateSignature.date=Date
validateSignature.reason=Reason
validateSignature.location=Location
validateSignature.noSignatures=No digital signatures found in this document
validateSignature.status.valid=Valid
validateSignature.status.invalid=Invalid
validateSignature.chain.invalid=Certificate chain validation failed - cannot verify signer's identity
validateSignature.trust.invalid=Certificate not in trust store - source cannot be verified
validateSignature.cert.expired=Certificate has expired
validateSignature.cert.revoked=Certificate has been revoked
validateSignature.signature.info=Signature Information
validateSignature.signature=Signature
validateSignature.signature.mathValid=Signature is mathematically valid BUT:
validateSignature.selectCustomCert=Custom Certificate File X.509 (Optional)
validateSignature.cert.info=Certificate Details
validateSignature.cert.issuer=Issuer
validateSignature.cert.subject=Subject
validateSignature.cert.serialNumber=Serial Number
validateSignature.cert.validFrom=Valid From
validateSignature.cert.validUntil=Valid Until
validateSignature.cert.algorithm=Algorithm
validateSignature.cert.keySize=Key Size
validateSignature.cert.version=Version
validateSignature.cert.keyUsage=Key Usage
validateSignature.cert.selfSigned=Self-Signed
validateSignature.cert.bits=bits

View File

@@ -512,10 +512,6 @@ home.splitPdfByChapters.title=Split PDF by Chapters
home.splitPdfByChapters.desc=Split a PDF into multiple files based on its chapter structure. home.splitPdfByChapters.desc=Split a PDF into multiple files based on its chapter structure.
splitPdfByChapters.tags=split,chapters,bookmarks,organize splitPdfByChapters.tags=split,chapters,bookmarks,organize
home.validateSignature.title=Validate PDF Signature
home.validateSignature.desc=Verify digital signatures and certificates in PDF documents
validateSignature.tags=signature,verify,validate,pdf,certificate,digital signature,Validate Signature,Validate certificate
#replace-invert-color #replace-invert-color
replace-color.title=Replace-Invert-Color replace-color.title=Replace-Invert-Color
replace-color.header=Replace-Invert Color PDF replace-color.header=Replace-Invert Color PDF
@@ -965,16 +961,6 @@ multiTool.dragDropMessage=Page(s) Selected
multiTool.undo=Undo multiTool.undo=Undo
multiTool.redo=Redo multiTool.redo=Redo
#decrypt
decrypt.passwordPrompt=This file is password-protected. Please enter the password:
decrypt.cancelled=Operation cancelled for PDF: {0}
decrypt.noPassword=No password provided for encrypted PDF: {0}
decrypt.invalidPassword=Please try again with the correct password.
decrypt.invalidPasswordHeader=Incorrect password or unsupported encryption for PDF: {0}
decrypt.unexpectedError=There was an error processing the file. Please try again.
decrypt.serverError=Server error while decrypting: {0}
decrypt.success=File decrypted successfully.
#multiTool-advert #multiTool-advert
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features! multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
@@ -1066,7 +1052,6 @@ addPassword.submit=Şifrele
#watermark #watermark
watermark.title=Filigran Ekle watermark.title=Filigran Ekle
watermark.header=Filigran Ekle watermark.header=Filigran Ekle
watermark.customColor=Özel Metin Rengi
watermark.selectText.1=Filigran eklemek için PDF seçin: watermark.selectText.1=Filigran eklemek için PDF seçin:
watermark.selectText.2=Filigran Metni: watermark.selectText.2=Filigran Metni:
watermark.selectText.3=Yazı Boyutu: watermark.selectText.3=Yazı Boyutu:
@@ -1278,11 +1263,6 @@ splitByChapters.desc.3=Include Metadata: If checked, the original PDF's metadata
splitByChapters.desc.4=Allow Duplicates: If checked, allows multiple bookmarks on the same page to create separate PDFs. splitByChapters.desc.4=Allow Duplicates: If checked, allows multiple bookmarks on the same page to create separate PDFs.
splitByChapters.submit=Split PDF splitByChapters.submit=Split PDF
#File Chooser
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes #release notes
releases.footer=Releases releases.footer=Releases
@@ -1290,38 +1270,3 @@ releases.title=Release Notes
releases.header=Release Notes releases.header=Release Notes
releases.current.version=Current Release releases.current.version=Current Release
releases.note=Release notes are only available in English releases.note=Release notes are only available in English
#Validate Signature
validateSignature.title=Validate PDF Signatures
validateSignature.header=Validate Digital Signatures
validateSignature.selectPDF=Select signed PDF file
validateSignature.submit=Validate Signatures
validateSignature.results=Validation Results
validateSignature.status=Status
validateSignature.signer=Signer
validateSignature.date=Date
validateSignature.reason=Reason
validateSignature.location=Location
validateSignature.noSignatures=No digital signatures found in this document
validateSignature.status.valid=Valid
validateSignature.status.invalid=Invalid
validateSignature.chain.invalid=Certificate chain validation failed - cannot verify signer's identity
validateSignature.trust.invalid=Certificate not in trust store - source cannot be verified
validateSignature.cert.expired=Certificate has expired
validateSignature.cert.revoked=Certificate has been revoked
validateSignature.signature.info=Signature Information
validateSignature.signature=Signature
validateSignature.signature.mathValid=Signature is mathematically valid BUT:
validateSignature.selectCustomCert=Custom Certificate File X.509 (Optional)
validateSignature.cert.info=Certificate Details
validateSignature.cert.issuer=Issuer
validateSignature.cert.subject=Subject
validateSignature.cert.serialNumber=Serial Number
validateSignature.cert.validFrom=Valid From
validateSignature.cert.validUntil=Valid Until
validateSignature.cert.algorithm=Algorithm
validateSignature.cert.keySize=Key Size
validateSignature.cert.version=Version
validateSignature.cert.keyUsage=Key Usage
validateSignature.cert.selfSigned=Self-Signed
validateSignature.cert.bits=bits

View File

@@ -512,10 +512,6 @@ home.splitPdfByChapters.title=Split PDF by Chapters
home.splitPdfByChapters.desc=Split a PDF into multiple files based on its chapter structure. home.splitPdfByChapters.desc=Split a PDF into multiple files based on its chapter structure.
splitPdfByChapters.tags=split,chapters,bookmarks,organize splitPdfByChapters.tags=split,chapters,bookmarks,organize
home.validateSignature.title=Validate PDF Signature
home.validateSignature.desc=Verify digital signatures and certificates in PDF documents
validateSignature.tags=signature,verify,validate,pdf,certificate,digital signature,Validate Signature,Validate certificate
#replace-invert-color #replace-invert-color
replace-color.title=Replace-Invert-Color replace-color.title=Replace-Invert-Color
replace-color.header=Replace-Invert Color PDF replace-color.header=Replace-Invert Color PDF
@@ -965,16 +961,6 @@ multiTool.dragDropMessage=Page(s) Selected
multiTool.undo=Undo multiTool.undo=Undo
multiTool.redo=Redo multiTool.redo=Redo
#decrypt
decrypt.passwordPrompt=This file is password-protected. Please enter the password:
decrypt.cancelled=Operation cancelled for PDF: {0}
decrypt.noPassword=No password provided for encrypted PDF: {0}
decrypt.invalidPassword=Please try again with the correct password.
decrypt.invalidPasswordHeader=Incorrect password or unsupported encryption for PDF: {0}
decrypt.unexpectedError=There was an error processing the file. Please try again.
decrypt.serverError=Server error while decrypting: {0}
decrypt.success=File decrypted successfully.
#multiTool-advert #multiTool-advert
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features! multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
@@ -1066,13 +1052,12 @@ addPassword.submit=Шифрувати
#watermark #watermark
watermark.title=Додати водяний знак watermark.title=Додати водяний знак
watermark.header=Додати водяний знак watermark.header=Додати водяний знак
watermark.customColor=Користувацький колір тексту
watermark.selectText.1=Виберіть PDF, щоб додати водяний знак: watermark.selectText.1=Виберіть PDF, щоб додати водяний знак:
watermark.selectText.2=Текст водяного знаку: watermark.selectText.2=Текст водяного знаку:
watermark.selectText.3=Розмір шрифту: watermark.selectText.3=Розмір шрифту:
watermark.selectText.4=Обертання (0-360): watermark.selectText.4=Обертання (0-360):
watermark.selectText.5=Width Spacer (проміжок між кожним водяним знаком по горизонталі): watermark.selectText.5=widthSpacer (проміжок між кожним водяним знаком по горизонталі):
watermark.selectText.6=Height Spacer (проміжок між кожним водяним знаком по вертикалі): watermark.selectText.6=heightSpacer (проміжок між кожним водяним знаком по вертикалі):
watermark.selectText.7=Непрозорість (0% - 100%): watermark.selectText.7=Непрозорість (0% - 100%):
watermark.selectText.8=Тип водяного знаку: watermark.selectText.8=Тип водяного знаку:
watermark.selectText.9=Зображення водяного знаку: watermark.selectText.9=Зображення водяного знаку:
@@ -1278,11 +1263,6 @@ splitByChapters.desc.3=Include Metadata: If checked, the original PDF's metadata
splitByChapters.desc.4=Allow Duplicates: If checked, allows multiple bookmarks on the same page to create separate PDFs. splitByChapters.desc.4=Allow Duplicates: If checked, allows multiple bookmarks on the same page to create separate PDFs.
splitByChapters.submit=Split PDF splitByChapters.submit=Split PDF
#File Chooser
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes #release notes
releases.footer=Releases releases.footer=Releases
@@ -1290,38 +1270,3 @@ releases.title=Release Notes
releases.header=Release Notes releases.header=Release Notes
releases.current.version=Current Release releases.current.version=Current Release
releases.note=Release notes are only available in English releases.note=Release notes are only available in English
#Validate Signature
validateSignature.title=Validate PDF Signatures
validateSignature.header=Validate Digital Signatures
validateSignature.selectPDF=Select signed PDF file
validateSignature.submit=Validate Signatures
validateSignature.results=Validation Results
validateSignature.status=Status
validateSignature.signer=Signer
validateSignature.date=Date
validateSignature.reason=Reason
validateSignature.location=Location
validateSignature.noSignatures=No digital signatures found in this document
validateSignature.status.valid=Valid
validateSignature.status.invalid=Invalid
validateSignature.chain.invalid=Certificate chain validation failed - cannot verify signer's identity
validateSignature.trust.invalid=Certificate not in trust store - source cannot be verified
validateSignature.cert.expired=Certificate has expired
validateSignature.cert.revoked=Certificate has been revoked
validateSignature.signature.info=Signature Information
validateSignature.signature=Signature
validateSignature.signature.mathValid=Signature is mathematically valid BUT:
validateSignature.selectCustomCert=Custom Certificate File X.509 (Optional)
validateSignature.cert.info=Certificate Details
validateSignature.cert.issuer=Issuer
validateSignature.cert.subject=Subject
validateSignature.cert.serialNumber=Serial Number
validateSignature.cert.validFrom=Valid From
validateSignature.cert.validUntil=Valid Until
validateSignature.cert.algorithm=Algorithm
validateSignature.cert.keySize=Key Size
validateSignature.cert.version=Version
validateSignature.cert.keyUsage=Key Usage
validateSignature.cert.selfSigned=Self-Signed
validateSignature.cert.bits=bits

View File

@@ -512,10 +512,6 @@ home.splitPdfByChapters.title=Split PDF by Chapters
home.splitPdfByChapters.desc=Split a PDF into multiple files based on its chapter structure. home.splitPdfByChapters.desc=Split a PDF into multiple files based on its chapter structure.
splitPdfByChapters.tags=split,chapters,bookmarks,organize splitPdfByChapters.tags=split,chapters,bookmarks,organize
home.validateSignature.title=Validate PDF Signature
home.validateSignature.desc=Verify digital signatures and certificates in PDF documents
validateSignature.tags=signature,verify,validate,pdf,certificate,digital signature,Validate Signature,Validate certificate
#replace-invert-color #replace-invert-color
replace-color.title=Replace-Invert-Color replace-color.title=Replace-Invert-Color
replace-color.header=Replace-Invert Color PDF replace-color.header=Replace-Invert Color PDF
@@ -965,16 +961,6 @@ multiTool.dragDropMessage=Page(s) Selected
multiTool.undo=Undo multiTool.undo=Undo
multiTool.redo=Redo multiTool.redo=Redo
#decrypt
decrypt.passwordPrompt=This file is password-protected. Please enter the password:
decrypt.cancelled=Operation cancelled for PDF: {0}
decrypt.noPassword=No password provided for encrypted PDF: {0}
decrypt.invalidPassword=Please try again with the correct password.
decrypt.invalidPasswordHeader=Incorrect password or unsupported encryption for PDF: {0}
decrypt.unexpectedError=There was an error processing the file. Please try again.
decrypt.serverError=Server error while decrypting: {0}
decrypt.success=File decrypted successfully.
#multiTool-advert #multiTool-advert
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features! multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
@@ -1066,7 +1052,6 @@ addPassword.submit=Mã hóa
#watermark #watermark
watermark.title=Thêm hình mờ watermark.title=Thêm hình mờ
watermark.header=Thêm hình mờ watermark.header=Thêm hình mờ
watermark.customColor=Màu văn bản tùy chỉnh
watermark.selectText.1=Chọn PDF để thêm hình mờ: watermark.selectText.1=Chọn PDF để thêm hình mờ:
watermark.selectText.2=Văn bản hình mờ: watermark.selectText.2=Văn bản hình mờ:
watermark.selectText.3=Cỡ chữ: watermark.selectText.3=Cỡ chữ:
@@ -1278,11 +1263,6 @@ splitByChapters.desc.3=Include Metadata: If checked, the original PDF's metadata
splitByChapters.desc.4=Allow Duplicates: If checked, allows multiple bookmarks on the same page to create separate PDFs. splitByChapters.desc.4=Allow Duplicates: If checked, allows multiple bookmarks on the same page to create separate PDFs.
splitByChapters.submit=Split PDF splitByChapters.submit=Split PDF
#File Chooser
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes #release notes
releases.footer=Releases releases.footer=Releases
@@ -1290,38 +1270,3 @@ releases.title=Release Notes
releases.header=Release Notes releases.header=Release Notes
releases.current.version=Current Release releases.current.version=Current Release
releases.note=Release notes are only available in English releases.note=Release notes are only available in English
#Validate Signature
validateSignature.title=Validate PDF Signatures
validateSignature.header=Validate Digital Signatures
validateSignature.selectPDF=Select signed PDF file
validateSignature.submit=Validate Signatures
validateSignature.results=Validation Results
validateSignature.status=Status
validateSignature.signer=Signer
validateSignature.date=Date
validateSignature.reason=Reason
validateSignature.location=Location
validateSignature.noSignatures=No digital signatures found in this document
validateSignature.status.valid=Valid
validateSignature.status.invalid=Invalid
validateSignature.chain.invalid=Certificate chain validation failed - cannot verify signer's identity
validateSignature.trust.invalid=Certificate not in trust store - source cannot be verified
validateSignature.cert.expired=Certificate has expired
validateSignature.cert.revoked=Certificate has been revoked
validateSignature.signature.info=Signature Information
validateSignature.signature=Signature
validateSignature.signature.mathValid=Signature is mathematically valid BUT:
validateSignature.selectCustomCert=Custom Certificate File X.509 (Optional)
validateSignature.cert.info=Certificate Details
validateSignature.cert.issuer=Issuer
validateSignature.cert.subject=Subject
validateSignature.cert.serialNumber=Serial Number
validateSignature.cert.validFrom=Valid From
validateSignature.cert.validUntil=Valid Until
validateSignature.cert.algorithm=Algorithm
validateSignature.cert.keySize=Key Size
validateSignature.cert.version=Version
validateSignature.cert.keyUsage=Key Usage
validateSignature.cert.selfSigned=Self-Signed
validateSignature.cert.bits=bits

File diff suppressed because it is too large Load Diff

View File

@@ -512,10 +512,6 @@ home.splitPdfByChapters.title=依章節分割 PDF
home.splitPdfByChapters.desc=根據 PDF 的章節結構將其分割成多個檔案。 home.splitPdfByChapters.desc=根據 PDF 的章節結構將其分割成多個檔案。
splitPdfByChapters.tags=分割,章節,書籤,整理 splitPdfByChapters.tags=分割,章節,書籤,整理
home.validateSignature.title=Validate PDF Signature
home.validateSignature.desc=Verify digital signatures and certificates in PDF documents
validateSignature.tags=signature,verify,validate,pdf,certificate,digital signature,Validate Signature,Validate certificate
#replace-invert-color #replace-invert-color
replace-color.title=取代-反轉顏色 replace-color.title=取代-反轉顏色
replace-color.header=取代-反轉 PDF 顏色 replace-color.header=取代-反轉 PDF 顏色
@@ -965,16 +961,6 @@ multiTool.dragDropMessage=Page(s) Selected
multiTool.undo=Undo multiTool.undo=Undo
multiTool.redo=Redo multiTool.redo=Redo
#decrypt
decrypt.passwordPrompt=This file is password-protected. Please enter the password:
decrypt.cancelled=Operation cancelled for PDF: {0}
decrypt.noPassword=No password provided for encrypted PDF: {0}
decrypt.invalidPassword=Please try again with the correct password.
decrypt.invalidPasswordHeader=Incorrect password or unsupported encryption for PDF: {0}
decrypt.unexpectedError=There was an error processing the file. Please try again.
decrypt.serverError=Server error while decrypting: {0}
decrypt.success=File decrypted successfully.
#multiTool-advert #multiTool-advert
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features! multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
@@ -1066,13 +1052,12 @@ addPassword.submit=加密
#watermark #watermark
watermark.title=新增浮水印 watermark.title=新增浮水印
watermark.header=新增浮水印 watermark.header=新增浮水印
watermark.customColor=自訂文字顏色
watermark.selectText.1=選擇要新增浮水印的 PDF watermark.selectText.1=選擇要新增浮水印的 PDF
watermark.selectText.2=浮水印文字: watermark.selectText.2=浮水印文字:
watermark.selectText.3=字型大小: watermark.selectText.3=字型大小:
watermark.selectText.4=旋轉0-360 watermark.selectText.4=旋轉0-360
watermark.selectText.5=Width Spacer每個浮水印之間的水平間距 watermark.selectText.5=widthSpacer每個浮水印之間的水平間距
watermark.selectText.6=Height Spacer每個浮水印之間的垂直間距 watermark.selectText.6=heightSpacer每個浮水印之間的垂直間距
watermark.selectText.7=不透明度0% - 100% watermark.selectText.7=不透明度0% - 100%
watermark.selectText.8=浮水印類型: watermark.selectText.8=浮水印類型:
watermark.selectText.9=浮水印影像: watermark.selectText.9=浮水印影像:
@@ -1278,11 +1263,6 @@ splitByChapters.desc.3=包含中繼資料:如果勾選,原始 PDF 的中繼
splitByChapters.desc.4=允許重複:如果勾選,允許同一頁面上的多個書籤建立獨立的 PDF。 splitByChapters.desc.4=允許重複:如果勾選,允許同一頁面上的多個書籤建立獨立的 PDF。
splitByChapters.submit=分割 PDF splitByChapters.submit=分割 PDF
#File Chooser
fileChooser.click=Click
fileChooser.or=or
fileChooser.dragAndDrop=Drag & Drop
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
#release notes #release notes
releases.footer=Releases releases.footer=Releases
@@ -1290,38 +1270,3 @@ releases.title=Release Notes
releases.header=Release Notes releases.header=Release Notes
releases.current.version=Current Release releases.current.version=Current Release
releases.note=Release notes are only available in English releases.note=Release notes are only available in English
#Validate Signature
validateSignature.title=Validate PDF Signatures
validateSignature.header=Validate Digital Signatures
validateSignature.selectPDF=Select signed PDF file
validateSignature.submit=Validate Signatures
validateSignature.results=Validation Results
validateSignature.status=Status
validateSignature.signer=Signer
validateSignature.date=Date
validateSignature.reason=Reason
validateSignature.location=Location
validateSignature.noSignatures=No digital signatures found in this document
validateSignature.status.valid=Valid
validateSignature.status.invalid=Invalid
validateSignature.chain.invalid=Certificate chain validation failed - cannot verify signer's identity
validateSignature.trust.invalid=Certificate not in trust store - source cannot be verified
validateSignature.cert.expired=Certificate has expired
validateSignature.cert.revoked=Certificate has been revoked
validateSignature.signature.info=Signature Information
validateSignature.signature=Signature
validateSignature.signature.mathValid=Signature is mathematically valid BUT:
validateSignature.selectCustomCert=Custom Certificate File X.509 (Optional)
validateSignature.cert.info=Certificate Details
validateSignature.cert.issuer=Issuer
validateSignature.cert.subject=Subject
validateSignature.cert.serialNumber=Serial Number
validateSignature.cert.validFrom=Valid From
validateSignature.cert.validUntil=Valid Until
validateSignature.cert.algorithm=Algorithm
validateSignature.cert.keySize=Key Size
validateSignature.cert.version=Version
validateSignature.cert.keyUsage=Key Usage
validateSignature.cert.selfSigned=Self-Signed
validateSignature.cert.bits=bits

View File

@@ -13,7 +13,7 @@
security: security:
enableLogin: false # set to 'true' to enable login enableLogin: false # set to 'true' to enable login
csrfDisabled: false # set to 'true' to disable CSRF protection (not recommended for production) csrfDisabled: true # set to 'true' to disable CSRF protection (not recommended for production)
loginAttemptCount: 5 # lock user account after 5 tries; when using e.g. Fail2Ban you can deactivate the function with -1 loginAttemptCount: 5 # lock user account after 5 tries; when using e.g. Fail2Ban you can deactivate the function with -1
loginResetTimeMinutes: 120 # lock account for 2 hours after x attempts loginResetTimeMinutes: 120 # lock account for 2 hours after x attempts
loginMethod: all # Accepts values like 'all' and 'normal'(only Login with Username/Password), 'oauth2'(only Login with OAuth2) or 'saml2'(only Login with SAML2) loginMethod: all # Accepts values like 'all' and 'normal'(only Login with Username/Password), 'oauth2'(only Login with OAuth2) or 'saml2'(only Login with SAML2)
@@ -102,7 +102,6 @@ metrics:
AutomaticallyGenerated: AutomaticallyGenerated:
key: example key: example
UUID: example UUID: example
appVersion: 0.35.0
processExecutor: processExecutor:
sessionLimit: # Process executor instances limits sessionLimit: # Process executor instances limits

View File

@@ -172,13 +172,6 @@
"moduleLicense": "The Apache Software License, Version 2.0", "moduleLicense": "The Apache Software License, Version 2.0",
"moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt" "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
}, },
{
"moduleName": "com.google.code.gson:gson",
"moduleUrl": "https://github.com/google/gson",
"moduleVersion": "2.11.0",
"moduleLicense": "Apache-2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
},
{ {
"moduleName": "com.google.errorprone:error_prone_annotations", "moduleName": "com.google.errorprone:error_prone_annotations",
"moduleUrl": "https://errorprone.info/error_prone_annotations", "moduleUrl": "https://errorprone.info/error_prone_annotations",
@@ -598,34 +591,6 @@
"moduleLicense": "GPL2 w/ CPE", "moduleLicense": "GPL2 w/ CPE",
"moduleLicenseUrl": "https://oss.oracle.com/licenses/CDDL+GPL-1.1" "moduleLicenseUrl": "https://oss.oracle.com/licenses/CDDL+GPL-1.1"
}, },
{
"moduleName": "me.friwi:gluegen-rt",
"moduleUrl": "http://jogamp.org/gluegen/www/",
"moduleVersion": "v2.4.0-rc-20210111",
"moduleLicense": "BSD-4 License",
"moduleLicenseUrl": "http://www.spdx.org/licenses/BSD-4-Clause"
},
{
"moduleName": "me.friwi:jcef-api",
"moduleUrl": "https://bitbucket.org/chromiumembedded/java-cef/",
"moduleVersion": "jcef-99c2f7a+cef-127.3.1+g6cbb30e+chromium-127.0.6533.100",
"moduleLicense": "BSD License",
"moduleLicenseUrl": "https://bitbucket.org/chromiumembedded/java-cef/src/master/LICENSE.txt"
},
{
"moduleName": "me.friwi:jcefmaven",
"moduleUrl": "https://github.com/jcefmaven/jcefmaven/",
"moduleVersion": "127.3.1",
"moduleLicense": "Apache-2.0 License",
"moduleLicenseUrl": "https://github.com/jcefmaven/jcefmaven/blob/master/LICENSE"
},
{
"moduleName": "me.friwi:jogl-all",
"moduleUrl": "http://jogamp.org/jogl/www/",
"moduleVersion": "v2.4.0-rc-20210111",
"moduleLicense": "Ubuntu Font Licence 1.0",
"moduleLicenseUrl": "http://font.ubuntu.com/ufl/ubuntu-font-licence-1.0.txt"
},
{ {
"moduleName": "net.bytebuddy:byte-buddy", "moduleName": "net.bytebuddy:byte-buddy",
"moduleVersion": "1.15.10", "moduleVersion": "1.15.10",
@@ -666,13 +631,6 @@
"moduleLicense": "Apache License, Version 2.0", "moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt" "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
}, },
{
"moduleName": "org.apache.commons:commons-compress",
"moduleUrl": "https://commons.apache.org/proper/commons-compress/",
"moduleVersion": "1.21",
"moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
},
{ {
"moduleName": "org.apache.commons:commons-csv", "moduleName": "org.apache.commons:commons-csv",
"moduleUrl": "https://commons.apache.org/proper/commons-csv/", "moduleUrl": "https://commons.apache.org/proper/commons-csv/",
@@ -1121,30 +1079,6 @@
"moduleLicense": "Eclipse Public License, Version 2.0", "moduleLicense": "Eclipse Public License, Version 2.0",
"moduleLicenseUrl": "https://github.com/locationtech/jts/blob/master/LICENSE_EPLv2.txt" "moduleLicenseUrl": "https://github.com/locationtech/jts/blob/master/LICENSE_EPLv2.txt"
}, },
{
"moduleName": "org.openjfx:javafx-base",
"moduleVersion": "21",
"moduleLicense": "GPLv2+CE",
"moduleLicenseUrl": "https://openjdk.java.net/legal/gplv2+ce.html"
},
{
"moduleName": "org.openjfx:javafx-controls",
"moduleVersion": "21",
"moduleLicense": "GPLv2+CE",
"moduleLicenseUrl": "https://openjdk.java.net/legal/gplv2+ce.html"
},
{
"moduleName": "org.openjfx:javafx-graphics",
"moduleVersion": "21",
"moduleLicense": "GPLv2+CE",
"moduleLicenseUrl": "https://openjdk.java.net/legal/gplv2+ce.html"
},
{
"moduleName": "org.openjfx:javafx-swing",
"moduleVersion": "21",
"moduleLicense": "GPLv2+CE",
"moduleLicenseUrl": "https://openjdk.java.net/legal/gplv2+ce.html"
},
{ {
"moduleName": "org.opensaml:opensaml-core", "moduleName": "org.opensaml:opensaml-core",
"moduleVersion": "4.3.2", "moduleVersion": "4.3.2",
@@ -1452,13 +1386,6 @@
"moduleLicense": "Apache License, Version 2.0", "moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
}, },
{
"moduleName": "org.springframework.session:spring-session-core",
"moduleUrl": "https://spring.io/projects/spring-session",
"moduleVersion": "3.4.0",
"moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
},
{ {
"moduleName": "org.springframework:spring-aop", "moduleName": "org.springframework:spring-aop",
"moduleUrl": "https://github.com/spring-projects/spring-framework", "moduleUrl": "https://github.com/spring-projects/spring-framework",
@@ -1545,7 +1472,7 @@
}, },
{ {
"moduleName": "org.thymeleaf.extras:thymeleaf-extras-springsecurity5", "moduleName": "org.thymeleaf.extras:thymeleaf-extras-springsecurity5",
"moduleVersion": "3.1.3.RELEASE", "moduleVersion": "3.1.2.RELEASE",
"moduleLicense": "The Apache Software License, Version 2.0", "moduleLicense": "The Apache Software License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt" "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
}, },
@@ -1557,7 +1484,7 @@
}, },
{ {
"moduleName": "org.thymeleaf:thymeleaf-spring5", "moduleName": "org.thymeleaf:thymeleaf-spring5",
"moduleVersion": "3.1.3.RELEASE", "moduleVersion": "3.1.2.RELEASE",
"moduleLicense": "The Apache Software License, Version 2.0", "moduleLicense": "The Apache Software License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt" "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
}, },

View File

@@ -1,221 +1,10 @@
.custom-file-chooser {
display: flex;
flex-direction: column;
position: relative;
min-height: 55px;
border-radius: 1rem;
--selected-files-display: none;
}
.input-container {
position: relative;
border-radius: 1rem;
border: 1px dashed rgb(105, 116, 134);
column-gap: 7px;
row-gap: 7px;
height: 150px;
width: 100%;
--overlay-display: none;
transition: background-color 0.5s linear;
}
.input-container:hover {
outline: none;
border: none;
background-color: var(--md-sys-color-surface-container-low);
-webkit-transition: box-shadow 1s ease, background-color 2s linear;
-moz-transition: box-shadow 1s ease, background-color 2s linear;
-o-transition: box-shadow 1s ease, background-color 2s linear;
-ms-transition: box-shadow 1s ease, background-color 2s linear;
transition: box-shadow 1s ease, background-color 2s linear;
box-shadow: 0 0 10px rgb(105, 116, 134);
cursor: pointer;
}
.input-container * {
user-select: none;
pointer-events: none;
}
.input-container::before {
display: var(--overlay-display);
position: absolute;
content: '';
top: 0;
left: 0;
height: 100%;
width: 100%;
background-color: var(--md-sys-color-surface);
z-index: 1;
white-space: pre;
border-radius: 1rem;
}
.input-container::after {
display: var(--overlay-display);
position: absolute;
content: attr(data-text);
font-size: 0.9rem;
font-weight: 550;
color: var(--md-sys-color-on-surface);
background-color: transparent;
min-width: 150px;
top: 50%;
left: 50%;
transform: translateX(-50%) translateY(-50%);
text-align: center;
z-index: 2;
}
.input-container input[type="file"] {
display: none;
}
.input-container div:nth-of-type(2) {
color: var(--md-sys-color-on-surface);
}
.input-container div:nth-of-type(1), .input-container div:nth-of-type(3) {
color: var(--md-sys-color-on-surface);
font-size: 16px;
font-weight: bold;
}
.file-input-btn {
display: inline-block;
border: 1px solid #ccc;
padding: 6px 12px;
cursor: pointer;
color: #212529;
font-size: 1rem;
border-radius: 3rem;
background-color: #DDE0E3;
}
.small-file-container {
padding-top: 1px;
position: relative;
row-gap: 1px;
height: 60px;
width: 60px;
}
.file-icon {
display: flex;
align-items: center;
justify-content: center;
height: 30px;
width: 30px;
}
.file-icon * {
height: inherit;
width: inherit;
}
.file-info {
min-width: 0;
}
.file-info > div:nth-child(1) {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
color: var(--md-sys-color-on-surface);
max-width: 60px;
font-size: 0.75rem;
}
.file-info > div:nth-child(2) {
overflow: hidden;
text-overflow: ellipsis;
color: grey;
max-width: 60px;
font-size: 10px;
}
.remove-selected-file {
display: flex;
justify-content: center;
align-items: center;
position: absolute;
height: 15px;
width: 15px;
right: 10px;
top: -5px;
}
.remove-selected-file * {
overflow: hidden;
height: inherit;
width: inherit;
z-index: 3;
pointer-events: none;
user-select: none;
}
.remove-selected-file:after {
content: '';
position: absolute;
left: 1;
width: 10px;
height: 10px;
border-radius: 50%;
background-color: white;
z-index: 2;
user-select: none;
pointer-events: none;
}
.remove-selected-file:hover {
cursor: pointer;
}
.custom-file-label { .custom-file-label {
padding-right: 90px; padding-right: 90px;
} }
.selected-files { .selected-files {
display: var(--selected-files-display); margin-top: 10px;
padding-left: 5px; max-height: 150px;
padding-right: 3px; overflow-y: auto;
padding-top: 15px;
padding-bottom: 15px;
flex: 1;
white-space: pre-wrap; white-space: pre-wrap;
row-gap: 12px;
column-gap: 5px;
border-radius: 1rem;
border: 1px solid rgb(105, 116, 134, 0.5);
} }

View File

@@ -65,7 +65,6 @@
overflow: hidden; overflow: hidden;
margin: -20px; margin: -20px;
padding: 20px; padding: 20px;
box-sizing:content-box;
} }
.feature-group-container.animated-group { .feature-group-container.animated-group {

View File

@@ -1,219 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-ir" viewBox="0 0 640 480">
<defs>
<clipPath id="ir-a">
<path fill-opacity=".7" d="M-85.3 0h682.7v512H-85.3z"/>
</clipPath>
</defs>
<g fill-rule="evenodd" clip-path="url(#ir-a)" transform="translate(80)scale(.9375)">
<path fill="#fff" d="M-192 0h896v512h-896z"/>
<path fill="#da0000" d="M-192 343.8h896V512h-896z"/>
<g fill="#fff" stroke-width="1pt">
<path d="M-21.6 351h49v3.3h-49zm7.3 16.8h3.4v3.3h-3.4zm41.9 0v3.3h-9.8v-3.4zm5.2-16.8h3.4v20h-3.4z"/>
<path d="M52.4 367.7v3.4H33.8v-3.4zm-34.6-7.9H21v11.3h-3.3z"/>
<path d="M49.6 351H53v20h-3.4zm-8.4 0h3.3v20h-3.3zm-44.8 8v3.4h-18V359zm39.3 0v3.4h-18V359z"/>
<path d="M17.8 359.9H21V371h-3.3z"/>
<path d="M17.8 359.9H21V371h-3.3z"/>
<path d="M17.8 359.9H21V371h-3.3zm-39.3 0h3.3V371h-3.3zm28.8 0h3.4V371H7.3zm-14.3 0h3.4V371H-7z"/>
<path d="M9.6 367.7v3.4H-5.5v-3.4zm1-8.7v3.4H1V359z"/>
</g>
<g fill="#fff" stroke-width="1pt">
<path d="M-102.2 351h49v3.3h-49zm7.3 16.8h3.4v3.3H-95zm41.9 0v3.3h-9.8v-3.4zm5.2-16.8h3.4v20h-3.4z"/>
<path d="M-28.2 367.7v3.4h-18.6v-3.4zm-34.6-7.9h3.3v11.3h-3.3z"/>
<path d="M-31 351h3.4v20H-31zm-8.4 0h3.3v20h-3.3zm-44.8 8v3.4h-18V359zm39.3 0v3.4h-18V359z"/>
<path d="M-62.8 359.9h3.3V371h-3.3z"/>
<path d="M-62.8 359.9h3.3V371h-3.3z"/>
<path d="M-62.8 359.9h3.3V371h-3.3zm-39.3 0h3.3V371h-3.3zm28.8 0h3.3V371h-3.3zm-14.3 0h3.4V371h-3.4z"/>
<path d="M-71 367.7v3.4h-15v-3.4zm1-8.7v3.4h-9.6V359z"/>
</g>
<g fill="#fff" stroke-width="1pt">
<path d="M58.3 351h49v3.3h-49zm7.3 16.8H69v3.3h-3.4zm41.9 0v3.3h-9.8v-3.4zm5.3-16.8h3.4v20h-3.4z"/>
<path d="M132.3 367.7v3.4h-18.6v-3.4zm-34.6-7.9h3.4v11.3h-3.4z"/>
<path d="M129.5 351h3.4v20h-3.4zm-8.4 0h3.4v20H121zm-44.8 8v3.4h-18V359zm39.3 0v3.4h-18V359z"/>
<path d="M97.7 359.9h3.4V371h-3.4z"/>
<path d="M97.7 359.9h3.4V371h-3.4z"/>
<path d="M97.7 359.9h3.4V371h-3.4zm-39.3 0h3.4V371h-3.4zm28.8 0h3.4V371h-3.4zm-14.3 0h3.4V371h-3.4z"/>
<path d="M89.6 367.7v3.4H74.4v-3.4zm1-8.7v3.4H81V359z"/>
</g>
<g fill="#fff" stroke-width="1pt">
<path d="M622.7 351h49v3.3h-49zm7.3 16.8h3.4v3.3H630zm41.9 0v3.3H662v-3.4zm5.3-16.8h3.3v20h-3.4z"/>
<path d="M696.7 367.7v3.4H678v-3.4zm-34.6-7.9h3.4v11.3H662z"/>
<path d="M694 351h3.3v20h-3.4zm-8.5 0h3.4v20h-3.4zm-44.8 8v3.4h-18V359zm39.3 0v3.4h-18V359z"/>
<path d="M662 359.9h3.5V371H662z"/>
<path d="M662 359.9h3.5V371H662z"/>
<path d="M662 359.9h3.5V371H662zm-39.2 0h3.4V371h-3.4zm28.8 0h3.4V371h-3.4zm-14.3 0h3.4V371h-3.4z"/>
<path d="M654 367.7v3.4h-15.2v-3.4zm1-8.7v3.4h-9.6V359z"/>
</g>
<g fill="#fff" stroke-width="1pt">
<path d="M138.7 351h49.1v3.3h-49zm7.4 16.8h3.3v3.3h-3.3zm41.8 0v3.3h-9.8v-3.4zm5.3-16.8h3.4v20h-3.4z"/>
<path d="M212.8 367.7v3.4h-18.6v-3.4zm-34.7-7.9h3.4v11.3h-3.4z"/>
<path d="M210 351h3.4v20H210zm-8.5 0h3.4v20h-3.4zm-44.8 8v3.4h-17.9V359zm39.3 0v3.4h-17.9V359z"/>
<path d="M178.1 359.9h3.4V371h-3.4z"/>
<path d="M178.1 359.9h3.4V371h-3.4z"/>
<path d="M178.1 359.9h3.4V371h-3.4zm-39.3 0h3.4V371h-3.4zm28.8 0h3.4V371h-3.4zm-14.2 0h3.3V371h-3.3z"/>
<path d="M170 367.7v3.4h-15.1v-3.4zm1-8.7v3.4h-9.6V359z"/>
</g>
<g fill="#fff" stroke-width="1pt">
<path d="M219.5 351h49v3.3h-49zm7.3 16.8h3.4v3.3h-3.4zm41.9 0v3.3h-9.8v-3.4zM274 351h3.3v20H274z"/>
<path d="M293.5 367.7v3.4h-18.6v-3.4zm-34.6-7.9h3.4v11.3h-3.4z"/>
<path d="M290.7 351h3.4v20h-3.4zm-8.4 0h3.4v20h-3.4zm-44.8 8v3.4h-18V359zm39.3 0v3.4h-18V359z"/>
<path d="M258.9 359.9h3.4V371h-3.4z"/>
<path d="M258.9 359.9h3.4V371h-3.4z"/>
<path d="M258.9 359.9h3.4V371h-3.4zm-39.3 0h3.3V371h-3.3zm28.8 0h3.4V371h-3.4zm-14.3 0h3.4V371H234z"/>
<path d="M250.8 367.7v3.4h-15.2v-3.4zm1-8.7v3.4H242V359z"/>
</g>
<path fill="#239f40" d="M-192 0h896v168.2h-896z"/>
<g fill="#fff" stroke-width="1pt">
<path d="M300.7 351h49v3.3h-49zm7.3 16.8h3.4v3.3H308zm41.9 0v3.3H340v-3.4zm5.3-16.8h3.3v20h-3.3z"/>
<path d="M374.7 367.7v3.4h-18.6v-3.4zm-34.6-7.9h3.4v11.3H340z"/>
<path d="M372 351h3.3v20H372zm-8.5 0h3.4v20h-3.4zm-44.8 8v3.4h-18V359zm39.3 0v3.4h-18V359z"/>
<path d="M340 359.9h3.5V371H340z"/>
<path d="M340 359.9h3.5V371H340z"/>
<path d="M340 359.9h3.5V371H340zm-39.2 0h3.4V371h-3.4zm28.8 0h3.4V371h-3.4zm-14.3 0h3.4V371h-3.4z"/>
<path d="M332 367.7v3.4h-15.2v-3.4zm1-8.7v3.4h-9.6V359z"/>
</g>
<g fill="#fff" stroke-width="1pt">
<path d="M381.4 351h49v3.3h-49zm7.3 16.8h3.4v3.3h-3.4zm42 0v3.3h-9.9v-3.4zm5.2-16.8h3.4v20h-3.4z"/>
<path d="M455.4 367.7v3.4h-18.6v-3.4zm-34.6-7.9h3.4v11.3h-3.4z"/>
<path d="M452.7 351h3.3v20h-3.3zm-8.5 0h3.4v20h-3.4zm-44.8 8v3.4h-17.9V359zm39.3 0v3.4h-17.9V359z"/>
<path d="M420.8 359.9h3.4V371h-3.4z"/>
<path d="M420.8 359.9h3.4V371h-3.4z"/>
<path d="M420.8 359.9h3.4V371h-3.4zm-39.3 0h3.4V371h-3.4zm28.8 0h3.4V371h-3.4zm-14.3 0h3.4V371h-3.3z"/>
<path d="M412.7 367.7v3.4h-15.1v-3.4zm1-8.7v3.4H404V359z"/>
</g>
<g fill="#fff" stroke-width="1pt">
<path d="M462.2 351h49v3.3h-49zm7.3 16.8h3.4v3.3h-3.4zm41.9 0v3.3h-9.8v-3.4zm5.2-16.8h3.4v20h-3.4z"/>
<path d="M536.2 367.7v3.4h-18.6v-3.4zm-34.7-7.9h3.4v11.3h-3.4z"/>
<path d="M533.4 351h3.4v20h-3.4zm-8.4 0h3.3v20H525zm-44.8 8v3.4h-18V359zm39.3 0v3.4h-18V359z"/>
<path d="M501.6 359.9h3.3V371h-3.3z"/>
<path d="M501.6 359.9h3.3V371h-3.3z"/>
<path d="M501.6 359.9h3.3V371h-3.3zm-39.4 0h3.4V371h-3.4zm28.9 0h3.3V371h-3.3zm-14.3 0h3.4V371h-3.4z"/>
<path d="M493.4 367.7v3.4h-15.1v-3.4zm1-8.7v3.4h-9.6V359z"/>
</g>
<g fill="#fff" stroke-width="1pt">
<path d="M543.4 351h49v3.3h-49zm7.3 16.8h3.4v3.3h-3.4zm41.9 0v3.3h-9.8v-3.4zm5.2-16.8h3.4v20h-3.4z"/>
<path d="M617.4 367.7v3.4h-18.6v-3.4zm-34.6-7.9h3.3v11.3h-3.3z"/>
<path d="M614.6 351h3.4v20h-3.4zm-8.4 0h3.3v20h-3.3zm-44.8 8v3.4h-18V359zm39.3 0v3.4h-18V359z"/>
<path d="M582.8 359.9h3.3V371h-3.3z"/>
<path d="M582.8 359.9h3.3V371h-3.3z"/>
<path d="M582.8 359.9h3.3V371h-3.3zm-39.3 0h3.3V371h-3.3zm28.8 0h3.4V371h-3.4zm-14.3 0h3.4V371H558z"/>
<path d="M574.6 367.7v3.4h-15.1v-3.4zm1-8.7v3.4H566V359z"/>
</g>
<g fill="#fff" stroke-width="1pt">
<path d="M-183.8 351h49v3.3h-49zm7.3 16.8h3.4v3.3h-3.4zm42 0v3.3h-9.9v-3.4zm5.2-16.8h3.4v20h-3.4z"/>
<path d="M-109.8 367.7v3.4h-18.6v-3.4zm-34.6-7.9h3.4v11.3h-3.4z"/>
<path d="M-112.5 351h3.3v20h-3.3zm-8.5 0h3.4v20h-3.4zm-44.8 8v3.4h-17.9V359zm39.3 0v3.4h-17.9V359z"/>
<path d="M-144.4 359.9h3.4V371h-3.4z"/>
<path d="M-144.4 359.9h3.4V371h-3.4z"/>
<path d="M-144.4 359.9h3.4V371h-3.4zm-39.3 0h3.4V371h-3.4zm28.8 0h3.4V371h-3.4zm-14.3 0h3.4V371h-3.4z"/>
<path d="M-152.5 367.7v3.4h-15.2v-3.4zm1-8.7v3.4h-9.6V359z"/>
</g>
<g fill="#fff" stroke-width="1pt">
<path d="M-21.6 143.4h49v3.4h-49zm7.3 17h3.4v3.2h-3.4zm41.9-.2v3.4h-9.8v-3.4zm5.2-16.8h3.4v20.2h-3.4z"/>
<path d="M52.4 160.2v3.4H33.8v-3.4zm-34.6-7.9H21v11.3h-3.3z"/>
<path d="M49.6 143.4H53v20.2h-3.4zm-8.4 0h3.3v20.2h-3.3zm-44.8 8v3.4h-18v-3.3zm39.3 0v3.4h-18v-3.3z"/>
<path d="M17.8 152.3H21v11.3h-3.3z"/>
<path d="M17.8 152.3H21v11.3h-3.3z"/>
<path d="M17.8 152.3H21v11.3h-3.3zm-39.3 0h3.3v11.3h-3.3zm28.8 0h3.4v11.3H7.3zm-14.3 0h3.4v11.3H-7z"/>
<path d="M9.6 160.2v3.4H-5.5v-3.4zm1-8.7v3.3H1v-3.3z"/>
</g>
<g fill="#fff" stroke-width="1pt">
<path d="M-102.2 143.4h49v3.4h-49zm7.3 17h3.4v3.2H-95zm41.9-.2v3.4h-9.8v-3.4zm5.2-16.8h3.4v20.2h-3.4z"/>
<path d="M-28.2 160.2v3.4h-18.6v-3.4zm-34.6-7.9h3.3v11.3h-3.3z"/>
<path d="M-31 143.4h3.4v20.2H-31zm-8.4 0h3.3v20.2h-3.3zm-44.8 8v3.4h-18v-3.3zm39.3 0v3.4h-18v-3.3z"/>
<path d="M-62.8 152.3h3.3v11.3h-3.3z"/>
<path d="M-62.8 152.3h3.3v11.3h-3.3z"/>
<path d="M-62.8 152.3h3.3v11.3h-3.3zm-39.3 0h3.3v11.3h-3.3zm28.8 0h3.3v11.3h-3.3zm-14.3 0h3.4v11.3h-3.4z"/>
<path d="M-71 160.2v3.4h-15v-3.4zm1-8.7v3.3h-9.6v-3.3z"/>
</g>
<g fill="#fff" stroke-width="1pt">
<path d="M58.3 143.4h49v3.4h-49zm7.3 17H69v3.2h-3.4zm41.9-.2v3.4h-9.8v-3.4zm5.3-16.8h3.4v20.2h-3.4z"/>
<path d="M132.3 160.2v3.4h-18.6v-3.4zm-34.6-7.9h3.4v11.3h-3.4z"/>
<path d="M129.5 143.4h3.4v20.2h-3.4zm-8.4 0h3.4v20.2H121zm-44.8 8v3.4h-18v-3.3zm39.3 0v3.4h-18v-3.3z"/>
<path d="M97.7 152.3h3.4v11.3h-3.4z"/>
<path d="M97.7 152.3h3.4v11.3h-3.4z"/>
<path d="M97.7 152.3h3.4v11.3h-3.4zm-39.3 0h3.4v11.3h-3.4zm28.8 0h3.4v11.3h-3.4zm-14.3 0h3.4v11.3h-3.4z"/>
<path d="M89.6 160.2v3.4H74.4v-3.4zm1-8.7v3.3H81v-3.3z"/>
</g>
<g fill="#fff" stroke-width="1pt">
<path d="M622.7 143.4h49v3.4h-49zm7.3 17h3.4v3.2H630zm41.9-.2v3.4H662v-3.4zm5.3-16.8h3.3v20.2h-3.4z"/>
<path d="M696.7 160.2v3.4H678v-3.4zm-34.6-7.9h3.4v11.3H662z"/>
<path d="M694 143.4h3.3v20.2h-3.4zm-8.5 0h3.4v20.2h-3.4zm-44.8 8v3.4h-18v-3.3zm39.3 0v3.4h-18v-3.3z"/>
<path d="M662 152.3h3.5v11.3H662z"/>
<path d="M662 152.3h3.5v11.3H662z"/>
<path d="M662 152.3h3.5v11.3H662zm-39.2 0h3.4v11.3h-3.4zm28.8 0h3.4v11.3h-3.4zm-14.3 0h3.4v11.3h-3.4z"/>
<path d="M654 160.2v3.4h-15.2v-3.4zm1-8.7v3.3h-9.6v-3.3z"/>
</g>
<g fill="#fff" stroke-width="1pt">
<path d="M138.7 143.4h49.1v3.4h-49zm7.4 17h3.3v3.2h-3.3zm41.8-.2v3.4h-9.8v-3.4zm5.3-16.8h3.4v20.2h-3.4z"/>
<path d="M212.8 160.2v3.4h-18.6v-3.4zm-34.7-7.9h3.4v11.3h-3.4z"/>
<path d="M210 143.4h3.4v20.2H210zm-8.5 0h3.4v20.2h-3.4zm-44.8 8v3.4h-17.9v-3.3zm39.3 0v3.4h-17.9v-3.3z"/>
<path d="M178.1 152.3h3.4v11.3h-3.4z"/>
<path d="M178.1 152.3h3.4v11.3h-3.4z"/>
<path d="M178.1 152.3h3.4v11.3h-3.4zm-39.3 0h3.4v11.3h-3.4zm28.8 0h3.4v11.3h-3.4zm-14.2 0h3.3v11.3h-3.3z"/>
<path d="M170 160.2v3.4h-15.1v-3.4zm1-8.7v3.3h-9.6v-3.3z"/>
</g>
<g fill="#fff" stroke-width="1pt">
<path d="M219.5 143.4h49v3.4h-49zm7.3 17h3.4v3.2h-3.4zm41.9-.2v3.4h-9.8v-3.4zm5.3-16.8h3.3v20.2H274z"/>
<path d="M293.5 160.2v3.4h-18.6v-3.4zm-34.6-7.9h3.4v11.3h-3.4z"/>
<path d="M290.7 143.4h3.4v20.2h-3.4zm-8.4 0h3.4v20.2h-3.4zm-44.8 8v3.4h-18v-3.3zm39.3 0v3.4h-18v-3.3z"/>
<path d="M258.9 152.3h3.4v11.3h-3.4z"/>
<path d="M258.9 152.3h3.4v11.3h-3.4z"/>
<path d="M258.9 152.3h3.4v11.3h-3.4zm-39.3 0h3.3v11.3h-3.3zm28.8 0h3.4v11.3h-3.4zm-14.3 0h3.4v11.3H234z"/>
<path d="M250.8 160.2v3.4h-15.2v-3.4zm1-8.7v3.3H242v-3.3z"/>
</g>
<g fill="#fff" stroke-width="1pt">
<path d="M300.7 143.4h49v3.4h-49zm7.3 17h3.4v3.2H308zm41.9-.2v3.4H340v-3.4zm5.3-16.8h3.3v20.2h-3.3z"/>
<path d="M374.7 160.2v3.4h-18.6v-3.4zm-34.6-7.9h3.4v11.3H340z"/>
<path d="M372 143.4h3.3v20.2H372zm-8.5 0h3.4v20.2h-3.4zm-44.8 8v3.4h-18v-3.3zm39.3 0v3.4h-18v-3.3z"/>
<path d="M340 152.3h3.5v11.3H340z"/>
<path d="M340 152.3h3.5v11.3H340z"/>
<path d="M340 152.3h3.5v11.3H340zm-39.2 0h3.4v11.3h-3.4zm28.8 0h3.4v11.3h-3.4zm-14.3 0h3.4v11.3h-3.4z"/>
<path d="M332 160.2v3.4h-15.2v-3.4zm1-8.7v3.3h-9.6v-3.3z"/>
</g>
<g fill="#fff" stroke-width="1pt">
<path d="M381.4 143.4h49v3.4h-49zm7.3 17h3.4v3.2h-3.4zm42-.2v3.4h-9.9v-3.4zm5.2-16.8h3.4v20.2h-3.4z"/>
<path d="M455.4 160.2v3.4h-18.6v-3.4zm-34.6-7.9h3.4v11.3h-3.4z"/>
<path d="M452.7 143.4h3.3v20.2h-3.3zm-8.5 0h3.4v20.2h-3.4zm-44.8 8v3.4h-17.9v-3.3zm39.3 0v3.4h-17.9v-3.3z"/>
<path d="M420.8 152.3h3.4v11.3h-3.4z"/>
<path d="M420.8 152.3h3.4v11.3h-3.4z"/>
<path d="M420.8 152.3h3.4v11.3h-3.4zm-39.3 0h3.4v11.3h-3.4zm28.8 0h3.4v11.3h-3.4zm-14.3 0h3.4v11.3h-3.3z"/>
<path d="M412.7 160.2v3.4h-15.1v-3.4zm1-8.7v3.3H404v-3.3z"/>
</g>
<g fill="#fff" stroke-width="1pt">
<path d="M462.2 143.4h49v3.4h-49zm7.3 17h3.4v3.2h-3.4zm41.9-.2v3.4h-9.8v-3.4zm5.2-16.8h3.4v20.2h-3.4z"/>
<path d="M536.2 160.2v3.4h-18.6v-3.4zm-34.7-7.9h3.4v11.3h-3.4z"/>
<path d="M533.4 143.4h3.4v20.2h-3.4zm-8.4 0h3.3v20.2H525zm-44.8 8v3.4h-18v-3.3zm39.3 0v3.4h-18v-3.3z"/>
<path d="M501.6 152.3h3.3v11.3h-3.3z"/>
<path d="M501.6 152.3h3.3v11.3h-3.3z"/>
<path d="M501.6 152.3h3.3v11.3h-3.3zm-39.4 0h3.4v11.3h-3.4zm28.9 0h3.3v11.3h-3.3zm-14.3 0h3.4v11.3h-3.4z"/>
<path d="M493.4 160.2v3.4h-15.1v-3.4zm1-8.7v3.3h-9.6v-3.3z"/>
</g>
<g fill="#fff" stroke-width="1pt">
<path d="M543.4 143.4h49v3.4h-49zm7.3 17h3.4v3.2h-3.4zm41.9-.2v3.4h-9.8v-3.4zm5.2-16.8h3.4v20.2h-3.4z"/>
<path d="M617.4 160.2v3.4h-18.6v-3.4zm-34.6-7.9h3.3v11.3h-3.3z"/>
<path d="M614.6 143.4h3.4v20.2h-3.4zm-8.4 0h3.3v20.2h-3.3zm-44.8 8v3.4h-18v-3.3zm39.3 0v3.4h-18v-3.3z"/>
<path d="M582.8 152.3h3.3v11.3h-3.3z"/>
<path d="M582.8 152.3h3.3v11.3h-3.3z"/>
<path d="M582.8 152.3h3.3v11.3h-3.3zm-39.3 0h3.3v11.3h-3.3zm28.8 0h3.4v11.3h-3.4zm-14.3 0h3.4v11.3H558z"/>
<path d="M574.6 160.2v3.4h-15.1v-3.4zm1-8.7v3.3H566v-3.3z"/>
</g>
<g fill="#fff" stroke-width="1pt">
<path d="M-183.8 143.4h49v3.4h-49zm7.3 17h3.4v3.2h-3.4zm42-.2v3.4h-9.9v-3.4zm5.2-16.8h3.4v20.2h-3.4z"/>
<path d="M-109.8 160.2v3.4h-18.6v-3.4zm-34.6-7.9h3.4v11.3h-3.4z"/>
<path d="M-112.5 143.4h3.3v20.2h-3.3zm-8.5 0h3.4v20.2h-3.4zm-44.8 8v3.4h-17.9v-3.3zm39.3 0v3.4h-17.9v-3.3z"/>
<path d="M-144.4 152.3h3.4v11.3h-3.4z"/>
<path d="M-144.4 152.3h3.4v11.3h-3.4z"/>
<path d="M-144.4 152.3h3.4v11.3h-3.4zm-39.3 0h3.4v11.3h-3.4zm28.8 0h3.4v11.3h-3.4zm-14.3 0h3.4v11.3h-3.4z"/>
<path d="M-152.5 160.2v3.4h-15.2v-3.4zm1-8.7v3.3h-9.6v-3.3z"/>
</g>
<path fill="#d90000" d="M-68.8 339.5h6V350h-6zm160.5 0h6V350h-6zm-283.7 0h6V350h-6zm81.5 0h6V350h-6zm80.9 0h6V350h-6zm40 0h6V350h-6zm40.9 0h6V350h-6zm80.4 0h6V350h-6zm203 0h6.1V350h-6zm-162.1 0h6V350h-6zm40 0h6V350h-6zm40.5 0h6V350h-6zm40.4 0h6V350h-6zm323.2 0h6V350h-6zm-242.7 0h6V350h-6zm40.8 0h6V350h-6zm41.3 0h6V350h-6zm38.8 0h6V350h-6zm41.3 0h6V350h-6zm40.4 0h6V350h-6zm119.7 0h6V350h-6zm-38.8 0h6V350h-6zm-808.9 0h6V350h-6z"/>
<path fill="#239e3f" d="M-68.8 162.6h6v10.5h-6zm160.5 0h6v10.5h-6zm-283.7 0h6v10.5h-6zm81.5 0h6v10.5h-6zm80.9 0h6v10.5h-6zm40 0h6v10.5h-6zm40.9 0h6v10.5h-6zm80.4 0h6v10.5h-6zm203 0h6.1v10.5h-6zm-162.1 0h6v10.5h-6zm40 0h6v10.5h-6zm40.5 0h6v10.5h-6zm40.4 0h6v10.5h-6zm323.2 0h6v10.5h-6zm-242.7 0h6v10.5h-6zm40.8 0h6v10.5h-6zm41.3 0h6v10.5h-6zm38.8 0h6v10.5h-6zm41.3 0h6v10.5h-6zm40.4 0h6v10.5h-6zm119.7 0h6v10.5h-6zm-38.8 0h6v10.5h-6zm-808.9 0h6v10.5h-6z"/>
<g fill="#da0000">
<path d="M279.8 197.5c8.4 10.4 34.5 67.6-15.7 105.2-23.7 17.8-9 18.6-8.3 21.6 38-20.1 50.3-47.5 50-72-.2-24.4-13.2-46-26-54.8"/>
<path d="M284.8 194.8a73.3 73.3 0 0 1 15.7 112.4c27.2-6 62-86.4-15.7-112.4m-57.6 0a73.3 73.3 0 0 0-15.6 112.4c-27.3-6-62-86.4 15.6-112.4"/>
<path d="M232.2 197.5c-8.4 10.4-34.5 67.6 15.7 105.2 23.6 17.8 9 18.6 8.3 21.6-38-20.1-50.3-47.5-50-72 .2-24.4 13.2-46 26-54.8"/>
<path d="M304.2 319.1c-14.9.2-33.6-2-47.5-9.3 2.3 4.5 4.2 7.3 6.5 11.7 13.2 1.3 31.5 2.8 41-2.4m-95 0c14.9.2 33.6-2 47.5-9.3-2.3 4.5-4.2 7.3-6.5 11.7-13.2 1.3-31.5 2.8-41-2.4m27.3-138.7c3 8 10.9 9.2 19.3 4.5 6.2 3.6 15.7 3.9 19-4.1 2.5 19.8-18.3 15-19 11.2-7.8 7.5-22.2 3.2-19.3-11.6"/>
<path d="m256.4 331.6 7.8-9 1.1-120.1-9.3-8.2-9.3 7.8 1.9 121z"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -1,131 +0,0 @@
export class DecryptFile {
async decryptFile(file, requiresPassword) {
try {
async function getCsrfToken() {
const cookieValue = document.cookie
.split('; ')
.find((row) => row.startsWith('XSRF-TOKEN='))
?.split('=')[1];
if (cookieValue) {
return cookieValue;
}
const csrfElement = document.querySelector('input[name="_csrf"]');
return csrfElement ? csrfElement.value : null;
}
const csrfToken = await getCsrfToken();
const formData = new FormData();
formData.append('fileInput', file);
if (requiresPassword) {
const password = prompt(`${window.decrypt.passwordPrompt}`);
if (password === null) {
// User cancelled
console.error(`Password prompt cancelled for PDF: ${file.name}`);
return null; // No file to return
}
if (!password) {
// No password provided
console.error(`No password provided for encrypted PDF: ${file.name}`);
this.showErrorBanner(
`${window.decrypt.noPassword.replace('{0}', file.name)}`,
'',
`${window.decrypt.unexpectedError}`
);
return null; // No file to return
}
formData.append('password', password);
}
// Send decryption request
const response = await fetch('/api/v1/security/remove-password', {
method: 'POST',
body: formData,
headers: csrfToken ? {'X-XSRF-TOKEN': csrfToken} : undefined,
});
if (response.ok) {
const decryptedBlob = await response.blob();
this.removeErrorBanner();
return new File([decryptedBlob], file.name, {type: 'application/pdf'});
} else {
const errorText = await response.text();
console.error(`${window.decrypt.invalidPassword} ${errorText}`);
this.showErrorBanner(
`${window.decrypt.invalidPassword}`,
errorText,
`${window.decrypt.invalidPasswordHeader.replace('{0}', file.name)}`
);
return null; // No file to return
}
} catch (error) {
// Handle network or unexpected errors
console.error(`Failed to decrypt PDF: ${file.name}`, error);
this.showErrorBanner(
`${window.decrypt.unexpectedError.replace('{0}', file.name)}`,
`${error.message || window.decrypt.unexpectedError}`,
error
);
return null; // No file to return
}
}
async checkFileEncrypted(file) {
try {
if (file.type !== 'application/pdf') {
return {isEncrypted: false, requiresPassword: false};
}
pdfjsLib.GlobalWorkerOptions.workerSrc = './pdfjs-legacy/pdf.worker.mjs';
const arrayBuffer = await file.arrayBuffer();
const arrayBufferForPdfLib = arrayBuffer.slice(0);
const loadingTask = pdfjsLib.getDocument({
data: arrayBuffer,
});
await loadingTask.promise;
try {
//Uses PDFLib.PDFDocument to check if unpassworded but encrypted
const pdfDoc = await PDFLib.PDFDocument.load(arrayBufferForPdfLib);
return {isEncrypted: false, requiresPassword: false};
} catch (error) {
if (error.message.includes('Input document to `PDFDocument.load` is encrypted')) {
return {isEncrypted: true, requiresPassword: false};
}
console.error('Error checking encryption:', error);
throw new Error('Failed to determine if the file is encrypted.');
}
} catch (error) {
if (error.name === 'PasswordException') {
if (error.code === pdfjsLib.PasswordResponses.NEED_PASSWORD) {
return {isEncrypted: true, requiresPassword: true};
} else if (error.code === pdfjsLib.PasswordResponses.INCORRECT_PASSWORD) {
return {isEncrypted: true, requiresPassword: false};
}
}
console.error('Error checking encryption:', error);
throw new Error('Failed to determine if the file is encrypted.');
}
}
showErrorBanner(message, stackTrace, error) {
const errorContainer = document.getElementById('errorContainer');
errorContainer.style.display = 'block'; // Display the banner
errorContainer.querySelector('.alert-heading').textContent = error;
errorContainer.querySelector('p').textContent = message;
document.querySelector('#traceContent').textContent = stackTrace;
}
removeErrorBanner() {
const errorContainer = document.getElementById('errorContainer');
errorContainer.style.display = 'none'; // Hide the banner
errorContainer.querySelector('.alert-heading').textContent = '';
errorContainer.querySelector('p').textContent = '';
document.querySelector('#traceContent').textContent = '';
}
}

View File

@@ -1,27 +0,0 @@
document.getElementById('download-pdf').addEventListener('click', async () => {
const modifiedPdf = await DraggableUtils.getOverlayedPdfDocument();
let decryptedFile = modifiedPdf;
let isEncrypted = false;
let requiresPassword = false;
await this.decryptFile
.checkFileEncrypted(decryptedFile)
.then((result) => {
isEncrypted = result.isEncrypted;
requiresPassword = result.requiresPassword;
})
.catch((error) => {
console.error(error);
});
if (decryptedFile.type === 'application/pdf' && isEncrypted) {
decryptedFile = await this.decryptFile.decryptFile(decryptedFile, requiresPassword);
if (!decryptedFile) {
throw new Error('File decryption failed.');
}
}
const modifiedPdfBytes = await modifiedPdf.save();
const blob = new Blob([modifiedPdfBytes], {type: 'application/pdf'});
const link = document.createElement('a');
link.href = URL.createObjectURL(blob);
link.download = originalFileName + '_signed.pdf';
link.click();
});

View File

@@ -1,36 +1,26 @@
(function () { (function() {
if (window.isDownloadScriptInitialized) return; // Prevent re-execution
window.isDownloadScriptInitialized = true;
const { const { pdfPasswordPrompt, multipleInputsForSingleRequest, disableMultipleFiles, remoteCall, sessionExpired, refreshPage, error } = window.stirlingPDF;
pdfPasswordPrompt,
multipleInputsForSingleRequest,
disableMultipleFiles,
remoteCall,
sessionExpired,
refreshPage,
error,
} = window.stirlingPDF;
function showErrorBanner(message, stackTrace) { function showErrorBanner(message, stackTrace) {
const errorContainer = document.getElementById('errorContainer'); const errorContainer = document.getElementById("errorContainer");
errorContainer.style.display = 'block'; // Display the banner errorContainer.style.display = "block"; // Display the banner
errorContainer.querySelector('.alert-heading').textContent = error; errorContainer.querySelector(".alert-heading").textContent = error;
errorContainer.querySelector('p').textContent = message; errorContainer.querySelector("p").textContent = message;
document.querySelector('#traceContent').textContent = stackTrace; document.querySelector("#traceContent").textContent = stackTrace;
} }
function showSessionExpiredPrompt() { function showSessionExpiredPrompt() {
const errorContainer = document.getElementById('errorContainer'); const errorContainer = document.getElementById("errorContainer");
errorContainer.style.display = 'block'; errorContainer.style.display = "block";
errorContainer.querySelector('.alert-heading').textContent = sessionExpired; errorContainer.querySelector(".alert-heading").textContent = sessionExpired;
errorContainer.querySelector('p').textContent = sessionExpired; errorContainer.querySelector("p").textContent = sessionExpired;
document.querySelector('#traceContent').textContent = ''; document.querySelector("#traceContent").textContent = "";
// Optional: Add a refresh button // Optional: Add a refresh button
const refreshButton = document.createElement('button'); const refreshButton = document.createElement("button");
refreshButton.textContent = refreshPage; refreshButton.textContent = refreshPage;
refreshButton.className = 'btn btn-primary mt-3'; refreshButton.className = "btn btn-primary mt-3";
refreshButton.onclick = () => location.reload(); refreshButton.onclick = () => location.reload();
errorContainer.appendChild(refreshButton); errorContainer.appendChild(refreshButton);
} }
@@ -38,19 +28,19 @@
let firstErrorOccurred = false; let firstErrorOccurred = false;
$(document).ready(function () { $(document).ready(function () {
$('form').submit(async function (event) { $("form").submit(async function (event) {
event.preventDefault(); event.preventDefault();
firstErrorOccurred = false; firstErrorOccurred = false;
const url = this.action; const url = this.action;
let files = $('#fileInput-input')[0].files; const files = $("#fileInput-input")[0].files;
const formData = new FormData(this); const formData = new FormData(this);
const submitButton = document.getElementById('submitBtn'); const submitButton = document.getElementById("submitBtn");
const showGameBtn = document.getElementById('show-game-btn'); const showGameBtn = document.getElementById("show-game-btn");
const originalButtonText = submitButton.textContent; const originalButtonText = submitButton.textContent;
var boredWaiting = localStorage.getItem('boredWaiting') || 'disabled'; var boredWaiting = localStorage.getItem("boredWaiting") || "disabled";
if (showGameBtn) { if (showGameBtn) {
showGameBtn.style.display = 'none'; showGameBtn.style.display = "none";
} }
// Remove empty file entries // Remove empty file entries
@@ -59,70 +49,58 @@
formData.delete(key); formData.delete(key);
} }
} }
const override = $('#override').val() || ''; const override = $("#override").val() || "";
console.log(override); console.log(override);
// Set a timeout to show the game button if operation takes more than 5 seconds // Set a timeout to show the game button if operation takes more than 5 seconds
const timeoutId = setTimeout(() => { const timeoutId = setTimeout(() => {
if (boredWaiting === 'enabled' && showGameBtn) { if (boredWaiting === "enabled" && showGameBtn) {
showGameBtn.style.display = 'block'; showGameBtn.style.display = "block";
showGameBtn.parentNode.insertBefore(document.createElement('br'), showGameBtn.nextSibling); showGameBtn.parentNode.insertBefore(document.createElement('br'), showGameBtn.nextSibling);
} }
}, 5000); }, 5000);
try { try {
if (!url.includes('remove-password')) { submitButton.textContent = "Processing...";
// Check if any PDF files are encrypted and handle decryption if necessary
const decryptedFiles = await checkAndDecryptFiles(url, files);
files = decryptedFiles;
// Append decrypted files to formData
decryptedFiles.forEach((file, index) => {
formData.set(`fileInput`, file);
});
}
submitButton.textContent = 'Processing...';
submitButton.disabled = true; submitButton.disabled = true;
if (remoteCall === true) { if (remoteCall === true) {
if (override === 'multi' || (!multipleInputsForSingleRequest && files.length > 1 && override !== 'single')) { if (override === "multi" || (!multipleInputsForSingleRequest && files.length > 1 && override !== "single")) {
await submitMultiPdfForm(url, files); await submitMultiPdfForm(url, files);
} else { } else {
await handleSingleDownload(url, formData); await handleSingleDownload(url, formData);
} }
} }
//clearFileInput();
clearFileInput();
clearTimeout(timeoutId); clearTimeout(timeoutId);
if (showGameBtn) { if (showGameBtn) {
showGameBtn.style.display = 'none'; showGameBtn.style.display = "none";
showGameBtn.style.marginTop = ''; showGameBtn.style.marginTop = "";
} }
submitButton.textContent = originalButtonText; submitButton.textContent = originalButtonText;
submitButton.disabled = false; submitButton.disabled = false;
// After process finishes, check for boredWaiting and gameDialog open status // After process finishes, check for boredWaiting and gameDialog open status
const gameDialog = document.getElementById('game-container-wrapper'); const gameDialog = document.getElementById('game-container-wrapper');
if (boredWaiting === 'enabled' && gameDialog && gameDialog.open) { if (boredWaiting === "enabled" && gameDialog && gameDialog.open) {
// Display a green banner at the bottom of the screen saying "Download complete" // Display a green banner at the bottom of the screen saying "Download complete"
let downloadCompleteText = 'Download Complete'; let downloadCompleteText = "Download Complete";
if (window.downloadCompleteText) { if(window.downloadCompleteText){
downloadCompleteText = window.downloadCompleteText; downloadCompleteText = window.downloadCompleteText;
} }
$('body').append( $("body").append('<div id="download-complete-banner" style="position:fixed;bottom:0;left:0;width:100%;background-color:green;color:white;text-align:center;padding:10px;font-size:16px;z-index:1000;">'+ downloadCompleteText + '</div>');
'<div id="download-complete-banner" style="position:fixed;bottom:0;left:0;width:100%;background-color:green;color:white;text-align:center;padding:10px;font-size:16px;z-index:1000;">' + setTimeout(function() {
downloadCompleteText + $("#download-complete-banner").fadeOut("slow", function() {
'</div>'
);
setTimeout(function () {
$('#download-complete-banner').fadeOut('slow', function () {
$(this).remove(); // Remove the banner after fading out $(this).remove(); // Remove the banner after fading out
}); });
}, 5000); // Banner will fade out after 5 seconds }, 5000); // Banner will fade out after 5 seconds
} }
} catch (error) { } catch (error) {
clearTimeout(timeoutId); clearTimeout(timeoutId);
showGameBtn.style.display = 'none'; showGameBtn.style.display = "none";
submitButton.textContent = originalButtonText; submitButton.textContent = originalButtonText;
submitButton.disabled = false; submitButton.disabled = false;
handleDownloadError(error); handleDownloadError(error);
@@ -134,8 +112,8 @@
async function getPDFPageCount(file) { async function getPDFPageCount(file) {
try { try {
const arrayBuffer = await file.arrayBuffer(); const arrayBuffer = await file.arrayBuffer();
pdfjsLib.GlobalWorkerOptions.workerSrc = '/pdfjs-legacy/pdf.worker.mjs'; pdfjsLib.GlobalWorkerOptions.workerSrc = '/pdfjs-legacy/pdf.worker.mjs'
const pdf = await pdfjsLib.getDocument({data: arrayBuffer}).promise; const pdf = await pdfjsLib.getDocument({ data: arrayBuffer }).promise;
return pdf.numPages; return pdf.numPages;
} catch (error) { } catch (error) {
console.error('Error getting PDF page count:', error); console.error('Error getting PDF page count:', error);
@@ -143,98 +121,6 @@
} }
} }
async function checkAndDecryptFiles(url, files) {
const decryptedFiles = [];
pdfjsLib.GlobalWorkerOptions.workerSrc = './pdfjs-legacy/pdf.worker.mjs';
// Extract the base URL
const baseUrl = new URL(url);
let removePasswordUrl = `${baseUrl.origin}`;
// Check if there's a path before /api/
const apiIndex = baseUrl.pathname.indexOf('/api/');
if (apiIndex > 0) {
removePasswordUrl += baseUrl.pathname.substring(0, apiIndex);
}
// Append the new endpoint
removePasswordUrl += '/api/v1/security/remove-password';
console.log(`Remove password URL: ${removePasswordUrl}`);
for (const file of files) {
console.log(`Processing file: ${file.name}`);
if (file.type !== 'application/pdf') {
console.log(`Skipping non-PDF file: ${file.name}`);
decryptedFiles.push(file);
continue;
}
try {
const arrayBuffer = await file.arrayBuffer();
const loadingTask = pdfjsLib.getDocument({data: arrayBuffer});
console.log(`Attempting to load PDF: ${file.name}`);
const pdf = await loadingTask.promise;
console.log(`File is not encrypted: ${file.name}`);
decryptedFiles.push(file); // If no error, file is not encrypted
} catch (error) {
if (error.name === 'PasswordException' && error.code === 1) {
console.log(`PDF requires password: ${file.name}`, error);
console.log(`Attempting to remove password from PDF: ${file.name} with password.`);
const password = prompt(`${window.translations.decrypt.passwordPrompt}`);
if (!password) {
console.error(`No password provided for encrypted PDF: ${file.name}`);
showErrorBanner(
`${window.translations.decrypt.noPassword.replace('{0}', file.name)}`,
`${window.translations.decrypt.unexpectedError}`
);
throw error;
}
try {
// Prepare FormData for the decryption request
const formData = new FormData();
formData.append('fileInput', file);
formData.append('password', password);
// Use handleSingleDownload to send the request
const decryptionResult = await fetch(removePasswordUrl, {method: 'POST', body: formData});
if (decryptionResult && decryptionResult.blob) {
const decryptedBlob = await decryptionResult.blob();
const decryptedFile = new File([decryptedBlob], file.name, {type: 'application/pdf'});
/* // Create a link element to download the file
const link = document.createElement('a');
link.href = URL.createObjectURL(decryptedBlob);
link.download = 'test.pdf';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
*/
decryptedFiles.push(decryptedFile);
console.log(`Successfully decrypted PDF: ${file.name}`);
} else {
throw new Error('Decryption failed: No valid response from server');
}
} catch (decryptError) {
console.error(`Failed to decrypt PDF: ${file.name}`, decryptError);
showErrorBanner(
`${window.translations.invalidPasswordHeader.replace('{0}', file.name)}`,
`${window.translations.invalidPassword}`
);
throw decryptError;
}
} else {
console.log(`Error loading PDF: ${file.name}`, error);
throw error;
}
}
}
return decryptedFiles;
}
async function handleSingleDownload(url, formData, isMulti = false, isZip = false) { async function handleSingleDownload(url, formData, isMulti = false, isZip = false) {
const startTime = performance.now(); const startTime = performance.now();
const file = formData.get('fileInput'); const file = formData.get('fileInput');
@@ -242,8 +128,8 @@
let errorMessage = null; let errorMessage = null;
try { try {
const response = await window.fetchWithCsrf(url, {method: 'POST', body: formData}); const response = await fetch(url, { method: "POST", body: formData });
const contentType = response.headers.get('content-type'); const contentType = response.headers.get("content-type");
if (!response.ok) { if (!response.ok) {
errorMessage = response.status; errorMessage = response.status;
@@ -251,57 +137,58 @@
showSessionExpiredPrompt(); showSessionExpiredPrompt();
return; return;
} }
if (contentType && contentType.includes('application/json')) { if (contentType && contentType.includes("application/json")) {
console.error('Throwing error banner, response was not okay'); console.error("Throwing error banner, response was not okay");
return handleJsonResponse(response); return handleJsonResponse(response);
} }
throw new Error(`HTTP error! status: ${response.status}`); throw new Error(`HTTP error! status: ${response.status}`);
} }
const contentDisposition = response.headers.get('Content-Disposition'); const contentDisposition = response.headers.get("Content-Disposition");
let filename = getFilenameFromContentDisposition(contentDisposition); let filename = getFilenameFromContentDisposition(contentDisposition);
const blob = await response.blob(); const blob = await response.blob();
success = true; success = true;
if (contentType.includes('application/pdf') || contentType.includes('image/')) { if (contentType.includes("application/pdf") || contentType.includes("image/")) {
//clearFileInput(); clearFileInput();
return handleResponse(blob, filename, !isMulti, isZip); return handleResponse(blob, filename, !isMulti, isZip);
} else { } else {
//clearFileInput(); clearFileInput();
return handleResponse(blob, filename, false, isZip); return handleResponse(blob, filename, false, isZip);
} }
} catch (error) { } catch (error) {
success = false; success = false;
errorMessage = error.message; errorMessage = error.message;
console.error('Error in handleSingleDownload:', error); console.error("Error in handleSingleDownload:", error);
throw error; throw error;
} finally { } finally {
const processingTime = performance.now() - startTime; const processingTime = performance.now() - startTime;
// Capture analytics // Capture analytics
const pageCount = file && file.type === 'application/pdf' ? await getPDFPageCount(file) : null; const pageCount = file && file.type === 'application/pdf' ? await getPDFPageCount(file) : null;
if (analyticsEnabled) { if(analyticsEnabled) {
posthog.capture('file_processing', { posthog.capture('file_processing', {
success: success, success: success,
file_type: file ? file.type || 'unknown' : 'unknown', file_type: file ? file.type || 'unknown' : 'unknown',
file_size: file ? file.size : 0, file_size: file ? file.size : 0,
processing_time: processingTime, processing_time: processingTime,
error_message: errorMessage, error_message: errorMessage,
pdf_pages: pageCount, pdf_pages: pageCount
}); });
} }
} }
} }
function getFilenameFromContentDisposition(contentDisposition) { function getFilenameFromContentDisposition(contentDisposition) {
let filename; let filename;
if (contentDisposition && contentDisposition.indexOf('attachment') !== -1) { if (contentDisposition && contentDisposition.indexOf("attachment") !== -1) {
filename = decodeURIComponent(contentDisposition.split('filename=')[1].replace(/"/g, '')).trim(); filename = decodeURIComponent(contentDisposition.split("filename=")[1].replace(/"/g, "")).trim();
} else { } else {
// If the Content-Disposition header is not present or does not contain the filename, use a default filename // If the Content-Disposition header is not present or does not contain the filename, use a default filename
filename = 'download'; filename = "download";
} }
return filename; return filename;
@@ -311,37 +198,37 @@
const json = await response.json(); const json = await response.json();
const errorMessage = JSON.stringify(json, null, 2); const errorMessage = JSON.stringify(json, null, 2);
if ( if (
errorMessage.toLowerCase().includes('the password is incorrect') || errorMessage.toLowerCase().includes("the password is incorrect") ||
errorMessage.toLowerCase().includes('Password is not provided') || errorMessage.toLowerCase().includes("Password is not provided") ||
errorMessage.toLowerCase().includes('PDF contains an encryption dictionary') errorMessage.toLowerCase().includes("PDF contains an encryption dictionary")
) { ) {
if (!firstErrorOccurred) { if (!firstErrorOccurred) {
firstErrorOccurred = true; firstErrorOccurred = true;
alert(pdfPasswordPrompt); alert(pdfPasswordPrompt);
} }
} else { } else {
showErrorBanner(json.error + ':' + json.message, json.trace); showErrorBanner(json.error + ":" + json.message, json.trace);
} }
} }
async function handleResponse(blob, filename, considerViewOptions = false, isZip = false) { async function handleResponse(blob, filename, considerViewOptions = false, isZip = false) {
if (!blob) return; if (!blob) return;
const downloadOption = localStorage.getItem('downloadOption'); const downloadOption = localStorage.getItem("downloadOption");
if (considerViewOptions) { if (considerViewOptions) {
if (downloadOption === 'sameWindow') { if (downloadOption === "sameWindow") {
const url = URL.createObjectURL(blob); const url = URL.createObjectURL(blob);
window.location.href = url; window.location.href = url;
return; return;
} else if (downloadOption === 'newWindow') { } else if (downloadOption === "newWindow") {
const url = URL.createObjectURL(blob); const url = URL.createObjectURL(blob);
window.open(url, '_blank'); window.open(url, "_blank");
return; return;
} }
} }
if (!isZip) { if (!isZip) {
downloadFile(blob, filename); downloadFile(blob, filename);
} }
return {filename, blob}; return { filename, blob };
} }
function handleDownloadError(error) { function handleDownloadError(error) {
@@ -353,32 +240,32 @@
function downloadFile(blob, filename) { function downloadFile(blob, filename) {
if (!(blob instanceof Blob)) { if (!(blob instanceof Blob)) {
console.error('Invalid blob passed to downloadFile function'); console.error("Invalid blob passed to downloadFile function");
return; return;
} }
const url = URL.createObjectURL(blob); const url = URL.createObjectURL(blob);
const a = document.createElement('a'); const a = document.createElement("a");
a.href = url; a.href = url;
a.download = filename; a.download = filename;
a.click(); a.click();
urls.push(url); // Store the URL so it doesn't get garbage collected too soon urls.push(url); // Store the URL so it doesn't get garbage collected too soon
return {filename, blob}; return { filename, blob };
} }
async function submitMultiPdfForm(url, files) { async function submitMultiPdfForm(url, files) {
const zipThreshold = parseInt(localStorage.getItem('zipThreshold'), 10) || 4; const zipThreshold = parseInt(localStorage.getItem("zipThreshold"), 10) || 4;
const zipFiles = files.length > zipThreshold; const zipFiles = files.length > zipThreshold;
let jszip = null; let jszip = null;
// Add Space below Progress Bar before Showing // Add Space below Progress Bar before Showing
$('.progressBarContainer').after($('<br>')); $('.progressBarContainer').after($('<br>'));
$('.progressBarContainer').show(); $(".progressBarContainer").show();
// Initialize the progress bar // Initialize the progress bar
let progressBar = $('.progressBar'); let progressBar = $(".progressBar");
progressBar.css('width', '0%'); progressBar.css("width", "0%");
progressBar.attr('aria-valuenow', 0); progressBar.attr("aria-valuenow", 0);
progressBar.attr('aria-valuemax', files.length); progressBar.attr("aria-valuemax", files.length);
if (zipFiles) { if (zipFiles) {
jszip = new JSZip(); jszip = new JSZip();
@@ -392,10 +279,10 @@
if (postForm) { if (postForm) {
formData = new FormData($(postForm)[0]); // Convert the form to a jQuery object and get the raw DOM element formData = new FormData($(postForm)[0]); // Convert the form to a jQuery object and get the raw DOM element
} else { } else {
console.log('No form with POST method found.'); console.log("No form with POST method found.");
} }
//Remove file to reuse parameters for other runs //Remove file to reuse parameters for other runs
formData.delete('fileInput'); formData.delete("fileInput");
// Remove empty file entries // Remove empty file entries
for (let [key, value] of formData.entries()) { for (let [key, value] of formData.entries()) {
if (value instanceof File && !value.name) { if (value instanceof File && !value.name) {
@@ -411,12 +298,12 @@
for (const chunk of chunks) { for (const chunk of chunks) {
const promises = chunk.map(async (file) => { const promises = chunk.map(async (file) => {
let fileFormData = new FormData(); let fileFormData = new FormData();
fileFormData.append('fileInput', file); fileFormData.append("fileInput", file);
console.log(fileFormData); console.log(fileFormData);
// Add other form data // Add other form data
for (let pair of formData.entries()) { for (let pair of formData.entries()) {
fileFormData.append(pair[0], pair[1]); fileFormData.append(pair[0], pair[1]);
console.log(pair[0] + ', ' + pair[1]); console.log(pair[0] + ", " + pair[1]);
} }
try { try {
@@ -438,47 +325,47 @@
if (zipFiles) { if (zipFiles) {
try { try {
const content = await jszip.generateAsync({type: 'blob'}); const content = await jszip.generateAsync({ type: "blob" });
downloadFile(content, 'files.zip'); downloadFile(content, "files.zip");
} catch (error) { } catch (error) {
console.error('Error generating ZIP file: ' + error); console.error("Error generating ZIP file: " + error);
} }
} }
progressBar.css('width', '100%'); progressBar.css("width", "100%");
progressBar.attr('aria-valuenow', Array.from(files).length); progressBar.attr("aria-valuenow", Array.from(files).length);
} }
function updateProgressBar(progressBar, files) { function updateProgressBar(progressBar, files) {
let progress = (progressBar.attr('aria-valuenow') / files.length) * 100 + 100 / files.length; let progress = (progressBar.attr("aria-valuenow") / files.length) * 100 + 100 / files.length;
progressBar.css('width', progress + '%'); progressBar.css("width", progress + "%");
progressBar.attr('aria-valuenow', parseInt(progressBar.attr('aria-valuenow')) + 1); progressBar.attr("aria-valuenow", parseInt(progressBar.attr("aria-valuenow")) + 1);
} }
window.addEventListener('unload', () => { window.addEventListener("unload", () => {
for (const url of urls) { for (const url of urls) {
URL.revokeObjectURL(url); URL.revokeObjectURL(url);
} }
}); });
// Clear file input after job // Clear file input after job
function clearFileInput() { function clearFileInput(){
let pathname = document.location.pathname; let pathname = document.location.pathname;
if (pathname != '/merge-pdfs') { if(pathname != "/merge-pdfs"){
let formElement = document.querySelector('#fileInput-input'); let formElement = document.querySelector("#fileInput-input");
formElement.value = ''; formElement.value = '';
let editSectionElement = document.querySelector('#editSection'); let editSectionElement = document.querySelector("#editSection");
if (editSectionElement) { if(editSectionElement){
editSectionElement.style.display = 'none'; editSectionElement.style.display = "none";
} }
let cropPdfCanvas = document.querySelector('#cropPdfCanvas'); let cropPdfCanvas = document.querySelector("#cropPdfCanvas");
let overlayCanvas = document.querySelector('#overlayCanvas'); let overlayCanvas = document.querySelector("#overlayCanvas");
if (cropPdfCanvas && overlayCanvas) { if(cropPdfCanvas && overlayCanvas){
cropPdfCanvas.width = 0; cropPdfCanvas.width = 0;
cropPdfCanvas.height = 0; cropPdfCanvas.height = 0;
overlayCanvas.width = 0; overlayCanvas.width = 0;
overlayCanvas.height = 0; overlayCanvas.height = 0;
} }
} else { } else{
console.log("Disabled for 'Merge'"); console.log("Disabled for 'Merge'");
} }
} }

View File

@@ -1,6 +1,6 @@
const DraggableUtils = { const DraggableUtils = {
boxDragContainer: document.getElementById('box-drag-container'), boxDragContainer: document.getElementById("box-drag-container"),
pdfCanvas: document.getElementById('pdf-canvas'), pdfCanvas: document.getElementById("pdf-canvas"),
nextId: 0, nextId: 0,
pdfDoc: null, pdfDoc: null,
pageIndex: 0, pageIndex: 0,
@@ -9,17 +9,19 @@ const DraggableUtils = {
lastInteracted: null, lastInteracted: null,
init() { init() {
interact('.draggable-canvas') interact(".draggable-canvas")
.draggable({ .draggable({
listeners: { listeners: {
move: (event) => { move: (event) => {
const target = event.target; const target = event.target;
const x = (parseFloat(target.getAttribute('data-bs-x')) || 0) + event.dx; const x = (parseFloat(target.getAttribute("data-bs-x")) || 0)
const y = (parseFloat(target.getAttribute('data-bs-y')) || 0) + event.dy; + event.dx;
const y = (parseFloat(target.getAttribute("data-bs-y")) || 0)
+ event.dy;
target.style.transform = `translate(${x}px, ${y}px)`; target.style.transform = `translate(${x}px, ${y}px)`;
target.setAttribute('data-bs-x', x); target.setAttribute("data-bs-x", x);
target.setAttribute('data-bs-y', y); target.setAttribute("data-bs-y", y);
this.onInteraction(target); this.onInteraction(target);
//update the last interacted element //update the last interacted element
@@ -28,12 +30,12 @@ const DraggableUtils = {
}, },
}) })
.resizable({ .resizable({
edges: {left: true, right: true, bottom: true, top: true}, edges: { left: true, right: true, bottom: true, top: true },
listeners: { listeners: {
move: (event) => { move: (event) => {
var target = event.target; var target = event.target;
var x = parseFloat(target.getAttribute('data-bs-x')) || 0; var x = parseFloat(target.getAttribute("data-bs-x")) || 0;
var y = parseFloat(target.getAttribute('data-bs-y')) || 0; var y = parseFloat(target.getAttribute("data-bs-y")) || 0;
// check if control key is pressed // check if control key is pressed
if (event.ctrlKey) { if (event.ctrlKey) {
@@ -42,7 +44,8 @@ const DraggableUtils = {
let width = event.rect.width; let width = event.rect.width;
let height = event.rect.height; let height = event.rect.height;
if (Math.abs(event.deltaRect.width) >= Math.abs(event.deltaRect.height)) { if (Math.abs(event.deltaRect.width) >= Math.abs(
event.deltaRect.height)) {
height = width / aspectRatio; height = width / aspectRatio;
} else { } else {
width = height * aspectRatio; width = height * aspectRatio;
@@ -52,18 +55,19 @@ const DraggableUtils = {
event.rect.height = height; event.rect.height = height;
} }
target.style.width = event.rect.width + 'px'; target.style.width = event.rect.width + "px";
target.style.height = event.rect.height + 'px'; target.style.height = event.rect.height + "px";
// translate when resizing from top or left edges // translate when resizing from top or left edges
x += event.deltaRect.left; x += event.deltaRect.left;
y += event.deltaRect.top; y += event.deltaRect.top;
target.style.transform = 'translate(' + x + 'px,' + y + 'px)'; target.style.transform = "translate(" + x + "px," + y + "px)";
target.setAttribute('data-bs-x', x); target.setAttribute("data-bs-x", x);
target.setAttribute('data-bs-y', y); target.setAttribute("data-bs-y", y);
target.textContent = Math.round(event.rect.width) + '\u00D7' + Math.round(event.rect.height); target.textContent = Math.round(event.rect.width) + "\u00D7"
+ Math.round(event.rect.height);
this.onInteraction(target); this.onInteraction(target);
}, },
@@ -71,7 +75,7 @@ const DraggableUtils = {
modifiers: [ modifiers: [
interact.modifiers.restrictSize({ interact.modifiers.restrictSize({
min: {width: 5, height: 5}, min: { width: 5, height: 5 },
}), }),
], ],
inertia: true, inertia: true,
@@ -91,8 +95,8 @@ const DraggableUtils = {
const stepY = target.offsetHeight * 0.05; const stepY = target.offsetHeight * 0.05;
// Get the current x and y coordinates // Get the current x and y coordinates
let x = parseFloat(target.getAttribute('data-bs-x')) || 0; let x = (parseFloat(target.getAttribute('data-bs-x')) || 0);
let y = parseFloat(target.getAttribute('data-bs-y')) || 0; let y = (parseFloat(target.getAttribute('data-bs-y')) || 0);
// Check which key was pressed and update the coordinates accordingly // Check which key was pressed and update the coordinates accordingly
switch (event.key) { switch (event.key) {
@@ -131,15 +135,15 @@ const DraggableUtils = {
}, },
createDraggableCanvas() { createDraggableCanvas() {
const createdCanvas = document.createElement('canvas'); const createdCanvas = document.createElement("canvas");
createdCanvas.id = `draggable-canvas-${this.nextId++}`; createdCanvas.id = `draggable-canvas-${this.nextId++}`;
createdCanvas.classList.add('draggable-canvas'); createdCanvas.classList.add("draggable-canvas");
const x = 0; const x = 0;
const y = 20; const y = 20;
createdCanvas.style.transform = `translate(${x}px, ${y}px)`; createdCanvas.style.transform = `translate(${x}px, ${y}px)`;
createdCanvas.setAttribute('data-bs-x', x); createdCanvas.setAttribute("data-bs-x", x);
createdCanvas.setAttribute('data-bs-y', y); createdCanvas.setAttribute("data-bs-y", y);
//Click element in order to enable arrow keys //Click element in order to enable arrow keys
createdCanvas.addEventListener('click', () => { createdCanvas.addEventListener('click', () => {
@@ -182,29 +186,29 @@ const DraggableUtils = {
newHeight = newHeight * scaleMultiplier; newHeight = newHeight * scaleMultiplier;
} }
createdCanvas.style.width = newWidth + 'px'; createdCanvas.style.width = newWidth + "px";
createdCanvas.style.height = newHeight + 'px'; createdCanvas.style.height = newHeight + "px";
var myContext = createdCanvas.getContext('2d'); var myContext = createdCanvas.getContext("2d");
myContext.drawImage(myImage, 0, 0); myContext.drawImage(myImage, 0, 0);
resolve(createdCanvas); resolve(createdCanvas);
}; };
}); });
}, },
deleteAllDraggableCanvases() { deleteAllDraggableCanvases() {
this.boxDragContainer.querySelectorAll('.draggable-canvas').forEach((el) => el.remove()); this.boxDragContainer.querySelectorAll(".draggable-canvas").forEach((el) => el.remove());
}, },
async addAllPagesDraggableCanvas(element) { async addAllPagesDraggableCanvas(element) {
if (element) { if (element) {
let currentPage = this.pageIndex; let currentPage = this.pageIndex
if (!this.elementAllPages.includes(element)) { if (!this.elementAllPages.includes(element)) {
this.elementAllPages.push(element); this.elementAllPages.push(element)
element.style.filter = 'sepia(1) hue-rotate(90deg) brightness(1.2)'; element.style.filter = 'sepia(1) hue-rotate(90deg) brightness(1.2)';
let newElement = { let newElement = {
element: element, "element": element,
offsetWidth: element.width, "offsetWidth": element.width,
offsetHeight: element.height, "offsetHeight": element.height
}; }
let pagesMap = this.documentsMap.get(this.pdfDoc); let pagesMap = this.documentsMap.get(this.pdfDoc);
@@ -212,20 +216,21 @@ const DraggableUtils = {
pagesMap = {}; pagesMap = {};
this.documentsMap.set(this.pdfDoc, pagesMap); this.documentsMap.set(this.pdfDoc, pagesMap);
} }
let page = this.pageIndex; let page = this.pageIndex
for (let pageIndex = 0; pageIndex < this.pdfDoc.numPages; pageIndex++) { for (let pageIndex = 0; pageIndex < this.pdfDoc.numPages; pageIndex++) {
if (pagesMap[`${pageIndex}-offsetWidth`]) { if (pagesMap[`${pageIndex}-offsetWidth`]) {
if (!pagesMap[pageIndex].includes(newElement)) { if (!pagesMap[pageIndex].includes(newElement)) {
pagesMap[pageIndex].push(newElement); pagesMap[pageIndex].push(newElement);
} }
} else { } else {
pagesMap[pageIndex] = []; pagesMap[pageIndex] = []
pagesMap[pageIndex].push(newElement); pagesMap[pageIndex].push(newElement)
pagesMap[`${pageIndex}-offsetWidth`] = pagesMap[`${page}-offsetWidth`]; pagesMap[`${pageIndex}-offsetWidth`] = pagesMap[`${page}-offsetWidth`];
pagesMap[`${pageIndex}-offsetHeight`] = pagesMap[`${page}-offsetHeight`]; pagesMap[`${pageIndex}-offsetHeight`] = pagesMap[`${page}-offsetHeight`];
} }
await this.goToPage(pageIndex); await this.goToPage(pageIndex)
} }
} else { } else {
const index = this.elementAllPages.indexOf(element); const index = this.elementAllPages.indexOf(element);
@@ -242,17 +247,17 @@ const DraggableUtils = {
for (let pageIndex = 0; pageIndex < this.pdfDoc.numPages; pageIndex++) { for (let pageIndex = 0; pageIndex < this.pdfDoc.numPages; pageIndex++) {
if (pagesMap[`${pageIndex}-offsetWidth`] && pageIndex != currentPage) { if (pagesMap[`${pageIndex}-offsetWidth`] && pageIndex != currentPage) {
const pageElements = pagesMap[pageIndex]; const pageElements = pagesMap[pageIndex];
pageElements.forEach((elementPage) => { pageElements.forEach(elementPage => {
const elementIndex = pageElements.findIndex((elementPage) => elementPage['element'].id === element.id); const elementIndex = pageElements.findIndex(elementPage => elementPage['element'].id === element.id);
if (elementIndex !== -1) { if (elementIndex !== -1) {
pageElements.splice(elementIndex, 1); pageElements.splice(elementIndex, 1);
} }
}); });
} }
await this.goToPage(pageIndex); await this.goToPage(pageIndex)
} }
} }
await this.goToPage(currentPage); await this.goToPage(currentPage)
} }
}, },
deleteDraggableCanvas(element) { deleteDraggableCanvas(element) {
@@ -266,7 +271,7 @@ const DraggableUtils = {
} }
}, },
getLastInteracted() { getLastInteracted() {
return this.boxDragContainer.querySelector('.draggable-canvas:last-of-type'); return this.boxDragContainer.querySelector(".draggable-canvas:last-of-type");
}, },
storePageContents() { storePageContents() {
@@ -275,7 +280,7 @@ const DraggableUtils = {
pagesMap = {}; pagesMap = {};
} }
const elements = [...this.boxDragContainer.querySelectorAll('.draggable-canvas')]; const elements = [...this.boxDragContainer.querySelectorAll(".draggable-canvas")];
const draggablesData = elements.map((el) => { const draggablesData = elements.map((el) => {
return { return {
element: el, element: el,
@@ -286,8 +291,8 @@ const DraggableUtils = {
elements.forEach((el) => this.boxDragContainer.removeChild(el)); elements.forEach((el) => this.boxDragContainer.removeChild(el));
pagesMap[this.pageIndex] = draggablesData; pagesMap[this.pageIndex] = draggablesData;
pagesMap[this.pageIndex + '-offsetWidth'] = this.pdfCanvas.offsetWidth; pagesMap[this.pageIndex + "-offsetWidth"] = this.pdfCanvas.offsetWidth;
pagesMap[this.pageIndex + '-offsetHeight'] = this.pdfCanvas.offsetHeight; pagesMap[this.pageIndex + "-offsetHeight"] = this.pdfCanvas.offsetHeight;
this.documentsMap.set(this.pdfDoc, pagesMap); this.documentsMap.set(this.pdfDoc, pagesMap);
}, },
@@ -324,8 +329,8 @@ const DraggableUtils = {
// render the page onto the canvas // render the page onto the canvas
var renderContext = { var renderContext = {
canvasContext: this.pdfCanvas.getContext('2d'), canvasContext: this.pdfCanvas.getContext("2d"),
viewport: page.getViewport({scale: 1}), viewport: page.getViewport({ scale: 1 }),
}; };
await page.render(renderContext).promise; await page.render(renderContext).promise;
@@ -353,7 +358,7 @@ const DraggableUtils = {
} }
}, },
parseTransform(element) {}, parseTransform(element) { },
async getOverlayedPdfDocument() { async getOverlayedPdfDocument() {
const pdfBytes = await this.pdfDoc.getData(); const pdfBytes = await this.pdfDoc.getData();
const pdfDocModified = await PDFLib.PDFDocument.load(pdfBytes, { const pdfDocModified = await PDFLib.PDFDocument.load(pdfBytes, {
@@ -364,7 +369,7 @@ const DraggableUtils = {
const pagesMap = this.documentsMap.get(this.pdfDoc); const pagesMap = this.documentsMap.get(this.pdfDoc);
for (let pageIdx in pagesMap) { for (let pageIdx in pagesMap) {
if (pageIdx.includes('offset')) { if (pageIdx.includes("offset")) {
continue; continue;
} }
console.log(typeof pageIdx); console.log(typeof pageIdx);
@@ -372,8 +377,9 @@ const DraggableUtils = {
const page = pdfDocModified.getPage(parseInt(pageIdx)); const page = pdfDocModified.getPage(parseInt(pageIdx));
let draggablesData = pagesMap[pageIdx]; let draggablesData = pagesMap[pageIdx];
const offsetWidth = pagesMap[pageIdx + '-offsetWidth']; const offsetWidth = pagesMap[pageIdx + "-offsetWidth"];
const offsetHeight = pagesMap[pageIdx + '-offsetHeight']; const offsetHeight = pagesMap[pageIdx + "-offsetHeight"];
for (const draggableData of draggablesData) { for (const draggableData of draggablesData) {
// embed the draggable canvas // embed the draggable canvas
@@ -383,8 +389,8 @@ const DraggableUtils = {
const pdfImageObject = await pdfDocModified.embedPng(draggableImgBytes); const pdfImageObject = await pdfDocModified.embedPng(draggableImgBytes);
// calculate the position in the pdf document // calculate the position in the pdf document
const tansform = draggableElement.style.transform.replace(/[^.,-\d]/g, ''); const tansform = draggableElement.style.transform.replace(/[^.,-\d]/g, "");
const transformComponents = tansform.split(','); const transformComponents = tansform.split(",");
const draggablePositionPixels = { const draggablePositionPixels = {
x: parseFloat(transformComponents[0]), x: parseFloat(transformComponents[0]),
y: parseFloat(transformComponents[1]), y: parseFloat(transformComponents[1]),
@@ -423,8 +429,9 @@ const DraggableUtils = {
}; };
//Defining the image if the page has a 0-degree angle //Defining the image if the page has a 0-degree angle
let x = draggablePositionPdf.x; let x = draggablePositionPdf.x
let y = heightAdjusted - draggablePositionPdf.y - draggablePositionPdf.height; let y = heightAdjusted - draggablePositionPdf.y - draggablePositionPdf.height
//Defining the image position if it is at other angles //Defining the image position if it is at other angles
if (normalizedAngle === 90) { if (normalizedAngle === 90) {
@@ -444,7 +451,7 @@ const DraggableUtils = {
y: y, y: y,
width: draggablePositionPdf.width, width: draggablePositionPdf.width,
height: draggablePositionPdf.height, height: draggablePositionPdf.height,
rotate: rotation, rotate: rotation
}); });
} }
} }
@@ -453,6 +460,6 @@ const DraggableUtils = {
}, },
}; };
document.addEventListener('DOMContentLoaded', () => { document.addEventListener("DOMContentLoaded", () => {
DraggableUtils.init(); DraggableUtils.init();
}); });

View File

@@ -8,6 +8,7 @@ window.fetchWithCsrf = async function(url, options = {}) {
if (cookieValue) { if (cookieValue) {
return cookieValue; return cookieValue;
} }
const csrfElement = document.querySelector('input[name="_csrf"]'); const csrfElement = document.querySelector('input[name="_csrf"]');
return csrfElement ? csrfElement.value : null; return csrfElement ? csrfElement.value : null;
} }

View File

@@ -1,52 +0,0 @@
class FileIconFactory {
static createFileIcon(fileExtension) {
let ext = fileExtension.toLowerCase();
switch (ext) {
case "pdf":
return this.createPDFIcon();
case "csv":
return this.createCSVIcon();
case "jpe":
case "jpg":
case "jpeg":
case "gif":
case "png":
case "bmp":
case "ico":
case "svg":
case "svgz":
case "tif":
case "tiff":
case "ai":
case "drw":
case "pct":
case "psp":
case "xcf":
case "psd":
case "raw":
case "webp":
case "heic":
return this.createImageIcon();
default:
return this.createUnknownFileIcon();
}
}
static createPDFIcon() {
return `
<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" class="bi bi-filetype-pdf" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M14 4.5V14a2 2 0 0 1-2 2h-1v-1h1a1 1 0 0 0 1-1V4.5h-2A1.5 1.5 0 0 1 9.5 3V1H4a1 1 0 0 0-1 1v9H2V2a2 2 0 0 1 2-2h5.5zM1.6 11.85H0v3.999h.791v-1.342h.803q.43 0 .732-.173.305-.175.463-.474a1.4 1.4 0 0 0 .161-.677q0-.375-.158-.677a1.2 1.2 0 0 0-.46-.477q-.3-.18-.732-.179m.545 1.333a.8.8 0 0 1-.085.38.57.57 0 0 1-.238.241.8.8 0 0 1-.375.082H.788V12.48h.66q.327 0 .512.181.185.183.185.522m1.217-1.333v3.999h1.46q.602 0 .998-.237a1.45 1.45 0 0 0 .595-.689q.196-.45.196-1.084 0-.63-.196-1.075a1.43 1.43 0 0 0-.589-.68q-.396-.234-1.005-.234zm.791.645h.563q.371 0 .609.152a.9.9 0 0 1 .354.454q.118.302.118.753a2.3 2.3 0 0 1-.068.592 1.1 1.1 0 0 1-.196.422.8.8 0 0 1-.334.252 1.3 1.3 0 0 1-.483.082h-.563zm3.743 1.763v1.591h-.79V11.85h2.548v.653H7.896v1.117h1.606v.638z"/>
</svg>
`;
}
static createImageIcon() {
return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960" fill="currentColor"><path d="M216-144q-30 0-51-21.5T144-216v-528q0-29 21-50.5t51-21.5h528q30 0 51 21.5t21 50.5v528q0 29-21 50.5T744-144H216Zm48-144h432L552-480 444-336l-72-96-108 144Z"/></svg>`;
}
static createUnknownFileIcon() {
return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960" fill="currentColor"><path d="M263.72-96Q234-96 213-117.15T192-168v-624q0-29.7 21.15-50.85Q234.3-864 264-864h312l192 192v504q0 29.7-21.16 50.85Q725.68-96 695.96-96H263.72ZM528-624h168L528-792v168Z"/></svg>`;
}
}
export default FileIconFactory;

View File

@@ -1,31 +0,0 @@
class FileUtils {
static extractFileExtension(filename) {
if (!filename || filename.trim().length <= 0) return "";
let trimmedName = filename.trim();
return trimmedName.substring(trimmedName.lastIndexOf(".") + 1);
}
static transformFileSize(size) {
if (!size) return `0Bs`;
let oneKB = 1024;
let oneMB = oneKB * 1024;
let oneGB = oneMB * 1024;
let oneTB = oneGB * 1024;
if (size < oneKB) return `${this._toFixed(size)}Bs`;
else if (oneKB <= size && size < oneMB) return `${this._toFixed(size / oneKB)}KBs`;
else if (oneMB <= size && size < oneGB) return `${this._toFixed(size / oneMB)}MBs`;
else if (oneGB <= size && size < oneTB) return `${this._toFixed(size / oneGB)}GBs`;
else return `${this._toFixed(size / oneTB)}TBs`;
}
static _toFixed(val, digits = 1) {
// Return value without ending 0s after decimal point
// Example: if res == 145.0 then return 145, else if 145.x (where x != 0) return 145.x
let res = val.toFixed(digits);
let resRounded = (res|0);
return res == resRounded ? resRounded : res;
}
}
export default FileUtils;

View File

@@ -1,92 +1,73 @@
import FileIconFactory from './file-icon-factory.js'; document.addEventListener("DOMContentLoaded", function () {
import FileUtils from './file-utils.js'; document.querySelectorAll(".custom-file-chooser").forEach(setupFileInput);
import UUID from './uuid.js'; });
import {DecryptFile} from './DecryptFiles.js';
let isScriptExecuted = false;
if (!isScriptExecuted) {
isScriptExecuted = true;
document.addEventListener('DOMContentLoaded', function () {
document.querySelectorAll('.custom-file-chooser').forEach(setupFileInput);
});
}
function setupFileInput(chooser) { function setupFileInput(chooser) {
const elementId = chooser.getAttribute('data-bs-element-id'); const elementId = chooser.getAttribute("data-bs-element-id");
const filesSelected = chooser.getAttribute('data-bs-files-selected'); const filesSelected = chooser.getAttribute("data-bs-files-selected");
const pdfPrompt = chooser.getAttribute('data-bs-pdf-prompt'); const pdfPrompt = chooser.getAttribute("data-bs-pdf-prompt");
const inputContainerId = chooser.getAttribute('data-bs-element-container-id');
let inputContainer = document.getElementById(inputContainerId);
let allFiles = []; let allFiles = [];
let overlay; let overlay;
let dragCounter = 0; let dragCounter = 0;
inputContainer.addEventListener('click', (e) => {
let inputBtn = document.getElementById(elementId);
inputBtn.click();
});
const dragenterListener = function () { const dragenterListener = function () {
dragCounter++; dragCounter++;
if (!overlay) { if (!overlay) {
// Show overlay by removing display: none from pseudo elements (::before and ::after) overlay = document.createElement("div");
inputContainer.style.setProperty('--overlay-display', "''"); overlay.style.position = "fixed";
overlay = true; overlay.style.top = 0;
overlay.style.left = 0;
overlay.style.width = "100%";
overlay.style.height = "100%";
overlay.style.background = "rgba(0, 0, 0, 0.5)";
overlay.style.color = "#fff";
overlay.style.zIndex = "1000";
overlay.style.display = "flex";
overlay.style.alignItems = "center";
overlay.style.justifyContent = "center";
overlay.style.pointerEvents = "none";
overlay.innerHTML = "<p>Drop files anywhere to upload</p>";
document.getElementById("content-wrap").appendChild(overlay);
} }
}; };
const dragleaveListener = function () { const dragleaveListener = function () {
dragCounter--; dragCounter--;
if (dragCounter === 0) { if (dragCounter === 0) {
hideOverlay(); if (overlay) {
overlay.remove();
overlay = null;
}
} }
}; };
function hideOverlay() {
if (!overlay) return;
inputContainer.style.setProperty('--overlay-display', 'none');
overlay = false;
}
const dropListener = function (e) { const dropListener = function (e) {
e.preventDefault(); e.preventDefault();
// Drag and Drop shall only affect the target file chooser
if (e.target !== inputContainer) {
hideOverlay();
dragCounter = 0;
return;
}
const dt = e.dataTransfer; const dt = e.dataTransfer;
const files = dt.files; const files = dt.files;
const fileInput = document.getElementById(elementId); for (let i = 0; i < files.length; i++) {
if (fileInput?.hasAttribute('multiple')) { allFiles.push(files[i]);
pushFileListTo(files, allFiles);
} else if (fileInput) {
allFiles = [files[0]];
} }
const dataTransfer = new DataTransfer(); const dataTransfer = new DataTransfer();
allFiles.forEach((file) => dataTransfer.items.add(file)); allFiles.forEach((file) => dataTransfer.items.add(file));
const fileInput = document.getElementById(elementId);
fileInput.files = dataTransfer.files; fileInput.files = dataTransfer.files;
hideOverlay(); if (overlay) {
overlay.remove();
overlay = null;
}
dragCounter = 0; dragCounter = 0;
fileInput.dispatchEvent(new CustomEvent('change', {bubbles: true, detail: {source: 'drag-drop'}})); fileInput.dispatchEvent(new CustomEvent("change", { bubbles: true, detail: {source: 'drag-drop'} }));
}; };
function pushFileListTo(fileList, container) { ["dragenter", "dragover", "dragleave", "drop"].forEach((eventName) => {
for (let file of fileList) {
container.push(file);
}
}
['dragenter', 'dragover', 'dragleave', 'drop'].forEach((eventName) => {
document.body.addEventListener(eventName, preventDefaults, false); document.body.addEventListener(eventName, preventDefaults, false);
}); });
@@ -95,144 +76,52 @@ function setupFileInput(chooser) {
e.stopPropagation(); e.stopPropagation();
} }
document.body.addEventListener('dragenter', dragenterListener); document.body.addEventListener("dragenter", dragenterListener);
document.body.addEventListener('dragleave', dragleaveListener); document.body.addEventListener("dragleave", dragleaveListener);
document.body.addEventListener('drop', dropListener); document.body.addEventListener("drop", dropListener);
$('#' + elementId).on('change', async function (e) { $("#" + elementId).on("change", function (e) {
let element = e.target; let element = e.target;
const isDragAndDrop = e.detail?.source == 'drag-drop'; const isDragAndDrop = e.detail?.source == 'drag-drop';
if (element instanceof HTMLInputElement && element.hasAttribute('multiple')) { if (element instanceof HTMLInputElement && element.hasAttribute("multiple")) {
allFiles = isDragAndDrop ? allFiles : [...allFiles, ...element.files]; allFiles = isDragAndDrop ? allFiles : [... allFiles, ... element.files];
} else { } else {
allFiles = Array.from(isDragAndDrop ? allFiles : [element.files[0]]); allFiles = Array.from(isDragAndDrop ? allFiles : [element.files[0]]);
} }
allFiles = await Promise.all(
allFiles.map(async (file) => {
let decryptedFile = file;
try {
const decryptFile = new DecryptFile();
const {isEncrypted, requiresPassword} = await decryptFile.checkFileEncrypted(file);
if (file.type === 'application/pdf' && isEncrypted) {
decryptedFile = await decryptFile.decryptFile(file, requiresPassword);
if (!decryptedFile) throw new Error('File decryption failed.');
}
decryptedFile.uniqueId = UUID.uuidv4();
return decryptedFile;
} catch (error) {
console.error(`Error decrypting file: ${file.name}`, error);
if (!file.uniqueId) file.uniqueId = UUID.uuidv4();
return file;
}
})
);
if (!isDragAndDrop) { if (!isDragAndDrop) {
let dataTransfer = toDataTransfer(allFiles); let dataTransfer = new DataTransfer();
element.files = dataTransfer.files; allFiles.forEach(file => dataTransfer.items.add(file));
element.files = dataTransfer.files;
} }
handleFileInputChange(this); handleFileInputChange(this);
this.dispatchEvent(new CustomEvent('file-input-change', {bubbles: true, detail: {elementId, allFiles}})); this.dispatchEvent(new CustomEvent("file-input-change", { bubbles: true }));
}); });
function toDataTransfer(files) {
let dataTransfer = new DataTransfer();
files.forEach((file) => dataTransfer.items.add(file));
return dataTransfer;
}
function handleFileInputChange(inputElement) { function handleFileInputChange(inputElement) {
const files = allFiles; const files = allFiles;
showOrHideSelectedFilesContainer(files); const fileNames = files.map((f) => f.name);
const selectedFilesContainer = $(inputElement).siblings(".selected-files");
const filesInfo = files.map((f) => ({name: f.name, size: f.size, uniqueId: f.uniqueId}));
const selectedFilesContainer = $(inputContainer).siblings('.selected-files');
selectedFilesContainer.empty(); selectedFilesContainer.empty();
filesInfo.forEach((info) => { fileNames.forEach((fileName) => {
let fileContainerClasses = 'small-file-container d-flex flex-column justify-content-center align-items-center'; selectedFilesContainer.append("<div>" + fileName + "</div>");
let fileContainer = document.createElement('div');
$(fileContainer).addClass(fileContainerClasses);
$(fileContainer).attr('id', info.uniqueId);
let fileIconContainer = createFileIconContainer(info);
let fileInfoContainer = createFileInfoContainer(info);
let removeBtn = document.createElement('div');
removeBtn.classList.add('remove-selected-file');
let removeBtnIconHTML = `<svg xmlns="http://www.w3.org/2000/svg" height="20px" viewBox="0 -960 960 960" width="20px" fill="#C02223"><path d="m339-288 141-141 141 141 51-51-141-141 141-141-51-51-141 141-141-141-51 51 141 141-141 141 51 51ZM480-96q-79 0-149-30t-122.5-82.5Q156-261 126-331T96-480q0-80 30-149.5t82.5-122Q261-804 331-834t149-30q80 0 149.5 30t122 82.5Q804-699 834-629.5T864-480q0 79-30 149t-82.5 122.5Q699-156 629.5-126T480-96Z"/></svg>`;
$(removeBtn).append(removeBtnIconHTML);
$(removeBtn).attr('data-file-id', info.uniqueId).click(removeFileListener);
$(fileContainer).append(fileIconContainer);
$(fileContainer).append(fileInfoContainer);
$(fileContainer).append(removeBtn);
selectedFilesContainer.append(fileContainer);
}); });
if (fileNames.length === 1) {
showOrHideSelectedFilesContainer(filesInfo); $(inputElement).siblings(".custom-file-label").addClass("selected").html(fileNames[0]);
} else if (fileNames.length > 1) {
$(inputElement)
.siblings(".custom-file-label")
.addClass("selected")
.html(fileNames.length + " " + filesSelected);
} else {
$(inputElement).siblings(".custom-file-label").addClass("selected").html(pdfPrompt);
}
} }
function showOrHideSelectedFilesContainer(files) {
if (files && files.length > 0) chooser.style.setProperty('--selected-files-display', 'flex');
else chooser.style.setProperty('--selected-files-display', 'none');
}
function removeFileListener(e) {
const fileId = e.target.getAttribute('data-file-id');
let inputElement = document.getElementById(elementId);
removeFileById(fileId, inputElement);
showOrHideSelectedFilesContainer(allFiles);
inputElement.dispatchEvent(new CustomEvent('file-input-change', {bubbles: true}));
}
function removeFileById(fileId, inputElement) {
let fileContainer = document.getElementById(fileId);
fileContainer.remove();
allFiles = allFiles.filter((v) => v.uniqueId != fileId);
let dataTransfer = toDataTransfer(allFiles);
if (inputElement) inputElement.files = dataTransfer.files;
}
function createFileIconContainer(info) {
let fileIconContainer = document.createElement('div');
fileIconContainer.classList.add('file-icon');
// Add icon based on the extension
let fileExtension = FileUtils.extractFileExtension(info.name);
let fileIcon = FileIconFactory.createFileIcon(fileExtension);
$(fileIconContainer).append(fileIcon);
return fileIconContainer;
}
function createFileInfoContainer(info) {
let fileInfoContainer = document.createElement('div');
let fileInfoContainerClasses = 'file-info d-flex flex-column align-items-center justify-content-center';
$(fileInfoContainer).addClass(fileInfoContainerClasses);
$(fileInfoContainer).append(`<div title="${info.name}">${info.name}</div>`);
let fileSizeWithUnits = FileUtils.transformFileSize(info.size);
$(fileInfoContainer).append(`<div title="${info.size}">${fileSizeWithUnits}</div>`);
return fileInfoContainer;
}
//Listen for event of file being removed and the filter it out of the allFiles array //Listen for event of file being removed and the filter it out of the allFiles array
document.addEventListener('fileRemoved', function (e) { document.addEventListener("fileRemoved", function (e) {
const fileId = e.detail; const fileName = e.detail;
let inputElement = document.getElementById(elementId); allFiles = allFiles.filter(file => file.name !== fileName);
removeFileById(fileId, inputElement);
showOrHideSelectedFilesContainer(allFiles);
}); });
} }

View File

@@ -268,7 +268,7 @@ document.addEventListener("DOMContentLoaded", function () {
const parent = header.parentNode; const parent = header.parentNode;
const container = header.parentNode.querySelector(".feature-group-container"); const container = header.parentNode.querySelector(".feature-group-container");
if (parent.id !== "groupFavorites") { if (parent.id !== "groupFavorites") {
container.style.maxHeight = container.scrollHeight + "px"; container.style.maxHeight = container.clientHeight + "px";
} }
header.onclick = () => { header.onclick = () => {
expandCollapseToggle(parent); expandCollapseToggle(parent);

View File

@@ -29,7 +29,6 @@ async function displayFiles(files) {
// Create filename div and set textContent to sanitize // Create filename div and set textContent to sanitize
const fileNameDiv = document.createElement("div"); const fileNameDiv = document.createElement("div");
fileNameDiv.className = "filename"; fileNameDiv.className = "filename";
fileNameDiv.setAttribute("data-file-id", files[i].uniqueId);
fileNameDiv.textContent = files[i].name; fileNameDiv.textContent = files[i].name;
// Create page info div and set textContent to sanitize // Create page info div and set textContent to sanitize
@@ -111,13 +110,11 @@ function attachMoveButtons() {
event.preventDefault(); event.preventDefault();
var parent = this.closest(".list-group-item"); var parent = this.closest(".list-group-item");
//Get name of removed file //Get name of removed file
let filenameNode = parent.querySelector(".filename"); var fileName = parent.querySelector(".filename").innerText;
var fileName = filenameNode.innerText;
const fileId = filenameNode.getAttribute("data-file-id");
parent.remove(); parent.remove();
updateFiles(); updateFiles();
//Dispatch a custom event with the name of the removed file //Dispatch a custom event with the name of the removed file
var event = new CustomEvent("fileRemoved", { detail: fileId }); var event = new CustomEvent("fileRemoved", { detail: fileName });
document.dispatchEvent(event); document.dispatchEvent(event);
}); });
} }

View File

@@ -1,11 +1,8 @@
import {MovePageUpCommand, MovePageDownCommand} from './commands/move-page.js'; import { MovePageUpCommand, MovePageDownCommand } from "./commands/move-page.js";
import {RemoveSelectedCommand} from './commands/remove.js'; import { RemoveSelectedCommand } from "./commands/remove.js";
import {RotateAllCommand, RotateElementCommand} from './commands/rotate.js'; import { RotateAllCommand, RotateElementCommand } from "./commands/rotate.js";
import {SplitAllCommand} from './commands/split.js'; import { SplitAllCommand } from "./commands/split.js";
import {UndoManager} from './UndoManager.js'; import { UndoManager } from "./UndoManager.js";
import {PageBreakCommand} from './commands/page-break.js';
import {AddFilesCommand} from './commands/add-page.js';
import {DecryptFile} from '../DecryptFiles.js';
class PdfContainer { class PdfContainer {
fileName; fileName;
@@ -37,12 +34,10 @@ class PdfContainer {
this.updateSelectedPagesDisplay = this.updateSelectedPagesDisplay.bind(this); this.updateSelectedPagesDisplay = this.updateSelectedPagesDisplay.bind(this);
this.toggleSelectPageVisibility = this.toggleSelectPageVisibility.bind(this); this.toggleSelectPageVisibility = this.toggleSelectPageVisibility.bind(this);
this.updatePagesFromCSV = this.updatePagesFromCSV.bind(this); this.updatePagesFromCSV = this.updatePagesFromCSV.bind(this);
this.addFilesBlankAll = this.addFilesBlankAll.bind(this); this.addFilesBlankAll = this.addFilesBlankAll.bind(this)
this.removeAllElements = this.removeAllElements.bind(this); this.removeAllElements = this.removeAllElements.bind(this);
this.resetPages = this.resetPages.bind(this); this.resetPages = this.resetPages.bind(this);
this.decryptFile = new DecryptFile();
this.undoManager = undoManager || new UndoManager(); this.undoManager = undoManager || new UndoManager();
this.pdfAdapters = pdfAdapters; this.pdfAdapters = pdfAdapters;
@@ -68,7 +63,7 @@ class PdfContainer {
window.updatePagesFromCSV = this.updatePagesFromCSV; window.updatePagesFromCSV = this.updatePagesFromCSV;
window.updateSelectedPagesDisplay = this.updateSelectedPagesDisplay; window.updateSelectedPagesDisplay = this.updateSelectedPagesDisplay;
window.updatePageNumbersAndCheckboxes = this.updatePageNumbersAndCheckboxes; window.updatePageNumbersAndCheckboxes = this.updatePageNumbersAndCheckboxes;
window.addFilesBlankAll = this.addFilesBlankAll; window.addFilesBlankAll = this.addFilesBlankAll
window.removeAllElements = this.removeAllElements; window.removeAllElements = this.removeAllElements;
window.resetPages = this.resetPages; window.resetPages = this.resetPages;
@@ -81,7 +76,7 @@ class PdfContainer {
undoBtn.disabled = !canUndo; undoBtn.disabled = !canUndo;
redoBtn.disabled = !canRedo; redoBtn.disabled = !canRedo;
}); })
window.undo = () => { window.undo = () => {
if (undoManager.canUndo()) undoManager.undo(); if (undoManager.canUndo()) undoManager.undo();
@@ -89,7 +84,7 @@ class PdfContainer {
undoBtn.disabled = !undoManager.canUndo(); undoBtn.disabled = !undoManager.canUndo();
redoBtn.disabled = !undoManager.canRedo(); redoBtn.disabled = !undoManager.canRedo();
} }
}; }
window.redo = () => { window.redo = () => {
if (undoManager.canRedo()) undoManager.redo(); if (undoManager.canRedo()) undoManager.redo();
@@ -97,15 +92,15 @@ class PdfContainer {
undoBtn.disabled = !undoManager.canUndo(); undoBtn.disabled = !undoManager.canUndo();
redoBtn.disabled = !undoManager.canRedo(); redoBtn.disabled = !undoManager.canRedo();
} }
}; }
const filenameInput = document.getElementById('filename-input'); const filenameInput = document.getElementById("filename-input");
const downloadBtn = document.getElementById('export-button'); const downloadBtn = document.getElementById("export-button");
filenameInput.onkeyup = this.updateFilename; filenameInput.onkeyup = this.updateFilename;
filenameInput.onkeydown = this.preventIllegalChars; filenameInput.onkeydown = this.preventIllegalChars;
filenameInput.disabled = false; filenameInput.disabled = false;
filenameInput.innerText = ''; filenameInput.innerText = "";
downloadBtn.disabled = true; downloadBtn.disabled = true;
} }
@@ -133,119 +128,86 @@ class PdfContainer {
return movePageCommand; return movePageCommand;
} }
async addFiles(element) { addFiles(nextSiblingElement, blank = false) {
let addFilesCommand = new AddFilesCommand( if (blank) {
element,
window.selectedPages,
this.addFilesAction.bind(this),
this.pagesContainer
);
await addFilesCommand.execute(); this.addFilesBlank(nextSiblingElement);
this.undoManager.pushUndoClearRedo(addFilesCommand); } else {
} var input = document.createElement("input");
input.type = "file";
async addFilesAction(nextSiblingElement) {
let pages = [];
return new Promise((resolve) => {
var input = document.createElement('input');
input.type = 'file';
input.multiple = true; input.multiple = true;
input.setAttribute('accept', 'application/pdf,image/*'); input.setAttribute("accept", "application/pdf,image/*");
input.onchange = async (e) => { input.onchange = async (e) => {
const files = e.target.files; const files = e.target.files;
if (files.length > 0) {
pages = await this.addFilesFromFiles(files, nextSiblingElement, pages); this.addFilesFromFiles(files, nextSiblingElement);
this.updateFilename(files[0].name); this.updateFilename(files ? files[0].name : "");
const selectAll = document.getElementById('select-pages-container'); const selectAll = document.getElementById("select-pages-container");
selectAll.classList.toggle('hidden', false); selectAll.classList.toggle("hidden", false);
}
resolve(pages);
}; };
input.click(); input.click();
}); }
} }
async addFilesFromFiles(files, nextSiblingElement, pages) {
async addFilesFromFiles(files, nextSiblingElement) {
this.fileName = files[0].name; this.fileName = files[0].name;
for (var i = 0; i < files.length; i++) { for (var i = 0; i < files.length; i++) {
const startTime = Date.now(); const startTime = Date.now();
let processingTime, let processingTime, errorMessage = null, pageCount = 0;
errorMessage = null,
pageCount = 0;
try { try {
let decryptedFile = files[i]; const file = files[i];
let isEncrypted = false; if (file.type === "application/pdf") {
let requiresPassword = false; const { renderer, pdfDocument } = await this.loadFile(file);
await this.decryptFile pageCount = renderer.pageCount || 0;
.checkFileEncrypted(decryptedFile) await this.addPdfFile(renderer, pdfDocument, nextSiblingElement);
.then((result) => { } else if (file.type.startsWith("image/")) {
isEncrypted = result.isEncrypted; await this.addImageFile(file, nextSiblingElement);
requiresPassword = result.requiresPassword;
})
.catch((error) => {
console.error(error);
});
if (decryptedFile.type === 'application/pdf' && isEncrypted) {
decryptedFile = await this.decryptFile.decryptFile(decryptedFile, requiresPassword);
if (!decryptedFile) {
throw new Error('File decryption failed.');
}
}
if (decryptedFile.type === 'application/pdf') {
const {renderer, pdfDocument} = await this.loadFile(decryptedFile);
pageCount = renderer.pageCount || 0;
pages = await this.addPdfFile(renderer, pdfDocument, nextSiblingElement, pages);
} else if (decryptedFile.type.startsWith('image/')) {
pages = await this.addImageFile(decryptedFile, nextSiblingElement, pages);
}
processingTime = Date.now() - startTime;
this.captureFileProcessingEvent(true, decryptedFile, processingTime, null, pageCount);
} catch (error) {
processingTime = Date.now() - startTime;
errorMessage = error.message || 'Unknown error';
this.captureFileProcessingEvent(false, files[i], processingTime, errorMessage, pageCount);
} }
processingTime = Date.now() - startTime;
this.captureFileProcessingEvent(true, file, processingTime, null, pageCount);
} catch (error) {
processingTime = Date.now() - startTime;
errorMessage = error.message || "Unknown error";
this.captureFileProcessingEvent(false, files[i], processingTime, errorMessage, pageCount);
}
} }
document.querySelectorAll('.enable-on-file').forEach((element) => { document.querySelectorAll(".enable-on-file").forEach((element) => {
element.disabled = false; element.disabled = false;
}); });
return pages;
} }
captureFileProcessingEvent(success, file, processingTime, errorMessage, pageCount) { captureFileProcessingEvent(success, file, processingTime, errorMessage, pageCount) {
try { try{
if (analyticsEnabled) { if(analyticsEnabled){
posthog.capture('file_processing', { posthog.capture('file_processing', {
success, success,
file_type: file?.type || 'unknown', file_type: file?.type || 'unknown',
file_size: file?.size || 0, file_size: file?.size || 0,
processing_time: processingTime, processing_time: processingTime,
error_message: errorMessage, error_message: errorMessage,
pdf_pages: pageCount, pdf_pages: pageCount,
}); });
} }
} catch {} }catch{
} }
}
async addFilesBlank(nextSiblingElement, pages) {
async addFilesBlank(nextSiblingElement) {
let doc = await PDFLib.PDFDocument.create(); let doc = await PDFLib.PDFDocument.create();
let docBytes = await doc.save(); let docBytes = await doc.save();
const url = URL.createObjectURL(new Blob([docBytes], {type: 'application/pdf'})); const url = URL.createObjectURL(new Blob([docBytes], { type: 'application/pdf' }));
const renderer = await this.toRenderer(url); const renderer = await this.toRenderer(url);
pages = await this.addPdfFile(renderer, doc, nextSiblingElement, pages);
return pages; await this.addPdfFile(renderer, doc, nextSiblingElement);
} }
rotateElement(element, deg) { rotateElement(element, deg) {
let rotateCommand = new RotateElementCommand(element, deg); let rotateCommand = new RotateElementCommand(element, deg);
rotateCommand.execute(); rotateCommand.execute();
@@ -253,43 +215,37 @@ class PdfContainer {
return rotateCommand; return rotateCommand;
} }
async addPdfFile(renderer, pdfDocument, nextSiblingElement, pages) { async addPdfFile(renderer, pdfDocument, nextSiblingElement) {
for (var i = 0; i < renderer.pageCount; i++) { for (var i = 0; i < renderer.pageCount; i++) {
const div = document.createElement('div'); const div = document.createElement("div");
div.classList.add('page-container'); div.classList.add("page-container");
div.id = 'page-container-' + (i + 1); div.id = "page-container-" + (i + 1);
var img = document.createElement('img'); var img = document.createElement("img");
img.classList.add('page-image'); img.classList.add("page-image");
const imageSrc = await renderer.renderPage(i); const imageSrc = await renderer.renderPage(i);
img.src = imageSrc; img.src = imageSrc;
img.pageIdx = i; img.pageIdx = i;
img.rend = renderer; img.rend = renderer;
img.doc = pdfDocument; img.doc = pdfDocument;
div.appendChild(img); div.appendChild(img);
this.pdfAdapters.forEach((adapter) => { this.pdfAdapters.forEach((adapter) => {
adapter.adapt?.(div); adapter.adapt?.(div);
}); });
if (nextSiblingElement) { if (nextSiblingElement) {
this.pagesContainer.insertBefore(div, nextSiblingElement); this.pagesContainer.insertBefore(div, nextSiblingElement);
} else { } else {
this.pagesContainer.appendChild(div); this.pagesContainer.appendChild(div);
} }
pages.push(div);
} }
return pages;
} }
async addImageFile(file, nextSiblingElement, pages) { async addImageFile(file, nextSiblingElement) {
const div = document.createElement('div'); const div = document.createElement("div");
div.classList.add('page-container'); div.classList.add("page-container");
var img = document.createElement('img'); var img = document.createElement("img");
img.classList.add('page-image'); img.classList.add("page-image");
img.src = URL.createObjectURL(file); img.src = URL.createObjectURL(file);
div.appendChild(img); div.appendChild(img);
@@ -301,19 +257,17 @@ class PdfContainer {
} else { } else {
this.pagesContainer.appendChild(div); this.pagesContainer.appendChild(div);
} }
pages.push(div);
return pages;
} }
async loadFile(file) { async loadFile(file) {
var objectUrl = URL.createObjectURL(file); var objectUrl = URL.createObjectURL(file);
var pdfDocument = await this.toPdfLib(objectUrl); var pdfDocument = await this.toPdfLib(objectUrl);
var renderer = await this.toRenderer(objectUrl); var renderer = await this.toRenderer(objectUrl);
return {renderer, pdfDocument}; return { renderer, pdfDocument };
} }
async toRenderer(objectUrl) { async toRenderer(objectUrl) {
pdfjsLib.GlobalWorkerOptions.workerSrc = './pdfjs-legacy/pdf.worker.mjs'; pdfjsLib.GlobalWorkerOptions.workerSrc = "./pdfjs-legacy/pdf.worker.mjs";
const pdf = await pdfjsLib.getDocument(objectUrl).promise; const pdf = await pdfjsLib.getDocument(objectUrl).promise;
return { return {
document: pdf, document: pdf,
@@ -321,7 +275,7 @@ class PdfContainer {
renderPage: async function (pageIdx) { renderPage: async function (pageIdx) {
const page = await this.document.getPage(pageIdx + 1); const page = await this.document.getPage(pageIdx + 1);
const canvas = document.createElement('canvas'); const canvas = document.createElement("canvas");
// set the canvas size to the size of the page // set the canvas size to the size of the page
if (page.rotate == 90 || page.rotate == 270) { if (page.rotate == 90 || page.rotate == 270) {
@@ -334,8 +288,8 @@ class PdfContainer {
// render the page onto the canvas // render the page onto the canvas
var renderContext = { var renderContext = {
canvasContext: canvas.getContext('2d'), canvasContext: canvas.getContext("2d"),
viewport: page.getViewport({scale: 1}), viewport: page.getViewport({ scale: 1 }),
}; };
await page.render(renderContext).promise; await page.render(renderContext).promise;
@@ -362,7 +316,7 @@ class PdfContainer {
//if in page select mode is active rotate only selected pages //if in page select mode is active rotate only selected pages
if (window.selectPage && !window.selectedPages.includes(pageIndex)) continue; if (window.selectPage && !window.selectedPages.includes(pageIndex)) continue;
const img = child.querySelector('img'); const img = child.querySelector("img");
if (!img) continue; if (!img) continue;
elementsToRotate.push(img); elementsToRotate.push(img);
@@ -374,12 +328,12 @@ class PdfContainer {
this.undoManager.pushUndoClearRedo(rotateAllCommand); this.undoManager.pushUndoClearRedo(rotateAllCommand);
} }
removeAllElements() { removeAllElements(){
let pageContainerNodeList = document.querySelectorAll('.page-container'); let pageContainerNodeList = document.querySelectorAll(".page-container");
for (var i = 0; i < pageContainerNodeList.length; i++) { for (var i = 0; i < pageContainerNodeList.length; i++) {
pageContainerNodeList[i].remove(); pageContainerNodeList[i].remove();
} }
document.querySelectorAll('.enable-on-file').forEach((element) => { document.querySelectorAll(".enable-on-file").forEach((element) => {
element.disabled = true; element.disabled = true;
}); });
} }
@@ -391,24 +345,25 @@ class PdfContainer {
window.selectedPages, window.selectedPages,
this.updatePageNumbersAndCheckboxes this.updatePageNumbersAndCheckboxes
); );
removeSelectedCommand.execute();
this.undoManager.pushUndoClearRedo(removeSelectedCommand); this.undoManager.pushUndoClearRedo(removeSelectedCommand);
} }
toggleSelectAll() { toggleSelectAll() {
const checkboxes = document.querySelectorAll('.pdf-actions_checkbox'); const checkboxes = document.querySelectorAll(".pdf-actions_checkbox");
window.selectAll = !window.selectAll; window.selectAll = !window.selectAll;
const selectIcon = document.getElementById('select-All-Container'); const selectIcon = document.getElementById("select-All-Container");
const deselectIcon = document.getElementById('deselect-All-Container'); const deselectIcon = document.getElementById("deselect-All-Container");
if (selectIcon.style.display === 'none') { if (selectIcon.style.display === "none") {
selectIcon.style.display = 'inline'; selectIcon.style.display = "inline";
deselectIcon.style.display = 'none'; deselectIcon.style.display = "none";
} else { } else {
selectIcon.style.display = 'none'; selectIcon.style.display = "none";
deselectIcon.style.display = 'inline'; deselectIcon.style.display = "inline";
} }
checkboxes.forEach((checkbox) => { checkboxes.forEach((checkbox) => {
checkbox.checked = window.selectAll; checkbox.checked = window.selectAll;
const pageNumber = Array.from(checkbox.parentNode.parentNode.children).indexOf(checkbox.parentNode) + 1; const pageNumber = Array.from(checkbox.parentNode.parentNode.children).indexOf(checkbox.parentNode) + 1;
@@ -431,20 +386,18 @@ class PdfContainer {
parseCSVInput(csvInput, maxPageIndex) { parseCSVInput(csvInput, maxPageIndex) {
const pages = new Set(); const pages = new Set();
csvInput.split(',').forEach((item) => { csvInput.split(",").forEach((item) => {
const range = item.split('-').map((p) => parseInt(p.trim())); const range = item.split("-").map((p) => parseInt(p.trim()));
if (range.length === 2) { if (range.length === 2) {
const [start, end] = range; const [start, end] = range;
for (let i = start; i <= end && i <= maxPageIndex; i++) { for (let i = start; i <= end && i <= maxPageIndex; i++) {
if (i > 0) { if (i > 0) { // Ensure the page number is greater than 0
// Ensure the page number is greater than 0
pages.add(i); pages.add(i);
} }
} }
} else if (range.length === 1 && Number.isInteger(range[0])) { } else if (range.length === 1 && Number.isInteger(range[0])) {
const page = range[0]; const page = range[0];
if (page > 0 && page <= maxPageIndex) { if (page > 0 && page <= maxPageIndex) { // Ensure page is within valid range
// Ensure page is within valid range
pages.add(page); pages.add(page);
} }
} }
@@ -454,24 +407,24 @@ class PdfContainer {
} }
updatePagesFromCSV() { updatePagesFromCSV() {
const csvInput = document.getElementById('csv-input').value; const csvInput = document.getElementById("csv-input").value;
const allPages = this.pagesContainer.querySelectorAll('.page-container'); const allPages = this.pagesContainer.querySelectorAll(".page-container");
const maxPageIndex = allPages.length; const maxPageIndex = allPages.length;
window.selectedPages = this.parseCSVInput(csvInput, maxPageIndex); window.selectedPages = this.parseCSVInput(csvInput, maxPageIndex);
this.updateSelectedPagesDisplay(); this.updateSelectedPagesDisplay();
const allCheckboxes = document.querySelectorAll('.pdf-actions_checkbox'); const allCheckboxes = document.querySelectorAll(".pdf-actions_checkbox");
allCheckboxes.forEach((checkbox) => { allCheckboxes.forEach((checkbox) => {
const page = parseInt(checkbox.getAttribute('data-page-number')); const page = parseInt(checkbox.getAttribute("data-page-number"));
checkbox.checked = window.selectedPages.includes(page); checkbox.checked = window.selectedPages.includes(page);
}); });
} }
formatSelectedPages(pages) { formatSelectedPages(pages) {
if (pages.length === 0) return ''; if (pages.length === 0) return "";
pages.sort((a, b) => a - b); // Sort the page numbers in ascending order pages.sort((a, b) => a - b); // Sort the page numbers in ascending order
const ranges = []; const ranges = [];
@@ -492,27 +445,27 @@ class PdfContainer {
// Add the last range // Add the last range
ranges.push(start === end ? `${start}` : `${start}-${end}`); ranges.push(start === end ? `${start}` : `${start}-${end}`);
return ranges.join(', '); return ranges.join(", ");
} }
updateSelectedPagesDisplay() { updateSelectedPagesDisplay() {
const selectedPagesList = document.getElementById('selected-pages-list'); const selectedPagesList = document.getElementById("selected-pages-list");
const selectedPagesInput = document.getElementById('csv-input'); const selectedPagesInput = document.getElementById("csv-input");
selectedPagesList.innerHTML = ''; // Clear the list selectedPagesList.innerHTML = ""; // Clear the list
window.selectedPages.sort((a, b) => a - b); window.selectedPages.sort((a, b) => a - b);
window.selectedPages.forEach((page) => { window.selectedPages.forEach((page) => {
const pageItem = document.createElement('div'); const pageItem = document.createElement("div");
pageItem.className = 'page-item'; pageItem.className = "page-item";
const pageNumber = document.createElement('span'); const pageNumber = document.createElement("span");
const pagelabel = /*[[#{multiTool.page}]]*/ 'Page'; const pagelabel = /*[[#{multiTool.page}]]*/ 'Page';
pageNumber.className = 'selected-page-number'; pageNumber.className = "selected-page-number";
pageNumber.innerText = `${pagelabel} ${page}`; pageNumber.innerText = `${pagelabel} ${page}`;
pageItem.appendChild(pageNumber); pageItem.appendChild(pageNumber);
const removeBtn = document.createElement('span'); const removeBtn = document.createElement("span");
removeBtn.className = 'remove-btn'; removeBtn.className = "remove-btn";
removeBtn.innerHTML = '✕'; removeBtn.innerHTML = "✕";
// Remove page from selected pages list and update display and checkbox // Remove page from selected pages list and update display and checkbox
removeBtn.onclick = () => { removeBtn.onclick = () => {
@@ -536,7 +489,7 @@ class PdfContainer {
parsePageRanges(ranges) { parsePageRanges(ranges) {
const pages = new Set(); const pages = new Set();
ranges.split(',').forEach((range) => { ranges.split(',').forEach(range => {
const [start, end] = range.split('-').map(Number); const [start, end] = range.split('-').map(Number);
if (end) { if (end) {
for (let i = start; i <= end; i++) { for (let i = start; i <= end; i++) {
@@ -550,25 +503,23 @@ class PdfContainer {
return Array.from(pages).sort((a, b) => a - b); return Array.from(pages).sort((a, b) => a - b);
} }
async addFilesBlankAll() { addFilesBlankAll() {
const allPages = this.pagesContainer.querySelectorAll('.page-container'); const allPages = this.pagesContainer.querySelectorAll(".page-container");
allPages.forEach((page, index) => {
let pageBreakCommand = new PageBreakCommand( if (index !== 0) {
allPages, this.addFiles(page, true)
window.selectPage, }
window.selectedPages, });
this.addFilesBlank.bind(this),
this.pagesContainer
);
await pageBreakCommand.execute();
this.undoManager.pushUndoClearRedo(pageBreakCommand);
} }
splitAll() { splitAll() {
const allPages = this.pagesContainer.querySelectorAll('.page-container'); const allPages = this.pagesContainer.querySelectorAll(".page-container");
let splitAllCommand = new SplitAllCommand(allPages, window.selectPage, window.selectedPages, 'split-before'); let splitAllCommand = new SplitAllCommand(
allPages,
window.selectPage,
window.selectedPages,
"split-before"
);
splitAllCommand.execute(); splitAllCommand.execute();
this.undoManager.pushUndoClearRedo(splitAllCommand); this.undoManager.pushUndoClearRedo(splitAllCommand);
@@ -578,7 +529,7 @@ class PdfContainer {
const baseDocument = await PDFLib.PDFDocument.load(baseDocBytes); const baseDocument = await PDFLib.PDFDocument.load(baseDocBytes);
const pageNum = baseDocument.getPages().length; const pageNum = baseDocument.getPages().length;
splitters.sort((a, b) => a - b); // We'll sort the separator indexes just in case querySelectorAll does something funny. splitters.sort((a, b) => a - b);; // We'll sort the separator indexes just in case querySelectorAll does something funny.
splitters.push(pageNum); // We'll also add a faux separator at the end in order to get the pages after the last separator. splitters.push(pageNum); // We'll also add a faux separator at the end in order to get the pages after the last separator.
const splitDocuments = []; const splitDocuments = [];
@@ -589,18 +540,18 @@ class PdfContainer {
let firstPage = splitterIndex === 0 ? 0 : splitters[splitterIndex - 1]; let firstPage = splitterIndex === 0 ? 0 : splitters[splitterIndex - 1];
const pageIndices = Array.from({length: splitterPosition - firstPage}, (value, key) => firstPage + key); const pageIndices = Array.from({ length: splitterPosition - firstPage }, (value, key) => firstPage + key);
const copiedPages = await subDocument.copyPages(baseDocument, pageIndices); const copiedPages = await subDocument.copyPages(baseDocument, pageIndices);
copiedPages.forEach((copiedPage) => { copiedPages.forEach(copiedPage => {
subDocument.addPage(copiedPage); subDocument.addPage(copiedPage);
}); });
const subDocumentBytes = await subDocument.save(); const subDocumentBytes = await subDocument.save();
splitDocuments.push(subDocumentBytes); splitDocuments.push(subDocumentBytes);
} };
return splitDocuments; return splitDocuments;
} }
@@ -609,10 +560,8 @@ class PdfContainer {
const zip = new JSZip(); const zip = new JSZip();
for (let i = 0; i < pdfBytesArray.length; i++) { for (let i = 0; i < pdfBytesArray.length; i++) {
const documentBlob = new Blob([pdfBytesArray[i]], { const documentBlob = new Blob([pdfBytesArray[i]], { type: "application/pdf" });
type: 'application/pdf', zip.file(baseNameString + "-" + (i + 1) + ".pdf", documentBlob);
});
zip.file(baseNameString + '-' + (i + 1) + '.pdf', documentBlob);
} }
return zip; return zip;
@@ -620,10 +569,10 @@ class PdfContainer {
async exportPdf(selected) { async exportPdf(selected) {
const pdfDoc = await PDFLib.PDFDocument.create(); const pdfDoc = await PDFLib.PDFDocument.create();
const pageContainers = this.pagesContainer.querySelectorAll('.page-container'); // Select all .page-container elements const pageContainers = this.pagesContainer.querySelectorAll(".page-container"); // Select all .page-container elements
for (var i = 0; i < pageContainers.length; i++) { for (var i = 0; i < pageContainers.length; i++) {
if (!selected || window.selectedPages.includes(i + 1)) { if (!selected || window.selectedPages.includes(i + 1)) {
const img = pageContainers[i].querySelector('img'); // Find the img element within each .page-container const img = pageContainers[i].querySelector("img"); // Find the img element within each .page-container
if (!img) continue; if (!img) continue;
let page; let page;
if (img.doc) { if (img.doc) {
@@ -663,7 +612,7 @@ class PdfContainer {
} }
const rotation = img.style.rotate; const rotation = img.style.rotate;
if (rotation) { if (rotation) {
const rotationAngle = parseInt(rotation.replace(/[^\d-]/g, '')); const rotationAngle = parseInt(rotation.replace(/[^\d-]/g, ""));
page.setRotation(PDFLib.degrees(page.getRotation().angle + rotationAngle)); page.setRotation(PDFLib.degrees(page.getRotation().angle + rotationAngle));
} }
} }
@@ -672,11 +621,11 @@ class PdfContainer {
pdfDoc.setProducer(stirlingPDFLabel); pdfDoc.setProducer(stirlingPDFLabel);
const pdfBytes = await pdfDoc.save(); const pdfBytes = await pdfDoc.save();
const pdfBlob = new Blob([pdfBytes], {type: 'application/pdf'}); const pdfBlob = new Blob([pdfBytes], { type: "application/pdf" });
const filenameInput = document.getElementById('filename-input'); const filenameInput = document.getElementById("filename-input");
let inputArr = filenameInput.value.split('.'); let inputArr = filenameInput.value.split(".");
if (inputArr !== null && inputArr !== undefined && inputArr.length > 0) { if (inputArr !== null && inputArr !== undefined && inputArr.length > 0) {
inputArr = inputArr.filter((n) => n); // remove all empty strings, nulls or undefined inputArr = inputArr.filter((n) => n); // remove all empty strings, nulls or undefined
@@ -685,18 +634,17 @@ class PdfContainer {
inputArr.pop(); // remove right part after last dot inputArr.pop(); // remove right part after last dot
} }
filenameInput.value = inputArr.join(''); filenameInput.value = inputArr.join("");
this.fileName = filenameInput.value; this.fileName = filenameInput.value;
} }
const separators = this.pagesContainer.querySelectorAll('.split-before'); const separators = this.pagesContainer.querySelectorAll(".split-before");
if (separators.length !== 0) { if (separators.length !== 0) { // Split the pdf if there are separators.
// Split the pdf if there are separators. const baseName = this.fileName ? this.fileName : "managed";
const baseName = this.fileName ? this.fileName : 'managed';
const pagesArray = Array.from(this.pagesContainer.children); const pagesArray = Array.from(this.pagesContainer.children);
const splitters = []; const splitters = [];
separators.forEach((page) => { separators.forEach(page => {
const pageIndex = pagesArray.indexOf(page); const pageIndex = pagesArray.indexOf(page);
if (pageIndex !== 0) { if (pageIndex !== 0) {
splitters.push(pageIndex); splitters.push(pageIndex);
@@ -707,80 +655,80 @@ class PdfContainer {
const archivedDocuments = await this.nameAndArchiveFiles(splitDocuments, baseName); const archivedDocuments = await this.nameAndArchiveFiles(splitDocuments, baseName);
const self = this; const self = this;
archivedDocuments.generateAsync({type: 'base64'}).then(function (base64) { archivedDocuments.generateAsync({ type: "base64" }).then(function (base64) {
const url = 'data:application/zip;base64,' + base64; const url = "data:application/zip;base64," + base64;
self.downloadLink = document.createElement('a'); self.downloadLink = document.createElement("a");
self.downloadLink.href = url; self.downloadLink.href = url;
self.downloadLink.setAttribute('download', baseName + '.zip'); self.downloadLink.setAttribute("download", baseName + ".zip");
self.downloadLink.setAttribute('target', '_blank'); self.downloadLink.setAttribute("target", "_blank");
self.downloadLink.click(); self.downloadLink.click();
}); });
} else {
// Continue normally if there are no separators } else { // Continue normally if there are no separators
const url = URL.createObjectURL(pdfBlob); const url = URL.createObjectURL(pdfBlob);
const downloadOption = localStorage.getItem('downloadOption'); const downloadOption = localStorage.getItem("downloadOption");
if (!filenameInput.value.includes('.pdf')) { if (!filenameInput.value.includes(".pdf")) {
filenameInput.value = filenameInput.value + '.pdf'; filenameInput.value = filenameInput.value + ".pdf";
this.fileName = filenameInput.value; this.fileName = filenameInput.value;
} }
if (downloadOption === 'sameWindow') { if (downloadOption === "sameWindow") {
// Open the file in the same window // Open the file in the same window
window.location.href = url; window.location.href = url;
} else if (downloadOption === 'newWindow') { } else if (downloadOption === "newWindow") {
// Open the file in a new window // Open the file in a new window
window.open(url, '_blank'); window.open(url, "_blank");
} else { } else {
// Download the file // Download the file
this.downloadLink = document.createElement('a'); this.downloadLink = document.createElement("a");
this.downloadLink.id = 'download-link'; this.downloadLink.id = "download-link";
this.downloadLink.href = url; this.downloadLink.href = url;
// downloadLink.download = this.fileName ? this.fileName : 'managed.pdf'; // downloadLink.download = this.fileName ? this.fileName : 'managed.pdf';
// downloadLink.download = this.fileName; // downloadLink.download = this.fileName;
this.downloadLink.setAttribute('download', this.fileName ? this.fileName : 'managed.pdf'); this.downloadLink.setAttribute("download", this.fileName ? this.fileName : "managed.pdf");
this.downloadLink.setAttribute('target', '_blank'); this.downloadLink.setAttribute("target", "_blank");
this.downloadLink.onclick = this.setDownloadAttribute; this.downloadLink.onclick = this.setDownloadAttribute;
this.downloadLink.click(); this.downloadLink.click();
} }
} }
} }
resetPages() { resetPages() {
const pageContainers = this.pagesContainer.querySelectorAll('.page-container'); const pageContainers = this.pagesContainer.querySelectorAll(".page-container");
pageContainers.forEach((container, index) => { pageContainers.forEach((container, index) => {
container.id = 'page-container-' + (index + 1); container.id = "page-container-" + (index + 1);
}); });
const checkboxes = document.querySelectorAll('.pdf-actions_checkbox'); const checkboxes = document.querySelectorAll(".pdf-actions_checkbox");
window.selectAll = false; window.selectAll = false;
const selectIcon = document.getElementById('select-All-Container'); const selectIcon = document.getElementById("select-All-Container");
const deselectIcon = document.getElementById('deselect-All-Container'); const deselectIcon = document.getElementById("deselect-All-Container");
selectIcon.style.display = 'inline'; selectIcon.style.display = "inline";
deselectIcon.style.display = 'none'; deselectIcon.style.display = "none";
checkboxes.forEach((checkbox) => { checkboxes.forEach((checkbox) => {
const pageNumber = Array.from(checkbox.parentNode.parentNode.children).indexOf(checkbox.parentNode) + 1; const pageNumber = Array.from(checkbox.parentNode.parentNode.children).indexOf(checkbox.parentNode) + 1;
const index = window.selectedPages.indexOf(pageNumber); const index = window.selectedPages.indexOf(pageNumber);
if (index !== -1) { if (index !== -1) {
window.selectedPages.splice(index, 1); window.selectedPages.splice(index, 1);
} }
}); });
window.toggleSelectPageVisibility(); window.toggleSelectPageVisibility();
} }
setDownloadAttribute() { setDownloadAttribute() {
this.downloadLink.setAttribute('download', this.fileName ? this.fileName : 'managed.pdf'); this.downloadLink.setAttribute("download", this.fileName ? this.fileName : "managed.pdf");
} }
updateFilename(fileName = '') { updateFilename(fileName = "") {
const filenameInput = document.getElementById('filename-input'); const filenameInput = document.getElementById("filename-input");
const pagesContainer = document.getElementById('pages-container'); const pagesContainer = document.getElementById("pages-container");
const downloadBtn = document.getElementById('export-button'); const downloadBtn = document.getElementById("export-button");
downloadBtn.disabled = pagesContainer.childElementCount === 0; downloadBtn.disabled = pagesContainer.childElementCount === 0;
@@ -804,36 +752,38 @@ class PdfContainer {
// } // }
} }
toggleSelectPageVisibility() { toggleSelectPageVisibility() {
window.selectPage = !window.selectPage; window.selectPage = !window.selectPage;
const checkboxes = document.querySelectorAll('.pdf-actions_checkbox'); const checkboxes = document.querySelectorAll(".pdf-actions_checkbox");
checkboxes.forEach((checkbox) => { checkboxes.forEach(checkbox => {
checkbox.classList.toggle('hidden', !window.selectPage); checkbox.classList.toggle("hidden", !window.selectPage);
}); });
const deleteButton = document.getElementById('delete-button'); const deleteButton = document.getElementById("delete-button");
deleteButton.classList.toggle('hidden', !window.selectPage); deleteButton.classList.toggle("hidden", !window.selectPage);
const selectedPages = document.getElementById('selected-pages-display'); const selectedPages = document.getElementById("selected-pages-display");
selectedPages.classList.toggle('hidden', !window.selectPage); selectedPages.classList.toggle("hidden", !window.selectPage);
const selectAll = document.getElementById('select-All-Container'); const selectAll = document.getElementById("select-All-Container");
selectAll.classList.toggle('hidden', !window.selectPage); selectAll.classList.toggle("hidden", !window.selectPage);
const exportSelected = document.getElementById('export-selected-button'); const exportSelected = document.getElementById("export-selected-button");
exportSelected.classList.toggle('hidden', !window.selectPage); exportSelected.classList.toggle("hidden", !window.selectPage);
const selectPagesButton = document.getElementById('select-pages-button'); const selectPagesButton = document.getElementById("select-pages-button");
selectPagesButton.style.opacity = window.selectPage ? '1' : '0.5'; selectPagesButton.style.opacity = window.selectPage ? "1" : "0.5";
if (window.selectPage) { if (window.selectPage) {
this.updatePageNumbersAndCheckboxes(); this.updatePageNumbersAndCheckboxes();
} }
} }
updatePageNumbersAndCheckboxes() { updatePageNumbersAndCheckboxes() {
const pageDivs = document.querySelectorAll('.pdf-actions_container'); const pageDivs = document.querySelectorAll(".pdf-actions_container");
pageDivs.forEach((div, index) => { pageDivs.forEach((div, index) => {
const pageNumber = index + 1; const pageNumber = index + 1;
const checkbox = div.querySelector('.pdf-actions_checkbox'); const checkbox = div.querySelector(".pdf-actions_checkbox");
checkbox.id = `selectPageCheckbox-${pageNumber}`; checkbox.id = `selectPageCheckbox-${pageNumber}`;
checkbox.setAttribute('data-page-number', pageNumber); checkbox.setAttribute("data-page-number", pageNumber);
checkbox.checked = window.selectedPages.includes(pageNumber); checkbox.checked = window.selectedPages.includes(pageNumber);
}); });
} }
@@ -851,10 +801,8 @@ function detectImageType(uint8Array) {
} }
// Check for TIFF signature (little-endian and big-endian) // Check for TIFF signature (little-endian and big-endian)
if ( if ((uint8Array[0] === 73 && uint8Array[1] === 73 && uint8Array[2] === 42 && uint8Array[3] === 0) ||
(uint8Array[0] === 73 && uint8Array[1] === 73 && uint8Array[2] === 42 && uint8Array[3] === 0) || (uint8Array[0] === 77 && uint8Array[1] === 77 && uint8Array[2] === 0 && uint8Array[3] === 42)) {
(uint8Array[0] === 77 && uint8Array[1] === 77 && uint8Array[2] === 0 && uint8Array[3] === 42)
) {
return 'TIFF'; return 'TIFF';
} }
@@ -866,4 +814,6 @@ function detectImageType(uint8Array) {
return 'UNKNOWN'; return 'UNKNOWN';
} }
export default PdfContainer; export default PdfContainer;

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