Compare commits
40 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
205b849f25 | ||
|
|
16adfe1f59 | ||
|
|
397352c9f1 | ||
|
|
97675c2320 | ||
|
|
f4cfcb2ac7 | ||
|
|
56729648e9 | ||
|
|
2317642fdc | ||
|
|
a400fe6015 | ||
|
|
b47df3d252 | ||
|
|
cb6e1cd94e | ||
|
|
2ec8c97737 | ||
|
|
397a07afe8 | ||
|
|
b072c39fd9 | ||
|
|
1bc6b4149c | ||
|
|
5a5a8bb7ba | ||
|
|
b6eca59f23 | ||
|
|
7108424a92 | ||
|
|
400965ffc8 | ||
|
|
1895a04394 | ||
|
|
f8f137a30a | ||
|
|
f6a2d4784b | ||
|
|
526dc9f911 | ||
|
|
cce9f74eb9 | ||
|
|
0e3865618d | ||
|
|
d888ed1ae0 | ||
|
|
99d1b46d97 | ||
|
|
32e46eeb73 | ||
|
|
b7da84d257 | ||
|
|
1c1ead5d62 | ||
|
|
6ff53aa5b3 | ||
|
|
8d60b08cd9 | ||
|
|
64cf5167c0 | ||
|
|
de4637e8d4 | ||
|
|
3c0a8071dc | ||
|
|
04ccdf6f76 | ||
|
|
db02fba31f | ||
|
|
5b6f649e4e | ||
|
|
de23bb702c | ||
|
|
25e564154e | ||
|
|
3633a979d3 |
67
.github/workflows/mac-unix-artifact-creation.yml
vendored
Normal file
67
.github/workflows/mac-unix-artifact-creation.yml
vendored
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
name: Create Application Bundles
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- 'mac'
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
create-unix-bundle:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up JDK
|
||||||
|
uses: actions/setup-java@v4
|
||||||
|
with:
|
||||||
|
java-version: '21'
|
||||||
|
distribution: 'temurin'
|
||||||
|
|
||||||
|
- name: Build with Gradle
|
||||||
|
run: ./gradlew bootJar
|
||||||
|
|
||||||
|
- name: Create tar.gz Bundle
|
||||||
|
run: |
|
||||||
|
mkdir -p Stirling-PDF-unix
|
||||||
|
cp build/libs/Stirling-PDF-*.jar Stirling-PDF-unix/Stirling-PDF.jar
|
||||||
|
cp scripts/launcher.sh Stirling-PDF-unix/Stirling-PDF
|
||||||
|
cp src/main/resources/static/favicon.ico Stirling-PDF-unix/icon.png
|
||||||
|
chmod +x Stirling-PDF-unix/Stirling-PDF
|
||||||
|
tar -czf Stirling-PDF-unix.tar.gz Stirling-PDF-unix/
|
||||||
|
|
||||||
|
- name: Upload Unix Bundle
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: unix-bundle
|
||||||
|
path: Stirling-PDF-unix.tar.gz
|
||||||
|
|
||||||
|
create-mac-bundle:
|
||||||
|
runs-on: macos-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up JDK
|
||||||
|
uses: actions/setup-java@v4
|
||||||
|
with:
|
||||||
|
java-version: '21'
|
||||||
|
distribution: 'temurin'
|
||||||
|
|
||||||
|
- name: Build with Gradle
|
||||||
|
run: ./gradlew bootJar
|
||||||
|
|
||||||
|
- name: Create Mac App Bundle
|
||||||
|
run: |
|
||||||
|
cp build/libs/Stirling-PDF-*.jar build/libs/Stirling-PDF.jar
|
||||||
|
chmod +x scripts/create-mac-launcher.sh
|
||||||
|
./scripts/create-mac-launcher.sh
|
||||||
|
|
||||||
|
- name: Create DMG
|
||||||
|
run: |
|
||||||
|
hdiutil create -volname "Stirling-PDF" -srcfolder "Stirling-PDF.app" -ov -format UDZO "Stirling-PDF-mac.dmg"
|
||||||
|
|
||||||
|
- name: Upload Mac Bundle
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: mac-bundle
|
||||||
|
path: Stirling-PDF-mac.dmg
|
||||||
73
README.md
73
README.md
@@ -19,7 +19,7 @@ All files and PDFs exist either exclusively on the client side, reside in server
|
|||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- Enterprise features like SSO Check [here](https://docs.stirlingpdf.com/Enterprise%20Edition)
|
- Enterprise features like SSO Check [here](https://docs.stirlingpdf.com/Enterprise%20Edition)
|
||||||
- Dark mode support
|
- Dark mode support
|
||||||
- Custom download options
|
- Custom download options
|
||||||
- Parallel file processing and downloads
|
- Parallel file processing and downloads
|
||||||
@@ -187,47 +187,48 @@ Certain functionality like `Sign` supports pre-saved files stored at `/customFil
|
|||||||
|
|
||||||
## Supported Languages
|
## Supported Languages
|
||||||
|
|
||||||
Stirling-PDF currently supports 37 languages!
|
Stirling-PDF currently supports 38 languages!
|
||||||
|
|
||||||
| Language | Progress |
|
| Language | Progress |
|
||||||
| -------------------------------------------- | -------------------------------------- |
|
| -------------------------------------------- | -------------------------------------- |
|
||||||
| Arabic (العربية) (ar_AR) |  |
|
| Arabic (العربية) (ar_AR) |  |
|
||||||
| Azerbaijani (Azərbaycan Dili) (az_AZ) |  |
|
| Azerbaijani (Azərbaycan Dili) (az_AZ) |  |
|
||||||
| Basque (Euskara) (eu_ES) |  |
|
| Basque (Euskara) (eu_ES) |  |
|
||||||
| Bulgarian (Български) (bg_BG) |  |
|
| Bulgarian (Български) (bg_BG) |  |
|
||||||
| Catalan (Català) (ca_CA) |  |
|
| Catalan (Català) (ca_CA) |  |
|
||||||
| Croatian (Hrvatski) (hr_HR) |  |
|
| Croatian (Hrvatski) (hr_HR) |  |
|
||||||
| Czech (Česky) (cs_CZ) |  |
|
| Czech (Česky) (cs_CZ) |  |
|
||||||
| Danish (Dansk) (da_DK) |  |
|
| Danish (Dansk) (da_DK) |  |
|
||||||
| Dutch (Nederlands) (nl_NL) |  |
|
| Dutch (Nederlands) (nl_NL) |  |
|
||||||
| English (English) (en_GB) |  |
|
| English (English) (en_GB) |  |
|
||||||
| English (US) (en_US) |  |
|
| English (US) (en_US) |  |
|
||||||
| French (Français) (fr_FR) |  |
|
| French (Français) (fr_FR) |  |
|
||||||
| German (Deutsch) (de_DE) |  |
|
| German (Deutsch) (de_DE) |  |
|
||||||
| Greek (Ελληνικά) (el_GR) |  |
|
| Greek (Ελληνικά) (el_GR) |  |
|
||||||
| Hindi (हिंदी) (hi_IN) |  |
|
| Hindi (हिंदी) (hi_IN) |  |
|
||||||
| Hungarian (Magyar) (hu_HU) |  |
|
| Hungarian (Magyar) (hu_HU) |  |
|
||||||
| Indonesian (Bahasa Indonesia) (id_ID) |  |
|
| Indonesian (Bahasa Indonesia) (id_ID) |  |
|
||||||
| Irish (Gaeilge) (ga_IE) |  |
|
| Irish (Gaeilge) (ga_IE) |  |
|
||||||
| Italian (Italiano) (it_IT) |  |
|
| Italian (Italiano) (it_IT) |  |
|
||||||
| Japanese (日本語) (ja_JP) |  |
|
| Japanese (日本語) (ja_JP) |  |
|
||||||
| Korean (한국어) (ko_KR) |  |
|
| Korean (한국어) (ko_KR) |  |
|
||||||
| Norwegian (Norsk) (no_NB) |  |
|
| Norwegian (Norsk) (no_NB) |  |
|
||||||
| Polish (Polski) (pl_PL) |  |
|
| Persian (فارسی) (fa_IR) |  |
|
||||||
| Portuguese (Português) (pt_PT) |  |
|
| Polish (Polski) (pl_PL) |  |
|
||||||
| Portuguese Brazilian (Português) (pt_BR) |  |
|
| Portuguese (Português) (pt_PT) |  |
|
||||||
| Romanian (Română) (ro_RO) |  |
|
| Portuguese Brazilian (Português) (pt_BR) |  |
|
||||||
| Russian (Русский) (ru_RU) |  |
|
| Romanian (Română) (ro_RO) |  |
|
||||||
| Serbian Latin alphabet (Srpski) (sr_LATN_RS) |  |
|
| Russian (Русский) (ru_RU) |  |
|
||||||
| Simplified Chinese (简体中文) (zh_CN) |  |
|
| Serbian Latin alphabet (Srpski) (sr_LATN_RS) |  |
|
||||||
| Slovakian (Slovensky) (sk_SK) |  |
|
| Simplified Chinese (简体中文) (zh_CN) |  |
|
||||||
| Spanish (Español) (es_ES) |  |
|
| Slovakian (Slovensky) (sk_SK) |  |
|
||||||
| Swedish (Svenska) (sv_SE) |  |
|
| Spanish (Español) (es_ES) |  |
|
||||||
| Thai (ไทย) (th_TH) |  |
|
| Swedish (Svenska) (sv_SE) |  |
|
||||||
| Traditional Chinese (繁體中文) (zh_TW) |  |
|
| Thai (ไทย) (th_TH) |  |
|
||||||
| Turkish (Türkçe) (tr_TR) |  |
|
| Traditional Chinese (繁體中文) (zh_TW) |  |
|
||||||
| Ukrainian (Українська) (uk_UA) |  |
|
| Turkish (Türkçe) (tr_TR) |  |
|
||||||
| Vietnamese (Tiếng Việt) (vi_VN) |  |
|
| Ukrainian (Українська) (uk_UA) |  |
|
||||||
|
| Vietnamese (Tiếng Việt) (vi_VN) |  |
|
||||||
|
|
||||||
## Contributing (Creating Issues, Translations, Fixing Bugs, etc.)
|
## Contributing (Creating Issues, Translations, Fixing Bugs, etc.)
|
||||||
|
|
||||||
@@ -240,7 +241,7 @@ Stirling PDF offers a Enterprise edition of its software, This is the same great
|
|||||||
### Whats included
|
### Whats included
|
||||||
|
|
||||||
- Prioritised Support tickets via support@stirlingpdf.com to reach directly to Stirling-PDF team for support and 1:1 meetings where applicable (Provided they come from same email domain registered with us)
|
- Prioritised Support tickets via support@stirlingpdf.com to reach directly to Stirling-PDF team for support and 1:1 meetings where applicable (Provided they come from same email domain registered with us)
|
||||||
- Prioritised Enhancements to Stirling-PDF where applicable
|
- Prioritised Enhancements to Stirling-PDF where applicable
|
||||||
- Base SSO support
|
- Base SSO support
|
||||||
- Advanced SSO such as automated login handling (Coming very soon)
|
- Advanced SSO such as automated login handling (Coming very soon)
|
||||||
- SAML SSO (Coming very soon)
|
- SAML SSO (Coming very soon)
|
||||||
|
|||||||
17
build.gradle
17
build.gradle
@@ -21,10 +21,12 @@ ext {
|
|||||||
imageioVersion = "3.12.0"
|
imageioVersion = "3.12.0"
|
||||||
lombokVersion = "1.18.36"
|
lombokVersion = "1.18.36"
|
||||||
bouncycastleVersion = "1.79"
|
bouncycastleVersion = "1.79"
|
||||||
|
springSecuritySamlVersion = "6.4.1"
|
||||||
|
openSamlVersion = "4.3.2"
|
||||||
}
|
}
|
||||||
|
|
||||||
group = "stirling.software"
|
group = "stirling.software"
|
||||||
version = "0.35.0"
|
version = "0.36.0"
|
||||||
|
|
||||||
|
|
||||||
java {
|
java {
|
||||||
@@ -144,17 +146,18 @@ dependencies {
|
|||||||
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"
|
||||||
|
|
||||||
implementation 'org.springframework.security:spring-security-saml2-service-provider:6.4.1'
|
implementation "org.springframework.session:spring-session-core:$springBootVersion"
|
||||||
|
|
||||||
implementation 'com.unboundid.product.scim2:scim2-sdk-client:2.3.5'
|
implementation 'com.unboundid.product.scim2:scim2-sdk-client:2.3.5'
|
||||||
// Don't upgrade h2database
|
// Don't upgrade h2database
|
||||||
runtimeOnly "com.h2database:h2:2.3.232"
|
runtimeOnly "com.h2database:h2:2.3.232"
|
||||||
constraints {
|
constraints {
|
||||||
implementation "org.opensaml:opensaml-core"
|
implementation "org.opensaml:opensaml-core:$openSamlVersion"
|
||||||
implementation "org.opensaml:opensaml-saml-api"
|
implementation "org.opensaml:opensaml-saml-api:$openSamlVersion"
|
||||||
implementation "org.opensaml:opensaml-saml-impl"
|
implementation "org.opensaml:opensaml-saml-impl:$openSamlVersion"
|
||||||
}
|
}
|
||||||
implementation "org.springframework.security:spring-security-saml2-service-provider"
|
implementation "org.springframework.security:spring-security-saml2-service-provider:$springSecuritySamlVersion"
|
||||||
|
// implementation 'org.springframework.security:spring-security-core:$springSecuritySamlVersion'
|
||||||
implementation 'com.coveo:saml-client:5.0.0'
|
implementation 'com.coveo:saml-client:5.0.0'
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -48,24 +48,6 @@ Feature: API Validation
|
|||||||
And the response status code should be 200
|
And the response status code should be 200
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ocr @negative
|
|
||||||
Scenario: Process PDF with text and OCR with type normal
|
|
||||||
Given I generate a PDF file as "fileInput"
|
|
||||||
And the pdf contains 3 pages with random text
|
|
||||||
And the request data includes
|
|
||||||
| parameter | value |
|
|
||||||
| languages | eng |
|
|
||||||
| sidecar | false |
|
|
||||||
| deskew | true |
|
|
||||||
| clean | true |
|
|
||||||
| cleanFinal | true |
|
|
||||||
| ocrType | Normal |
|
|
||||||
| ocrRenderType | hocr |
|
|
||||||
| removeImagesAfter| false |
|
|
||||||
When I send the API request to the endpoint "/api/v1/misc/ocr-pdf"
|
|
||||||
Then the response status code should be 500
|
|
||||||
|
|
||||||
@ocr @positive
|
@ocr @positive
|
||||||
Scenario: Process PDF with OCR
|
Scenario: Process PDF with OCR
|
||||||
Given I generate a PDF file as "fileInput"
|
Given I generate a PDF file as "fileInput"
|
||||||
@@ -83,26 +65,6 @@ Feature: API Validation
|
|||||||
Then the response content type should be "application/pdf"
|
Then the response content type should be "application/pdf"
|
||||||
And the response file should have size greater than 0
|
And the response file should have size greater than 0
|
||||||
And the response status code should be 200
|
And the response status code should be 200
|
||||||
|
|
||||||
@ocr @positive
|
|
||||||
Scenario: Process PDF with OCR with sidecar
|
|
||||||
Given I generate a PDF file as "fileInput"
|
|
||||||
And the request data includes
|
|
||||||
| parameter | value |
|
|
||||||
| languages | eng |
|
|
||||||
| sidecar | true |
|
|
||||||
| deskew | true |
|
|
||||||
| clean | true |
|
|
||||||
| cleanFinal | true |
|
|
||||||
| ocrType | Force |
|
|
||||||
| ocrRenderType | hocr |
|
|
||||||
| removeImagesAfter| false |
|
|
||||||
When I send the API request to the endpoint "/api/v1/misc/ocr-pdf"
|
|
||||||
Then the response content type should be "application/octet-stream"
|
|
||||||
And the response file should have extension ".zip"
|
|
||||||
And the response ZIP should contain 2 files
|
|
||||||
And the response file should have size greater than 0
|
|
||||||
And the response status code should be 200
|
|
||||||
|
|
||||||
|
|
||||||
@libre @positive
|
@libre @positive
|
||||||
|
|||||||
76
scripts/create-mac-launcher.sh
Normal file
76
scripts/create-mac-launcher.sh
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# scripts/create-mac-launcher.sh
|
||||||
|
|
||||||
|
# Create app structure
|
||||||
|
APP_NAME="Stirling-PDF"
|
||||||
|
APP_BUNDLE="$APP_NAME.app"
|
||||||
|
mkdir -p "$APP_BUNDLE/Contents/"{MacOS,Resources,Java}
|
||||||
|
|
||||||
|
# Convert icon
|
||||||
|
sips -s format icns "src/main/resources/static/favicon.ico" --out "$APP_BUNDLE/Contents/Resources/AppIcon.icns"
|
||||||
|
|
||||||
|
# Create Info.plist
|
||||||
|
cat > "$APP_BUNDLE/Contents/Info.plist" << EOF
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>$APP_NAME</string>
|
||||||
|
<key>CFBundleIconFile</key>
|
||||||
|
<string>AppIcon</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>com.frooodle.stirlingpdf</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>$APP_NAME</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>APPL</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>1.0</string>
|
||||||
|
<key>LSMinimumSystemVersion</key>
|
||||||
|
<string>10.10.0</string>
|
||||||
|
<key>LSEnvironment</key>
|
||||||
|
<dict>
|
||||||
|
<key>BROWSER_OPEN</key>
|
||||||
|
<string>true</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Create launcher script
|
||||||
|
cat > "$APP_BUNDLE/Contents/MacOS/$APP_NAME" << 'EOF'
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Configuration
|
||||||
|
MIN_JAVA_VERSION="17"
|
||||||
|
PREFERRED_JAVA_VERSION="21"
|
||||||
|
JAVA_DOWNLOAD_URL="https://download.oracle.com/java/21/latest/jdk-21_macos-x64_bin.dmg"
|
||||||
|
|
||||||
|
# Check Java version
|
||||||
|
if type -p java > /dev/null; then
|
||||||
|
_java=java
|
||||||
|
elif [[ -n "$JAVA_HOME" ]] && [[ -x "$JAVA_HOME/bin/java" ]]; then
|
||||||
|
_java="$JAVA_HOME/bin/java"
|
||||||
|
else
|
||||||
|
osascript -e 'display dialog "Java not found. Please install Java 21." buttons {"Download Java", "Cancel"} default button "Download Java"' \
|
||||||
|
&& open "$JAVA_DOWNLOAD_URL"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
version=$("$_java" -version 2>&1 | awk -F '"' '/version/ {print $2}' | cut -d'.' -f1)
|
||||||
|
if [[ "$version" -lt "$MIN_JAVA_VERSION" ]]; then
|
||||||
|
osascript -e 'display dialog "Wrong Java version. Please install Java 21." buttons {"Download Java", "Cancel"} default button "Download Java"' \
|
||||||
|
&& open "$JAVA_DOWNLOAD_URL"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Run application
|
||||||
|
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||||
|
exec java -jar "$DIR/../Java/Stirling-PDF.jar" "$@"
|
||||||
|
EOF
|
||||||
|
|
||||||
|
chmod +x "$APP_BUNDLE/Contents/MacOS/$APP_NAME"
|
||||||
|
|
||||||
|
# Copy JAR file
|
||||||
|
cp "build/libs/Stirling-PDF.jar" "$APP_BUNDLE/Contents/Java/"
|
||||||
36
scripts/create-unix-launcher.sh
Normal file
36
scripts/create-unix-launcher.sh
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# scripts/create-unix-launcher.sh
|
||||||
|
|
||||||
|
cat > launcher.sh << 'EOF'
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Configuration
|
||||||
|
APP_NAME="Stirling-PDF"
|
||||||
|
MIN_JAVA_VERSION="17"
|
||||||
|
PREFERRED_JAVA_VERSION="21"
|
||||||
|
JAVA_DOWNLOAD_URL="https://download.oracle.com/java/21/latest/jdk-21_linux-x64_bin.tar.gz"
|
||||||
|
BROWSER_OPEN="true"
|
||||||
|
|
||||||
|
# Check Java version
|
||||||
|
if type -p java > /dev/null; then
|
||||||
|
_java=java
|
||||||
|
elif [[ -n "$JAVA_HOME" ]] && [[ -x "$JAVA_HOME/bin/java" ]]; then
|
||||||
|
_java="$JAVA_HOME/bin/java"
|
||||||
|
else
|
||||||
|
echo "Java not found. Please install Java 21."
|
||||||
|
xdg-open "$JAVA_DOWNLOAD_URL"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
version=$("$_java" -version 2>&1 | awk -F '"' '/version/ {print $2}' | cut -d'.' -f1)
|
||||||
|
if [[ "$version" -lt "$MIN_JAVA_VERSION" ]]; then
|
||||||
|
echo "Java version $version detected. Please install Java 21."
|
||||||
|
xdg-open "$JAVA_DOWNLOAD_URL"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Run application
|
||||||
|
exec java -jar "$(dirname "$0")/Stirling-PDF.jar" "$@"
|
||||||
|
EOF
|
||||||
|
|
||||||
|
chmod +x launcher.sh
|
||||||
@@ -42,14 +42,19 @@ 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',
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -61,7 +66,6 @@ ignore = [
|
|||||||
[es_ES]
|
[es_ES]
|
||||||
ignore = [
|
ignore = [
|
||||||
'adminUserSettings.roles',
|
'adminUserSettings.roles',
|
||||||
'color',
|
|
||||||
'error',
|
'error',
|
||||||
'language.direction',
|
'language.direction',
|
||||||
'no',
|
'no',
|
||||||
|
|||||||
28
scripts/scripts/launcher.sh
Normal file
28
scripts/scripts/launcher.sh
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Configuration
|
||||||
|
MIN_JAVA_VERSION="17"
|
||||||
|
PREFERRED_JAVA_VERSION="21"
|
||||||
|
JAVA_DOWNLOAD_URL="https://download.oracle.com/java/21/latest/jdk-21_linux-x64_bin.tar.gz"
|
||||||
|
BROWSER_OPEN="true"
|
||||||
|
|
||||||
|
# Check Java version
|
||||||
|
if type -p java > /dev/null; then
|
||||||
|
_java=java
|
||||||
|
elif [[ -n "$JAVA_HOME" ]] && [[ -x "$JAVA_HOME/bin/java" ]]; then
|
||||||
|
_java="$JAVA_HOME/bin/java"
|
||||||
|
else
|
||||||
|
echo "Java not found. Please install Java 21."
|
||||||
|
xdg-open "$JAVA_DOWNLOAD_URL"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
version=$("$_java" -version 2>&1 | awk -F '"' '/version/ {print $2}' | cut -d'.' -f1)
|
||||||
|
if [[ "$version" -lt "$MIN_JAVA_VERSION" ]]; then
|
||||||
|
echo "Java version $version detected. Please install Java 21."
|
||||||
|
xdg-open "$JAVA_DOWNLOAD_URL"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Run application
|
||||||
|
exec java -jar "$(dirname "$0")/Stirling-PDF.jar" "$@"
|
||||||
@@ -3,13 +3,14 @@ package stirling.software.SPDF.EE;
|
|||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.context.annotation.Lazy;
|
import org.springframework.core.Ordered;
|
||||||
|
import org.springframework.core.annotation.Order;
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import stirling.software.SPDF.model.ApplicationProperties;
|
import stirling.software.SPDF.model.ApplicationProperties;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@Lazy
|
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class EEAppConfig {
|
public class EEAppConfig {
|
||||||
|
|
||||||
|
|||||||
@@ -25,9 +25,10 @@ public class LicenseKeyChecker {
|
|||||||
KeygenLicenseVerifier licenseService, ApplicationProperties applicationProperties) {
|
KeygenLicenseVerifier licenseService, ApplicationProperties applicationProperties) {
|
||||||
this.licenseService = licenseService;
|
this.licenseService = licenseService;
|
||||||
this.applicationProperties = applicationProperties;
|
this.applicationProperties = applicationProperties;
|
||||||
|
this.checkLicense();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Scheduled(fixedRate = 604800000, initialDelay = 1000) // 7 days in milliseconds
|
@Scheduled(initialDelay = 604800000,fixedRate = 604800000) // 7 days in milliseconds
|
||||||
public void checkLicensePeriodically() {
|
public void checkLicensePeriodically() {
|
||||||
checkLicense();
|
checkLicense();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,7 +43,6 @@ public class ExternalAppDepConfig {
|
|||||||
put("unoconv", List.of("Unoconv"));
|
put("unoconv", List.of("Unoconv"));
|
||||||
put("qpdf", List.of("qpdf"));
|
put("qpdf", List.of("qpdf"));
|
||||||
put("tesseract", List.of("tesseract"));
|
put("tesseract", List.of("tesseract"));
|
||||||
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -98,7 +97,7 @@ public class ExternalAppDepConfig {
|
|||||||
public void checkDependencies() {
|
public void checkDependencies() {
|
||||||
|
|
||||||
// Check core dependencies
|
// Check core dependencies
|
||||||
checkDependencyAndDisableGroup("tesseract");
|
checkDependencyAndDisableGroup("tesseract");
|
||||||
checkDependencyAndDisableGroup("soffice");
|
checkDependencyAndDisableGroup("soffice");
|
||||||
checkDependencyAndDisableGroup("qpdf");
|
checkDependencyAndDisableGroup("qpdf");
|
||||||
checkDependencyAndDisableGroup("weasyprint");
|
checkDependencyAndDisableGroup("weasyprint");
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ public class InitialSecuritySetup {
|
|||||||
initializeAdminUser();
|
initializeAdminUser();
|
||||||
} else {
|
} else {
|
||||||
databaseBackupHelper.exportDatabase();
|
databaseBackupHelper.exportDatabase();
|
||||||
|
userService.migrateOauth2ToSSO();
|
||||||
}
|
}
|
||||||
initializeInternalApiUser();
|
initializeInternalApiUser();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,14 +3,16 @@ package stirling.software.SPDF.config.security;
|
|||||||
import java.security.cert.X509Certificate;
|
import java.security.cert.X509Certificate;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
|
import org.opensaml.saml.saml2.core.AuthnRequest;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Qualifier;
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
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.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;
|
||||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
@@ -32,7 +34,8 @@ import org.springframework.security.saml2.provider.service.authentication.OpenSa
|
|||||||
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.web.authentication.Saml2WebSsoAuthenticationFilter;
|
import org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding;
|
||||||
|
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;
|
||||||
@@ -41,6 +44,7 @@ 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.util.matcher.AntPathRequestMatcher;
|
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||||
|
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
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;
|
||||||
@@ -64,6 +68,7 @@ import stirling.software.SPDF.repository.JPATokenRepositoryImpl;
|
|||||||
@EnableWebSecurity
|
@EnableWebSecurity
|
||||||
@EnableMethodSecurity
|
@EnableMethodSecurity
|
||||||
@Slf4j
|
@Slf4j
|
||||||
|
@DependsOn("runningEE")
|
||||||
public class SecurityConfiguration {
|
public class SecurityConfiguration {
|
||||||
|
|
||||||
@Autowired private CustomUserDetailsService userDetailsService;
|
@Autowired private CustomUserDetailsService userDetailsService;
|
||||||
@@ -79,6 +84,10 @@ public class SecurityConfiguration {
|
|||||||
@Qualifier("loginEnabled")
|
@Qualifier("loginEnabled")
|
||||||
public boolean loginEnabledValue;
|
public boolean loginEnabledValue;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
@Qualifier("runningEE")
|
||||||
|
public boolean runningEE;
|
||||||
|
|
||||||
@Autowired ApplicationProperties applicationProperties;
|
@Autowired ApplicationProperties applicationProperties;
|
||||||
|
|
||||||
@Autowired private UserAuthenticationFilter userAuthenticationFilter;
|
@Autowired private UserAuthenticationFilter userAuthenticationFilter;
|
||||||
@@ -90,13 +99,14 @@ public class SecurityConfiguration {
|
|||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||||
|
if (applicationProperties.getSecurity().getCsrfDisabled()) {
|
||||||
|
http.csrf(csrf -> csrf.disable());
|
||||||
|
}
|
||||||
|
|
||||||
if (loginEnabledValue) {
|
if (loginEnabledValue) {
|
||||||
http.addFilterBefore(
|
http.addFilterBefore(
|
||||||
userAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
|
userAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
|
||||||
if (applicationProperties.getSecurity().getCsrfDisabled()) {
|
if (!applicationProperties.getSecurity().getCsrfDisabled()) {
|
||||||
http.csrf(csrf -> csrf.disable());
|
|
||||||
} else {
|
|
||||||
CookieCsrfTokenRepository cookieRepo =
|
CookieCsrfTokenRepository cookieRepo =
|
||||||
CookieCsrfTokenRepository.withHttpOnlyFalse();
|
CookieCsrfTokenRepository.withHttpOnlyFalse();
|
||||||
CsrfTokenRequestAttributeHandler requestHandler =
|
CsrfTokenRequestAttributeHandler requestHandler =
|
||||||
@@ -245,12 +255,22 @@ public class SecurityConfiguration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Handle SAML
|
// Handle SAML
|
||||||
if (applicationProperties.getSecurity().isSaml2Activ()
|
if (applicationProperties.getSecurity().isSaml2Activ()) { // && runningEE
|
||||||
&& applicationProperties.getSystem().getEnableAlphaFunctionality()) {
|
// Configure the authentication provider
|
||||||
http.authenticationProvider(samlAuthenticationProvider());
|
OpenSaml4AuthenticationProvider authenticationProvider =
|
||||||
http.saml2Login(
|
new OpenSaml4AuthenticationProvider();
|
||||||
saml2 ->
|
authenticationProvider.setResponseAuthenticationConverter(
|
||||||
|
new CustomSaml2ResponseAuthenticationConverter(userService));
|
||||||
|
|
||||||
|
http.authenticationProvider(authenticationProvider)
|
||||||
|
.saml2Login(
|
||||||
|
saml2 -> {
|
||||||
|
try {
|
||||||
saml2.loginPage("/saml2")
|
saml2.loginPage("/saml2")
|
||||||
|
.relyingPartyRegistrationRepository(
|
||||||
|
relyingPartyRegistrations())
|
||||||
|
.authenticationManager(
|
||||||
|
new ProviderManager(authenticationProvider))
|
||||||
.successHandler(
|
.successHandler(
|
||||||
new CustomSaml2AuthenticationSuccessHandler(
|
new CustomSaml2AuthenticationSuccessHandler(
|
||||||
loginAttemptService,
|
loginAttemptService,
|
||||||
@@ -258,14 +278,18 @@ public class SecurityConfiguration {
|
|||||||
userService))
|
userService))
|
||||||
.failureHandler(
|
.failureHandler(
|
||||||
new CustomSaml2AuthenticationFailureHandler())
|
new CustomSaml2AuthenticationFailureHandler())
|
||||||
.permitAll())
|
.authenticationRequestResolver(
|
||||||
.addFilterBefore(
|
authenticationRequestResolver(
|
||||||
userAuthenticationFilter, Saml2WebSsoAuthenticationFilter.class);
|
relyingPartyRegistrations()));
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Error configuring SAML2 login", e);
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if (applicationProperties.getSecurity().getCsrfDisabled()) {
|
if (!applicationProperties.getSecurity().getCsrfDisabled()) {
|
||||||
http.csrf(csrf -> csrf.disable());
|
|
||||||
} else {
|
|
||||||
CookieCsrfTokenRepository cookieRepo =
|
CookieCsrfTokenRepository cookieRepo =
|
||||||
CookieCsrfTokenRepository.withHttpOnlyFalse();
|
CookieCsrfTokenRepository.withHttpOnlyFalse();
|
||||||
CsrfTokenRequestAttributeHandler requestHandler =
|
CsrfTokenRequestAttributeHandler requestHandler =
|
||||||
@@ -282,20 +306,6 @@ public class SecurityConfiguration {
|
|||||||
return http.build();
|
return http.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
|
||||||
@ConditionalOnProperty(
|
|
||||||
name = "security.saml2.enabled",
|
|
||||||
havingValue = "true",
|
|
||||||
matchIfMissing = false)
|
|
||||||
public AuthenticationProvider samlAuthenticationProvider() {
|
|
||||||
OpenSaml4AuthenticationProvider authenticationProvider =
|
|
||||||
new OpenSaml4AuthenticationProvider();
|
|
||||||
authenticationProvider.setResponseAuthenticationConverter(
|
|
||||||
new CustomSaml2ResponseAuthenticationConverter(userService));
|
|
||||||
return authenticationProvider;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Client Registration Repository for OAUTH2 OIDC Login
|
|
||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnProperty(
|
@ConditionalOnProperty(
|
||||||
value = "security.oauth2.enabled",
|
value = "security.oauth2.enabled",
|
||||||
@@ -432,11 +442,12 @@ public class SecurityConfiguration {
|
|||||||
havingValue = "true",
|
havingValue = "true",
|
||||||
matchIfMissing = false)
|
matchIfMissing = false)
|
||||||
public RelyingPartyRegistrationRepository relyingPartyRegistrations() throws Exception {
|
public RelyingPartyRegistrationRepository relyingPartyRegistrations() throws Exception {
|
||||||
|
|
||||||
SAML2 samlConf = applicationProperties.getSecurity().getSaml2();
|
SAML2 samlConf = applicationProperties.getSecurity().getSaml2();
|
||||||
|
|
||||||
Resource privateKeyResource = samlConf.getPrivateKey();
|
X509Certificate idpCert = CertificateUtils.readCertificate(samlConf.getidpCert());
|
||||||
|
Saml2X509Credential verificationCredential = Saml2X509Credential.verification(idpCert);
|
||||||
|
|
||||||
|
Resource privateKeyResource = samlConf.getPrivateKey();
|
||||||
Resource certificateResource = samlConf.getSpCert();
|
Resource certificateResource = samlConf.getSpCert();
|
||||||
|
|
||||||
Saml2X509Credential signingCredential =
|
Saml2X509Credential signingCredential =
|
||||||
@@ -445,26 +456,97 @@ public class SecurityConfiguration {
|
|||||||
CertificateUtils.readCertificate(certificateResource),
|
CertificateUtils.readCertificate(certificateResource),
|
||||||
Saml2X509CredentialType.SIGNING);
|
Saml2X509CredentialType.SIGNING);
|
||||||
|
|
||||||
X509Certificate idpCert = CertificateUtils.readCertificate(samlConf.getidpCert());
|
|
||||||
|
|
||||||
Saml2X509Credential verificationCredential = Saml2X509Credential.verification(idpCert);
|
|
||||||
|
|
||||||
RelyingPartyRegistration rp =
|
RelyingPartyRegistration rp =
|
||||||
RelyingPartyRegistration.withRegistrationId(samlConf.getRegistrationId())
|
RelyingPartyRegistration.withRegistrationId(samlConf.getRegistrationId())
|
||||||
.signingX509Credentials((c) -> c.add(signingCredential))
|
.signingX509Credentials(c -> c.add(signingCredential))
|
||||||
.assertingPartyMetadata(
|
.assertingPartyMetadata(
|
||||||
(details) ->
|
metadata ->
|
||||||
details.entityId(samlConf.getIdpIssuer())
|
metadata.entityId(samlConf.getIdpIssuer())
|
||||||
.singleSignOnServiceLocation(
|
.singleSignOnServiceLocation(
|
||||||
samlConf.getIdpSingleLoginUrl())
|
samlConf.getIdpSingleLoginUrl())
|
||||||
.verificationX509Credentials(
|
.verificationX509Credentials(
|
||||||
(c) -> c.add(verificationCredential))
|
c -> c.add(verificationCredential))
|
||||||
|
.singleSignOnServiceBinding(
|
||||||
|
Saml2MessageBinding.POST)
|
||||||
.wantAuthnRequestsSigned(true))
|
.wantAuthnRequestsSigned(true))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
return new InMemoryRelyingPartyRegistrationRepository(rp);
|
return new InMemoryRelyingPartyRegistrationRepository(rp);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
|
@ConditionalOnProperty(
|
||||||
|
name = "security.saml2.enabled",
|
||||||
|
havingValue = "true",
|
||||||
|
matchIfMissing = false)
|
||||||
|
public OpenSaml4AuthenticationRequestResolver authenticationRequestResolver(
|
||||||
|
RelyingPartyRegistrationRepository relyingPartyRegistrationRepository) {
|
||||||
|
OpenSaml4AuthenticationRequestResolver resolver =
|
||||||
|
new OpenSaml4AuthenticationRequestResolver(relyingPartyRegistrationRepository);
|
||||||
|
resolver.setAuthnRequestCustomizer(
|
||||||
|
customizer -> {
|
||||||
|
log.debug("Customizing SAML Authentication request");
|
||||||
|
|
||||||
|
AuthnRequest authnRequest = customizer.getAuthnRequest();
|
||||||
|
log.debug("AuthnRequest ID: {}", authnRequest.getID());
|
||||||
|
|
||||||
|
if (authnRequest.getID() == null) {
|
||||||
|
authnRequest.setID("ARQ" + UUID.randomUUID().toString());
|
||||||
|
}
|
||||||
|
log.debug("AuthnRequest new ID after set: {}", authnRequest.getID());
|
||||||
|
log.debug("AuthnRequest IssueInstant: {}", authnRequest.getIssueInstant());
|
||||||
|
log.debug(
|
||||||
|
"AuthnRequest Issuer: {}",
|
||||||
|
authnRequest.getIssuer() != null
|
||||||
|
? authnRequest.getIssuer().getValue()
|
||||||
|
: "null");
|
||||||
|
|
||||||
|
HttpServletRequest request = customizer.getRequest();
|
||||||
|
|
||||||
|
// Log HTTP request details
|
||||||
|
log.debug("HTTP Request Method: {}", request.getMethod());
|
||||||
|
log.debug("Request URI: {}", request.getRequestURI());
|
||||||
|
log.debug("Request URL: {}", request.getRequestURL().toString());
|
||||||
|
log.debug("Query String: {}", request.getQueryString());
|
||||||
|
log.debug("Remote Address: {}", request.getRemoteAddr());
|
||||||
|
|
||||||
|
// Log headers
|
||||||
|
Collections.list(request.getHeaderNames())
|
||||||
|
.forEach(
|
||||||
|
headerName -> {
|
||||||
|
log.debug(
|
||||||
|
"Header - {}: {}",
|
||||||
|
headerName,
|
||||||
|
request.getHeader(headerName));
|
||||||
|
});
|
||||||
|
|
||||||
|
// Log SAML specific parameters
|
||||||
|
log.debug("SAML Request Parameters:");
|
||||||
|
log.debug("SAMLRequest: {}", request.getParameter("SAMLRequest"));
|
||||||
|
log.debug("RelayState: {}", request.getParameter("RelayState"));
|
||||||
|
|
||||||
|
// Log session debugrmation if exists
|
||||||
|
if (request.getSession(false) != null) {
|
||||||
|
log.debug("Session ID: {}", request.getSession().getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log any assertions consumer service details if present
|
||||||
|
if (authnRequest.getAssertionConsumerServiceURL() != null) {
|
||||||
|
log.debug(
|
||||||
|
"AssertionConsumerServiceURL: {}",
|
||||||
|
authnRequest.getAssertionConsumerServiceURL());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log NameID policy if present
|
||||||
|
if (authnRequest.getNameIDPolicy() != null) {
|
||||||
|
log.debug(
|
||||||
|
"NameIDPolicy Format: {}",
|
||||||
|
authnRequest.getNameIDPolicy().getFormat());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return resolver;
|
||||||
|
}
|
||||||
|
|
||||||
public DaoAuthenticationProvider daoAuthenticationProvider() {
|
public DaoAuthenticationProvider daoAuthenticationProvider() {
|
||||||
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
|
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
|
||||||
provider.setUserDetailsService(userDetailsService);
|
provider.setUserDetailsService(userDetailsService);
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
|||||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
import org.springframework.security.oauth2.core.user.OAuth2User;
|
import org.springframework.security.oauth2.core.user.OAuth2User;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import stirling.software.SPDF.config.interfaces.DatabaseBackupInterface;
|
import stirling.software.SPDF.config.interfaces.DatabaseBackupInterface;
|
||||||
@@ -50,8 +51,19 @@ public class UserService implements UserServiceInterface {
|
|||||||
|
|
||||||
@Autowired ApplicationProperties applicationProperties;
|
@Autowired ApplicationProperties applicationProperties;
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public void migrateOauth2ToSSO() {
|
||||||
|
userRepository
|
||||||
|
.findByAuthenticationTypeIgnoreCase("OAUTH2")
|
||||||
|
.forEach(
|
||||||
|
user -> {
|
||||||
|
user.setAuthenticationType(AuthenticationType.SSO);
|
||||||
|
userRepository.save(user);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Handle OAUTH2 login and user auto creation.
|
// Handle OAUTH2 login and user auto creation.
|
||||||
public boolean processOAuth2PostLogin(String username, boolean autoCreateUser)
|
public boolean processSSOPostLogin(String username, boolean autoCreateUser)
|
||||||
throws IllegalArgumentException, IOException {
|
throws IllegalArgumentException, IOException {
|
||||||
if (!isUsernameValid(username)) {
|
if (!isUsernameValid(username)) {
|
||||||
return false;
|
return false;
|
||||||
@@ -61,7 +73,7 @@ public class UserService implements UserServiceInterface {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (autoCreateUser) {
|
if (autoCreateUser) {
|
||||||
saveUser(username, AuthenticationType.OAUTH2);
|
saveUser(username, AuthenticationType.SSO);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -82,8 +82,7 @@ public class CustomOAuth2AuthenticationSuccessHandler
|
|||||||
}
|
}
|
||||||
if (userService.usernameExistsIgnoreCase(username)
|
if (userService.usernameExistsIgnoreCase(username)
|
||||||
&& userService.hasPassword(username)
|
&& userService.hasPassword(username)
|
||||||
&& !userService.isAuthenticationTypeByUsername(
|
&& !userService.isAuthenticationTypeByUsername(username, AuthenticationType.SSO)
|
||||||
username, AuthenticationType.OAUTH2)
|
|
||||||
&& oAuth.getAutoCreateUser()) {
|
&& oAuth.getAutoCreateUser()) {
|
||||||
response.sendRedirect(contextPath + "/logout?oauth2AuthenticationErrorWeb=true");
|
response.sendRedirect(contextPath + "/logout?oauth2AuthenticationErrorWeb=true");
|
||||||
return;
|
return;
|
||||||
@@ -95,7 +94,7 @@ public class CustomOAuth2AuthenticationSuccessHandler
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (principal instanceof OAuth2User) {
|
if (principal instanceof OAuth2User) {
|
||||||
userService.processOAuth2PostLogin(username, oAuth.getAutoCreateUser());
|
userService.processSSOPostLogin(username, oAuth.getAutoCreateUser());
|
||||||
}
|
}
|
||||||
response.sendRedirect(contextPath + "/");
|
response.sendRedirect(contextPath + "/");
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -3,12 +3,14 @@ package stirling.software.SPDF.config.security.saml2;
|
|||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.security.KeyFactory;
|
|
||||||
import java.security.cert.CertificateFactory;
|
import java.security.cert.CertificateFactory;
|
||||||
import java.security.cert.X509Certificate;
|
import java.security.cert.X509Certificate;
|
||||||
import java.security.interfaces.RSAPrivateKey;
|
import java.security.interfaces.RSAPrivateKey;
|
||||||
import java.security.spec.PKCS8EncodedKeySpec;
|
|
||||||
|
|
||||||
|
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
|
||||||
|
import org.bouncycastle.openssl.PEMKeyPair;
|
||||||
|
import org.bouncycastle.openssl.PEMParser;
|
||||||
|
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
|
||||||
import org.bouncycastle.util.io.pem.PemObject;
|
import org.bouncycastle.util.io.pem.PemObject;
|
||||||
import org.bouncycastle.util.io.pem.PemReader;
|
import org.bouncycastle.util.io.pem.PemReader;
|
||||||
import org.springframework.core.io.Resource;
|
import org.springframework.core.io.Resource;
|
||||||
@@ -28,15 +30,26 @@ public class CertificateUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static RSAPrivateKey readPrivateKey(Resource privateKeyResource) throws Exception {
|
public static RSAPrivateKey readPrivateKey(Resource privateKeyResource) throws Exception {
|
||||||
try (PemReader pemReader =
|
try (PEMParser pemParser =
|
||||||
new PemReader(
|
new PEMParser(
|
||||||
new InputStreamReader(
|
new InputStreamReader(
|
||||||
privateKeyResource.getInputStream(), StandardCharsets.UTF_8))) {
|
privateKeyResource.getInputStream(), StandardCharsets.UTF_8))) {
|
||||||
PemObject pemObject = pemReader.readPemObject();
|
|
||||||
byte[] decodedKey = pemObject.getContent();
|
Object object = pemParser.readObject();
|
||||||
return (RSAPrivateKey)
|
JcaPEMKeyConverter converter = new JcaPEMKeyConverter();
|
||||||
KeyFactory.getInstance("RSA")
|
|
||||||
.generatePrivate(new PKCS8EncodedKeySpec(decodedKey));
|
if (object instanceof PEMKeyPair) {
|
||||||
|
// Handle traditional RSA private key format
|
||||||
|
PEMKeyPair keypair = (PEMKeyPair) object;
|
||||||
|
return (RSAPrivateKey) converter.getPrivateKey(keypair.getPrivateKeyInfo());
|
||||||
|
} else if (object instanceof PrivateKeyInfo) {
|
||||||
|
// Handle PKCS#8 format
|
||||||
|
return (RSAPrivateKey) converter.getPrivateKey((PrivateKeyInfo) object);
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Unsupported key format: "
|
||||||
|
+ (object != null ? object.getClass().getName() : "null"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import jakarta.servlet.http.HttpServletRequest;
|
|||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import jakarta.servlet.http.HttpSession;
|
import jakarta.servlet.http.HttpSession;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import stirling.software.SPDF.config.security.LoginAttemptService;
|
import stirling.software.SPDF.config.security.LoginAttemptService;
|
||||||
import stirling.software.SPDF.config.security.UserService;
|
import stirling.software.SPDF.config.security.UserService;
|
||||||
import stirling.software.SPDF.model.ApplicationProperties;
|
import stirling.software.SPDF.model.ApplicationProperties;
|
||||||
@@ -20,11 +21,11 @@ import stirling.software.SPDF.model.AuthenticationType;
|
|||||||
import stirling.software.SPDF.utils.RequestUriUtils;
|
import stirling.software.SPDF.utils.RequestUriUtils;
|
||||||
|
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
|
@Slf4j
|
||||||
public class CustomSaml2AuthenticationSuccessHandler
|
public class CustomSaml2AuthenticationSuccessHandler
|
||||||
extends SavedRequestAwareAuthenticationSuccessHandler {
|
extends SavedRequestAwareAuthenticationSuccessHandler {
|
||||||
|
|
||||||
private LoginAttemptService loginAttemptService;
|
private LoginAttemptService loginAttemptService;
|
||||||
|
|
||||||
private ApplicationProperties applicationProperties;
|
private ApplicationProperties applicationProperties;
|
||||||
private UserService userService;
|
private UserService userService;
|
||||||
|
|
||||||
@@ -34,10 +35,12 @@ public class CustomSaml2AuthenticationSuccessHandler
|
|||||||
throws ServletException, IOException {
|
throws ServletException, IOException {
|
||||||
|
|
||||||
Object principal = authentication.getPrincipal();
|
Object principal = authentication.getPrincipal();
|
||||||
|
log.debug("Starting SAML2 authentication success handling");
|
||||||
|
|
||||||
if (principal instanceof CustomSaml2AuthenticatedPrincipal) {
|
if (principal instanceof CustomSaml2AuthenticatedPrincipal) {
|
||||||
String username = ((CustomSaml2AuthenticatedPrincipal) principal).getName();
|
String username = ((CustomSaml2AuthenticatedPrincipal) principal).getName();
|
||||||
// Get the saved request
|
log.debug("Authenticated principal found for user: {}", username);
|
||||||
|
|
||||||
HttpSession session = request.getSession(false);
|
HttpSession session = request.getSession(false);
|
||||||
String contextPath = request.getContextPath();
|
String contextPath = request.getContextPath();
|
||||||
SavedRequest savedRequest =
|
SavedRequest savedRequest =
|
||||||
@@ -45,46 +48,77 @@ public class CustomSaml2AuthenticationSuccessHandler
|
|||||||
? (SavedRequest) session.getAttribute("SPRING_SECURITY_SAVED_REQUEST")
|
? (SavedRequest) session.getAttribute("SPRING_SECURITY_SAVED_REQUEST")
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
|
log.debug(
|
||||||
|
"Session exists: {}, Saved request exists: {}",
|
||||||
|
session != null,
|
||||||
|
savedRequest != null);
|
||||||
|
|
||||||
if (savedRequest != null
|
if (savedRequest != null
|
||||||
&& !RequestUriUtils.isStaticResource(
|
&& !RequestUriUtils.isStaticResource(
|
||||||
contextPath, savedRequest.getRedirectUrl())) {
|
contextPath, savedRequest.getRedirectUrl())) {
|
||||||
// Redirect to the original destination
|
log.debug(
|
||||||
|
"Valid saved request found, redirecting to original destination: {}",
|
||||||
|
savedRequest.getRedirectUrl());
|
||||||
super.onAuthenticationSuccess(request, response, authentication);
|
super.onAuthenticationSuccess(request, response, authentication);
|
||||||
} else {
|
} else {
|
||||||
SAML2 saml2 = applicationProperties.getSecurity().getSaml2();
|
SAML2 saml2 = applicationProperties.getSecurity().getSaml2();
|
||||||
|
log.debug(
|
||||||
|
"Processing SAML2 authentication with autoCreateUser: {}",
|
||||||
|
saml2.getAutoCreateUser());
|
||||||
|
|
||||||
if (loginAttemptService.isBlocked(username)) {
|
if (loginAttemptService.isBlocked(username)) {
|
||||||
|
log.debug("User {} is blocked due to too many login attempts", username);
|
||||||
if (session != null) {
|
if (session != null) {
|
||||||
session.removeAttribute("SPRING_SECURITY_SAVED_REQUEST");
|
session.removeAttribute("SPRING_SECURITY_SAVED_REQUEST");
|
||||||
}
|
}
|
||||||
throw new LockedException(
|
throw new LockedException(
|
||||||
"Your account has been locked due to too many failed login attempts.");
|
"Your account has been locked due to too many failed login attempts.");
|
||||||
}
|
}
|
||||||
if (userService.usernameExistsIgnoreCase(username)
|
|
||||||
&& userService.hasPassword(username)
|
boolean userExists = userService.usernameExistsIgnoreCase(username);
|
||||||
&& !userService.isAuthenticationTypeByUsername(
|
boolean hasPassword = userExists && userService.hasPassword(username);
|
||||||
username, AuthenticationType.OAUTH2)
|
boolean isSSOUser =
|
||||||
&& saml2.getAutoCreateUser()) {
|
userExists
|
||||||
|
&& userService.isAuthenticationTypeByUsername(
|
||||||
|
username, AuthenticationType.SSO);
|
||||||
|
|
||||||
|
log.debug(
|
||||||
|
"User status - Exists: {}, Has password: {}, Is SSO user: {}",
|
||||||
|
userExists,
|
||||||
|
hasPassword,
|
||||||
|
isSSOUser);
|
||||||
|
|
||||||
|
if (userExists && hasPassword && !isSSOUser && saml2.getAutoCreateUser()) {
|
||||||
|
log.debug(
|
||||||
|
"User {} exists with password but is not SSO user, redirecting to logout",
|
||||||
|
username);
|
||||||
response.sendRedirect(
|
response.sendRedirect(
|
||||||
contextPath + "/logout?oauth2AuthenticationErrorWeb=true");
|
contextPath + "/logout?oauth2AuthenticationErrorWeb=true");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (saml2.getBlockRegistration()
|
if (saml2.getBlockRegistration() && !userExists) {
|
||||||
&& !userService.usernameExistsIgnoreCase(username)) {
|
log.debug("Registration blocked for new user: {}", username);
|
||||||
response.sendRedirect(
|
response.sendRedirect(
|
||||||
contextPath + "/login?erroroauth=oauth2_admin_blocked_user");
|
contextPath + "/login?erroroauth=oauth2_admin_blocked_user");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
userService.processOAuth2PostLogin(username, saml2.getAutoCreateUser());
|
log.debug("Processing SSO post-login for user: {}", username);
|
||||||
|
userService.processSSOPostLogin(username, saml2.getAutoCreateUser());
|
||||||
|
log.debug("Successfully processed authentication for user: {}", username);
|
||||||
response.sendRedirect(contextPath + "/");
|
response.sendRedirect(contextPath + "/");
|
||||||
return;
|
return;
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
|
log.debug(
|
||||||
|
"Invalid username detected for user: {}, redirecting to logout",
|
||||||
|
username);
|
||||||
response.sendRedirect(contextPath + "/logout?invalidUsername=true");
|
response.sendRedirect(contextPath + "/logout?invalidUsername=true");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
log.debug("Non-SAML2 principal detected, delegating to parent handler");
|
||||||
super.onAuthenticationSuccess(request, response, authentication);
|
super.onAuthenticationSuccess(request, response, authentication);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,8 +3,6 @@ package stirling.software.SPDF.config.security.saml2;
|
|||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import org.opensaml.core.xml.XMLObject;
|
import org.opensaml.core.xml.XMLObject;
|
||||||
import org.opensaml.core.xml.schema.XSBoolean;
|
|
||||||
import org.opensaml.core.xml.schema.XSString;
|
|
||||||
import org.opensaml.saml.saml2.core.Assertion;
|
import org.opensaml.saml.saml2.core.Assertion;
|
||||||
import org.opensaml.saml.saml2.core.Attribute;
|
import org.opensaml.saml.saml2.core.Attribute;
|
||||||
import org.opensaml.saml.saml2.core.AttributeStatement;
|
import org.opensaml.saml.saml2.core.AttributeStatement;
|
||||||
@@ -30,15 +28,60 @@ public class CustomSaml2ResponseAuthenticationConverter
|
|||||||
this.userService = userService;
|
this.userService = userService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Map<String, List<Object>> extractAttributes(Assertion assertion) {
|
||||||
|
Map<String, List<Object>> attributes = new HashMap<>();
|
||||||
|
|
||||||
|
for (AttributeStatement attributeStatement : assertion.getAttributeStatements()) {
|
||||||
|
for (Attribute attribute : attributeStatement.getAttributes()) {
|
||||||
|
String attributeName = attribute.getName();
|
||||||
|
List<Object> values = new ArrayList<>();
|
||||||
|
|
||||||
|
for (XMLObject xmlObject : attribute.getAttributeValues()) {
|
||||||
|
// Get the text content directly
|
||||||
|
String value = xmlObject.getDOM().getTextContent();
|
||||||
|
if (value != null && !value.trim().isEmpty()) {
|
||||||
|
values.add(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!values.isEmpty()) {
|
||||||
|
// Store with both full URI and last part of the URI
|
||||||
|
attributes.put(attributeName, values);
|
||||||
|
String shortName = attributeName.substring(attributeName.lastIndexOf('/') + 1);
|
||||||
|
attributes.put(shortName, values);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return attributes;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Saml2Authentication convert(ResponseToken responseToken) {
|
public Saml2Authentication convert(ResponseToken responseToken) {
|
||||||
// Extract the assertion from the response
|
|
||||||
Assertion assertion = responseToken.getResponse().getAssertions().get(0);
|
Assertion assertion = responseToken.getResponse().getAssertions().get(0);
|
||||||
|
Map<String, List<Object>> attributes = extractAttributes(assertion);
|
||||||
|
|
||||||
// Extract the NameID
|
// Debug log with actual values
|
||||||
String nameId = assertion.getSubject().getNameID().getValue();
|
log.debug("Extracted SAML Attributes: " + attributes);
|
||||||
|
|
||||||
Optional<User> userOpt = userService.findByUsernameIgnoreCase(nameId);
|
// Try to get username/identifier in order of preference
|
||||||
|
String userIdentifier = null;
|
||||||
|
if (hasAttribute(attributes, "username")) {
|
||||||
|
userIdentifier = getFirstAttributeValue(attributes, "username");
|
||||||
|
} else if (hasAttribute(attributes, "emailaddress")) {
|
||||||
|
userIdentifier = getFirstAttributeValue(attributes, "emailaddress");
|
||||||
|
} else if (hasAttribute(attributes, "name")) {
|
||||||
|
userIdentifier = getFirstAttributeValue(attributes, "name");
|
||||||
|
} else if (hasAttribute(attributes, "upn")) {
|
||||||
|
userIdentifier = getFirstAttributeValue(attributes, "upn");
|
||||||
|
} else if (hasAttribute(attributes, "uid")) {
|
||||||
|
userIdentifier = getFirstAttributeValue(attributes, "uid");
|
||||||
|
} else {
|
||||||
|
userIdentifier = assertion.getSubject().getNameID().getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rest of your existing code...
|
||||||
|
Optional<User> userOpt = userService.findByUsernameIgnoreCase(userIdentifier);
|
||||||
SimpleGrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority("ROLE_USER");
|
SimpleGrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority("ROLE_USER");
|
||||||
if (userOpt.isPresent()) {
|
if (userOpt.isPresent()) {
|
||||||
User user = userOpt.get();
|
User user = userOpt.get();
|
||||||
@@ -48,39 +91,27 @@ public class CustomSaml2ResponseAuthenticationConverter
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract the SessionIndexes
|
|
||||||
List<String> sessionIndexes = new ArrayList<>();
|
List<String> sessionIndexes = new ArrayList<>();
|
||||||
for (AuthnStatement authnStatement : assertion.getAuthnStatements()) {
|
for (AuthnStatement authnStatement : assertion.getAuthnStatements()) {
|
||||||
sessionIndexes.add(authnStatement.getSessionIndex());
|
sessionIndexes.add(authnStatement.getSessionIndex());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract the Attributes
|
|
||||||
Map<String, List<Object>> attributes = extractAttributes(assertion);
|
|
||||||
|
|
||||||
// Create the custom principal
|
|
||||||
CustomSaml2AuthenticatedPrincipal principal =
|
CustomSaml2AuthenticatedPrincipal principal =
|
||||||
new CustomSaml2AuthenticatedPrincipal(nameId, attributes, nameId, sessionIndexes);
|
new CustomSaml2AuthenticatedPrincipal(
|
||||||
|
userIdentifier, attributes, userIdentifier, sessionIndexes);
|
||||||
|
|
||||||
// Create the Saml2Authentication
|
|
||||||
return new Saml2Authentication(
|
return new Saml2Authentication(
|
||||||
principal,
|
principal,
|
||||||
responseToken.getToken().getSaml2Response(),
|
responseToken.getToken().getSaml2Response(),
|
||||||
Collections.singletonList(simpleGrantedAuthority));
|
Collections.singletonList(simpleGrantedAuthority));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<String, List<Object>> extractAttributes(Assertion assertion) {
|
private boolean hasAttribute(Map<String, List<Object>> attributes, String name) {
|
||||||
Map<String, List<Object>> attributes = new HashMap<>();
|
return attributes.containsKey(name) && !attributes.get(name).isEmpty();
|
||||||
for (AttributeStatement attributeStatement : assertion.getAttributeStatements()) {
|
}
|
||||||
for (Attribute attribute : attributeStatement.getAttributes()) {
|
|
||||||
String attributeName = attribute.getName();
|
private String getFirstAttributeValue(Map<String, List<Object>> attributes, String name) {
|
||||||
List<Object> values = new ArrayList<>();
|
List<Object> values = attributes.get(name);
|
||||||
for (XMLObject xmlObject : attribute.getAttributeValues()) {
|
return values != null && !values.isEmpty() ? values.get(0).toString() : null;
|
||||||
log.info("BOOL: " + ((XSBoolean) xmlObject).getValue());
|
|
||||||
values.add(((XSString) xmlObject).getValue());
|
|
||||||
}
|
|
||||||
attributes.put(attributeName, values);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return attributes;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -244,8 +244,8 @@ public class UserController {
|
|||||||
return new RedirectView("/addUsers?messageType=invalidRole", true);
|
return new RedirectView("/addUsers?messageType=invalidRole", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (authType.equalsIgnoreCase(AuthenticationType.OAUTH2.toString())) {
|
if (authType.equalsIgnoreCase(AuthenticationType.SSO.toString())) {
|
||||||
userService.saveUser(username, AuthenticationType.OAUTH2, role);
|
userService.saveUser(username, AuthenticationType.SSO, role);
|
||||||
} else {
|
} else {
|
||||||
if (password.isBlank()) {
|
if (password.isBlank()) {
|
||||||
return new RedirectView("/addUsers?messageType=invalidPassword", true);
|
return new RedirectView("/addUsers?messageType=invalidPassword", true);
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ 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;
|
||||||
@@ -26,6 +28,7 @@ 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")
|
||||||
@@ -44,6 +47,11 @@ 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",
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
package stirling.software.SPDF.controller.api.misc;
|
package stirling.software.SPDF.controller.api.misc;
|
||||||
|
|
||||||
import io.github.pixee.security.BoundedLineReader;
|
|
||||||
import io.github.pixee.security.Filenames;
|
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@@ -35,6 +33,8 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import io.github.pixee.security.BoundedLineReader;
|
||||||
|
import io.github.pixee.security.Filenames;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@@ -176,7 +176,9 @@ public class OCRController {
|
|||||||
// Read the final PDF file
|
// Read the final PDF file
|
||||||
byte[] pdfContent = Files.readAllBytes(finalOutputFile);
|
byte[] pdfContent = Files.readAllBytes(finalOutputFile);
|
||||||
String outputFilename =
|
String outputFilename =
|
||||||
Filenames.toSimpleFileName(inputFile.getOriginalFilename()).replaceFirst("[.][^.]+$", "") + "_OCR.pdf";
|
Filenames.toSimpleFileName(inputFile.getOriginalFilename())
|
||||||
|
.replaceFirst("[.][^.]+$", "")
|
||||||
|
+ "_OCR.pdf";
|
||||||
|
|
||||||
return ResponseEntity.ok()
|
return ResponseEntity.ok()
|
||||||
.header(
|
.header(
|
||||||
|
|||||||
@@ -50,7 +50,6 @@ public class RepairController {
|
|||||||
MultipartFile inputFile = request.getFileInput();
|
MultipartFile inputFile = request.getFileInput();
|
||||||
// Save the uploaded file to a temporary location
|
// Save the uploaded file to a temporary location
|
||||||
Path tempInputFile = Files.createTempFile("input_", ".pdf");
|
Path tempInputFile = Files.createTempFile("input_", ".pdf");
|
||||||
Path tempOutputFile = Files.createTempFile("output_", ".pdf");
|
|
||||||
byte[] pdfBytes = null;
|
byte[] pdfBytes = null;
|
||||||
inputFile.transferTo(tempInputFile.toFile());
|
inputFile.transferTo(tempInputFile.toFile());
|
||||||
try {
|
try {
|
||||||
@@ -61,14 +60,13 @@ public class RepairController {
|
|||||||
command.add("--qdf"); // Linearizes and normalizes PDF structure
|
command.add("--qdf"); // Linearizes and normalizes PDF structure
|
||||||
command.add("--object-streams=disable"); // Can help with some corruptions
|
command.add("--object-streams=disable"); // Can help with some corruptions
|
||||||
command.add(tempInputFile.toString());
|
command.add(tempInputFile.toString());
|
||||||
command.add(tempOutputFile.toString());
|
|
||||||
|
|
||||||
ProcessExecutorResult returnCode =
|
ProcessExecutorResult returnCode =
|
||||||
ProcessExecutor.getInstance(ProcessExecutor.Processes.QPDF)
|
ProcessExecutor.getInstance(ProcessExecutor.Processes.QPDF)
|
||||||
.runCommandWithOutputHandling(command);
|
.runCommandWithOutputHandling(command);
|
||||||
|
|
||||||
// Read the optimized PDF file
|
// Read the optimized PDF file
|
||||||
pdfBytes = pdfDocumentFactory.loadToBytes(tempOutputFile.toFile());
|
pdfBytes = pdfDocumentFactory.loadToBytes(tempInputFile.toFile());
|
||||||
|
|
||||||
// Return the optimized PDF as a response
|
// Return the optimized PDF as a response
|
||||||
String outputFilename =
|
String outputFilename =
|
||||||
@@ -79,7 +77,6 @@ public class RepairController {
|
|||||||
} finally {
|
} finally {
|
||||||
// Clean up the temporary files
|
// Clean up the temporary files
|
||||||
Files.deleteIfExists(tempInputFile);
|
Files.deleteIfExists(tempInputFile);
|
||||||
Files.deleteIfExists(tempOutputFile);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -229,10 +229,22 @@ 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();
|
||||||
contentStream.setTextMatrix(Matrix.getRotateInstance(Math.toRadians(rotation), x, y));
|
for (int i = 0; i < lines.length; i++) {
|
||||||
contentStream.showText(stampText);
|
String line = lines[i];
|
||||||
|
// 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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -322,27 +322,14 @@ 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();
|
||||||
@@ -584,6 +571,7 @@ 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);
|
||||||
|
|
||||||
@@ -602,6 +590,22 @@ 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;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,168 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -69,6 +69,7 @@ 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
|
||||||
@@ -97,7 +98,8 @@ 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,
|
||||||
@@ -136,7 +138,8 @@ 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);
|
||||||
@@ -173,7 +176,18 @@ 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;
|
||||||
|
|||||||
@@ -53,6 +53,13 @@ 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) {
|
||||||
|
|||||||
@@ -2,5 +2,5 @@ package stirling.software.SPDF.model;
|
|||||||
|
|
||||||
public enum AuthenticationType {
|
public enum AuthenticationType {
|
||||||
WEB,
|
WEB,
|
||||||
OAUTH2
|
SSO
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,6 +45,9 @@ 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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
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
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package stirling.software.SPDF.repository;
|
package stirling.software.SPDF.repository;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
@@ -19,4 +20,6 @@ public interface UserRepository extends JpaRepository<User, Long> {
|
|||||||
Optional<User> findByUsername(String username);
|
Optional<User> findByUsername(String username);
|
||||||
|
|
||||||
Optional<User> findByApiKey(String apiKey);
|
Optional<User> findByApiKey(String apiKey);
|
||||||
|
|
||||||
|
List<User> findByAuthenticationTypeIgnoreCase(String authenticationType);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,157 @@
|
|||||||
|
package stirling.software.SPDF.service;
|
||||||
|
|
||||||
|
import io.github.pixee.security.BoundedLineReader;
|
||||||
|
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 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -24,7 +24,7 @@ public class MetricsAggregatorService {
|
|||||||
this.postHogService = postHogService;
|
this.postHogService = postHogService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Scheduled(fixedRate = 1800000) // Run every 30 minutes
|
@Scheduled(fixedRate = 7200000) // Run every 2 hours
|
||||||
public void aggregateAndSendMetrics() {
|
public void aggregateAndSendMetrics() {
|
||||||
Map<String, Object> metrics = new HashMap<>();
|
Map<String, Object> metrics = new HashMap<>();
|
||||||
Search.in(meterRegistry)
|
Search.in(meterRegistry)
|
||||||
|
|||||||
@@ -17,15 +17,18 @@ public class PdfMetadataService {
|
|||||||
private final ApplicationProperties applicationProperties;
|
private final ApplicationProperties applicationProperties;
|
||||||
private final String stirlingPDFLabel;
|
private final String stirlingPDFLabel;
|
||||||
private final UserServiceInterface userService;
|
private final UserServiceInterface userService;
|
||||||
|
private final boolean runningEE;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public PdfMetadataService(
|
public PdfMetadataService(
|
||||||
ApplicationProperties applicationProperties,
|
ApplicationProperties applicationProperties,
|
||||||
@Qualifier("StirlingPDFLabel") String stirlingPDFLabel,
|
@Qualifier("StirlingPDFLabel") String stirlingPDFLabel,
|
||||||
|
@Qualifier("runningEE") boolean runningEE,
|
||||||
@Autowired(required = false) UserServiceInterface userService) {
|
@Autowired(required = false) UserServiceInterface userService) {
|
||||||
this.applicationProperties = applicationProperties;
|
this.applicationProperties = applicationProperties;
|
||||||
this.stirlingPDFLabel = stirlingPDFLabel;
|
this.stirlingPDFLabel = stirlingPDFLabel;
|
||||||
this.userService = userService;
|
this.userService = userService;
|
||||||
|
this.runningEE = runningEE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PdfMetadata extractMetadataFromPdf(PDDocument pdf) {
|
public PdfMetadata extractMetadataFromPdf(PDDocument pdf) {
|
||||||
@@ -61,10 +64,8 @@ public class PdfMetadataService {
|
|||||||
|
|
||||||
String creator = stirlingPDFLabel;
|
String creator = stirlingPDFLabel;
|
||||||
|
|
||||||
if (applicationProperties
|
if (applicationProperties.getEnterpriseEdition().getCustomMetadata().isAutoUpdateMetadata()
|
||||||
.getEnterpriseEdition()
|
&& runningEE) {
|
||||||
.getCustomMetadata()
|
|
||||||
.isAutoUpdateMetadata()) {
|
|
||||||
|
|
||||||
creator = applicationProperties.getEnterpriseEdition().getCustomMetadata().getCreator();
|
creator = applicationProperties.getEnterpriseEdition().getCustomMetadata().getCreator();
|
||||||
pdf.getDocumentInformation().setProducer(stirlingPDFLabel);
|
pdf.getDocumentInformation().setProducer(stirlingPDFLabel);
|
||||||
@@ -83,10 +84,8 @@ public class PdfMetadataService {
|
|||||||
pdf.getDocumentInformation().setModificationDate(Calendar.getInstance());
|
pdf.getDocumentInformation().setModificationDate(Calendar.getInstance());
|
||||||
|
|
||||||
String author = pdfMetadata.getAuthor();
|
String author = pdfMetadata.getAuthor();
|
||||||
if (applicationProperties
|
if (applicationProperties.getEnterpriseEdition().getCustomMetadata().isAutoUpdateMetadata()
|
||||||
.getEnterpriseEdition()
|
&& runningEE) {
|
||||||
.getCustomMetadata()
|
|
||||||
.isAutoUpdateMetadata()) {
|
|
||||||
author = applicationProperties.getEnterpriseEdition().getCustomMetadata().getAuthor();
|
author = applicationProperties.getEnterpriseEdition().getCustomMetadata().getAuthor();
|
||||||
|
|
||||||
if (userService != null) {
|
if (userService != null) {
|
||||||
|
|||||||
@@ -241,7 +241,7 @@ public class FileToPdf {
|
|||||||
Files.deleteIfExists(tempOutputFile);
|
Files.deleteIfExists(tempOutputFile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static String sanitizeZipFilename(String entryName) {
|
static String sanitizeZipFilename(String entryName) {
|
||||||
if (entryName == null || entryName.trim().isEmpty()) {
|
if (entryName == null || entryName.trim().isEmpty()) {
|
||||||
return entryName;
|
return entryName;
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,6 +3,10 @@ multipart.enabled=true
|
|||||||
logging.level.org.springframework=WARN
|
logging.level.org.springframework=WARN
|
||||||
logging.level.org.hibernate=WARN
|
logging.level.org.hibernate=WARN
|
||||||
logging.level.org.eclipse.jetty=WARN
|
logging.level.org.eclipse.jetty=WARN
|
||||||
|
#logging.level.org.springframework.security.saml2=TRACE
|
||||||
|
#logging.level.org.springframework.security=DEBUG
|
||||||
|
#logging.level.org.opensaml=DEBUG
|
||||||
|
#logging.level.stirling.software.SPDF.config.security: DEBUG
|
||||||
logging.level.com.zaxxer.hikari=WARN
|
logging.level.com.zaxxer.hikari=WARN
|
||||||
|
|
||||||
spring.jpa.open-in-view=false
|
spring.jpa.open-in-view=false
|
||||||
@@ -27,6 +31,8 @@ server.servlet.context-path=${SYSTEM_ROOTURIPATH:/}
|
|||||||
|
|
||||||
spring.devtools.restart.enabled=true
|
spring.devtools.restart.enabled=true
|
||||||
spring.devtools.livereload.enabled=true
|
spring.devtools.livereload.enabled=true
|
||||||
|
spring.devtools.restart.exclude=stirling.software.SPDF.config.security/**
|
||||||
|
|
||||||
spring.thymeleaf.encoding=UTF-8
|
spring.thymeleaf.encoding=UTF-8
|
||||||
|
|
||||||
spring.web.resources.mime-mappings.webmanifest=application/manifest+json
|
spring.web.resources.mime-mappings.webmanifest=application/manifest+json
|
||||||
|
|||||||
25972
src/main/resources/certdata.txt
Normal file
25972
src/main/resources/certdata.txt
Normal file
File diff suppressed because it is too large
Load Diff
@@ -512,6 +512,10 @@ 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
|
||||||
@@ -1052,6 +1056,7 @@ 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=حجم الخط:
|
||||||
@@ -1263,6 +1268,11 @@ 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
|
||||||
@@ -1270,3 +1280,38 @@ 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
|
||||||
|
|||||||
@@ -512,6 +512,10 @@ 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
|
||||||
@@ -1052,6 +1056,7 @@ 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ü:
|
||||||
@@ -1263,6 +1268,11 @@ 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
|
||||||
@@ -1270,3 +1280,38 @@ 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
|
||||||
|
|||||||
@@ -512,6 +512,10 @@ 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
|
||||||
@@ -1052,6 +1056,7 @@ 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=Размер на шрифта:
|
||||||
@@ -1263,6 +1268,11 @@ 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
|
||||||
@@ -1270,3 +1280,38 @@ 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
|
||||||
|
|||||||
@@ -512,6 +512,10 @@ 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
|
||||||
@@ -1052,6 +1056,7 @@ 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:
|
||||||
@@ -1263,6 +1268,11 @@ 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
|
||||||
@@ -1270,3 +1280,38 @@ 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
|
||||||
|
|||||||
@@ -512,6 +512,10 @@ 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
|
||||||
@@ -1052,6 +1056,7 @@ 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:
|
||||||
@@ -1263,6 +1268,11 @@ 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
|
||||||
@@ -1270,3 +1280,38 @@ 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
|
||||||
|
|||||||
@@ -512,6 +512,10 @@ 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
|
||||||
@@ -1052,6 +1056,7 @@ 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:
|
||||||
@@ -1263,6 +1268,11 @@ 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
|
||||||
@@ -1270,3 +1280,38 @@ 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
|
||||||
|
|||||||
@@ -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=Zeichen und Sicherheit
|
navbar.sections.security=Signieren und Sicherheit
|
||||||
navbar.sections.advance=Fortschrittlich
|
navbar.sections.advance=Erweiterte Funktionen
|
||||||
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=Refresh Page
|
session.refreshPage=Seite aktualisieren
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# HOME-PAGE #
|
# HOME-PAGE #
|
||||||
@@ -512,6 +512,10 @@ 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
|
||||||
@@ -818,12 +822,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=Add to all pages
|
sign.addToAll=Zu allen Seiten hinzufügen
|
||||||
sign.delete=Delete
|
sign.delete=Löschen
|
||||||
sign.first=First page
|
sign.first=Erste Seite
|
||||||
sign.last=Last page
|
sign.last=Letzte Seite
|
||||||
sign.next=Next page
|
sign.next=Nächste Seite
|
||||||
sign.previous=Previous page
|
sign.previous=Vorherige Seite
|
||||||
|
|
||||||
#repair
|
#repair
|
||||||
repair.title=Reparieren
|
repair.title=Reparieren
|
||||||
@@ -949,17 +953,17 @@ multiTool.deleteSelected=Auswahl löschen
|
|||||||
multiTool.downloadAll=Downloaden
|
multiTool.downloadAll=Downloaden
|
||||||
multiTool.downloadSelected=Auswahl downloaden
|
multiTool.downloadSelected=Auswahl downloaden
|
||||||
|
|
||||||
multiTool.insertPageBreak=Insert Page Break
|
multiTool.insertPageBreak=Seitenumbruch einfügen
|
||||||
multiTool.addFile=Add File
|
multiTool.addFile=Datei hinzufügen
|
||||||
multiTool.rotateLeft=Rotate Left
|
multiTool.rotateLeft=Nach links drehen
|
||||||
multiTool.rotateRight=Rotate Right
|
multiTool.rotateRight=Nach rechts drehen
|
||||||
multiTool.split=Split
|
multiTool.split=Teilen
|
||||||
multiTool.moveLeft=Move Left
|
multiTool.moveLeft=Nach links verschieben
|
||||||
multiTool.moveRight=Move Right
|
multiTool.moveRight=Nach rechts verschieben
|
||||||
multiTool.delete=Delete
|
multiTool.delete=Löschen
|
||||||
multiTool.dragDropMessage=Page(s) Selected
|
multiTool.dragDropMessage=Ausgewählte Seite(n)
|
||||||
multiTool.undo=Undo
|
multiTool.undo=Rückgängig machen
|
||||||
multiTool.redo=Redo
|
multiTool.redo=Wiederherstellen
|
||||||
|
|
||||||
#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!
|
||||||
@@ -1052,6 +1056,7 @@ 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:
|
||||||
@@ -1263,10 +1268,50 @@ 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=Releases
|
releases.footer=Veröffentlichungen
|
||||||
releases.title=Release Notes
|
releases.title=Versionshinweise
|
||||||
releases.header=Release Notes
|
releases.header=Versionshinweise
|
||||||
releases.current.version=Current Release
|
releases.current.version=Aktuelle Version
|
||||||
releases.note=Release notes are only available in English
|
releases.note=Versionshinweise sind nur auf Englisch verfügbar
|
||||||
|
|
||||||
|
#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
|
||||||
|
|||||||
@@ -512,6 +512,10 @@ 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=Αντικατάσταση-Αντίστροφη Παθωμένη Πίντσουρ
|
||||||
@@ -1052,12 +1056,13 @@ 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=widthSpacer (Κενό μεταξύ κάθε υδατογραφήματος οριζόντια):
|
watermark.selectText.5=Width Spacer (Κενό μεταξύ κάθε υδατογραφήματος οριζόντια):
|
||||||
watermark.selectText.6=heightSpacer (Κενό μεταξύ κάθε υδατογραφήματος κάθετα):
|
watermark.selectText.6=Height Spacer (Κενό μεταξύ κάθε υδατογραφήματος κάθετα):
|
||||||
watermark.selectText.7=Αδιαφάνεια (Opacity) (0% - 100%):
|
watermark.selectText.7=Αδιαφάνεια (Opacity) (0% - 100%):
|
||||||
watermark.selectText.8=Τύπος Υδατογραφήματος:
|
watermark.selectText.8=Τύπος Υδατογραφήματος:
|
||||||
watermark.selectText.9=Εικόνα Υδατογραφήματος:
|
watermark.selectText.9=Εικόνα Υδατογραφήματος:
|
||||||
@@ -1263,6 +1268,11 @@ 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
|
||||||
@@ -1270,3 +1280,38 @@ 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
|
||||||
|
|||||||
@@ -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=Color
|
color=Colour
|
||||||
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 Colors/Contrast
|
home.adjust-contrast.title=Adjust Colours/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
|
adjust-contrast.tags=color-correction,tune,modify,enhance,colour-correction
|
||||||
|
|
||||||
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
|
split-by-sections.tags=Section Split, Divide, Customize,Customise
|
||||||
|
|
||||||
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
|
AddStampRequest.tags=Stamp, Add image, center image, Watermark, PDF, Embed, Customize,Customise
|
||||||
|
|
||||||
|
|
||||||
home.PDFToBook.title=PDF to Book
|
home.PDFToBook.title=PDF to Book
|
||||||
@@ -512,23 +512,27 @@ 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 Color PDF
|
replace-color.header=Replace-Invert Colour PDF
|
||||||
home.replaceColorPdf.title=Advanced Colour options
|
home.replaceColorPdf.title=Advanced Colour options
|
||||||
home.replaceColorPdf.desc=Replace color for text and background in PDF and invert full color of pdf to reduce file size
|
home.replaceColorPdf.desc=Replace colour for text and background in PDF and invert full colour of pdf to reduce file size
|
||||||
replaceColorPdf.tags=Replace Color,Page operations,Back end,server side
|
replaceColorPdf.tags=Replace Colour,Page operations,Back end,server side
|
||||||
replace-color.selectText.1=Replace or Invert color Options
|
replace-color.selectText.1=Replace or Invert colour Options
|
||||||
replace-color.selectText.2=Default(Default high contrast colors)
|
replace-color.selectText.2=Default(Default high contrast colours)
|
||||||
replace-color.selectText.3=Custom(Customized colors)
|
replace-color.selectText.3=Custom(Customised colours)
|
||||||
replace-color.selectText.4=Full-Invert(Invert all colors)
|
replace-color.selectText.4=Full-Invert(Invert all colours)
|
||||||
replace-color.selectText.5=High contrast color options
|
replace-color.selectText.5=High contrast colour 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 Color
|
replace-color.selectText.10=Choose text Colour
|
||||||
replace-color.selectText.11=Choose background Color
|
replace-color.selectText.11=Choose background Colour
|
||||||
replace-color.submit=Replace
|
replace-color.submit=Replace
|
||||||
|
|
||||||
|
|
||||||
@@ -651,7 +655,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 Color
|
AddStampRequest.customColor=Custom Text Colour
|
||||||
AddStampRequest.submit=Submit
|
AddStampRequest.submit=Submit
|
||||||
|
|
||||||
|
|
||||||
@@ -783,8 +787,8 @@ removeAnnotations.submit=Remove
|
|||||||
#compare
|
#compare
|
||||||
compare.title=Compare
|
compare.title=Compare
|
||||||
compare.header=Compare PDFs
|
compare.header=Compare PDFs
|
||||||
compare.highlightColor.1=Highlight Color 1:
|
compare.highlightColor.1=Highlight Colour 1:
|
||||||
compare.highlightColor.2=Highlight Color 2:
|
compare.highlightColor.2=Highlight Colour 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
|
||||||
@@ -842,7 +846,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 color variation around the estimated background color (default: 30).
|
ScannerImageSplit.selectText.4=Determines the range of colour variation around the estimated background colour (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:
|
||||||
@@ -894,7 +898,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=Optimization level:
|
compress.selectText.2=Optimisation 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)
|
||||||
@@ -1052,12 +1056,13 @@ 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=widthSpacer (Space between each watermark horizontally):
|
watermark.selectText.5=Width Spacer (Space between each watermark horizontally):
|
||||||
watermark.selectText.6=heightSpacer (Space between each watermark vertically):
|
watermark.selectText.6=Height Spacer (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:
|
||||||
@@ -1263,6 +1268,11 @@ 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
|
||||||
@@ -1270,3 +1280,38 @@ 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
|
||||||
|
|||||||
@@ -512,6 +512,10 @@ 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
|
||||||
@@ -1052,12 +1056,13 @@ 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=widthSpacer (Space between each watermark horizontally):
|
watermark.selectText.5=Width Spacer (Space between each watermark horizontally):
|
||||||
watermark.selectText.6=heightSpacer (Space between each watermark vertically):
|
watermark.selectText.6=Height Spacer (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:
|
||||||
@@ -1263,6 +1268,11 @@ 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
|
||||||
@@ -1270,3 +1280,38 @@ 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
|
||||||
|
|||||||
@@ -512,6 +512,10 @@ 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
|
||||||
@@ -1052,6 +1056,7 @@ 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:
|
||||||
@@ -1263,6 +1268,11 @@ 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
|
||||||
@@ -1270,3 +1280,38 @@ 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
|
||||||
|
|||||||
@@ -512,6 +512,10 @@ 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
|
||||||
@@ -1052,6 +1056,7 @@ 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:
|
||||||
@@ -1263,6 +1268,11 @@ 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
|
||||||
@@ -1270,3 +1280,38 @@ 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
|
||||||
|
|||||||
1317
src/main/resources/messages_fa_IR.properties
Normal file
1317
src/main/resources/messages_fa_IR.properties
Normal file
File diff suppressed because it is too large
Load Diff
@@ -512,6 +512,10 @@ 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
|
||||||
@@ -1052,12 +1056,13 @@ 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=widthSpacer (espace entre chaque filigrane horizontalement)
|
watermark.selectText.5=Width Spacer (espace entre chaque filigrane horizontalement)
|
||||||
watermark.selectText.6=heightSpacer (espace entre chaque filigrane verticalement)
|
watermark.selectText.6=Height Spacer (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
|
||||||
@@ -1263,6 +1268,11 @@ 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
|
||||||
@@ -1270,3 +1280,38 @@ 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
|
||||||
|
|||||||
@@ -512,6 +512,10 @@ 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
|
||||||
@@ -1052,11 +1056,12 @@ 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=widthSpacer (Spás idir gach comhartha uisce go cothrománach):
|
watermark.selectText.5=Width Spacer (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:
|
||||||
@@ -1263,6 +1268,11 @@ 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
|
||||||
@@ -1270,3 +1280,38 @@ 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
|
||||||
|
|||||||
@@ -512,6 +512,10 @@ 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
|
||||||
@@ -1052,6 +1056,7 @@ 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=फ़ॉन्ट साइज़:
|
||||||
@@ -1263,6 +1268,11 @@ 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
|
||||||
@@ -1270,3 +1280,38 @@ 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
|
||||||
|
|||||||
@@ -512,6 +512,10 @@ 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
|
||||||
@@ -1052,6 +1056,7 @@ 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:
|
||||||
@@ -1263,6 +1268,11 @@ 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
|
||||||
@@ -1270,3 +1280,38 @@ 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
|
||||||
|
|||||||
@@ -512,6 +512,10 @@ 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
|
||||||
@@ -1052,12 +1056,13 @@ 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=widthSpacer (Hely a vízjelek között vízszintesen):
|
watermark.selectText.5=Width Spacer (Hely a vízjelek között vízszintesen):
|
||||||
watermark.selectText.6=heightSpacer (Hely a vízjelek között függőlegesen):
|
watermark.selectText.6=Height Spacer (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:
|
||||||
@@ -1263,6 +1268,11 @@ 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
|
||||||
@@ -1270,3 +1280,38 @@ 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
|
||||||
|
|||||||
@@ -512,6 +512,10 @@ 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
|
||||||
@@ -1052,12 +1056,13 @@ 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=widthSpacer (Spasi diantara setiap watermark horisontal):
|
watermark.selectText.5=Width Spacer (Spasi diantara setiap watermark horisontal):
|
||||||
watermark.selectText.6=heightSpacer (Spasi diantara setiap watermark vertikal):
|
watermark.selectText.6=Height Spacer (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:
|
||||||
@@ -1263,6 +1268,11 @@ 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
|
||||||
@@ -1270,3 +1280,38 @@ 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
|
||||||
|
|||||||
@@ -512,6 +512,10 @@ 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
|
||||||
@@ -958,8 +962,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=Undo
|
multiTool.undo=Annulla
|
||||||
multiTool.redo=Redo
|
multiTool.redo=Rifai
|
||||||
|
|
||||||
#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!
|
||||||
@@ -1052,6 +1056,7 @@ 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:
|
||||||
@@ -1263,6 +1268,11 @@ 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
|
||||||
@@ -1270,3 +1280,38 @@ 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
|
||||||
|
|||||||
@@ -512,6 +512,10 @@ 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
|
||||||
@@ -1052,6 +1056,7 @@ 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=文字サイズ:
|
||||||
@@ -1263,6 +1268,11 @@ 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
|
||||||
@@ -1270,3 +1280,38 @@ 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
|
||||||
|
|||||||
@@ -512,6 +512,10 @@ 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
|
||||||
@@ -1052,6 +1056,7 @@ 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=폰트 크기:
|
||||||
@@ -1263,6 +1268,11 @@ 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
|
||||||
@@ -1270,3 +1280,38 @@ 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
|
||||||
|
|||||||
@@ -512,6 +512,10 @@ 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
|
||||||
@@ -1052,6 +1056,7 @@ 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:
|
||||||
@@ -1263,6 +1268,11 @@ 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
|
||||||
@@ -1270,3 +1280,38 @@ 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
|
||||||
|
|||||||
@@ -512,6 +512,10 @@ 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
|
||||||
@@ -1052,6 +1056,7 @@ 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:
|
||||||
@@ -1263,6 +1268,11 @@ 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
|
||||||
@@ -1270,3 +1280,38 @@ 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
|
||||||
|
|||||||
45
src/main/resources/messages_pl_PL.properties
Executable file → Normal file
45
src/main/resources/messages_pl_PL.properties
Executable file → Normal file
@@ -512,6 +512,10 @@ 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
|
||||||
@@ -1052,6 +1056,7 @@ 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:
|
||||||
@@ -1263,6 +1268,11 @@ 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
|
||||||
@@ -1270,3 +1280,38 @@ 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
|
||||||
|
|||||||
@@ -512,6 +512,10 @@ home.splitPdfByChapters.title=Divide PDF por Capítulos
|
|||||||
home.splitPdfByChapters.desc=Divide um PDF em vários arquivos baseado na sua estrutura de capítulos.
|
home.splitPdfByChapters.desc=Divide um PDF em vários arquivos baseado na sua estrutura de capítulos.
|
||||||
splitPdfByChapters.tags=dividir,capítulos,favoritos,organizar
|
splitPdfByChapters.tags=dividir,capítulos,favoritos,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=Substituir-Inverter-Cor
|
replace-color.title=Substituir-Inverter-Cor
|
||||||
replace-color.header=Substitui-Inverter Cor PDF
|
replace-color.header=Substitui-Inverter Cor PDF
|
||||||
@@ -1052,12 +1056,13 @@ addPassword.submit=Criptografar
|
|||||||
#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=Cor de texto personalizada
|
||||||
watermark.selectText.1=Selecione PDF para adicionar a marca d'água:
|
watermark.selectText.1=Selecione 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=widthSpacer (Espaço entre cada marca d'água horizontalmente):
|
watermark.selectText.5=Width Spacer (Espaço entre cada marca d'água horizontalmente):
|
||||||
watermark.selectText.6=heightSpacer (Espaço entre cada marca d'água verticalmente):
|
watermark.selectText.6=Height Spacer (Espaço entre cada marca d'água verticalmente):
|
||||||
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:
|
||||||
@@ -1263,6 +1268,11 @@ splitByChapters.desc.3=Incluir Metadados: Se marcado, os metadados do PDF origin
|
|||||||
splitByChapters.desc.4=Permitir Cópias: Se marcado, habilita vários marcadores na mesma página para criar PDFs separados.
|
splitByChapters.desc.4=Permitir Cópias: Se marcado, habilita vários marcadores na mesma página para criar PDFs 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
|
||||||
@@ -1270,3 +1280,38 @@ 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
|
||||||
|
|||||||
@@ -512,6 +512,10 @@ 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
|
||||||
@@ -1052,12 +1056,13 @@ 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 (widthSpacer)
|
watermark.selectText.5=Espaçamento Horizontal (Width Spacer)
|
||||||
watermark.selectText.6=Espaçamento Vertical (heightSpacer)
|
watermark.selectText.6=Espaçamento Vertical (Height Spacer)
|
||||||
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
|
||||||
@@ -1263,6 +1268,11 @@ 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
|
||||||
@@ -1270,3 +1280,38 @@ 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
|
||||||
|
|||||||
@@ -512,6 +512,10 @@ 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
|
||||||
@@ -1052,6 +1056,7 @@ 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:
|
||||||
@@ -1263,6 +1268,11 @@ 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
|
||||||
@@ -1270,3 +1280,38 @@ 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
|
||||||
|
|||||||
@@ -512,6 +512,10 @@ 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
|
||||||
@@ -1052,12 +1056,13 @@ 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=widthSpacer (пробел между каждым водяным знаком по горизонтали):
|
watermark.selectText.5=Width Spacer (пробел между каждым водяным знаком по горизонтали):
|
||||||
watermark.selectText.6=heightSpacer (пробел между каждым водяным знаком по вертикали):
|
watermark.selectText.6=Height Spacer (пробел между каждым водяным знаком по вертикали):
|
||||||
watermark.selectText.7=Непрозрачность (0% - 100%):
|
watermark.selectText.7=Непрозрачность (0% - 100%):
|
||||||
watermark.selectText.8=Тип водяного знака:
|
watermark.selectText.8=Тип водяного знака:
|
||||||
watermark.selectText.9=Изображение водяного знака:
|
watermark.selectText.9=Изображение водяного знака:
|
||||||
@@ -1263,6 +1268,11 @@ 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
|
||||||
@@ -1270,3 +1280,38 @@ 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
|
||||||
|
|||||||
@@ -512,6 +512,10 @@ 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
|
||||||
@@ -1052,6 +1056,7 @@ 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:
|
||||||
@@ -1263,6 +1268,11 @@ 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
|
||||||
@@ -1270,3 +1280,38 @@ 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
|
||||||
|
|||||||
@@ -512,6 +512,10 @@ 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
|
||||||
@@ -1052,6 +1056,7 @@ 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:
|
||||||
@@ -1263,6 +1268,11 @@ 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
|
||||||
@@ -1270,3 +1280,38 @@ 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
|
||||||
|
|||||||
@@ -512,6 +512,10 @@ 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
|
||||||
@@ -1052,12 +1056,13 @@ 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=widthSpacer (mellanrum mellan varje vattenstämpel horisontellt):
|
watermark.selectText.5=Width Spacer (mellanrum mellan varje vattenstämpel horisontellt):
|
||||||
watermark.selectText.6=heightSpacer (mellanrum mellan varje vattenstämpel vertikalt):
|
watermark.selectText.6=Height Spacer (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:
|
||||||
@@ -1263,6 +1268,11 @@ 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
|
||||||
@@ -1270,3 +1280,38 @@ 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
|
||||||
|
|||||||
@@ -512,6 +512,10 @@ 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
|
||||||
@@ -1052,6 +1056,7 @@ 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=ขนาดฟอนต์:
|
||||||
@@ -1263,6 +1268,11 @@ 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
|
||||||
@@ -1270,3 +1280,38 @@ 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
|
||||||
|
|||||||
@@ -512,6 +512,10 @@ 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
|
||||||
@@ -1052,6 +1056,7 @@ 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:
|
||||||
@@ -1263,6 +1268,11 @@ 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
|
||||||
@@ -1270,3 +1280,38 @@ 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
|
||||||
|
|||||||
@@ -512,6 +512,10 @@ 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
|
||||||
@@ -1052,12 +1056,13 @@ 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=widthSpacer (проміжок між кожним водяним знаком по горизонталі):
|
watermark.selectText.5=Width Spacer (проміжок між кожним водяним знаком по горизонталі):
|
||||||
watermark.selectText.6=heightSpacer (проміжок між кожним водяним знаком по вертикалі):
|
watermark.selectText.6=Height Spacer (проміжок між кожним водяним знаком по вертикалі):
|
||||||
watermark.selectText.7=Непрозорість (0% - 100%):
|
watermark.selectText.7=Непрозорість (0% - 100%):
|
||||||
watermark.selectText.8=Тип водяного знаку:
|
watermark.selectText.8=Тип водяного знаку:
|
||||||
watermark.selectText.9=Зображення водяного знаку:
|
watermark.selectText.9=Зображення водяного знаку:
|
||||||
@@ -1263,6 +1268,11 @@ 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
|
||||||
@@ -1270,3 +1280,38 @@ 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
|
||||||
|
|||||||
@@ -512,6 +512,10 @@ 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
|
||||||
@@ -1052,6 +1056,7 @@ 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ữ:
|
||||||
@@ -1263,6 +1268,11 @@ 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
|
||||||
@@ -1270,3 +1280,38 @@ 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
|
||||||
|
|||||||
@@ -512,6 +512,10 @@ 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
|
||||||
@@ -1052,6 +1056,7 @@ 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=字体大小:
|
||||||
@@ -1263,6 +1268,11 @@ 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
|
||||||
@@ -1270,3 +1280,38 @@ 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
|
||||||
|
|||||||
@@ -512,6 +512,10 @@ 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 顏色
|
||||||
@@ -1052,12 +1056,13 @@ 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=widthSpacer(每個浮水印之間的水平間距):
|
watermark.selectText.5=Width Spacer(每個浮水印之間的水平間距):
|
||||||
watermark.selectText.6=heightSpacer(每個浮水印之間的垂直間距):
|
watermark.selectText.6=Height Spacer(每個浮水印之間的垂直間距):
|
||||||
watermark.selectText.7=不透明度(0% - 100%):
|
watermark.selectText.7=不透明度(0% - 100%):
|
||||||
watermark.selectText.8=浮水印類型:
|
watermark.selectText.8=浮水印類型:
|
||||||
watermark.selectText.9=浮水印影像:
|
watermark.selectText.9=浮水印影像:
|
||||||
@@ -1263,6 +1268,11 @@ 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
|
||||||
@@ -1270,3 +1280,38 @@ 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
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ security:
|
|||||||
csrfDisabled: true # 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 # 'all' (Login Username/Password and OAuth2[must be enabled and configured]), 'normal'(only Login with Username/Password) or 'oauth2'(only Login with OAuth2)
|
loginMethod: all # Accepts values like 'all' and 'normal'(only Login with Username/Password), 'oauth2'(only Login with OAuth2) or 'saml2'(only Login with SAML2)
|
||||||
initialLogin:
|
initialLogin:
|
||||||
username: '' # initial username for the first login
|
username: '' # initial username for the first login
|
||||||
password: '' # initial password for the first login
|
password: '' # initial password for the first login
|
||||||
@@ -42,14 +42,14 @@ security:
|
|||||||
issuer: '' # set to any provider that supports OpenID Connect Discovery (/.well-known/openid-configuration) endpoint
|
issuer: '' # set to any provider that supports OpenID Connect Discovery (/.well-known/openid-configuration) endpoint
|
||||||
clientId: '' # client ID from your provider
|
clientId: '' # client ID from your provider
|
||||||
clientSecret: '' # client secret from your provider
|
clientSecret: '' # client secret from your provider
|
||||||
autoCreateUser: false # set to 'true' to allow auto-creation of non-existing users
|
autoCreateUser: true # set to 'true' to allow auto-creation of non-existing users
|
||||||
blockRegistration: false # set to 'true' to deny login with SSO without prior registration by an admin
|
blockRegistration: false # set to 'true' to deny login with SSO without prior registration by an admin
|
||||||
useAsUsername: email # default is 'email'; custom fields can be used as the username
|
useAsUsername: email # default is 'email'; custom fields can be used as the username
|
||||||
scopes: openid, profile, email # specify the scopes for which the application will request permissions
|
scopes: openid, profile, email # specify the scopes for which the application will request permissions
|
||||||
provider: google # set this to your OAuth provider's name, e.g., 'google' or 'keycloak'
|
provider: google # set this to your OAuth provider's name, e.g., 'google' or 'keycloak'
|
||||||
saml2:
|
saml2:
|
||||||
enabled: false # currently in alpha, not recommended for use yet, enableAlphaFunctionality must be set to true
|
enabled: false # Only enabled for paid enterprise clients (enterpriseEdition.enabled must be true)
|
||||||
autoCreateUser: false # set to 'true' to allow auto-creation of non-existing users
|
autoCreateUser: true # set to 'true' to allow auto-creation of non-existing users
|
||||||
blockRegistration: false # set to 'true' to deny login with SSO without prior registration by an admin
|
blockRegistration: false # set to 'true' to deny login with SSO without prior registration by an admin
|
||||||
registrationId: stirling
|
registrationId: stirling
|
||||||
idpMetadataUri: https://dev-XXXXXXXX.okta.com/app/externalKey/sso/saml/metadata
|
idpMetadataUri: https://dev-XXXXXXXX.okta.com/app/externalKey/sso/saml/metadata
|
||||||
|
|||||||
@@ -1386,6 +1386,13 @@
|
|||||||
"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",
|
||||||
|
|||||||
@@ -1,10 +1,221 @@
|
|||||||
|
.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 {
|
||||||
margin-top: 10px;
|
display: var(--selected-files-display);
|
||||||
max-height: 150px;
|
padding-left: 5px;
|
||||||
overflow-y: auto;
|
padding-right: 3px;
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
|||||||
219
src/main/resources/static/images/flags/ir.svg
Normal file
219
src/main/resources/static/images/flags/ir.svg
Normal file
@@ -0,0 +1,219 @@
|
|||||||
|
<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>
|
||||||
|
After Width: | Height: | Size: 15 KiB |
@@ -1,26 +1,36 @@
|
|||||||
(function() {
|
(function () {
|
||||||
|
if (window.isDownloadScriptInitialized) return; // Prevent re-execution
|
||||||
|
window.isDownloadScriptInitialized = true;
|
||||||
|
|
||||||
const { pdfPasswordPrompt, multipleInputsForSingleRequest, disableMultipleFiles, remoteCall, sessionExpired, refreshPage, error } = window.stirlingPDF;
|
const {
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
@@ -28,19 +38,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;
|
||||||
const 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
|
||||||
@@ -49,58 +59,60 @@
|
|||||||
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 {
|
||||||
submitButton.textContent = "Processing...";
|
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('<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>');
|
$('body').append(
|
||||||
setTimeout(function() {
|
'<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;">' +
|
||||||
$("#download-complete-banner").fadeOut("slow", function() {
|
downloadCompleteText +
|
||||||
|
'</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);
|
||||||
@@ -112,8 +124,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);
|
||||||
@@ -128,8 +140,8 @@
|
|||||||
let errorMessage = null;
|
let errorMessage = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(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;
|
||||||
@@ -137,58 +149,57 @@
|
|||||||
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;
|
||||||
@@ -198,37 +209,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) {
|
||||||
@@ -240,32 +251,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();
|
||||||
@@ -279,10 +290,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) {
|
||||||
@@ -298,12 +309,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 {
|
||||||
@@ -325,47 +336,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'");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
52
src/main/resources/static/js/file-icon-factory.js
Normal file
52
src/main/resources/static/js/file-icon-factory.js
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
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;
|
||||||
31
src/main/resources/static/js/file-utils.js
Normal file
31
src/main/resources/static/js/file-utils.js
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
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;
|
||||||
@@ -1,72 +1,92 @@
|
|||||||
document.addEventListener("DOMContentLoaded", function () {
|
import FileIconFactory from "./file-icon-factory.js";
|
||||||
document.querySelectorAll(".custom-file-chooser").forEach(setupFileInput);
|
import FileUtils from "./file-utils.js";
|
||||||
});
|
import UUID from './uuid.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) {
|
||||||
overlay = document.createElement("div");
|
// Show overlay by removing display: none from pseudo elements (::before and ::after)
|
||||||
overlay.style.position = "fixed";
|
inputContainer.style.setProperty('--overlay-display', "''");
|
||||||
overlay.style.top = 0;
|
overlay = true;
|
||||||
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) {
|
||||||
if (overlay) {
|
hideOverlay();
|
||||||
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;
|
||||||
|
|
||||||
for (let i = 0; i < files.length; i++) {
|
const fileInput = document.getElementById(elementId);
|
||||||
allFiles.push(files[i]);
|
if (fileInput?.hasAttribute("multiple")) {
|
||||||
|
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;
|
||||||
|
|
||||||
if (overlay) {
|
hideOverlay();
|
||||||
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) {
|
||||||
|
for (let file of fileList) {
|
||||||
|
container.push(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
["dragenter", "dragover", "dragleave", "drop"].forEach((eventName) => {
|
["dragenter", "dragover", "dragleave", "drop"].forEach((eventName) => {
|
||||||
document.body.addEventListener(eventName, preventDefaults, false);
|
document.body.addEventListener(eventName, preventDefaults, false);
|
||||||
});
|
});
|
||||||
@@ -83,44 +103,130 @@ function setupFileInput(chooser) {
|
|||||||
$("#" + elementId).on("change", 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 = allFiles.map(file => {
|
||||||
|
if (!file.uniqueId) file.uniqueId = UUID.uuidv4();
|
||||||
|
return file;
|
||||||
|
});
|
||||||
|
|
||||||
if (!isDragAndDrop) {
|
if (!isDragAndDrop) {
|
||||||
let dataTransfer = new DataTransfer();
|
let dataTransfer = toDataTransfer(allFiles);
|
||||||
allFiles.forEach(file => dataTransfer.items.add(file));
|
element.files = dataTransfer.files;
|
||||||
element.files = dataTransfer.files;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleFileInputChange(this);
|
handleFileInputChange(this);
|
||||||
this.dispatchEvent(new CustomEvent("file-input-change", { bubbles: true }));
|
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;
|
||||||
const fileNames = files.map((f) => f.name);
|
showOrHideSelectedFilesContainer(files);
|
||||||
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();
|
||||||
fileNames.forEach((fileName) => {
|
filesInfo.forEach((info) => {
|
||||||
selectedFilesContainer.append("<div>" + fileName + "</div>");
|
let fileContainerClasses = 'small-file-container d-flex flex-column justify-content-center align-items-center';
|
||||||
|
|
||||||
|
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) {
|
|
||||||
$(inputElement).siblings(".custom-file-label").addClass("selected").html(fileNames[0]);
|
showOrHideSelectedFilesContainer(filesInfo);
|
||||||
} 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 fileName = e.detail;
|
const fileId = e.detail;
|
||||||
allFiles = allFiles.filter(file => file.name !== fileName);
|
let inputElement = document.getElementById(elementId);
|
||||||
|
removeFileById(fileId, inputElement);
|
||||||
|
showOrHideSelectedFilesContainer(allFiles);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ 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
|
||||||
@@ -110,11 +111,13 @@ 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
|
||||||
var fileName = parent.querySelector(".filename").innerText;
|
let filenameNode = parent.querySelector(".filename");
|
||||||
|
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: fileName });
|
var event = new CustomEvent("fileRemoved", { detail: fileId });
|
||||||
document.dispatchEvent(event);
|
document.dispatchEvent(event);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
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';
|
||||||
|
|
||||||
class PdfContainer {
|
class PdfContainer {
|
||||||
fileName;
|
fileName;
|
||||||
@@ -34,7 +36,7 @@ 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);
|
||||||
|
|
||||||
@@ -63,7 +65,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;
|
||||||
|
|
||||||
@@ -76,7 +78,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();
|
||||||
@@ -84,7 +86,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();
|
||||||
@@ -92,15 +94,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,86 +130,99 @@ class PdfContainer {
|
|||||||
return movePageCommand;
|
return movePageCommand;
|
||||||
}
|
}
|
||||||
|
|
||||||
addFiles(nextSiblingElement, blank = false) {
|
async addFiles(element) {
|
||||||
if (blank) {
|
let addFilesCommand = new AddFilesCommand(
|
||||||
|
element,
|
||||||
|
window.selectedPages,
|
||||||
|
this.addFilesAction.bind(this),
|
||||||
|
this.pagesContainer
|
||||||
|
);
|
||||||
|
|
||||||
this.addFilesBlank(nextSiblingElement);
|
await addFilesCommand.execute();
|
||||||
|
|
||||||
} else {
|
this.undoManager.pushUndoClearRedo(addFilesCommand);
|
||||||
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) {
|
||||||
this.addFilesFromFiles(files, nextSiblingElement);
|
pages = await this.addFilesFromFiles(files, nextSiblingElement, pages);
|
||||||
this.updateFilename(files ? files[0].name : "");
|
this.updateFilename(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) {
|
|
||||||
this.fileName = files[0].name;
|
|
||||||
for (var i = 0; i < files.length; i++) {
|
|
||||||
const startTime = Date.now();
|
|
||||||
let processingTime, errorMessage = null, pageCount = 0;
|
|
||||||
try {
|
|
||||||
const file = files[i];
|
|
||||||
if (file.type === "application/pdf") {
|
|
||||||
const { renderer, pdfDocument } = await this.loadFile(file);
|
|
||||||
pageCount = renderer.pageCount || 0;
|
|
||||||
await this.addPdfFile(renderer, pdfDocument, nextSiblingElement);
|
|
||||||
} else if (file.type.startsWith("image/")) {
|
|
||||||
await this.addImageFile(file, nextSiblingElement);
|
|
||||||
}
|
|
||||||
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) => {
|
|
||||||
element.disabled = false;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
captureFileProcessingEvent(success, file, processingTime, errorMessage, pageCount) {
|
async addFilesFromFiles(files, nextSiblingElement, pages) {
|
||||||
try{
|
this.fileName = files[0].name;
|
||||||
if(analyticsEnabled){
|
for (var i = 0; i < files.length; i++) {
|
||||||
posthog.capture('file_processing', {
|
const startTime = Date.now();
|
||||||
success,
|
let processingTime,
|
||||||
file_type: file?.type || 'unknown',
|
errorMessage = null,
|
||||||
file_size: file?.size || 0,
|
pageCount = 0;
|
||||||
processing_time: processingTime,
|
try {
|
||||||
error_message: errorMessage,
|
const file = files[i];
|
||||||
pdf_pages: pageCount,
|
if (file.type === 'application/pdf') {
|
||||||
});
|
const {renderer, pdfDocument} = await this.loadFile(file);
|
||||||
}
|
pageCount = renderer.pageCount || 0;
|
||||||
}catch{
|
pages = await this.addPdfFile(renderer, pdfDocument, nextSiblingElement, pages);
|
||||||
}
|
} else if (file.type.startsWith('image/')) {
|
||||||
}
|
pages = await this.addImageFile(file, nextSiblingElement, pages);
|
||||||
|
}
|
||||||
|
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) => {
|
||||||
|
element.disabled = false;
|
||||||
|
});
|
||||||
|
return pages;
|
||||||
|
}
|
||||||
|
|
||||||
async addFilesBlank(nextSiblingElement) {
|
captureFileProcessingEvent(success, file, processingTime, errorMessage, pageCount) {
|
||||||
|
try {
|
||||||
|
if (analyticsEnabled) {
|
||||||
|
posthog.capture('file_processing', {
|
||||||
|
success,
|
||||||
|
file_type: file?.type || 'unknown',
|
||||||
|
file_size: file?.size || 0,
|
||||||
|
processing_time: processingTime,
|
||||||
|
error_message: errorMessage,
|
||||||
|
pdf_pages: pageCount,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
|
||||||
|
async addFilesBlank(nextSiblingElement, pages) {
|
||||||
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);
|
||||||
await this.addPdfFile(renderer, doc, nextSiblingElement);
|
return pages;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
rotateElement(element, deg) {
|
rotateElement(element, deg) {
|
||||||
let rotateCommand = new RotateElementCommand(element, deg);
|
let rotateCommand = new RotateElementCommand(element, deg);
|
||||||
rotateCommand.execute();
|
rotateCommand.execute();
|
||||||
@@ -215,37 +230,43 @@ class PdfContainer {
|
|||||||
return rotateCommand;
|
return rotateCommand;
|
||||||
}
|
}
|
||||||
|
|
||||||
async addPdfFile(renderer, pdfDocument, nextSiblingElement) {
|
async addPdfFile(renderer, pdfDocument, nextSiblingElement, pages) {
|
||||||
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) {
|
async addImageFile(file, nextSiblingElement, pages) {
|
||||||
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);
|
||||||
|
|
||||||
@@ -257,17 +278,19 @@ 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,
|
||||||
@@ -275,7 +298,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) {
|
||||||
@@ -288,8 +311,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;
|
||||||
@@ -316,7 +339,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);
|
||||||
@@ -328,12 +351,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;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -345,25 +368,24 @@ 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;
|
||||||
@@ -386,18 +408,20 @@ 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) { // Ensure the page number is greater than 0
|
if (i > 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) { // Ensure page is within valid range
|
if (page > 0 && page <= maxPageIndex) {
|
||||||
|
// Ensure page is within valid range
|
||||||
pages.add(page);
|
pages.add(page);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -407,24 +431,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 = [];
|
||||||
@@ -445,27 +469,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 = () => {
|
||||||
@@ -489,7 +513,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++) {
|
||||||
@@ -503,23 +527,25 @@ class PdfContainer {
|
|||||||
return Array.from(pages).sort((a, b) => a - b);
|
return Array.from(pages).sort((a, b) => a - b);
|
||||||
}
|
}
|
||||||
|
|
||||||
addFilesBlankAll() {
|
async addFilesBlankAll() {
|
||||||
const allPages = this.pagesContainer.querySelectorAll(".page-container");
|
const allPages = this.pagesContainer.querySelectorAll('.page-container');
|
||||||
allPages.forEach((page, index) => {
|
|
||||||
if (index !== 0) {
|
|
||||||
this.addFiles(page, true)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
splitAll() {
|
let pageBreakCommand = new PageBreakCommand(
|
||||||
const allPages = this.pagesContainer.querySelectorAll(".page-container");
|
|
||||||
let splitAllCommand = new SplitAllCommand(
|
|
||||||
allPages,
|
allPages,
|
||||||
window.selectPage,
|
window.selectPage,
|
||||||
window.selectedPages,
|
window.selectedPages,
|
||||||
"split-before"
|
this.addFilesBlank.bind(this),
|
||||||
|
this.pagesContainer
|
||||||
);
|
);
|
||||||
|
|
||||||
|
await pageBreakCommand.execute();
|
||||||
|
|
||||||
|
this.undoManager.pushUndoClearRedo(pageBreakCommand);
|
||||||
|
}
|
||||||
|
|
||||||
|
splitAll() {
|
||||||
|
const allPages = this.pagesContainer.querySelectorAll('.page-container');
|
||||||
|
let splitAllCommand = new SplitAllCommand(allPages, window.selectPage, window.selectedPages, 'split-before');
|
||||||
splitAllCommand.execute();
|
splitAllCommand.execute();
|
||||||
|
|
||||||
this.undoManager.pushUndoClearRedo(splitAllCommand);
|
this.undoManager.pushUndoClearRedo(splitAllCommand);
|
||||||
@@ -529,7 +555,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 = [];
|
||||||
@@ -540,18 +566,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;
|
||||||
}
|
}
|
||||||
@@ -560,8 +586,10 @@ 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]], { type: "application/pdf" });
|
const documentBlob = new Blob([pdfBytesArray[i]], {
|
||||||
zip.file(baseNameString + "-" + (i + 1) + ".pdf", documentBlob);
|
type: 'application/pdf',
|
||||||
|
});
|
||||||
|
zip.file(baseNameString + '-' + (i + 1) + '.pdf', documentBlob);
|
||||||
}
|
}
|
||||||
|
|
||||||
return zip;
|
return zip;
|
||||||
@@ -569,10 +597,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) {
|
||||||
@@ -612,7 +640,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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -621,11 +649,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
|
||||||
@@ -634,17 +662,18 @@ 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) { // Split the pdf if there are separators.
|
if (separators.length !== 0) {
|
||||||
const baseName = this.fileName ? this.fileName : "managed";
|
// Split the pdf if there are separators.
|
||||||
|
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);
|
||||||
@@ -655,80 +684,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 {
|
||||||
} else { // Continue normally if there are no separators
|
// 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;
|
||||||
|
|
||||||
@@ -752,38 +781,36 @@ 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);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -801,8 +828,10 @@ function detectImageType(uint8Array) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check for TIFF signature (little-endian and big-endian)
|
// Check for TIFF signature (little-endian and big-endian)
|
||||||
if ((uint8Array[0] === 73 && uint8Array[1] === 73 && uint8Array[2] === 42 && uint8Array[3] === 0) ||
|
if (
|
||||||
(uint8Array[0] === 77 && uint8Array[1] === 77 && uint8Array[2] === 0 && uint8Array[3] === 42)) {
|
(uint8Array[0] === 73 && uint8Array[1] === 73 && uint8Array[2] === 42 && uint8Array[3] === 0) ||
|
||||||
|
(uint8Array[0] === 77 && uint8Array[1] === 77 && uint8Array[2] === 0 && uint8Array[3] === 42)
|
||||||
|
) {
|
||||||
return 'TIFF';
|
return 'TIFF';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -814,6 +843,4 @@ function detectImageType(uint8Array) {
|
|||||||
return 'UNKNOWN';
|
return 'UNKNOWN';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export default PdfContainer;
|
export default PdfContainer;
|
||||||
|
|||||||
53
src/main/resources/static/js/multitool/commands/add-page.js
Normal file
53
src/main/resources/static/js/multitool/commands/add-page.js
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
import {Command} from './command.js';
|
||||||
|
|
||||||
|
export class AddFilesCommand extends Command {
|
||||||
|
constructor(element, selectedPages, addFilesAction, pagesContainer) {
|
||||||
|
super();
|
||||||
|
this.element = element;
|
||||||
|
this.selectedPages = selectedPages;
|
||||||
|
this.addFilesAction = addFilesAction;
|
||||||
|
this.pagesContainer = pagesContainer;
|
||||||
|
this.addedElements = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
async execute() {
|
||||||
|
const undoBtn = document.getElementById('undo-btn');
|
||||||
|
undoBtn.disabled = true;
|
||||||
|
if (this.element) {
|
||||||
|
const newElement = await this.addFilesAction(this.element);
|
||||||
|
if (newElement) {
|
||||||
|
this.addedElements = newElement;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const newElement = await this.addFilesAction(false);
|
||||||
|
if (newElement) {
|
||||||
|
this.addedElements = newElement;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
undoBtn.disabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
undo() {
|
||||||
|
this.addedElements.forEach((element) => {
|
||||||
|
const nextSibling = element.nextSibling;
|
||||||
|
this.pagesContainer.removeChild(element);
|
||||||
|
|
||||||
|
if (this.pagesContainer.childElementCount === 0) {
|
||||||
|
const filenameInput = document.getElementById('filename-input');
|
||||||
|
const filenameParagraph = document.getElementById('filename');
|
||||||
|
const downloadBtn = document.getElementById('export-button');
|
||||||
|
|
||||||
|
filenameInput.disabled = true;
|
||||||
|
filenameInput.value = '';
|
||||||
|
filenameParagraph.innerText = '';
|
||||||
|
downloadBtn.disabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
element._nextSibling = nextSibling;
|
||||||
|
});
|
||||||
|
this.addedElements = [];
|
||||||
|
}
|
||||||
|
redo() {
|
||||||
|
this.execute();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,7 +10,9 @@ export class DeletePageCommand extends Command {
|
|||||||
this.filenameInputValue = document.getElementById("filename-input").value;
|
this.filenameInputValue = document.getElementById("filename-input").value;
|
||||||
|
|
||||||
const filenameParagraph = document.getElementById("filename");
|
const filenameParagraph = document.getElementById("filename");
|
||||||
this.filenameParagraphText = filenameParagraph ? filenameParagraph.innerText : "";
|
this.filenameParagraphText = filenameParagraph
|
||||||
|
? filenameParagraph.innerText
|
||||||
|
: "";
|
||||||
}
|
}
|
||||||
|
|
||||||
execute() {
|
execute() {
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
import {Command} from './command.js';
|
||||||
|
|
||||||
|
export class PageBreakCommand extends Command {
|
||||||
|
constructor(elements, isSelectedInWindow, selectedPages, pageBreakCallback, pagesContainer) {
|
||||||
|
super();
|
||||||
|
this.elements = elements;
|
||||||
|
this.isSelectedInWindow = isSelectedInWindow;
|
||||||
|
this.selectedPages = selectedPages;
|
||||||
|
this.pageBreakCallback = pageBreakCallback;
|
||||||
|
this.pagesContainer = pagesContainer;
|
||||||
|
this.addedElements = [];
|
||||||
|
this.originalStates = Array.from(elements, (element) => ({
|
||||||
|
element,
|
||||||
|
hasContent: element.innerHTML.trim() !== '',
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
async execute() {
|
||||||
|
const undoBtn = document.getElementById('undo-btn');
|
||||||
|
undoBtn.disabled = true;
|
||||||
|
for (const [index, element] of this.elements.entries()) {
|
||||||
|
if (!this.isSelectedInWindow || this.selectedPages.includes(index)) {
|
||||||
|
if (index !== 0) {
|
||||||
|
const newElement = await this.pageBreakCallback(element, this.addedElements);
|
||||||
|
|
||||||
|
if (newElement) {
|
||||||
|
this.addedElements = newElement;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
undoBtn.disabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
undo() {
|
||||||
|
this.addedElements.forEach((element) => {
|
||||||
|
const nextSibling = element.nextSibling;
|
||||||
|
|
||||||
|
this.pagesContainer.removeChild(element);
|
||||||
|
|
||||||
|
if (this.pagesContainer.childElementCount === 0) {
|
||||||
|
const filenameInput = document.getElementById('filename-input');
|
||||||
|
const filenameParagraph = document.getElementById('filename');
|
||||||
|
const downloadBtn = document.getElementById('export-button');
|
||||||
|
|
||||||
|
filenameInput.disabled = true;
|
||||||
|
filenameInput.value = '';
|
||||||
|
filenameParagraph.innerText = '';
|
||||||
|
downloadBtn.disabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
element._nextSibling = nextSibling;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
redo() {
|
||||||
|
this.execute();
|
||||||
|
}
|
||||||
|
}
|
||||||
9
src/main/resources/static/js/uuid.js
Normal file
9
src/main/resources/static/js/uuid.js
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
class UUID {
|
||||||
|
static uuidv4() {
|
||||||
|
return "10000000-1000-4000-8000-100000000000".replace(/[018]/g, c =>
|
||||||
|
(+c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> +c / 4).toString(16)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default UUID;
|
||||||
@@ -189,7 +189,7 @@
|
|||||||
<label for="authType">Authentication Type</label>
|
<label for="authType">Authentication Type</label>
|
||||||
<select id="authType" name="authType" class="form-control" required>
|
<select id="authType" name="authType" class="form-control" required>
|
||||||
<option value="web" selected>WEB</option>
|
<option value="web" selected>WEB</option>
|
||||||
<option value="oauth2">OAUTH2</option>
|
<option value="sso">SSO</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-check mb-3" id="checkboxContainer">
|
<div class="form-check mb-3" id="checkboxContainer">
|
||||||
@@ -267,7 +267,7 @@
|
|||||||
var passwordFieldContainer = $('#passwordContainer');
|
var passwordFieldContainer = $('#passwordContainer');
|
||||||
var checkboxContainer = $('#checkboxContainer');
|
var checkboxContainer = $('#checkboxContainer');
|
||||||
|
|
||||||
if (authType === 'oauth2') {
|
if (authType === 'sso') {
|
||||||
passwordField.removeAttr('required');
|
passwordField.removeAttr('required');
|
||||||
passwordField.prop('disabled', true).val('');
|
passwordField.prop('disabled', true).val('');
|
||||||
passwordFieldContainer.slideUp('fast');
|
passwordFieldContainer.slideUp('fast');
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
<input id="y" type="hidden" name="y">
|
<input id="y" type="hidden" name="y">
|
||||||
<input id="width" type="hidden" name="width">
|
<input id="width" type="hidden" name="width">
|
||||||
<input id="height" type="hidden" name="height">
|
<input id="height" type="hidden" name="height">
|
||||||
<button type="submit" class="btn btn-primary" th:text="#{crop.submit}"></button>
|
<button id="submitBtn" type="submit" class="btn btn-primary" th:text="#{crop.submit}"></button>
|
||||||
</form>
|
</form>
|
||||||
<div id="canvasesContainer" style="position: relative; margin: 20px 0; width: auto;">
|
<div id="canvasesContainer" style="position: relative; margin: 20px 0; width: auto;">
|
||||||
<canvas id="cropPdfCanvas" style="width: 100%"></canvas>
|
<canvas id="cropPdfCanvas" style="width: 100%"></canvas>
|
||||||
|
|||||||
@@ -59,7 +59,7 @@
|
|||||||
<link rel="stylesheet" th:href="@{'/css/fileSelect.css'}" th:if="${currentPage != 'home'}">
|
<link rel="stylesheet" th:href="@{'/css/fileSelect.css'}" th:if="${currentPage != 'home'}">
|
||||||
<link rel="stylesheet" th:href="@{'/css/footer.css'}">
|
<link rel="stylesheet" th:href="@{'/css/footer.css'}">
|
||||||
|
|
||||||
<link rel="preload" href="/fonts/google-symbol.woff2" as="font" type="font/woff2" crossorigin="anonymous">
|
<link rel="preload" th:href="@{'/fonts/google-symbol.woff2'}" as="font" type="font/woff2" crossorigin="anonymous">
|
||||||
|
|
||||||
|
|
||||||
<script th:src="@{'/js/thirdParty/fontfaceobserver.standalone.js'}"></script>
|
<script th:src="@{'/js/thirdParty/fontfaceobserver.standalone.js'}"></script>
|
||||||
@@ -204,11 +204,17 @@
|
|||||||
<script type="module" th:src="@{'/pdfjs-legacy/pdf.mjs'}"></script>
|
<script type="module" th:src="@{'/pdfjs-legacy/pdf.mjs'}"></script>
|
||||||
<script th:src="@{'/js/downloader.js'}"></script>
|
<script th:src="@{'/js/downloader.js'}"></script>
|
||||||
|
|
||||||
<div class="custom-file-chooser" th:attr="data-bs-unique-id=${name}, data-bs-element-id=${name+'-input'}, data-bs-files-selected=#{filesSelected}, data-bs-pdf-prompt=#{pdfPrompt}">
|
<div class="custom-file-chooser mb-3" th:attr="data-bs-unique-id=${name}, data-bs-element-id=${name+'-input'}, data-bs-element-container-id=${name+'-input-container'}, data-bs-files-selected=#{filesSelected}, data-bs-pdf-prompt=#{pdfPrompt}">
|
||||||
<div class="mb-3">
|
<div class="mb-3 d-flex flex-row justify-content-center align-items-center flex-wrap input-container" th:name="${name}+'-input'" th:id="${name}+'-input-container'" th:data-text="#{fileChooser.hoveredDragAndDrop}">
|
||||||
<input type="file" class="form-control" th:name="${name}" th:id="${name}+'-input'" th:accept="${accept}" th:attr="multiple=${!disableMultipleFiles}" th:required="${notRequired} ? null : 'required'">
|
<label class="file-input-btn d-none">
|
||||||
|
<input type="file" class="form-control" th:name="${name}" th:id="${name}+'-input'" th:accept="${accept}" th:attr="multiple=${!disableMultipleFiles}" th:required="${notRequired} ? null : 'required'">
|
||||||
|
Browse
|
||||||
|
</label>
|
||||||
|
<div th:text="#{fileChooser.click}"></div>
|
||||||
|
<div th:text="#{fileChooser.or}"></div>
|
||||||
|
<div th:text="#{fileChooser.dragAndDrop}"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="selected-files"></div>
|
<div class="selected-files flex-wrap"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="progressBarContainer" style="display: none; position: relative;">
|
<div class="progressBarContainer" style="display: none; position: relative;">
|
||||||
@@ -218,5 +224,5 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script th:src="@{'/js/fileInput.js'}"></script>
|
<script th:src="@{'/js/fileInput.js'}" type="module"></script>
|
||||||
</th:block>
|
</th:block>
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="ga_IE"> <img th:src="@{'/images/flags/ie.svg'}" alt="icon" width="20" height="15"> Irish</a>
|
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="ga_IE"> <img th:src="@{'/images/flags/ie.svg'}" alt="icon" width="20" height="15"> Irish</a>
|
||||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="it_IT"> <img th:src="@{'/images/flags/it.svg'}" alt="icon" width="20" height="15"> Italiano</a>
|
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="it_IT"> <img th:src="@{'/images/flags/it.svg'}" alt="icon" width="20" height="15"> Italiano</a>
|
||||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="nl_NL"> <img th:src="@{'/images/flags/nl.svg'}" alt="icon" width="20" height="15"> Nederlands</a>
|
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="nl_NL"> <img th:src="@{'/images/flags/nl.svg'}" alt="icon" width="20" height="15"> Nederlands</a>
|
||||||
|
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="fa_IR"> <img th:src="@{'/images/flags/ir.svg'}" alt="icon" width="20" height="15"> پارسی</a>
|
||||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="pl_PL"> <img th:src="@{'/images/flags/pl.svg'}" alt="icon" width="20" height="15"> Polski</a>
|
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="pl_PL"> <img th:src="@{'/images/flags/pl.svg'}" alt="icon" width="20" height="15"> Polski</a>
|
||||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="pt_BR"> <img th:src="@{'/images/flags/pt_br.svg'}" alt="icon" width="20" height="15"> Português (BR)</a>
|
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="pt_BR"> <img th:src="@{'/images/flags/pt_br.svg'}" alt="icon" width="20" height="15"> Português (BR)</a>
|
||||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="pt_PT"> <img th:src="@{'/images/flags/pt_pt.svg'}" alt="icon" width="20" height="15"> Português (PT)</a>
|
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="pt_PT"> <img th:src="@{'/images/flags/pt_pt.svg'}" alt="icon" width="20" height="15"> Português (PT)</a>
|
||||||
|
|||||||
@@ -154,6 +154,9 @@
|
|||||||
<div
|
<div
|
||||||
th:replace="~{fragments/navbarEntry :: navbarEntry ('cert-sign', 'workspace_premium', 'home.certSign.title', 'home.certSign.desc', 'certSign.tags', 'security')}">
|
th:replace="~{fragments/navbarEntry :: navbarEntry ('cert-sign', 'workspace_premium', 'home.certSign.title', 'home.certSign.desc', 'certSign.tags', 'security')}">
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
th:replace="~{fragments/navbarEntry :: navbarEntry('validate-signature','verified','home.validateSignature.title','home.validateSignature.desc','validateSignature.tags','security')}">
|
||||||
|
</div>
|
||||||
<div
|
<div
|
||||||
th:replace="~{fragments/navbarEntry :: navbarEntry ('remove-cert-sign', 'remove_moderator', 'home.removeCertSign.title', 'home.removeCertSign.desc', 'removeCertSign.tags', 'security')}">
|
th:replace="~{fragments/navbarEntry :: navbarEntry ('remove-cert-sign', 'remove_moderator', 'home.removeCertSign.title', 'home.removeCertSign.desc', 'removeCertSign.tags', 'security')}">
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -215,6 +215,9 @@
|
|||||||
<div
|
<div
|
||||||
th:replace="~{fragments/card :: card(id='cert-sign', cardTitle=#{home.certSign.title}, cardText=#{home.certSign.desc}, cardLink='cert-sign', toolIcon='workspace_premium', tags=#{certSign.tags}, toolGroup='security')}">
|
th:replace="~{fragments/card :: card(id='cert-sign', cardTitle=#{home.certSign.title}, cardText=#{home.certSign.desc}, cardLink='cert-sign', toolIcon='workspace_premium', tags=#{certSign.tags}, toolGroup='security')}">
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
th:replace="~{fragments/card :: card(id='validate-signature', cardTitle=#{home.validateSignature.title}, cardText=#{home.validateSignature.desc}, cardLink='validate-signature', toolIcon='verified', tags=#{validateSignature.tags}, toolGroup='security')}">
|
||||||
|
</div>
|
||||||
<div
|
<div
|
||||||
th:replace="~{fragments/card :: card(id='remove-cert-sign', cardTitle=#{home.removeCertSign.title}, cardText=#{home.removeCertSign.desc}, cardLink='remove-cert-sign', toolIcon='remove_moderator', tags=#{removeCertSign.tags}, toolGroup='security')}">
|
th:replace="~{fragments/card :: card(id='remove-cert-sign', cardTitle=#{home.removeCertSign.title}, cardText=#{home.removeCertSign.desc}, cardLink='remove-cert-sign', toolIcon='remove_moderator', tags=#{removeCertSign.tags}, toolGroup='security')}">
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -164,13 +164,13 @@
|
|||||||
keyInput.type = "text";
|
keyInput.type = "text";
|
||||||
keyInput.placeholder = 'Key';
|
keyInput.placeholder = 'Key';
|
||||||
keyInput.className = "form-control";
|
keyInput.className = "form-control";
|
||||||
keyInput.name = "customKey" + count;
|
keyInput.name = `allRequestParams[customKey${count}]`;
|
||||||
|
|
||||||
const valueInput = document.createElement("input");
|
const valueInput = document.createElement("input");
|
||||||
valueInput.type = "text";
|
valueInput.type = "text";
|
||||||
valueInput.placeholder = 'Value';
|
valueInput.placeholder = 'Value';
|
||||||
valueInput.className = "form-control";
|
valueInput.className = "form-control";
|
||||||
valueInput.name = "customValue" + count;
|
valueInput.name = `allRequestParams[customValue${count}]`;
|
||||||
count = count + 1;
|
count = count + 1;
|
||||||
|
|
||||||
const formGroup = document.createElement("div");
|
const formGroup = document.createElement("div");
|
||||||
|
|||||||
@@ -156,7 +156,7 @@
|
|||||||
resultDiv2.innerHTML = loading;
|
resultDiv2.innerHTML = loading;
|
||||||
|
|
||||||
// Create a new Worker
|
// Create a new Worker
|
||||||
const worker = new Worker('/js/compare/pdfWorker.js');
|
const worker = new Worker('./js/compare/pdfWorker.js');
|
||||||
|
|
||||||
|
|
||||||
// Post messages to the worker
|
// Post messages to the worker
|
||||||
|
|||||||
@@ -276,7 +276,8 @@ function formatText(text) {
|
|||||||
const container = document.createElement('div');
|
const container = document.createElement('div');
|
||||||
|
|
||||||
// Split the text into lines
|
// Split the text into lines
|
||||||
const lines = text.split('\n');
|
const textWithoutComments = text.replace(/<!--[\s\S]*?-->/g, '');
|
||||||
|
const lines = textWithoutComments.split('\n');
|
||||||
let currentList = null;
|
let currentList = null;
|
||||||
|
|
||||||
lines.forEach(line => {
|
lines.forEach(line => {
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user