Compare commits
1 Commits
v0.36.2
...
quickfixes
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ea4e6fb3ef |
2
.github/ISSUE_TEMPLATE/config.yml
vendored
2
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,5 +1,5 @@
|
|||||||
blank_issues_enabled: true
|
blank_issues_enabled: true
|
||||||
contact_links:
|
contact_links:
|
||||||
- name: 💬 Discord Server
|
- name: 💬 Discord Server
|
||||||
url: https://discord.gg/HYmhKj45pU
|
url: https://discord.gg/Cn8pWhQRxZ
|
||||||
about: You can join our Discord server for real time discussion and support
|
about: You can join our Discord server for real time discussion and support
|
||||||
|
|||||||
96
.github/workflows/multiOSReleases.yml
vendored
96
.github/workflows/multiOSReleases.yml
vendored
@@ -1,96 +0,0 @@
|
|||||||
name: Test Installers Build
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
release:
|
|
||||||
types: [created]
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
packages: write
|
|
||||||
jobs:
|
|
||||||
build-installers:
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- os: windows-latest
|
|
||||||
platform: win
|
|
||||||
ext: exe
|
|
||||||
#- os: macos-latest
|
|
||||||
# platform: mac
|
|
||||||
# ext: dmg
|
|
||||||
#- os: ubuntu-latest
|
|
||||||
# platform: linux
|
|
||||||
# ext: deb
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Set up JDK 21
|
|
||||||
uses: actions/setup-java@v4
|
|
||||||
with:
|
|
||||||
java-version: "21"
|
|
||||||
distribution: "temurin"
|
|
||||||
|
|
||||||
- uses: gradle/actions/setup-gradle@v4
|
|
||||||
with:
|
|
||||||
gradle-version: 8.7
|
|
||||||
|
|
||||||
# Install Windows dependencies
|
|
||||||
- name: Install WiX Toolset
|
|
||||||
if: matrix.os == 'windows-latest'
|
|
||||||
run: |
|
|
||||||
curl -L -o wix.exe https://github.com/wixtoolset/wix3/releases/download/wix3141rtm/wix314.exe
|
|
||||||
.\wix.exe /install /quiet
|
|
||||||
|
|
||||||
# Install Linux dependencies
|
|
||||||
- name: Install Linux Dependencies
|
|
||||||
if: matrix.os == 'ubuntu-latest'
|
|
||||||
run: |
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install -y fakeroot rpm
|
|
||||||
|
|
||||||
# Get version number
|
|
||||||
- name: Get version number
|
|
||||||
id: versionNumber
|
|
||||||
run: echo "versionNumber=$(./gradlew printVersion --quiet | tail -1)" >> $GITHUB_OUTPUT
|
|
||||||
shell: bash
|
|
||||||
|
|
||||||
- name: Get version number mac
|
|
||||||
id: versionNumberMac
|
|
||||||
run: echo "versionNumberMac=$(./gradlew printMacVersion --quiet | tail -1)" >> $GITHUB_OUTPUT
|
|
||||||
shell: bash
|
|
||||||
|
|
||||||
# Build installer
|
|
||||||
- name: Build Installer
|
|
||||||
run: ./gradlew build jpackage -x test --info
|
|
||||||
env:
|
|
||||||
DOCKER_ENABLE_SECURITY: false
|
|
||||||
STIRLING_PDF_DESKTOP_UI: true
|
|
||||||
|
|
||||||
# Rename and collect artifacts based on OS
|
|
||||||
- name: Prepare artifacts
|
|
||||||
id: prepare
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
if [ "${{ matrix.os }}" = "windows-latest" ]; then
|
|
||||||
mv "build/jpackage/Stirling-PDF-${{ steps.versionNumber.outputs.versionNumber }}.exe" "Stirling-PDF-${{ matrix.platform }}-installer.${{ matrix.ext }}"
|
|
||||||
elif [ "${{ matrix.os }}" = "macos-latest" ]; then
|
|
||||||
mv "build/jpackage/Stirling-PDF-${{ steps.versionNumberMac.outputs.versionNumberMac }}.dmg" "Stirling-PDF-${{ steps.versionNumber.outputs.versionNumber }}-${{ matrix.platform }}.${{ matrix.ext }}"
|
|
||||||
else
|
|
||||||
mv "build/jpackage/stirling-pdf_${{ steps.versionNumber.outputs.versionNumber }}-1_amd64.deb" "Stirling-PDF-${{ steps.versionNumber.outputs.versionNumber }}-${{ matrix.platform }}.${{ matrix.ext }}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Upload installer as artifact for testing
|
|
||||||
- name: Upload Installer Artifact
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: Stirling-PDF-${{ matrix.platform }}-installer.${{ matrix.ext }}
|
|
||||||
path: Stirling-PDF-${{ matrix.platform }}-installer.${{ matrix.ext }}
|
|
||||||
retention-days: 1
|
|
||||||
if-no-files-found: error
|
|
||||||
|
|
||||||
- name: Upload binaries to release
|
|
||||||
uses: softprops/action-gh-release@v2
|
|
||||||
with:
|
|
||||||
files: ./Stirling-PDF-${{ matrix.platform }}-installer.${{ matrix.ext }}
|
|
||||||
51
.github/workflows/releaseArtifacts.yml
vendored
51
.github/workflows/releaseArtifacts.yml
vendored
@@ -35,28 +35,27 @@ jobs:
|
|||||||
run: ./gradlew clean createExe
|
run: ./gradlew clean createExe
|
||||||
env:
|
env:
|
||||||
DOCKER_ENABLE_SECURITY: ${{ matrix.enable_security }}
|
DOCKER_ENABLE_SECURITY: ${{ matrix.enable_security }}
|
||||||
STIRLING_PDF_DESKTOP_UI: false
|
|
||||||
|
|
||||||
- name: Get version number
|
- name: Get version number
|
||||||
id: versionNumber
|
id: versionNumber
|
||||||
run: echo "versionNumber=$(./gradlew printVersion --quiet | tail -1)" >> $GITHUB_OUTPUT
|
run: echo "versionNumber=$(./gradlew printVersion --quiet | tail -1)" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Rename binarie
|
- name: Rename binarie
|
||||||
run: cp ./build/launch4j/Stirling-PDF.exe ./build/launch4j/Stirling-PDF-Server${{ matrix.file_suffix }}.exe
|
if: matrix.file_suffix != ''
|
||||||
|
run: cp ./build/launch4j/Stirling-PDF.exe ./build/launch4j/Stirling-PDF${{ matrix.file_suffix }}.exe
|
||||||
|
|
||||||
- name: Upload Assets binarie
|
- name: Upload Assets binarie
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
path: ./build/launch4j/Stirling-PDF-Server${{ matrix.file_suffix }}.exe
|
path: ./build/launch4j/Stirling-PDF${{ matrix.file_suffix }}.exe
|
||||||
name: Stirling-PDF-Server${{ matrix.file_suffix }}.exe
|
name: Stirling-PDF${{ matrix.file_suffix }}.exe
|
||||||
overwrite: true
|
overwrite: true
|
||||||
retention-days: 1
|
retention-days: 1
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
|
|
||||||
- name: Upload binaries to release
|
- name: Upload binaries to release
|
||||||
uses: softprops/action-gh-release@v2
|
uses: softprops/action-gh-release@v2
|
||||||
with:
|
with:
|
||||||
files: ./build/launch4j/Stirling-PDF-Server${{ matrix.file_suffix }}.exe
|
files: ./build/launch4j/Stirling-PDF${{ matrix.file_suffix }}.exe
|
||||||
|
|
||||||
- name: Rename jar binaries
|
- name: Rename jar binaries
|
||||||
run: cp ./build/libs/Stirling-PDF-${{ steps.versionNumber.outputs.versionNumber }}.jar ./build/libs/Stirling-PDF${{ matrix.file_suffix }}.jar
|
run: cp ./build/libs/Stirling-PDF-${{ steps.versionNumber.outputs.versionNumber }}.jar ./build/libs/Stirling-PDF${{ matrix.file_suffix }}.jar
|
||||||
@@ -74,43 +73,3 @@ jobs:
|
|||||||
uses: softprops/action-gh-release@v2
|
uses: softprops/action-gh-release@v2
|
||||||
with:
|
with:
|
||||||
files: ./build/libs/Stirling-PDF${{ matrix.file_suffix }}.jar
|
files: ./build/libs/Stirling-PDF${{ matrix.file_suffix }}.jar
|
||||||
|
|
||||||
|
|
||||||
push-ui:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Set up JDK 17
|
|
||||||
uses: actions/setup-java@v4
|
|
||||||
with:
|
|
||||||
java-version: "17"
|
|
||||||
distribution: "temurin"
|
|
||||||
|
|
||||||
- uses: gradle/actions/setup-gradle@v4
|
|
||||||
with:
|
|
||||||
gradle-version: 8.7
|
|
||||||
|
|
||||||
- name: Generate exe
|
|
||||||
run: ./gradlew clean createExe
|
|
||||||
env:
|
|
||||||
DOCKER_ENABLE_SECURITY: false
|
|
||||||
STIRLING_PDF_DESKTOP_UI: true
|
|
||||||
|
|
||||||
- name: Get version number
|
|
||||||
id: versionNumber
|
|
||||||
run: echo "versionNumber=$(./gradlew printVersion --quiet | tail -1)" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
- name: Upload Assets binarie
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
path: ./build/launch4j/Stirling-PDF.exe
|
|
||||||
name: Stirling-PDF.exe
|
|
||||||
overwrite: true
|
|
||||||
retention-days: 1
|
|
||||||
if-no-files-found: error
|
|
||||||
|
|
||||||
- name: Upload binaries to release
|
|
||||||
uses: softprops/action-gh-release@v2
|
|
||||||
with:
|
|
||||||
files: ./build/launch4j/Stirling-PDF.exe
|
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -161,4 +161,3 @@ out/
|
|||||||
.pytest_cache
|
.pytest_cache
|
||||||
.ipynb_checkpoints
|
.ipynb_checkpoints
|
||||||
|
|
||||||
**/jcef-bundle/
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
# use alpine
|
# use alpine
|
||||||
FROM alpine:3.21.0
|
FROM alpine:3.20.3
|
||||||
|
|
||||||
ARG VERSION_TAG
|
ARG VERSION_TAG
|
||||||
|
|
||||||
|
|||||||
53
README.md
53
README.md
@@ -2,7 +2,7 @@
|
|||||||
<h1 align="center">Stirling-PDF</h1>
|
<h1 align="center">Stirling-PDF</h1>
|
||||||
|
|
||||||
[](https://hub.docker.com/r/frooodle/s-pdf)
|
[](https://hub.docker.com/r/frooodle/s-pdf)
|
||||||
[](https://discord.gg/HYmhKj45pU)
|
[](https://discord.gg/Cn8pWhQRxZ)
|
||||||
[](https://github.com/Stirling-Tools/Stirling-PDF/)
|
[](https://github.com/Stirling-Tools/Stirling-PDF/)
|
||||||
[](https://github.com/Stirling-Tools/stirling-pdf)
|
[](https://github.com/Stirling-Tools/stirling-pdf)
|
||||||
|
|
||||||
@@ -187,47 +187,46 @@ Certain functionality like `Sign` supports pre-saved files stored at `/customFil
|
|||||||
|
|
||||||
## Supported Languages
|
## Supported Languages
|
||||||
|
|
||||||
Stirling-PDF currently supports 38 languages!
|
Stirling-PDF currently supports 37 languages!
|
||||||
|
|
||||||
| Language | Progress |
|
| Language | Progress |
|
||||||
| -------------------------------------------- | -------------------------------------- |
|
| -------------------------------------------- | -------------------------------------- |
|
||||||
| Arabic (العربية) (ar_AR) |  |
|
| 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) |  |
|
||||||
| Persian (فارسی) (fa_IR) |  |
|
| Polish (Polski) (pl_PL) |  |
|
||||||
| Polish (Polski) (pl_PL) |  |
|
|
||||||
| Portuguese (Português) (pt_PT) |  |
|
| Portuguese (Português) (pt_PT) |  |
|
||||||
| Portuguese Brazilian (Português) (pt_BR) |  |
|
| Portuguese Brazilian (Português) (pt_BR) |  |
|
||||||
| Romanian (Română) (ro_RO) |  |
|
| Romanian (Română) (ro_RO) |  |
|
||||||
| Russian (Русский) (ru_RU) |  |
|
| Russian (Русский) (ru_RU) |  |
|
||||||
| Serbian Latin alphabet (Srpski) (sr_LATN_RS) |  |
|
| Serbian Latin alphabet (Srpski) (sr_LATN_RS) |  |
|
||||||
| Simplified Chinese (简体中文) (zh_CN) |  |
|
| Simplified Chinese (简体中文) (zh_CN) |  |
|
||||||
| Slovakian (Slovensky) (sk_SK) |  |
|
| Slovakian (Slovensky) (sk_SK) |  |
|
||||||
| Spanish (Español) (es_ES) |  |
|
| Spanish (Español) (es_ES) |  |
|
||||||
| Swedish (Svenska) (sv_SE) |  |
|
| Swedish (Svenska) (sv_SE) |  |
|
||||||
| Thai (ไทย) (th_TH) |  |
|
| Thai (ไทย) (th_TH) |  |
|
||||||
| Traditional Chinese (繁體中文) (zh_TW) |  |
|
| Traditional Chinese (繁體中文) (zh_TW) |  |
|
||||||
| Turkish (Türkçe) (tr_TR) |  |
|
| Turkish (Türkçe) (tr_TR) |  |
|
||||||
| Ukrainian (Українська) (uk_UA) |  |
|
| Ukrainian (Українська) (uk_UA) |  |
|
||||||
| Vietnamese (Tiếng Việt) (vi_VN) |  |
|
| Vietnamese (Tiếng Việt) (vi_VN) |  |
|
||||||
|
|
||||||
## Contributing (Creating Issues, Translations, Fixing Bugs, etc.)
|
## Contributing (Creating Issues, Translations, Fixing Bugs, etc.)
|
||||||
@@ -405,7 +404,7 @@ To access your account settings, go to Account Settings in the settings cog menu
|
|||||||
|
|
||||||
To add new users, go to the bottom of Account Settings and hit 'Admin Settings'. Here you can add new users. The different roles mentioned within this are for rate limiting. This is a work in progress and will be expanded on more in the future.
|
To add new users, go to the bottom of Account Settings and hit 'Admin Settings'. Here you can add new users. The different roles mentioned within this are for rate limiting. This is a work in progress and will be expanded on more in the future.
|
||||||
|
|
||||||
For API usage, you must provide a header with `X-API-KEY` and the associated API key for that user.
|
For API usage, you must provide a header with `X-API-Key` and the associated API key for that user.
|
||||||
|
|
||||||
## FAQ
|
## FAQ
|
||||||
|
|
||||||
|
|||||||
173
build.gradle
173
build.gradle
@@ -8,7 +8,6 @@ plugins {
|
|||||||
id "com.diffplug.spotless" version "6.25.0"
|
id "com.diffplug.spotless" version "6.25.0"
|
||||||
id "com.github.jk1.dependency-license-report" version "2.9"
|
id "com.github.jk1.dependency-license-report" version "2.9"
|
||||||
//id "nebula.lint" version "19.0.3"
|
//id "nebula.lint" version "19.0.3"
|
||||||
id("org.panteleyev.jpackageplugin") version "1.6.0"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -27,7 +26,7 @@ ext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
group = "stirling.software"
|
group = "stirling.software"
|
||||||
version = "0.36.2"
|
version = "0.35.1"
|
||||||
|
|
||||||
|
|
||||||
java {
|
java {
|
||||||
@@ -42,9 +41,6 @@ repositories {
|
|||||||
maven {
|
maven {
|
||||||
url 'https://build.shibboleth.net/maven/releases'
|
url 'https://build.shibboleth.net/maven/releases'
|
||||||
}
|
}
|
||||||
maven { url "https://build.shibboleth.net/maven/releases" }
|
|
||||||
maven { url "https://maven.pkg.github.com/jcefmaven/jcefmaven" }
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
licenseReport {
|
licenseReport {
|
||||||
@@ -68,12 +64,6 @@ sourceSets {
|
|||||||
exclude "stirling/software/SPDF/model/User.java"
|
exclude "stirling/software/SPDF/model/User.java"
|
||||||
exclude "stirling/software/SPDF/repository/**"
|
exclude "stirling/software/SPDF/repository/**"
|
||||||
}
|
}
|
||||||
|
|
||||||
if (System.getenv("STIRLING_PDF_DESKTOP_UI") == "false") {
|
|
||||||
exclude "stirling/software/SPDF/UI/impl/**"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -84,153 +74,16 @@ openApi {
|
|||||||
outputFileName = "SwaggerDoc.json"
|
outputFileName = "SwaggerDoc.json"
|
||||||
}
|
}
|
||||||
|
|
||||||
//0.11.5 to 2024.11.5
|
|
||||||
def getMacVersion(String version) {
|
|
||||||
def currentYear = java.time.Year.now().getValue()
|
|
||||||
def versionParts = version.split("\\.", 2)
|
|
||||||
return "${currentYear}.${versionParts.length > 1 ? versionParts[1] : versionParts[0]}"
|
|
||||||
}
|
|
||||||
|
|
||||||
jpackage {
|
|
||||||
input = "build/libs"
|
|
||||||
|
|
||||||
appName = "Stirling-PDF"
|
|
||||||
appVersion = project.version
|
|
||||||
vendor = "Stirling-Software"
|
|
||||||
appDescription = "Stirling PDF - Your Local PDF Editor"
|
|
||||||
|
|
||||||
mainJar = "Stirling-PDF-${project.version}.jar"
|
|
||||||
mainClass = "org.springframework.boot.loader.launch.JarLauncher"
|
|
||||||
|
|
||||||
icon = "src/main/resources/static/favicon.ico"
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// JVM Options
|
|
||||||
javaOptions = [
|
|
||||||
"-DBROWSER_OPEN=true",
|
|
||||||
"-DSTIRLING_PDF_DESKTOP_UI=true",
|
|
||||||
"-Djava.awt.headless=false",
|
|
||||||
"-Dapple.awt.UIElement=true",
|
|
||||||
"--add-opens", "java.base/java.lang=ALL-UNNAMED",
|
|
||||||
"--add-opens", "java.desktop/java.awt.event=ALL-UNNAMED",
|
|
||||||
"--add-opens", "java.desktop/sun.awt=ALL-UNNAMED"
|
|
||||||
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
verbose = true
|
|
||||||
|
|
||||||
destination = "${projectDir}/build/jpackage"
|
|
||||||
|
|
||||||
// Windows-specific configuration
|
|
||||||
windows {
|
|
||||||
launcherAsService = false
|
|
||||||
appVersion = project.version
|
|
||||||
winConsole = false
|
|
||||||
winDirChooser = true
|
|
||||||
winMenu = true
|
|
||||||
winShortcut = true
|
|
||||||
winPerUserInstall = true
|
|
||||||
winMenuGroup = "Stirling Software"
|
|
||||||
winUpgradeUuid = "2a43ed0c-b8c2-40cf-89e1-751129b87641" // Unique identifier for updates
|
|
||||||
winHelpUrl = "https://github.com/Stirling-Tools/Stirling-PDF"
|
|
||||||
winUpdateUrl = "https://github.com/Stirling-Tools/Stirling-PDF/releases"
|
|
||||||
type = "exe"
|
|
||||||
installDir = "C:/Program Files/Stirling-PDF"
|
|
||||||
}
|
|
||||||
|
|
||||||
// macOS-specific configuration
|
|
||||||
mac {
|
|
||||||
appVersion = getMacVersion(project.version.toString())
|
|
||||||
icon = "src/main/resources/static/favicon.icns"
|
|
||||||
type = "dmg"
|
|
||||||
macPackageIdentifier = "com.stirling.software.pdf"
|
|
||||||
macPackageName = "Stirling-PDF"
|
|
||||||
macAppCategory = "public.app-category.productivity"
|
|
||||||
macSign = false // Enable signing
|
|
||||||
macAppStore = false // Not targeting App Store initially
|
|
||||||
|
|
||||||
//installDir = "Applications"
|
|
||||||
|
|
||||||
// Add license and other documentation to DMG
|
|
||||||
/*macDmgContent = [
|
|
||||||
"README.md",
|
|
||||||
"LICENSE",
|
|
||||||
"CHANGELOG.md"
|
|
||||||
]*/
|
|
||||||
|
|
||||||
// Enable Mac-specific entitlements
|
|
||||||
//macEntitlements = "entitlements.plist" // You'll need to create this file
|
|
||||||
}
|
|
||||||
|
|
||||||
// Linux-specific configuration
|
|
||||||
linux {
|
|
||||||
appVersion = project.version
|
|
||||||
icon = "src/main/resources/static/favicon.png"
|
|
||||||
type = "deb" // Can also use "rpm" for Red Hat-based systems
|
|
||||||
|
|
||||||
// Debian package configuration
|
|
||||||
//linuxPackageName = "stirlingpdf"
|
|
||||||
linuxDebMaintainer = "support@stirlingpdf.com"
|
|
||||||
linuxMenuGroup = "Office;PDF;Productivity"
|
|
||||||
linuxAppCategory = "Office"
|
|
||||||
linuxAppRelease = "1"
|
|
||||||
linuxPackageDeps = true
|
|
||||||
|
|
||||||
installDir = "/opt/Stirling-PDF"
|
|
||||||
|
|
||||||
// RPM-specific settings
|
|
||||||
//linuxRpmLicenseType = "MIT"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Common additional options
|
|
||||||
//jLinkOptions = [
|
|
||||||
// "--strip-debug",
|
|
||||||
// "--compress=2",
|
|
||||||
// "--no-header-files",
|
|
||||||
// "--no-man-pages"
|
|
||||||
//]
|
|
||||||
|
|
||||||
// Add any additional modules required
|
|
||||||
/*addModules = [
|
|
||||||
"java.base",
|
|
||||||
"java.desktop",
|
|
||||||
"java.logging",
|
|
||||||
"java.sql",
|
|
||||||
"java.xml",
|
|
||||||
"jdk.crypto.ec"
|
|
||||||
]*/
|
|
||||||
|
|
||||||
// Add copyright and license information
|
|
||||||
copyright = "Copyright © 2024 Stirling Software"
|
|
||||||
licenseFile = "LICENSE"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
launch4j {
|
launch4j {
|
||||||
icon = "${projectDir}/src/main/resources/static/favicon.ico"
|
icon = "${projectDir}/src/main/resources/static/favicon.ico"
|
||||||
|
|
||||||
outfile="Stirling-PDF.exe"
|
outfile="Stirling-PDF.exe"
|
||||||
|
headerType="console"
|
||||||
if(System.getenv("STIRLING_PDF_DESKTOP_UI") == 'true') {
|
|
||||||
headerType = "gui"
|
|
||||||
} else {
|
|
||||||
headerType = "console"
|
|
||||||
}
|
|
||||||
jarTask = tasks.bootJar
|
jarTask = tasks.bootJar
|
||||||
|
|
||||||
errTitle="Encountered error, Do you have Java 21?"
|
errTitle="Encountered error, Do you have Java 21?"
|
||||||
downloadUrl="https://download.oracle.com/java/21/latest/jdk-21_windows-x64_bin.exe"
|
downloadUrl="https://download.oracle.com/java/21/latest/jdk-21_windows-x64_bin.exe"
|
||||||
|
variables=["BROWSER_OPEN=true"]
|
||||||
if(System.getenv("STIRLING_PDF_DESKTOP_UI") == 'true') {
|
|
||||||
variables=["BROWSER_OPEN=true", "STIRLING_PDF_DESKTOP_UI=true"]
|
|
||||||
} else {
|
|
||||||
variables=["BROWSER_OPEN=true"]
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
jreMinVersion="17"
|
jreMinVersion="17"
|
||||||
|
|
||||||
mutexName="Stirling-PDF"
|
mutexName="Stirling-PDF"
|
||||||
@@ -270,13 +123,6 @@ configurations.all {
|
|||||||
exclude group: "org.springframework.boot", module: "spring-boot-starter-tomcat"
|
exclude group: "org.springframework.boot", module: "spring-boot-starter-tomcat"
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
|
|
||||||
if (System.getenv("STIRLING_PDF_DESKTOP_UI") != "false") {
|
|
||||||
implementation "me.friwi:jcefmaven:127.3.1"
|
|
||||||
implementation "org.openjfx:javafx-controls:21"
|
|
||||||
implementation "org.openjfx:javafx-swing:21"
|
|
||||||
}
|
|
||||||
|
|
||||||
//security updates
|
//security updates
|
||||||
implementation "org.springframework:spring-webmvc:6.2.0"
|
implementation "org.springframework:spring-webmvc:6.2.0"
|
||||||
|
|
||||||
@@ -296,7 +142,7 @@ dependencies {
|
|||||||
|
|
||||||
if (System.getenv("DOCKER_ENABLE_SECURITY") != "false") {
|
if (System.getenv("DOCKER_ENABLE_SECURITY") != "false") {
|
||||||
implementation "org.springframework.boot:spring-boot-starter-security:$springBootVersion"
|
implementation "org.springframework.boot:spring-boot-starter-security:$springBootVersion"
|
||||||
implementation "org.thymeleaf.extras:thymeleaf-extras-springsecurity5:3.1.3.RELEASE"
|
implementation "org.thymeleaf.extras:thymeleaf-extras-springsecurity5:3.1.2.RELEASE"
|
||||||
implementation "org.springframework.boot:spring-boot-starter-data-jpa:$springBootVersion"
|
implementation "org.springframework.boot:spring-boot-starter-data-jpa:$springBootVersion"
|
||||||
implementation "org.springframework.boot:spring-boot-starter-oauth2-client:$springBootVersion"
|
implementation "org.springframework.boot:spring-boot-starter-oauth2-client:$springBootVersion"
|
||||||
|
|
||||||
@@ -425,14 +271,7 @@ jar {
|
|||||||
tasks.named("test") {
|
tasks.named("test") {
|
||||||
useJUnitPlatform()
|
useJUnitPlatform()
|
||||||
}
|
}
|
||||||
task printVersion {
|
|
||||||
doLast {
|
|
||||||
println project.version
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
task printMacVersion {
|
task printVersion {
|
||||||
doLast {
|
println project.version
|
||||||
println getMacVersion(project.version.toString())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,10 +15,6 @@ import shutil
|
|||||||
import re
|
import re
|
||||||
from PIL import Image, ImageDraw
|
from PIL import Image, ImageDraw
|
||||||
|
|
||||||
API_HEADERS = {
|
|
||||||
'X-API-KEY': '123456789'
|
|
||||||
}
|
|
||||||
|
|
||||||
#########
|
#########
|
||||||
# GIVEN #
|
# GIVEN #
|
||||||
#########
|
#########
|
||||||
@@ -231,7 +227,7 @@ def save_generated_pdf(context, filename):
|
|||||||
def step_send_get_request(context, endpoint):
|
def step_send_get_request(context, endpoint):
|
||||||
base_url = "http://localhost:8080"
|
base_url = "http://localhost:8080"
|
||||||
full_url = f"{base_url}{endpoint}"
|
full_url = f"{base_url}{endpoint}"
|
||||||
response = requests.get(full_url, headers=API_HEADERS)
|
response = requests.get(full_url)
|
||||||
context.response = response
|
context.response = response
|
||||||
|
|
||||||
@when('I send a GET request to "{endpoint}" with parameters')
|
@when('I send a GET request to "{endpoint}" with parameters')
|
||||||
@@ -239,7 +235,7 @@ def step_send_get_request_with_params(context, endpoint):
|
|||||||
base_url = "http://localhost:8080"
|
base_url = "http://localhost:8080"
|
||||||
params = {row['parameter']: row['value'] for row in context.table}
|
params = {row['parameter']: row['value'] for row in context.table}
|
||||||
full_url = f"{base_url}{endpoint}"
|
full_url = f"{base_url}{endpoint}"
|
||||||
response = requests.get(full_url, params=params, headers=API_HEADERS)
|
response = requests.get(full_url, params=params)
|
||||||
context.response = response
|
context.response = response
|
||||||
|
|
||||||
@when('I send the API request to the endpoint "{endpoint}"')
|
@when('I send the API request to the endpoint "{endpoint}"')
|
||||||
@@ -260,7 +256,7 @@ def step_send_api_request(context, endpoint):
|
|||||||
print(f"form_data {file.name} with {mime_type}")
|
print(f"form_data {file.name} with {mime_type}")
|
||||||
form_data.append((key, (file.name, file, mime_type)))
|
form_data.append((key, (file.name, file, mime_type)))
|
||||||
|
|
||||||
response = requests.post(url, files=form_data, headers=API_HEADERS)
|
response = requests.post(url, files=form_data)
|
||||||
context.response = response
|
context.response = response
|
||||||
|
|
||||||
########
|
########
|
||||||
|
|||||||
@@ -1,34 +0,0 @@
|
|||||||
services:
|
|
||||||
stirling-pdf:
|
|
||||||
container_name: Stirling-PDF-Security-Fat
|
|
||||||
image: stirlingtools/stirling-pdf:latest-fat
|
|
||||||
deploy:
|
|
||||||
resources:
|
|
||||||
limits:
|
|
||||||
memory: 4G
|
|
||||||
healthcheck:
|
|
||||||
test: ["CMD-SHELL", "curl -f -H 'X-API-KEY: 123456789' http://localhost:8080/api/v1/info/status | grep -q 'UP'"]
|
|
||||||
interval: 5s
|
|
||||||
timeout: 10s
|
|
||||||
retries: 16
|
|
||||||
ports:
|
|
||||||
- 8080:8080
|
|
||||||
volumes:
|
|
||||||
- /stirling/latest/data:/usr/share/tessdata:rw
|
|
||||||
- /stirling/latest/config:/configs:rw
|
|
||||||
- /stirling/latest/logs:/logs:rw
|
|
||||||
environment:
|
|
||||||
DOCKER_ENABLE_SECURITY: "true"
|
|
||||||
SECURITY_ENABLELOGIN: "true"
|
|
||||||
PUID: 1002
|
|
||||||
PGID: 1002
|
|
||||||
UMASK: "022"
|
|
||||||
SYSTEM_DEFAULTLOCALE: en-US
|
|
||||||
UI_APPNAME: Stirling-PDF
|
|
||||||
UI_HOMEDESCRIPTION: Demo site for Stirling-PDF Latest-fat with Security
|
|
||||||
UI_APPNAMENAVBAR: Stirling-PDF Latest-fat
|
|
||||||
SYSTEM_MAXFILESIZE: "100"
|
|
||||||
METRICS_ENABLED: "true"
|
|
||||||
SYSTEM_GOOGLEVISIBILITY: "true"
|
|
||||||
SECURITY_CUSTOMGLOBALAPIKEY: "123456789"
|
|
||||||
restart: on-failure:5
|
|
||||||
@@ -42,19 +42,14 @@ ignore = [
|
|||||||
'addPageNumbers.selectText.3',
|
'addPageNumbers.selectText.3',
|
||||||
'alphabet',
|
'alphabet',
|
||||||
'certSign.name',
|
'certSign.name',
|
||||||
'fileChooser.dragAndDrop',
|
|
||||||
'home.pipeline.title',
|
'home.pipeline.title',
|
||||||
'language.direction',
|
'language.direction',
|
||||||
'legal.impressum',
|
|
||||||
'licenses.version',
|
'licenses.version',
|
||||||
'pipeline.title',
|
'pipeline.title',
|
||||||
'pipelineOptions.pipelineHeader',
|
'pipelineOptions.pipelineHeader',
|
||||||
'pro',
|
'pro',
|
||||||
'sponsor',
|
'sponsor',
|
||||||
'text',
|
'text',
|
||||||
'validateSignature.cert.bits',
|
|
||||||
'validateSignature.cert.version',
|
|
||||||
'validateSignature.status',
|
|
||||||
'watermark.type.1',
|
'watermark.type.1',
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -66,6 +61,7 @@ ignore = [
|
|||||||
[es_ES]
|
[es_ES]
|
||||||
ignore = [
|
ignore = [
|
||||||
'adminUserSettings.roles',
|
'adminUserSettings.roles',
|
||||||
|
'color',
|
||||||
'error',
|
'error',
|
||||||
'language.direction',
|
'language.direction',
|
||||||
'no',
|
'no',
|
||||||
@@ -77,11 +73,6 @@ ignore = [
|
|||||||
'language.direction',
|
'language.direction',
|
||||||
]
|
]
|
||||||
|
|
||||||
[fa_IR]
|
|
||||||
ignore = [
|
|
||||||
'language.direction',
|
|
||||||
]
|
|
||||||
|
|
||||||
[fr_FR]
|
[fr_FR]
|
||||||
ignore = [
|
ignore = [
|
||||||
'AddStampRequest.alphabet',
|
'AddStampRequest.alphabet',
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ public class LicenseKeyChecker {
|
|||||||
this.checkLicense();
|
this.checkLicense();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Scheduled(initialDelay = 604800000, fixedRate = 604800000) // 7 days in milliseconds
|
@Scheduled(initialDelay = 604800000,fixedRate = 604800000) // 7 days in milliseconds
|
||||||
public void checkLicensePeriodically() {
|
public void checkLicensePeriodically() {
|
||||||
checkLicense();
|
checkLicense();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package stirling.software.SPDF;
|
package stirling.software.SPDF;
|
||||||
|
|
||||||
import java.awt.*;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.ServerSocket;
|
import java.net.ServerSocket;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
@@ -9,9 +8,6 @@ import java.nio.file.Paths;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Properties;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@@ -23,16 +19,13 @@ import org.springframework.core.env.Environment;
|
|||||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||||
|
|
||||||
import io.github.pixee.security.SystemCommand;
|
import io.github.pixee.security.SystemCommand;
|
||||||
|
|
||||||
import jakarta.annotation.PostConstruct;
|
import jakarta.annotation.PostConstruct;
|
||||||
import jakarta.annotation.PreDestroy;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import stirling.software.SPDF.UI.WebBrowser;
|
|
||||||
import stirling.software.SPDF.config.ConfigInitializer;
|
import stirling.software.SPDF.config.ConfigInitializer;
|
||||||
import stirling.software.SPDF.model.ApplicationProperties;
|
import stirling.software.SPDF.model.ApplicationProperties;
|
||||||
|
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
@EnableScheduling
|
@EnableScheduling
|
||||||
@Slf4j
|
|
||||||
public class SPdfApplication {
|
public class SPdfApplication {
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(SPdfApplication.class);
|
private static final Logger logger = LoggerFactory.getLogger(SPdfApplication.class);
|
||||||
@@ -74,19 +67,36 @@ public class SPdfApplication {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
public void init() {
|
||||||
|
baseUrlStatic = this.baseUrl;
|
||||||
|
// Check if the BROWSER_OPEN environment variable is set to true
|
||||||
|
String browserOpenEnv = env.getProperty("BROWSER_OPEN");
|
||||||
|
boolean browserOpen = browserOpenEnv != null && "true".equalsIgnoreCase(browserOpenEnv);
|
||||||
|
if (browserOpen) {
|
||||||
|
try {
|
||||||
|
String url = baseUrl + ":" + getStaticPort();
|
||||||
|
|
||||||
|
String os = System.getProperty("os.name").toLowerCase();
|
||||||
|
Runtime rt = Runtime.getRuntime();
|
||||||
|
if (os.contains("win")) {
|
||||||
|
// For Windows
|
||||||
|
SystemCommand.runCommand(rt, "rundll32 url.dll,FileProtocolHandler " + url);
|
||||||
|
} else if (os.contains("mac")) {
|
||||||
|
SystemCommand.runCommand(rt, "open " + url);
|
||||||
|
} else if (os.contains("nix") || os.contains("nux")) {
|
||||||
|
SystemCommand.runCommand(rt, "xdg-open " + url);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("Error opening browser: {}", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
logger.info("Running configs {}", applicationProperties.toString());
|
||||||
|
}
|
||||||
|
|
||||||
public static void main(String[] args) throws IOException, InterruptedException {
|
public static void main(String[] args) throws IOException, InterruptedException {
|
||||||
|
|
||||||
SpringApplication app = new SpringApplication(SPdfApplication.class);
|
SpringApplication app = new SpringApplication(SPdfApplication.class);
|
||||||
|
|
||||||
Properties props = new Properties();
|
|
||||||
|
|
||||||
if (Boolean.parseBoolean(System.getProperty("STIRLING_PDF_DESKTOP_UI", "false"))) {
|
|
||||||
System.setProperty("java.awt.headless", "false");
|
|
||||||
app.setHeadless(false);
|
|
||||||
props.put("java.awt.headless", "false");
|
|
||||||
props.put("spring.main.web-application-type", "servlet");
|
|
||||||
}
|
|
||||||
|
|
||||||
app.setAdditionalProfiles("default");
|
app.setAdditionalProfiles("default");
|
||||||
app.addInitializers(new ConfigInitializer());
|
app.addInitializers(new ConfigInitializer());
|
||||||
Map<String, String> propertyFiles = new HashMap<>();
|
Map<String, String> propertyFiles = new HashMap<>();
|
||||||
@@ -110,20 +120,14 @@ public class SPdfApplication {
|
|||||||
} else {
|
} else {
|
||||||
logger.warn("Custom configuration file 'configs/custom_settings.yml' does not exist.");
|
logger.warn("Custom configuration file 'configs/custom_settings.yml' does not exist.");
|
||||||
}
|
}
|
||||||
Properties finalProps = new Properties();
|
|
||||||
|
|
||||||
if (!propertyFiles.isEmpty()) {
|
if (!propertyFiles.isEmpty()) {
|
||||||
finalProps.putAll(
|
app.setDefaultProperties(
|
||||||
Collections.singletonMap(
|
Collections.singletonMap(
|
||||||
"spring.config.additional-location",
|
"spring.config.additional-location",
|
||||||
propertyFiles.get("spring.config.additional-location")));
|
propertyFiles.get("spring.config.additional-location")));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!props.isEmpty()) {
|
|
||||||
finalProps.putAll(props);
|
|
||||||
}
|
|
||||||
app.setDefaultProperties(finalProps);
|
|
||||||
|
|
||||||
app.run(args);
|
app.run(args);
|
||||||
|
|
||||||
// Ensure directories are created
|
// Ensure directories are created
|
||||||
@@ -143,46 +147,6 @@ public class SPdfApplication {
|
|||||||
logger.info("Navigate to {}", url);
|
logger.info("Navigate to {}", url);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Autowired(required = false)
|
|
||||||
private WebBrowser webBrowser;
|
|
||||||
|
|
||||||
@PostConstruct
|
|
||||||
public void init() {
|
|
||||||
baseUrlStatic = this.baseUrl;
|
|
||||||
String url = baseUrl + ":" + getStaticPort();
|
|
||||||
if (webBrowser != null
|
|
||||||
&& Boolean.parseBoolean(System.getProperty("STIRLING_PDF_DESKTOP_UI", "false"))) {
|
|
||||||
webBrowser.initWebUI(url);
|
|
||||||
} else {
|
|
||||||
String browserOpenEnv = env.getProperty("BROWSER_OPEN");
|
|
||||||
boolean browserOpen = browserOpenEnv != null && "true".equalsIgnoreCase(browserOpenEnv);
|
|
||||||
if (browserOpen) {
|
|
||||||
try {
|
|
||||||
String os = System.getProperty("os.name").toLowerCase();
|
|
||||||
Runtime rt = Runtime.getRuntime();
|
|
||||||
if (os.contains("win")) {
|
|
||||||
// For Windows
|
|
||||||
SystemCommand.runCommand(rt, "rundll32 url.dll,FileProtocolHandler " + url);
|
|
||||||
} else if (os.contains("mac")) {
|
|
||||||
SystemCommand.runCommand(rt, "open " + url);
|
|
||||||
} else if (os.contains("nix") || os.contains("nux")) {
|
|
||||||
SystemCommand.runCommand(rt, "xdg-open " + url);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.error("Error opening browser: {}", e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
logger.info("Running configs {}", applicationProperties.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@PreDestroy
|
|
||||||
public void cleanup() {
|
|
||||||
if (webBrowser != null) {
|
|
||||||
webBrowser.cleanup();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getStaticBaseUrl() {
|
public static String getStaticBaseUrl() {
|
||||||
return baseUrlStatic;
|
return baseUrlStatic;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
package stirling.software.SPDF.UI;
|
|
||||||
|
|
||||||
public interface WebBrowser {
|
|
||||||
void initWebUI(String url);
|
|
||||||
|
|
||||||
void cleanup();
|
|
||||||
}
|
|
||||||
@@ -1,354 +0,0 @@
|
|||||||
package stirling.software.SPDF.UI.impl;
|
|
||||||
|
|
||||||
import java.awt.AWTException;
|
|
||||||
import java.awt.BorderLayout;
|
|
||||||
import java.awt.Frame;
|
|
||||||
import java.awt.Image;
|
|
||||||
import java.awt.MenuItem;
|
|
||||||
import java.awt.PopupMenu;
|
|
||||||
import java.awt.SystemTray;
|
|
||||||
import java.awt.TrayIcon;
|
|
||||||
import java.awt.event.WindowEvent;
|
|
||||||
import java.awt.event.WindowStateListener;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
|
|
||||||
import javax.imageio.ImageIO;
|
|
||||||
import javax.swing.JFrame;
|
|
||||||
import javax.swing.JPanel;
|
|
||||||
import javax.swing.SwingUtilities;
|
|
||||||
import javax.swing.Timer;
|
|
||||||
|
|
||||||
import org.cef.CefApp;
|
|
||||||
import org.cef.CefClient;
|
|
||||||
import org.cef.CefSettings;
|
|
||||||
import org.cef.browser.CefBrowser;
|
|
||||||
import org.cef.callback.CefBeforeDownloadCallback;
|
|
||||||
import org.cef.callback.CefDownloadItem;
|
|
||||||
import org.cef.callback.CefDownloadItemCallback;
|
|
||||||
import org.cef.handler.CefDownloadHandlerAdapter;
|
|
||||||
import org.cef.handler.CefLoadHandlerAdapter;
|
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
|
|
||||||
import jakarta.annotation.PreDestroy;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import me.friwi.jcefmaven.CefAppBuilder;
|
|
||||||
import me.friwi.jcefmaven.EnumProgress;
|
|
||||||
import me.friwi.jcefmaven.MavenCefAppHandlerAdapter;
|
|
||||||
import me.friwi.jcefmaven.impl.progress.ConsoleProgressHandler;
|
|
||||||
import stirling.software.SPDF.UI.WebBrowser;
|
|
||||||
|
|
||||||
@Component
|
|
||||||
@Slf4j
|
|
||||||
@ConditionalOnProperty(
|
|
||||||
name = "STIRLING_PDF_DESKTOP_UI",
|
|
||||||
havingValue = "true",
|
|
||||||
matchIfMissing = false)
|
|
||||||
public class DesktopBrowser implements WebBrowser {
|
|
||||||
private static CefApp cefApp;
|
|
||||||
private static CefClient client;
|
|
||||||
private static CefBrowser browser;
|
|
||||||
private static JFrame frame;
|
|
||||||
private static LoadingWindow loadingWindow;
|
|
||||||
private static volatile boolean browserInitialized = false;
|
|
||||||
private static TrayIcon trayIcon;
|
|
||||||
private static SystemTray systemTray;
|
|
||||||
|
|
||||||
public DesktopBrowser() {
|
|
||||||
SwingUtilities.invokeLater(
|
|
||||||
() -> {
|
|
||||||
loadingWindow = new LoadingWindow(null, "Initializing...");
|
|
||||||
loadingWindow.setVisible(true);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public void initWebUI(String url) {
|
|
||||||
CompletableFuture.runAsync(
|
|
||||||
() -> {
|
|
||||||
try {
|
|
||||||
CefAppBuilder builder = new CefAppBuilder();
|
|
||||||
configureCefSettings(builder);
|
|
||||||
builder.setProgressHandler(createProgressHandler());
|
|
||||||
|
|
||||||
// Build and initialize CEF
|
|
||||||
cefApp = builder.build();
|
|
||||||
client = cefApp.createClient();
|
|
||||||
|
|
||||||
// Set up download handler
|
|
||||||
setupDownloadHandler();
|
|
||||||
|
|
||||||
// Create browser and frame on EDT
|
|
||||||
SwingUtilities.invokeAndWait(
|
|
||||||
() -> {
|
|
||||||
browser = client.createBrowser(url, false, false);
|
|
||||||
setupMainFrame();
|
|
||||||
setupLoadHandler();
|
|
||||||
|
|
||||||
// Show the frame immediately but transparent
|
|
||||||
frame.setVisible(true);
|
|
||||||
});
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("Error initializing JCEF browser: ", e);
|
|
||||||
cleanup();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void configureCefSettings(CefAppBuilder builder) {
|
|
||||||
CefSettings settings = builder.getCefSettings();
|
|
||||||
settings.cache_path = new File("jcef-bundle").getAbsolutePath();
|
|
||||||
settings.root_cache_path = new File("jcef-bundle").getAbsolutePath();
|
|
||||||
settings.persist_session_cookies = true;
|
|
||||||
settings.windowless_rendering_enabled = false;
|
|
||||||
settings.log_severity = CefSettings.LogSeverity.LOGSEVERITY_INFO;
|
|
||||||
|
|
||||||
builder.setAppHandler(
|
|
||||||
new MavenCefAppHandlerAdapter() {
|
|
||||||
@Override
|
|
||||||
public void stateHasChanged(org.cef.CefApp.CefAppState state) {
|
|
||||||
log.info("CEF state changed: " + state);
|
|
||||||
if (state == CefApp.CefAppState.TERMINATED) {
|
|
||||||
System.exit(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setupDownloadHandler() {
|
|
||||||
client.addDownloadHandler(
|
|
||||||
new CefDownloadHandlerAdapter() {
|
|
||||||
@Override
|
|
||||||
public boolean onBeforeDownload(
|
|
||||||
CefBrowser browser,
|
|
||||||
CefDownloadItem downloadItem,
|
|
||||||
String suggestedName,
|
|
||||||
CefBeforeDownloadCallback callback) {
|
|
||||||
callback.Continue("", true);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDownloadUpdated(
|
|
||||||
CefBrowser browser,
|
|
||||||
CefDownloadItem downloadItem,
|
|
||||||
CefDownloadItemCallback callback) {
|
|
||||||
if (downloadItem.isComplete()) {
|
|
||||||
log.info("Download completed: " + downloadItem.getFullPath());
|
|
||||||
} else if (downloadItem.isCanceled()) {
|
|
||||||
log.info("Download canceled: " + downloadItem.getFullPath());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private ConsoleProgressHandler createProgressHandler() {
|
|
||||||
return new ConsoleProgressHandler() {
|
|
||||||
@Override
|
|
||||||
public void handleProgress(EnumProgress state, float percent) {
|
|
||||||
Objects.requireNonNull(state, "state cannot be null");
|
|
||||||
SwingUtilities.invokeLater(
|
|
||||||
() -> {
|
|
||||||
if (loadingWindow != null) {
|
|
||||||
switch (state) {
|
|
||||||
case LOCATING:
|
|
||||||
loadingWindow.setStatus("Locating Files...");
|
|
||||||
loadingWindow.setProgress(0);
|
|
||||||
break;
|
|
||||||
case DOWNLOADING:
|
|
||||||
if (percent >= 0) {
|
|
||||||
loadingWindow.setStatus(
|
|
||||||
String.format(
|
|
||||||
"Downloading additional files: %.0f%%",
|
|
||||||
percent));
|
|
||||||
loadingWindow.setProgress((int) percent);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case EXTRACTING:
|
|
||||||
loadingWindow.setStatus("Extracting files...");
|
|
||||||
loadingWindow.setProgress(60);
|
|
||||||
break;
|
|
||||||
case INITIALIZING:
|
|
||||||
loadingWindow.setStatus("Initializing UI...");
|
|
||||||
loadingWindow.setProgress(80);
|
|
||||||
break;
|
|
||||||
case INITIALIZED:
|
|
||||||
loadingWindow.setStatus("Finalising startup...");
|
|
||||||
loadingWindow.setProgress(90);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setupMainFrame() {
|
|
||||||
frame = new JFrame("Stirling-PDF");
|
|
||||||
frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
|
|
||||||
frame.setUndecorated(true);
|
|
||||||
frame.setOpacity(0.0f);
|
|
||||||
|
|
||||||
JPanel contentPane = new JPanel(new BorderLayout());
|
|
||||||
contentPane.setDoubleBuffered(true);
|
|
||||||
contentPane.add(browser.getUIComponent(), BorderLayout.CENTER);
|
|
||||||
frame.setContentPane(contentPane);
|
|
||||||
|
|
||||||
frame.addWindowListener(
|
|
||||||
new java.awt.event.WindowAdapter() {
|
|
||||||
@Override
|
|
||||||
public void windowClosing(java.awt.event.WindowEvent windowEvent) {
|
|
||||||
cleanup();
|
|
||||||
System.exit(0);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
frame.setSize(1280, 768);
|
|
||||||
frame.setLocationRelativeTo(null);
|
|
||||||
|
|
||||||
loadIcon();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setupLoadHandler() {
|
|
||||||
client.addLoadHandler(
|
|
||||||
new CefLoadHandlerAdapter() {
|
|
||||||
@Override
|
|
||||||
public void onLoadingStateChange(
|
|
||||||
CefBrowser browser,
|
|
||||||
boolean isLoading,
|
|
||||||
boolean canGoBack,
|
|
||||||
boolean canGoForward) {
|
|
||||||
if (!isLoading && !browserInitialized) {
|
|
||||||
browserInitialized = true;
|
|
||||||
SwingUtilities.invokeLater(
|
|
||||||
() -> {
|
|
||||||
if (loadingWindow != null) {
|
|
||||||
Timer timer =
|
|
||||||
new Timer(
|
|
||||||
500,
|
|
||||||
e -> {
|
|
||||||
loadingWindow.dispose();
|
|
||||||
loadingWindow = null;
|
|
||||||
|
|
||||||
frame.dispose();
|
|
||||||
frame.setOpacity(1.0f);
|
|
||||||
frame.setUndecorated(false);
|
|
||||||
frame.pack();
|
|
||||||
frame.setSize(1280, 800);
|
|
||||||
frame.setLocationRelativeTo(null);
|
|
||||||
frame.setVisible(true);
|
|
||||||
frame.requestFocus();
|
|
||||||
frame.toFront();
|
|
||||||
browser.getUIComponent()
|
|
||||||
.requestFocus();
|
|
||||||
});
|
|
||||||
timer.setRepeats(false);
|
|
||||||
timer.start();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setupTrayIcon(Image icon) {
|
|
||||||
if (!SystemTray.isSupported()) {
|
|
||||||
log.warn("System tray is not supported");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
systemTray = SystemTray.getSystemTray();
|
|
||||||
|
|
||||||
// Create popup menu
|
|
||||||
PopupMenu popup = new PopupMenu();
|
|
||||||
|
|
||||||
// Create menu items
|
|
||||||
MenuItem showItem = new MenuItem("Show");
|
|
||||||
showItem.addActionListener(
|
|
||||||
e -> {
|
|
||||||
frame.setVisible(true);
|
|
||||||
frame.setState(Frame.NORMAL);
|
|
||||||
});
|
|
||||||
|
|
||||||
MenuItem exitItem = new MenuItem("Exit");
|
|
||||||
exitItem.addActionListener(
|
|
||||||
e -> {
|
|
||||||
cleanup();
|
|
||||||
System.exit(0);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Add menu items to popup menu
|
|
||||||
popup.add(showItem);
|
|
||||||
popup.addSeparator();
|
|
||||||
popup.add(exitItem);
|
|
||||||
|
|
||||||
// Create tray icon
|
|
||||||
trayIcon = new TrayIcon(icon, "Stirling-PDF", popup);
|
|
||||||
trayIcon.setImageAutoSize(true);
|
|
||||||
|
|
||||||
// Add double-click behavior
|
|
||||||
trayIcon.addActionListener(
|
|
||||||
e -> {
|
|
||||||
frame.setVisible(true);
|
|
||||||
frame.setState(Frame.NORMAL);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Add tray icon to system tray
|
|
||||||
systemTray.add(trayIcon);
|
|
||||||
|
|
||||||
// Modify frame behavior to minimize to tray
|
|
||||||
frame.addWindowStateListener(
|
|
||||||
new WindowStateListener() {
|
|
||||||
public void windowStateChanged(WindowEvent e) {
|
|
||||||
if (e.getNewState() == Frame.ICONIFIED) {
|
|
||||||
frame.setVisible(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
} catch (AWTException e) {
|
|
||||||
log.error("Error setting up system tray icon", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void loadIcon() {
|
|
||||||
try {
|
|
||||||
Image icon = null;
|
|
||||||
String[] iconPaths = {"/static/favicon.ico"};
|
|
||||||
|
|
||||||
for (String path : iconPaths) {
|
|
||||||
if (icon != null) break;
|
|
||||||
try {
|
|
||||||
try (InputStream is = getClass().getResourceAsStream(path)) {
|
|
||||||
if (is != null) {
|
|
||||||
icon = ImageIO.read(is);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.debug("Could not load icon from " + path, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (icon != null) {
|
|
||||||
frame.setIconImage(icon);
|
|
||||||
setupTrayIcon(icon);
|
|
||||||
} else {
|
|
||||||
log.warn("Could not load icon from any source");
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("Error loading icon", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@PreDestroy
|
|
||||||
public void cleanup() {
|
|
||||||
if (browser != null) browser.close(true);
|
|
||||||
if (client != null) client.dispose();
|
|
||||||
if (cefApp != null) cefApp.dispose();
|
|
||||||
if (loadingWindow != null) loadingWindow.dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,114 +0,0 @@
|
|||||||
package stirling.software.SPDF.UI.impl;
|
|
||||||
|
|
||||||
import java.awt.*;
|
|
||||||
import java.io.InputStream;
|
|
||||||
|
|
||||||
import javax.imageio.ImageIO;
|
|
||||||
import javax.swing.*;
|
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
|
|
||||||
@Slf4j
|
|
||||||
public class LoadingWindow extends JDialog {
|
|
||||||
private final JProgressBar progressBar;
|
|
||||||
private final JLabel statusLabel;
|
|
||||||
private final JPanel mainPanel;
|
|
||||||
private final JLabel brandLabel;
|
|
||||||
|
|
||||||
public LoadingWindow(Frame parent, String initialUrl) {
|
|
||||||
super(parent, "Initializing Stirling-PDF", true);
|
|
||||||
|
|
||||||
// Initialize components
|
|
||||||
mainPanel = new JPanel();
|
|
||||||
mainPanel.setBackground(Color.WHITE);
|
|
||||||
mainPanel.setBorder(BorderFactory.createEmptyBorder(20, 30, 20, 30));
|
|
||||||
mainPanel.setLayout(new GridBagLayout());
|
|
||||||
GridBagConstraints gbc = new GridBagConstraints();
|
|
||||||
|
|
||||||
// Configure GridBagConstraints
|
|
||||||
gbc.gridwidth = GridBagConstraints.REMAINDER;
|
|
||||||
gbc.fill = GridBagConstraints.HORIZONTAL;
|
|
||||||
gbc.insets = new Insets(5, 5, 5, 5);
|
|
||||||
gbc.weightx = 1.0; // Add horizontal weight
|
|
||||||
gbc.weighty = 0.0; // Add vertical weight
|
|
||||||
|
|
||||||
// Add icon
|
|
||||||
try {
|
|
||||||
try (InputStream is = getClass().getResourceAsStream("/static/favicon.ico")) {
|
|
||||||
if (is != null) {
|
|
||||||
Image img = ImageIO.read(is);
|
|
||||||
if (img != null) {
|
|
||||||
Image scaledImg = img.getScaledInstance(48, 48, Image.SCALE_SMOOTH);
|
|
||||||
JLabel iconLabel = new JLabel(new ImageIcon(scaledImg));
|
|
||||||
iconLabel.setHorizontalAlignment(SwingConstants.CENTER);
|
|
||||||
gbc.gridy = 0;
|
|
||||||
mainPanel.add(iconLabel, gbc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("Failed to load icon", e);
|
|
||||||
}
|
|
||||||
// URL Label with explicit size
|
|
||||||
brandLabel = new JLabel(initialUrl);
|
|
||||||
brandLabel.setHorizontalAlignment(SwingConstants.CENTER);
|
|
||||||
brandLabel.setPreferredSize(new Dimension(300, 25));
|
|
||||||
brandLabel.setText("Stirling-PDF");
|
|
||||||
gbc.gridy = 1;
|
|
||||||
mainPanel.add(brandLabel, gbc);
|
|
||||||
|
|
||||||
// Status label with explicit size
|
|
||||||
statusLabel = new JLabel("Initializing...");
|
|
||||||
statusLabel.setHorizontalAlignment(SwingConstants.CENTER);
|
|
||||||
statusLabel.setPreferredSize(new Dimension(300, 25));
|
|
||||||
gbc.gridy = 2;
|
|
||||||
mainPanel.add(statusLabel, gbc);
|
|
||||||
// Progress bar with explicit size
|
|
||||||
progressBar = new JProgressBar(0, 100);
|
|
||||||
progressBar.setStringPainted(true);
|
|
||||||
progressBar.setPreferredSize(new Dimension(300, 25));
|
|
||||||
gbc.gridy = 3;
|
|
||||||
mainPanel.add(progressBar, gbc);
|
|
||||||
|
|
||||||
// Set dialog properties
|
|
||||||
setContentPane(mainPanel);
|
|
||||||
setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
|
|
||||||
setResizable(false);
|
|
||||||
setUndecorated(false);
|
|
||||||
|
|
||||||
// Set size and position
|
|
||||||
setSize(400, 200);
|
|
||||||
setLocationRelativeTo(parent);
|
|
||||||
setAlwaysOnTop(true);
|
|
||||||
setProgress(0);
|
|
||||||
setStatus("Starting...");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setProgress(final int progress) {
|
|
||||||
SwingUtilities.invokeLater(
|
|
||||||
() -> {
|
|
||||||
try {
|
|
||||||
progressBar.setValue(Math.min(Math.max(progress, 0), 100));
|
|
||||||
progressBar.setString(progress + "%");
|
|
||||||
mainPanel.revalidate();
|
|
||||||
mainPanel.repaint();
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("Error updating progress", e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setStatus(final String status) {
|
|
||||||
log.info(status);
|
|
||||||
SwingUtilities.invokeLater(
|
|
||||||
() -> {
|
|
||||||
try {
|
|
||||||
statusLabel.setText(status != null ? status : "");
|
|
||||||
mainPanel.revalidate();
|
|
||||||
mainPanel.repaint();
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("Error updating status", e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -260,9 +260,6 @@ public class EndpointConfiguration {
|
|||||||
|
|
||||||
// Pdftohtml dependent endpoints
|
// Pdftohtml dependent endpoints
|
||||||
addEndpointToGroup("Pdftohtml", "pdf-to-html");
|
addEndpointToGroup("Pdftohtml", "pdf-to-html");
|
||||||
|
|
||||||
// disabled for now while we resolve issues
|
|
||||||
disableEndpoint("pdf-to-pdfa");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processEnvironmentConfigs() {
|
private void processEnvironmentConfigs() {
|
||||||
|
|||||||
@@ -1,14 +1,11 @@
|
|||||||
package stirling.software.SPDF.config;
|
package stirling.software.SPDF.config;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Properties;
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.core.Ordered;
|
import org.springframework.core.Ordered;
|
||||||
import org.springframework.core.annotation.Order;
|
import org.springframework.core.annotation.Order;
|
||||||
import org.springframework.core.io.ClassPathResource;
|
|
||||||
import org.springframework.core.io.Resource;
|
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import io.micrometer.common.util.StringUtils;
|
import io.micrometer.common.util.StringUtils;
|
||||||
@@ -26,18 +23,6 @@ public class InitialSetup {
|
|||||||
@Autowired private ApplicationProperties applicationProperties;
|
@Autowired private ApplicationProperties applicationProperties;
|
||||||
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
public void init() throws IOException {
|
|
||||||
initUUIDKey();
|
|
||||||
|
|
||||||
initSecretKey();
|
|
||||||
|
|
||||||
initEnableCSRFSecurity();
|
|
||||||
|
|
||||||
initLegalUrls();
|
|
||||||
|
|
||||||
initSetAppVersion();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void initUUIDKey() throws IOException {
|
public void initUUIDKey() throws IOException {
|
||||||
String uuid = applicationProperties.getAutomaticallyGenerated().getUUID();
|
String uuid = applicationProperties.getAutomaticallyGenerated().getUUID();
|
||||||
if (!GeneralUtils.isValidUUID(uuid)) {
|
if (!GeneralUtils.isValidUUID(uuid)) {
|
||||||
@@ -47,6 +32,7 @@ public class InitialSetup {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
public void initSecretKey() throws IOException {
|
public void initSecretKey() throws IOException {
|
||||||
String secretKey = applicationProperties.getAutomaticallyGenerated().getKey();
|
String secretKey = applicationProperties.getAutomaticallyGenerated().getKey();
|
||||||
if (!GeneralUtils.isValidUUID(secretKey)) {
|
if (!GeneralUtils.isValidUUID(secretKey)) {
|
||||||
@@ -56,24 +42,13 @@ public class InitialSetup {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void initEnableCSRFSecurity() throws IOException {
|
@PostConstruct
|
||||||
if (GeneralUtils.isVersionHigher(
|
|
||||||
"0.36.0", applicationProperties.getAutomaticallyGenerated().getAppVersion())) {
|
|
||||||
Boolean csrf = applicationProperties.getSecurity().getCsrfDisabled();
|
|
||||||
if (!csrf) {
|
|
||||||
GeneralUtils.saveKeyToConfig("security.csrfDisabled", false, false);
|
|
||||||
GeneralUtils.saveKeyToConfig("system.enableAnalytics", "true", false);
|
|
||||||
applicationProperties.getSecurity().setCsrfDisabled(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void initLegalUrls() throws IOException {
|
public void initLegalUrls() throws IOException {
|
||||||
// Initialize Terms and Conditions
|
// Initialize Terms and Conditions
|
||||||
String termsUrl = applicationProperties.getLegal().getTermsAndConditions();
|
String termsUrl = applicationProperties.getLegal().getTermsAndConditions();
|
||||||
if (StringUtils.isEmpty(termsUrl)) {
|
if (StringUtils.isEmpty(termsUrl)) {
|
||||||
String defaultTermsUrl = "https://www.stirlingpdf.com/terms-and-conditions";
|
String defaultTermsUrl = "https://www.stirlingpdf.com/terms-and-conditions";
|
||||||
GeneralUtils.saveKeyToConfig("legal.termsAndConditions", defaultTermsUrl, false);
|
GeneralUtils.saveKeyToConfig("legal.termsAndConditions", defaultTermsUrl);
|
||||||
applicationProperties.getLegal().setTermsAndConditions(defaultTermsUrl);
|
applicationProperties.getLegal().setTermsAndConditions(defaultTermsUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,23 +56,8 @@ public class InitialSetup {
|
|||||||
String privacyUrl = applicationProperties.getLegal().getPrivacyPolicy();
|
String privacyUrl = applicationProperties.getLegal().getPrivacyPolicy();
|
||||||
if (StringUtils.isEmpty(privacyUrl)) {
|
if (StringUtils.isEmpty(privacyUrl)) {
|
||||||
String defaultPrivacyUrl = "https://www.stirlingpdf.com/privacy-policy";
|
String defaultPrivacyUrl = "https://www.stirlingpdf.com/privacy-policy";
|
||||||
GeneralUtils.saveKeyToConfig("legal.privacyPolicy", defaultPrivacyUrl, false);
|
GeneralUtils.saveKeyToConfig("legal.privacyPolicy", defaultPrivacyUrl);
|
||||||
applicationProperties.getLegal().setPrivacyPolicy(defaultPrivacyUrl);
|
applicationProperties.getLegal().setPrivacyPolicy(defaultPrivacyUrl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void initSetAppVersion() throws IOException {
|
|
||||||
|
|
||||||
String appVersion = "0.0.0";
|
|
||||||
Resource resource = new ClassPathResource("version.properties");
|
|
||||||
Properties props = new Properties();
|
|
||||||
try {
|
|
||||||
props.load(resource.getInputStream());
|
|
||||||
appVersion = props.getProperty("version");
|
|
||||||
} catch (Exception e) {
|
|
||||||
|
|
||||||
}
|
|
||||||
applicationProperties.getAutomaticallyGenerated().setAppVersion(appVersion);
|
|
||||||
GeneralUtils.saveKeyToConfig("AutomaticallyGenerated.appVersion", appVersion, false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,6 +75,5 @@ public class InitialSecuritySetup {
|
|||||||
userService.addApiKeyToUser(Role.INTERNAL_API_USER.getRoleId());
|
userService.addApiKeyToUser(Role.INTERNAL_API_USER.getRoleId());
|
||||||
log.info("Internal API user created: " + Role.INTERNAL_API_USER.getRoleId());
|
log.info("Internal API user created: " + Role.INTERNAL_API_USER.getRoleId());
|
||||||
}
|
}
|
||||||
userService.syncCustomApiUser(applicationProperties.getSecurity().getCustomGlobalAPIKey());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ public class SecurityConfiguration {
|
|||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||||
if (applicationProperties.getSecurity().getCsrfDisabled() || !loginEnabledValue) {
|
if (applicationProperties.getSecurity().getCsrfDisabled()) {
|
||||||
http.csrf(csrf -> csrf.disable());
|
http.csrf(csrf -> csrf.disable());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,7 +116,7 @@ public class SecurityConfiguration {
|
|||||||
csrf ->
|
csrf ->
|
||||||
csrf.ignoringRequestMatchers(
|
csrf.ignoringRequestMatchers(
|
||||||
request -> {
|
request -> {
|
||||||
String apiKey = request.getHeader("X-API-KEY");
|
String apiKey = request.getHeader("X-API-Key");
|
||||||
|
|
||||||
// If there's no API key, don't ignore CSRF
|
// If there's no API key, don't ignore CSRF
|
||||||
// (return false)
|
// (return false)
|
||||||
@@ -289,17 +289,17 @@ public class SecurityConfiguration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// if (!applicationProperties.getSecurity().getCsrfDisabled()) {
|
if (!applicationProperties.getSecurity().getCsrfDisabled()) {
|
||||||
// CookieCsrfTokenRepository cookieRepo =
|
CookieCsrfTokenRepository cookieRepo =
|
||||||
// CookieCsrfTokenRepository.withHttpOnlyFalse();
|
CookieCsrfTokenRepository.withHttpOnlyFalse();
|
||||||
// CsrfTokenRequestAttributeHandler requestHandler =
|
CsrfTokenRequestAttributeHandler requestHandler =
|
||||||
// new CsrfTokenRequestAttributeHandler();
|
new CsrfTokenRequestAttributeHandler();
|
||||||
// requestHandler.setCsrfRequestAttributeName(null);
|
requestHandler.setCsrfRequestAttributeName(null);
|
||||||
// http.csrf(
|
http.csrf(
|
||||||
// csrf ->
|
csrf ->
|
||||||
// csrf.csrfTokenRepository(cookieRepo)
|
csrf.csrfTokenRepository(cookieRepo)
|
||||||
// .csrfTokenRequestHandler(requestHandler));
|
.csrfTokenRequestHandler(requestHandler));
|
||||||
// }
|
}
|
||||||
http.authorizeHttpRequests(authz -> authz.anyRequest().permitAll());
|
http.authorizeHttpRequests(authz -> authz.anyRequest().permitAll());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ public class UserAuthenticationFilter extends OncePerRequestFilter {
|
|||||||
|
|
||||||
// Check for API key in the request headers if no authentication exists
|
// Check for API key in the request headers if no authentication exists
|
||||||
if (authentication == null || !authentication.isAuthenticated()) {
|
if (authentication == null || !authentication.isAuthenticated()) {
|
||||||
String apiKey = request.getHeader("X-API-KEY");
|
String apiKey = request.getHeader("X-API-Key");
|
||||||
if (apiKey != null && !apiKey.trim().isEmpty()) {
|
if (apiKey != null && !apiKey.trim().isEmpty()) {
|
||||||
try {
|
try {
|
||||||
// Use API key to authenticate. This requires you to have an authentication
|
// Use API key to authenticate. This requires you to have an authentication
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ public class UserBasedRateLimitingFilter extends OncePerRequestFilter {
|
|||||||
String identifier = null;
|
String identifier = null;
|
||||||
|
|
||||||
// Check for API key in the request headers
|
// Check for API key in the request headers
|
||||||
String apiKey = request.getHeader("X-API-KEY");
|
String apiKey = request.getHeader("X-API-Key");
|
||||||
if (apiKey != null && !apiKey.trim().isEmpty()) {
|
if (apiKey != null && !apiKey.trim().isEmpty()) {
|
||||||
identifier =
|
identifier =
|
||||||
"API_KEY_" + apiKey; // Prefix to distinguish between API keys and usernames
|
"API_KEY_" + apiKey; // Prefix to distinguish between API keys and usernames
|
||||||
@@ -79,7 +79,7 @@ public class UserBasedRateLimitingFilter extends OncePerRequestFilter {
|
|||||||
Role userRole =
|
Role userRole =
|
||||||
getRoleFromAuthentication(SecurityContextHolder.getContext().getAuthentication());
|
getRoleFromAuthentication(SecurityContextHolder.getContext().getAuthentication());
|
||||||
|
|
||||||
if (request.getHeader("X-API-KEY") != null) {
|
if (request.getHeader("X-API-Key") != null) {
|
||||||
// It's an API call
|
// It's an API call
|
||||||
processRequest(
|
processRequest(
|
||||||
userRole.getApiCallsPerDay(),
|
userRole.getApiCallsPerDay(),
|
||||||
|
|||||||
@@ -390,37 +390,6 @@ public class UserService implements UserServiceInterface {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
|
||||||
public void syncCustomApiUser(String customApiKey) throws IOException {
|
|
||||||
if (customApiKey == null || customApiKey.trim().length() == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
String username = "CUSTOM_API_USER";
|
|
||||||
Optional<User> existingUser = findByUsernameIgnoreCase(username);
|
|
||||||
|
|
||||||
if (!existingUser.isPresent()) {
|
|
||||||
// Create new user with API role
|
|
||||||
User user = new User();
|
|
||||||
user.setUsername(username);
|
|
||||||
user.setPassword(UUID.randomUUID().toString());
|
|
||||||
user.setEnabled(true);
|
|
||||||
user.setFirstLogin(false);
|
|
||||||
user.setAuthenticationType(AuthenticationType.WEB);
|
|
||||||
user.setApiKey(customApiKey);
|
|
||||||
user.addAuthority(new Authority(Role.INTERNAL_API_USER.getRoleId(), user));
|
|
||||||
userRepository.save(user);
|
|
||||||
databaseBackupHelper.exportDatabase();
|
|
||||||
} else {
|
|
||||||
// Update API key if it has changed
|
|
||||||
User user = existingUser.get();
|
|
||||||
if (!customApiKey.equals(user.getApiKey())) {
|
|
||||||
user.setApiKey(customApiKey);
|
|
||||||
userRepository.save(user);
|
|
||||||
databaseBackupHelper.exportDatabase();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getTotalUsersCount() {
|
public long getTotalUsersCount() {
|
||||||
return userRepository.count();
|
return userRepository.count();
|
||||||
|
|||||||
@@ -52,115 +52,84 @@ public class SplitPDFController {
|
|||||||
"This endpoint splits a given PDF file into separate documents based on the specified page numbers or ranges. Users can specify pages using individual numbers, ranges, or 'all' for every page. Input:PDF Output:PDF Type:SIMO")
|
"This endpoint splits a given PDF file into separate documents based on the specified page numbers or ranges. Users can specify pages using individual numbers, ranges, or 'all' for every page. Input:PDF Output:PDF Type:SIMO")
|
||||||
public ResponseEntity<byte[]> splitPdf(@ModelAttribute PDFWithPageNums request)
|
public ResponseEntity<byte[]> splitPdf(@ModelAttribute PDFWithPageNums request)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
|
MultipartFile file = request.getFileInput();
|
||||||
|
String pages = request.getPageNumbers();
|
||||||
|
// open the pdf document
|
||||||
|
|
||||||
PDDocument document = null;
|
PDDocument document = Loader.loadPDF(file.getBytes());
|
||||||
Path zipFile = null;
|
// PdfMetadata metadata = PdfMetadataService.extractMetadataFromPdf(document);
|
||||||
|
int totalPages = document.getNumberOfPages();
|
||||||
|
List<Integer> pageNumbers = request.getPageNumbersList(document, false);
|
||||||
|
if (!pageNumbers.contains(totalPages - 1)) {
|
||||||
|
// Create a mutable ArrayList so we can add to it
|
||||||
|
pageNumbers = new ArrayList<>(pageNumbers);
|
||||||
|
pageNumbers.add(totalPages - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info(
|
||||||
|
"Splitting PDF into pages: {}",
|
||||||
|
pageNumbers.stream().map(String::valueOf).collect(Collectors.joining(",")));
|
||||||
|
|
||||||
|
// split the document
|
||||||
List<ByteArrayOutputStream> splitDocumentsBoas = new ArrayList<>();
|
List<ByteArrayOutputStream> splitDocumentsBoas = new ArrayList<>();
|
||||||
|
int previousPageNumber = 0;
|
||||||
try {
|
for (int splitPoint : pageNumbers) {
|
||||||
|
try (PDDocument splitDocument =
|
||||||
MultipartFile file = request.getFileInput();
|
pdfDocumentFactory.createNewDocumentBasedOnOldDocument(document)) {
|
||||||
String pages = request.getPageNumbers();
|
for (int i = previousPageNumber; i <= splitPoint; i++) {
|
||||||
// open the pdf document
|
PDPage page = document.getPage(i);
|
||||||
|
splitDocument.addPage(page);
|
||||||
document = Loader.loadPDF(file.getBytes());
|
logger.info("Adding page {} to split document", i);
|
||||||
// PdfMetadata metadata = PdfMetadataService.extractMetadataFromPdf(document);
|
|
||||||
int totalPages = document.getNumberOfPages();
|
|
||||||
List<Integer> pageNumbers = request.getPageNumbersList(document, false);
|
|
||||||
if (!pageNumbers.contains(totalPages - 1)) {
|
|
||||||
// Create a mutable ArrayList so we can add to it
|
|
||||||
pageNumbers = new ArrayList<>(pageNumbers);
|
|
||||||
pageNumbers.add(totalPages - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.info(
|
|
||||||
"Splitting PDF into pages: {}",
|
|
||||||
pageNumbers.stream().map(String::valueOf).collect(Collectors.joining(",")));
|
|
||||||
|
|
||||||
// split the document
|
|
||||||
splitDocumentsBoas = new ArrayList<>();
|
|
||||||
int previousPageNumber = 0;
|
|
||||||
for (int splitPoint : pageNumbers) {
|
|
||||||
try (PDDocument splitDocument =
|
|
||||||
pdfDocumentFactory.createNewDocumentBasedOnOldDocument(document)) {
|
|
||||||
for (int i = previousPageNumber; i <= splitPoint; i++) {
|
|
||||||
PDPage page = document.getPage(i);
|
|
||||||
splitDocument.addPage(page);
|
|
||||||
logger.info("Adding page {} to split document", i);
|
|
||||||
}
|
|
||||||
previousPageNumber = splitPoint + 1;
|
|
||||||
|
|
||||||
// Transfer metadata to split pdf
|
|
||||||
// PdfMetadataService.setMetadataToPdf(splitDocument, metadata);
|
|
||||||
|
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
|
||||||
splitDocument.save(baos);
|
|
||||||
|
|
||||||
splitDocumentsBoas.add(baos);
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.error("Failed splitting documents and saving them", e);
|
|
||||||
throw e;
|
|
||||||
}
|
}
|
||||||
}
|
previousPageNumber = splitPoint + 1;
|
||||||
|
|
||||||
// closing the original document
|
// Transfer metadata to split pdf
|
||||||
document.close();
|
// PdfMetadataService.setMetadataToPdf(splitDocument, metadata);
|
||||||
|
|
||||||
zipFile = Files.createTempFile("split_documents", ".zip");
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
splitDocument.save(baos);
|
||||||
|
|
||||||
String filename =
|
splitDocumentsBoas.add(baos);
|
||||||
Filenames.toSimpleFileName(file.getOriginalFilename())
|
|
||||||
.replaceFirst("[.][^.]+$", "");
|
|
||||||
try (ZipOutputStream zipOut = new ZipOutputStream(Files.newOutputStream(zipFile))) {
|
|
||||||
// loop through the split documents and write them to the zip file
|
|
||||||
for (int i = 0; i < splitDocumentsBoas.size(); i++) {
|
|
||||||
String fileName = filename + "_" + (i + 1) + ".pdf";
|
|
||||||
ByteArrayOutputStream baos = splitDocumentsBoas.get(i);
|
|
||||||
byte[] pdf = baos.toByteArray();
|
|
||||||
|
|
||||||
// Add PDF file to the zip
|
|
||||||
ZipEntry pdfEntry = new ZipEntry(fileName);
|
|
||||||
zipOut.putNextEntry(pdfEntry);
|
|
||||||
zipOut.write(pdf);
|
|
||||||
zipOut.closeEntry();
|
|
||||||
|
|
||||||
logger.info("Wrote split document {} to zip file", fileName);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.error("Failed writing to zip", e);
|
logger.error("Failed splitting documents and saving them", e);
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info(
|
|
||||||
"Successfully created zip file with split documents: {}", zipFile.toString());
|
|
||||||
byte[] data = Files.readAllBytes(zipFile);
|
|
||||||
Files.deleteIfExists(zipFile);
|
|
||||||
|
|
||||||
// return the Resource in the response
|
|
||||||
return WebResponseUtils.bytesToWebResponse(
|
|
||||||
data, filename + ".zip", MediaType.APPLICATION_OCTET_STREAM);
|
|
||||||
|
|
||||||
} finally {
|
|
||||||
try {
|
|
||||||
// Close the main document
|
|
||||||
if (document != null) {
|
|
||||||
document.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close all ByteArrayOutputStreams
|
|
||||||
for (ByteArrayOutputStream baos : splitDocumentsBoas) {
|
|
||||||
if (baos != null) {
|
|
||||||
baos.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete temporary zip file
|
|
||||||
if (zipFile != null) {
|
|
||||||
Files.deleteIfExists(zipFile);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.error("Error while cleaning up resources", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// closing the original document
|
||||||
|
document.close();
|
||||||
|
|
||||||
|
Path zipFile = Files.createTempFile("split_documents", ".zip");
|
||||||
|
|
||||||
|
String filename =
|
||||||
|
Filenames.toSimpleFileName(file.getOriginalFilename())
|
||||||
|
.replaceFirst("[.][^.]+$", "");
|
||||||
|
try (ZipOutputStream zipOut = new ZipOutputStream(Files.newOutputStream(zipFile))) {
|
||||||
|
// loop through the split documents and write them to the zip file
|
||||||
|
for (int i = 0; i < splitDocumentsBoas.size(); i++) {
|
||||||
|
String fileName = filename + "_" + (i + 1) + ".pdf";
|
||||||
|
ByteArrayOutputStream baos = splitDocumentsBoas.get(i);
|
||||||
|
byte[] pdf = baos.toByteArray();
|
||||||
|
|
||||||
|
// Add PDF file to the zip
|
||||||
|
ZipEntry pdfEntry = new ZipEntry(fileName);
|
||||||
|
zipOut.putNextEntry(pdfEntry);
|
||||||
|
zipOut.write(pdf);
|
||||||
|
zipOut.closeEntry();
|
||||||
|
|
||||||
|
logger.info("Wrote split document {} to zip file", fileName);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("Failed writing to zip", e);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info("Successfully created zip file with split documents: {}", zipFile.toString());
|
||||||
|
byte[] data = Files.readAllBytes(zipFile);
|
||||||
|
Files.deleteIfExists(zipFile);
|
||||||
|
|
||||||
|
// return the Resource in the response
|
||||||
|
return WebResponseUtils.bytesToWebResponse(
|
||||||
|
data, filename + ".zip", MediaType.APPLICATION_OCTET_STREAM);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,86 +59,70 @@ public class SplitPdfByChaptersController {
|
|||||||
public ResponseEntity<byte[]> splitPdf(@ModelAttribute SplitPdfByChaptersRequest request)
|
public ResponseEntity<byte[]> splitPdf(@ModelAttribute SplitPdfByChaptersRequest request)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
MultipartFile file = request.getFileInput();
|
MultipartFile file = request.getFileInput();
|
||||||
PDDocument sourceDocument = null;
|
boolean includeMetadata = request.getIncludeMetadata();
|
||||||
Path zipFile = null;
|
Integer bookmarkLevel =
|
||||||
|
request.getBookmarkLevel(); // levels start from 0 (top most bookmarks)
|
||||||
try {
|
if (bookmarkLevel < 0) {
|
||||||
boolean includeMetadata = request.getIncludeMetadata();
|
return ResponseEntity.badRequest().body("Invalid bookmark level".getBytes());
|
||||||
Integer bookmarkLevel =
|
|
||||||
request.getBookmarkLevel(); // levels start from 0 (top most bookmarks)
|
|
||||||
if (bookmarkLevel < 0) {
|
|
||||||
return ResponseEntity.badRequest().body("Invalid bookmark level".getBytes());
|
|
||||||
}
|
|
||||||
sourceDocument = Loader.loadPDF(file.getBytes());
|
|
||||||
|
|
||||||
PDDocumentOutline outline = sourceDocument.getDocumentCatalog().getDocumentOutline();
|
|
||||||
|
|
||||||
if (outline == null) {
|
|
||||||
logger.warn("No outline found for {}", file.getOriginalFilename());
|
|
||||||
return ResponseEntity.badRequest().body("No outline found".getBytes());
|
|
||||||
}
|
|
||||||
List<Bookmark> bookmarks = new ArrayList<>();
|
|
||||||
try {
|
|
||||||
bookmarks =
|
|
||||||
extractOutlineItems(
|
|
||||||
sourceDocument,
|
|
||||||
outline.getFirstChild(),
|
|
||||||
bookmarks,
|
|
||||||
outline.getFirstChild().getNextSibling(),
|
|
||||||
0,
|
|
||||||
bookmarkLevel);
|
|
||||||
// to handle last page edge case
|
|
||||||
bookmarks.get(bookmarks.size() - 1).setEndPage(sourceDocument.getNumberOfPages());
|
|
||||||
Bookmark lastBookmark = bookmarks.get(bookmarks.size() - 1);
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.error("Unable to extract outline items", e);
|
|
||||||
return ResponseEntity.internalServerError()
|
|
||||||
.body("Unable to extract outline items".getBytes());
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean allowDuplicates = request.getAllowDuplicates();
|
|
||||||
if (!allowDuplicates) {
|
|
||||||
/*
|
|
||||||
duplicates are generated when multiple bookmarks correspond to the same page,
|
|
||||||
if the user doesn't want duplicates mergeBookmarksThatCorrespondToSamePage() method will merge the titles of all
|
|
||||||
the bookmarks that correspond to the same page, and treat them as a single bookmark
|
|
||||||
*/
|
|
||||||
bookmarks = mergeBookmarksThatCorrespondToSamePage(bookmarks);
|
|
||||||
}
|
|
||||||
for (Bookmark bookmark : bookmarks) {
|
|
||||||
logger.info(
|
|
||||||
"{}::::{} to {}",
|
|
||||||
bookmark.getTitle(),
|
|
||||||
bookmark.getStartPage(),
|
|
||||||
bookmark.getEndPage());
|
|
||||||
}
|
|
||||||
List<ByteArrayOutputStream> splitDocumentsBoas =
|
|
||||||
getSplitDocumentsBoas(sourceDocument, bookmarks, includeMetadata);
|
|
||||||
|
|
||||||
zipFile = createZipFile(bookmarks, splitDocumentsBoas);
|
|
||||||
|
|
||||||
byte[] data = Files.readAllBytes(zipFile);
|
|
||||||
Files.deleteIfExists(zipFile);
|
|
||||||
|
|
||||||
String filename =
|
|
||||||
Filenames.toSimpleFileName(file.getOriginalFilename())
|
|
||||||
.replaceFirst("[.][^.]+$", "");
|
|
||||||
sourceDocument.close();
|
|
||||||
return WebResponseUtils.bytesToWebResponse(
|
|
||||||
data, filename + ".zip", MediaType.APPLICATION_OCTET_STREAM);
|
|
||||||
} finally {
|
|
||||||
try {
|
|
||||||
if (sourceDocument != null) {
|
|
||||||
sourceDocument.close();
|
|
||||||
}
|
|
||||||
if (zipFile != null) {
|
|
||||||
Files.deleteIfExists(zipFile);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.error("Error while cleaning up resources", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
PDDocument sourceDocument = Loader.loadPDF(file.getBytes());
|
||||||
|
|
||||||
|
PDDocumentOutline outline = sourceDocument.getDocumentCatalog().getDocumentOutline();
|
||||||
|
|
||||||
|
if (outline == null) {
|
||||||
|
logger.warn("No outline found for {}", file.getOriginalFilename());
|
||||||
|
return ResponseEntity.badRequest().body("No outline found".getBytes());
|
||||||
|
}
|
||||||
|
List<Bookmark> bookmarks = new ArrayList<>();
|
||||||
|
try {
|
||||||
|
bookmarks =
|
||||||
|
extractOutlineItems(
|
||||||
|
sourceDocument,
|
||||||
|
outline.getFirstChild(),
|
||||||
|
bookmarks,
|
||||||
|
outline.getFirstChild().getNextSibling(),
|
||||||
|
0,
|
||||||
|
bookmarkLevel);
|
||||||
|
// to handle last page edge case
|
||||||
|
bookmarks.get(bookmarks.size() - 1).setEndPage(sourceDocument.getNumberOfPages());
|
||||||
|
Bookmark lastBookmark = bookmarks.get(bookmarks.size() - 1);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("Unable to extract outline items", e);
|
||||||
|
return ResponseEntity.internalServerError()
|
||||||
|
.body("Unable to extract outline items".getBytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean allowDuplicates = request.getAllowDuplicates();
|
||||||
|
if (!allowDuplicates) {
|
||||||
|
/*
|
||||||
|
duplicates are generated when multiple bookmarks correspond to the same page,
|
||||||
|
if the user doesn't want duplicates mergeBookmarksThatCorrespondToSamePage() method will merge the titles of all
|
||||||
|
the bookmarks that correspond to the same page, and treat them as a single bookmark
|
||||||
|
*/
|
||||||
|
bookmarks = mergeBookmarksThatCorrespondToSamePage(bookmarks);
|
||||||
|
}
|
||||||
|
for (Bookmark bookmark : bookmarks) {
|
||||||
|
logger.info(
|
||||||
|
"{}::::{} to {}",
|
||||||
|
bookmark.getTitle(),
|
||||||
|
bookmark.getStartPage(),
|
||||||
|
bookmark.getEndPage());
|
||||||
|
}
|
||||||
|
List<ByteArrayOutputStream> splitDocumentsBoas =
|
||||||
|
getSplitDocumentsBoas(sourceDocument, bookmarks, includeMetadata);
|
||||||
|
|
||||||
|
Path zipFile = createZipFile(bookmarks, splitDocumentsBoas);
|
||||||
|
|
||||||
|
byte[] data = Files.readAllBytes(zipFile);
|
||||||
|
Files.deleteIfExists(zipFile);
|
||||||
|
|
||||||
|
String filename =
|
||||||
|
Filenames.toSimpleFileName(file.getOriginalFilename())
|
||||||
|
.replaceFirst("[.][^.]+$", "");
|
||||||
|
sourceDocument.close();
|
||||||
|
return WebResponseUtils.bytesToWebResponse(
|
||||||
|
data, filename + ".zip", MediaType.APPLICATION_OCTET_STREAM);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Bookmark> mergeBookmarksThatCorrespondToSamePage(List<Bookmark> bookmarks) {
|
private List<Bookmark> mergeBookmarksThatCorrespondToSamePage(List<Bookmark> bookmarks) {
|
||||||
|
|||||||
@@ -105,13 +105,15 @@ public class SplitPdfBySectionsController {
|
|||||||
|
|
||||||
if (sectionNum == horiz * verti) pageNum++;
|
if (sectionNum == horiz * verti) pageNum++;
|
||||||
}
|
}
|
||||||
data = Files.readAllBytes(zipFile);
|
} catch (Exception e) {
|
||||||
return WebResponseUtils.bytesToWebResponse(
|
logger.error("exception", e);
|
||||||
data, filename + "_split.zip", MediaType.APPLICATION_OCTET_STREAM);
|
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
|
data = Files.readAllBytes(zipFile);
|
||||||
Files.deleteIfExists(zipFile);
|
Files.deleteIfExists(zipFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return WebResponseUtils.bytesToWebResponse(
|
||||||
|
data, filename + "_split.zip", MediaType.APPLICATION_OCTET_STREAM);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<PDDocument> splitPdfPages(
|
public List<PDDocument> splitPdfPages(
|
||||||
|
|||||||
@@ -65,137 +65,112 @@ public class ConvertImgPDFController {
|
|||||||
String colorType = request.getColorType();
|
String colorType = request.getColorType();
|
||||||
String dpi = request.getDpi();
|
String dpi = request.getDpi();
|
||||||
|
|
||||||
Path tempFile = null;
|
byte[] pdfBytes = file.getBytes();
|
||||||
Path tempOutputDir = null;
|
ImageType colorTypeResult = ImageType.RGB;
|
||||||
Path tempPdfPath = null;
|
if ("greyscale".equals(colorType)) {
|
||||||
|
colorTypeResult = ImageType.GRAY;
|
||||||
|
} else if ("blackwhite".equals(colorType)) {
|
||||||
|
colorTypeResult = ImageType.BINARY;
|
||||||
|
}
|
||||||
|
// returns bytes for image
|
||||||
|
boolean singleImage = "single".equals(singleOrMultiple);
|
||||||
byte[] result = null;
|
byte[] result = null;
|
||||||
|
String filename =
|
||||||
|
Filenames.toSimpleFileName(file.getOriginalFilename())
|
||||||
|
.replaceFirst("[.][^.]+$", "");
|
||||||
|
|
||||||
try {
|
result =
|
||||||
byte[] pdfBytes = file.getBytes();
|
PdfUtils.convertFromPdf(
|
||||||
ImageType colorTypeResult = ImageType.RGB;
|
pdfBytes,
|
||||||
if ("greyscale".equals(colorType)) {
|
"webp".equalsIgnoreCase(imageFormat) ? "png" : imageFormat.toUpperCase(),
|
||||||
colorTypeResult = ImageType.GRAY;
|
colorTypeResult,
|
||||||
} else if ("blackwhite".equals(colorType)) {
|
singleImage,
|
||||||
colorTypeResult = ImageType.BINARY;
|
Integer.valueOf(dpi),
|
||||||
}
|
filename);
|
||||||
// returns bytes for image
|
if (result == null || result.length == 0) {
|
||||||
boolean singleImage = "single".equals(singleOrMultiple);
|
logger.error("resultant bytes for {} is null, error converting ", filename);
|
||||||
String filename =
|
}
|
||||||
Filenames.toSimpleFileName(file.getOriginalFilename())
|
if ("webp".equalsIgnoreCase(imageFormat) && !CheckProgramInstall.isPythonAvailable()) {
|
||||||
.replaceFirst("[.][^.]+$", "");
|
throw new IOException("Python is not installed. Required for WebP conversion.");
|
||||||
|
} else if ("webp".equalsIgnoreCase(imageFormat)
|
||||||
result =
|
&& CheckProgramInstall.isPythonAvailable()) {
|
||||||
PdfUtils.convertFromPdf(
|
// Write the output stream to a temp file
|
||||||
pdfBytes,
|
Path tempFile = Files.createTempFile("temp_png", ".png");
|
||||||
"webp".equalsIgnoreCase(imageFormat)
|
try (FileOutputStream fos = new FileOutputStream(tempFile.toFile())) {
|
||||||
? "png"
|
fos.write(result);
|
||||||
: imageFormat.toUpperCase(),
|
fos.flush();
|
||||||
colorTypeResult,
|
|
||||||
singleImage,
|
|
||||||
Integer.valueOf(dpi),
|
|
||||||
filename);
|
|
||||||
if (result == null || result.length == 0) {
|
|
||||||
logger.error("resultant bytes for {} is null, error converting ", filename);
|
|
||||||
}
|
|
||||||
if ("webp".equalsIgnoreCase(imageFormat) && !CheckProgramInstall.isPythonAvailable()) {
|
|
||||||
throw new IOException("Python is not installed. Required for WebP conversion.");
|
|
||||||
} else if ("webp".equalsIgnoreCase(imageFormat)
|
|
||||||
&& CheckProgramInstall.isPythonAvailable()) {
|
|
||||||
// Write the output stream to a temp file
|
|
||||||
tempFile = Files.createTempFile("temp_png", ".png");
|
|
||||||
try (FileOutputStream fos = new FileOutputStream(tempFile.toFile())) {
|
|
||||||
fos.write(result);
|
|
||||||
fos.flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
String pythonVersion = CheckProgramInstall.getAvailablePythonCommand();
|
|
||||||
|
|
||||||
List<String> command = new ArrayList<>();
|
|
||||||
command.add(pythonVersion);
|
|
||||||
command.add("./scripts/png_to_webp.py"); // Python script to handle the conversion
|
|
||||||
|
|
||||||
// Create a temporary directory for the output WebP files
|
|
||||||
tempOutputDir = Files.createTempDirectory("webp_output");
|
|
||||||
if (singleImage) {
|
|
||||||
// Run the Python script to convert PNG to WebP
|
|
||||||
command.add(tempFile.toString());
|
|
||||||
command.add(tempOutputDir.toString());
|
|
||||||
command.add("--single");
|
|
||||||
} else {
|
|
||||||
// Save the uploaded PDF to a temporary file
|
|
||||||
tempPdfPath = Files.createTempFile("temp_pdf", ".pdf");
|
|
||||||
file.transferTo(tempPdfPath.toFile());
|
|
||||||
// Run the Python script to convert PDF to WebP
|
|
||||||
command.add(tempPdfPath.toString());
|
|
||||||
command.add(tempOutputDir.toString());
|
|
||||||
}
|
|
||||||
command.add("--dpi");
|
|
||||||
command.add(dpi);
|
|
||||||
ProcessExecutorResult resultProcess =
|
|
||||||
ProcessExecutor.getInstance(ProcessExecutor.Processes.PYTHON_OPENCV)
|
|
||||||
.runCommandWithOutputHandling(command);
|
|
||||||
|
|
||||||
// Find all WebP files in the output directory
|
|
||||||
List<Path> webpFiles =
|
|
||||||
Files.walk(tempOutputDir)
|
|
||||||
.filter(path -> path.toString().endsWith(".webp"))
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
|
|
||||||
if (webpFiles.isEmpty()) {
|
|
||||||
logger.error("No WebP files were created in: {}", tempOutputDir.toString());
|
|
||||||
throw new IOException(
|
|
||||||
"No WebP files were created. " + resultProcess.getMessages());
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] bodyBytes = new byte[0];
|
|
||||||
|
|
||||||
if (webpFiles.size() == 1) {
|
|
||||||
// Return the single WebP file directly
|
|
||||||
Path webpFilePath = webpFiles.get(0);
|
|
||||||
bodyBytes = Files.readAllBytes(webpFilePath);
|
|
||||||
} else {
|
|
||||||
// Create a ZIP file containing all WebP images
|
|
||||||
ByteArrayOutputStream zipOutputStream = new ByteArrayOutputStream();
|
|
||||||
try (ZipOutputStream zos = new ZipOutputStream(zipOutputStream)) {
|
|
||||||
for (Path webpFile : webpFiles) {
|
|
||||||
zos.putNextEntry(new ZipEntry(webpFile.getFileName().toString()));
|
|
||||||
Files.copy(webpFile, zos);
|
|
||||||
zos.closeEntry();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
bodyBytes = zipOutputStream.toByteArray();
|
|
||||||
}
|
|
||||||
// Clean up the temporary files
|
|
||||||
Files.deleteIfExists(tempFile);
|
|
||||||
if (tempOutputDir != null) FileUtils.deleteDirectory(tempOutputDir.toFile());
|
|
||||||
result = bodyBytes;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String pythonVersion = CheckProgramInstall.getAvailablePythonCommand();
|
||||||
|
|
||||||
|
List<String> command = new ArrayList<>();
|
||||||
|
command.add(pythonVersion);
|
||||||
|
command.add("./scripts/png_to_webp.py"); // Python script to handle the conversion
|
||||||
|
|
||||||
|
// Create a temporary directory for the output WebP files
|
||||||
|
Path tempOutputDir = Files.createTempDirectory("webp_output");
|
||||||
if (singleImage) {
|
if (singleImage) {
|
||||||
String docName = filename + "." + imageFormat;
|
// Run the Python script to convert PNG to WebP
|
||||||
MediaType mediaType = MediaType.parseMediaType(getMediaType(imageFormat));
|
command.add(tempFile.toString());
|
||||||
return WebResponseUtils.bytesToWebResponse(result, docName, mediaType);
|
command.add(tempOutputDir.toString());
|
||||||
|
command.add("--single");
|
||||||
} else {
|
} else {
|
||||||
String zipFilename = filename + "_convertedToImages.zip";
|
// Save the uploaded PDF to a temporary file
|
||||||
return WebResponseUtils.bytesToWebResponse(
|
Path tempPdfPath = Files.createTempFile("temp_pdf", ".pdf");
|
||||||
result, zipFilename, MediaType.APPLICATION_OCTET_STREAM);
|
file.transferTo(tempPdfPath.toFile());
|
||||||
|
// Run the Python script to convert PDF to WebP
|
||||||
|
command.add(tempPdfPath.toString());
|
||||||
|
command.add(tempOutputDir.toString());
|
||||||
|
}
|
||||||
|
command.add("--dpi");
|
||||||
|
command.add(dpi);
|
||||||
|
ProcessExecutorResult resultProcess =
|
||||||
|
ProcessExecutor.getInstance(ProcessExecutor.Processes.PYTHON_OPENCV)
|
||||||
|
.runCommandWithOutputHandling(command);
|
||||||
|
|
||||||
|
// Find all WebP files in the output directory
|
||||||
|
List<Path> webpFiles =
|
||||||
|
Files.walk(tempOutputDir)
|
||||||
|
.filter(path -> path.toString().endsWith(".webp"))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
if (webpFiles.isEmpty()) {
|
||||||
|
logger.error("No WebP files were created in: {}", tempOutputDir.toString());
|
||||||
|
throw new IOException("No WebP files were created. " + resultProcess.getMessages());
|
||||||
}
|
}
|
||||||
|
|
||||||
} finally {
|
byte[] bodyBytes = new byte[0];
|
||||||
try {
|
|
||||||
// Clean up temporary files
|
if (webpFiles.size() == 1) {
|
||||||
if (tempFile != null) {
|
// Return the single WebP file directly
|
||||||
Files.deleteIfExists(tempFile);
|
Path webpFilePath = webpFiles.get(0);
|
||||||
|
bodyBytes = Files.readAllBytes(webpFilePath);
|
||||||
|
} else {
|
||||||
|
// Create a ZIP file containing all WebP images
|
||||||
|
ByteArrayOutputStream zipOutputStream = new ByteArrayOutputStream();
|
||||||
|
try (ZipOutputStream zos = new ZipOutputStream(zipOutputStream)) {
|
||||||
|
for (Path webpFile : webpFiles) {
|
||||||
|
zos.putNextEntry(new ZipEntry(webpFile.getFileName().toString()));
|
||||||
|
Files.copy(webpFile, zos);
|
||||||
|
zos.closeEntry();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (tempPdfPath != null) {
|
bodyBytes = zipOutputStream.toByteArray();
|
||||||
Files.deleteIfExists(tempPdfPath);
|
|
||||||
}
|
|
||||||
if (tempOutputDir != null) {
|
|
||||||
FileUtils.deleteDirectory(tempOutputDir.toFile());
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.error("Error cleaning up temporary files", e);
|
|
||||||
}
|
}
|
||||||
|
// Clean up the temporary files
|
||||||
|
Files.deleteIfExists(tempFile);
|
||||||
|
if (tempOutputDir != null) FileUtils.deleteDirectory(tempOutputDir.toFile());
|
||||||
|
result = bodyBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (singleImage) {
|
||||||
|
String docName = filename + "." + imageFormat;
|
||||||
|
MediaType mediaType = MediaType.parseMediaType(getMediaType(imageFormat));
|
||||||
|
return WebResponseUtils.bytesToWebResponse(result, docName, mediaType);
|
||||||
|
} else {
|
||||||
|
String zipFilename = filename + "_convertedToImages.zip";
|
||||||
|
return WebResponseUtils.bytesToWebResponse(
|
||||||
|
result, zipFilename, MediaType.APPLICATION_OCTET_STREAM);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ public class OCRController {
|
|||||||
|
|
||||||
Files.createDirectories(tempOutputDir);
|
Files.createDirectories(tempOutputDir);
|
||||||
Files.createDirectories(tempImagesDir);
|
Files.createDirectories(tempImagesDir);
|
||||||
Process process = null;
|
|
||||||
try {
|
try {
|
||||||
// Save input file
|
// Save input file
|
||||||
inputFile.transferTo(tempInputFile.toFile());
|
inputFile.transferTo(tempInputFile.toFile());
|
||||||
@@ -139,7 +139,7 @@ public class OCRController {
|
|||||||
command.add("pdf"); // Always output PDF
|
command.add("pdf"); // Always output PDF
|
||||||
|
|
||||||
ProcessBuilder pb = new ProcessBuilder(command);
|
ProcessBuilder pb = new ProcessBuilder(command);
|
||||||
process = pb.start();
|
Process process = pb.start();
|
||||||
|
|
||||||
// Capture any error output
|
// Capture any error output
|
||||||
try (BufferedReader reader =
|
try (BufferedReader reader =
|
||||||
@@ -188,10 +188,6 @@ public class OCRController {
|
|||||||
.body(pdfContent);
|
.body(pdfContent);
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
if (process != null) {
|
|
||||||
process.destroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clean up temporary files
|
// Clean up temporary files
|
||||||
deleteDirectory(tempDir);
|
deleteDirectory(tempDir);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -221,7 +221,7 @@ public class PipelineProcessor {
|
|||||||
|
|
||||||
HttpHeaders headers = new HttpHeaders();
|
HttpHeaders headers = new HttpHeaders();
|
||||||
String apiKey = getApiKeyForUser();
|
String apiKey = getApiKeyForUser();
|
||||||
headers.add("X-API-KEY", apiKey);
|
headers.add("X-API-Key", apiKey);
|
||||||
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
|
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
|
||||||
|
|
||||||
// Create HttpEntity with the body and headers
|
// Create HttpEntity with the body and headers
|
||||||
|
|||||||
@@ -595,9 +595,7 @@ public class GetInfoOnPDF {
|
|||||||
|
|
||||||
permissionsNode.put("Document Assembly", getPermissionState(ap.canAssembleDocument()));
|
permissionsNode.put("Document Assembly", getPermissionState(ap.canAssembleDocument()));
|
||||||
permissionsNode.put("Extracting Content", getPermissionState(ap.canExtractContent()));
|
permissionsNode.put("Extracting Content", getPermissionState(ap.canExtractContent()));
|
||||||
permissionsNode.put(
|
permissionsNode.put("Extracting for accessibility", getPermissionState(ap.canExtractForAccessibility()));
|
||||||
"Extracting for accessibility",
|
|
||||||
getPermissionState(ap.canExtractForAccessibility()));
|
|
||||||
permissionsNode.put("Form Filling", getPermissionState(ap.canFillInForm()));
|
permissionsNode.put("Form Filling", getPermissionState(ap.canFillInForm()));
|
||||||
permissionsNode.put("Modifying", getPermissionState(ap.canModify()));
|
permissionsNode.put("Modifying", getPermissionState(ap.canModify()));
|
||||||
permissionsNode.put("Modifying annotations", getPermissionState(ap.canModifyAnnotations()));
|
permissionsNode.put("Modifying annotations", getPermissionState(ap.canModifyAnnotations()));
|
||||||
|
|||||||
@@ -39,7 +39,10 @@ public class PasswordController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping(consumes = "multipart/form-data", value = "/remove-password")
|
@PostMapping(consumes = "multipart/form-data", value = "/remove-password")
|
||||||
@Operation(summary = "Remove password from a PDF file", description = "This endpoint removes the password from a protected PDF file. Users need to provide the existing password. Input:PDF Output:PDF Type:SISO")
|
@Operation(
|
||||||
|
summary = "Remove password from a PDF file",
|
||||||
|
description =
|
||||||
|
"This endpoint removes the password from a protected PDF file. Users need to provide the existing password. Input:PDF Output:PDF Type:SISO")
|
||||||
public ResponseEntity<byte[]> removePassword(@ModelAttribute PDFPasswordRequest request)
|
public ResponseEntity<byte[]> removePassword(@ModelAttribute PDFPasswordRequest request)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
MultipartFile fileInput = request.getFileInput();
|
MultipartFile fileInput = request.getFileInput();
|
||||||
@@ -49,12 +52,15 @@ public class PasswordController {
|
|||||||
return WebResponseUtils.pdfDocToWebResponse(
|
return WebResponseUtils.pdfDocToWebResponse(
|
||||||
document,
|
document,
|
||||||
Filenames.toSimpleFileName(fileInput.getOriginalFilename())
|
Filenames.toSimpleFileName(fileInput.getOriginalFilename())
|
||||||
.replaceFirst("[.][^.]+$", "")
|
.replaceFirst("[.][^.]+$", "")
|
||||||
+ "_password_removed.pdf");
|
+ "_password_removed.pdf");
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping(consumes = "multipart/form-data", value = "/add-password")
|
@PostMapping(consumes = "multipart/form-data", value = "/add-password")
|
||||||
@Operation(summary = "Add password to a PDF file", description = "This endpoint adds password protection to a PDF file. Users can specify a set of permissions that should be applied to the file. Input:PDF Output:PDF")
|
@Operation(
|
||||||
|
summary = "Add password to a PDF file",
|
||||||
|
description =
|
||||||
|
"This endpoint adds password protection to a PDF file. Users can specify a set of permissions that should be applied to the file. Input:PDF Output:PDF")
|
||||||
public ResponseEntity<byte[]> addPassword(@ModelAttribute AddPasswordRequest request)
|
public ResponseEntity<byte[]> addPassword(@ModelAttribute AddPasswordRequest request)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
MultipartFile fileInput = request.getFileInput();
|
MultipartFile fileInput = request.getFileInput();
|
||||||
@@ -92,12 +98,12 @@ public class PasswordController {
|
|||||||
return WebResponseUtils.pdfDocToWebResponse(
|
return WebResponseUtils.pdfDocToWebResponse(
|
||||||
document,
|
document,
|
||||||
Filenames.toSimpleFileName(fileInput.getOriginalFilename())
|
Filenames.toSimpleFileName(fileInput.getOriginalFilename())
|
||||||
.replaceFirst("[.][^.]+$", "")
|
.replaceFirst("[.][^.]+$", "")
|
||||||
+ "_permissions.pdf");
|
+ "_permissions.pdf");
|
||||||
return WebResponseUtils.pdfDocToWebResponse(
|
return WebResponseUtils.pdfDocToWebResponse(
|
||||||
document,
|
document,
|
||||||
Filenames.toSimpleFileName(fileInput.getOriginalFilename())
|
Filenames.toSimpleFileName(fileInput.getOriginalFilename())
|
||||||
.replaceFirst("[.][^.]+$", "")
|
.replaceFirst("[.][^.]+$", "")
|
||||||
+ "_passworded.pdf");
|
+ "_passworded.pdf");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -92,29 +92,20 @@ public class ValidateSignatureController {
|
|||||||
SignerInformationStore signerStore = signedData.getSignerInfos();
|
SignerInformationStore signerStore = signedData.getSignerInfos();
|
||||||
|
|
||||||
for (SignerInformation signer : signerStore.getSigners()) {
|
for (SignerInformation signer : signerStore.getSigners()) {
|
||||||
X509CertificateHolder certHolder =
|
X509CertificateHolder certHolder = (X509CertificateHolder) certStore.getMatches(signer.getSID()).iterator().next();
|
||||||
(X509CertificateHolder)
|
X509Certificate cert = new JcaX509CertificateConverter().getCertificate(certHolder);
|
||||||
certStore.getMatches(signer.getSID()).iterator().next();
|
|
||||||
X509Certificate cert =
|
|
||||||
new JcaX509CertificateConverter().getCertificate(certHolder);
|
|
||||||
|
|
||||||
boolean isValid =
|
boolean isValid = signer.verify(new JcaSimpleSignerInfoVerifierBuilder().build(cert));
|
||||||
signer.verify(new JcaSimpleSignerInfoVerifierBuilder().build(cert));
|
|
||||||
result.setValid(isValid);
|
result.setValid(isValid);
|
||||||
|
|
||||||
// Additional validations
|
// Additional validations
|
||||||
result.setChainValid(
|
result.setChainValid(customCert != null
|
||||||
customCert != null
|
? certValidationService.validateCertificateChainWithCustomCert(cert, customCert)
|
||||||
? certValidationService
|
: certValidationService.validateCertificateChain(cert));
|
||||||
.validateCertificateChainWithCustomCert(
|
|
||||||
cert, customCert)
|
|
||||||
: certValidationService.validateCertificateChain(cert));
|
|
||||||
|
|
||||||
result.setTrustValid(
|
result.setTrustValid(customCert != null
|
||||||
customCert != null
|
? certValidationService.validateTrustWithCustomCert(cert, customCert)
|
||||||
? certValidationService.validateTrustWithCustomCert(
|
: certValidationService.validateTrustStore(cert));
|
||||||
cert, customCert)
|
|
||||||
: certValidationService.validateTrustStore(cert));
|
|
||||||
|
|
||||||
result.setNotRevoked(!certValidationService.isRevoked(cert));
|
result.setNotRevoked(!certValidationService.isRevoked(cert));
|
||||||
result.setNotExpired(!cert.getNotAfter().before(new Date()));
|
result.setNotExpired(!cert.getNotAfter().before(new Date()));
|
||||||
@@ -135,8 +126,7 @@ public class ValidateSignatureController {
|
|||||||
|
|
||||||
// Get key size (if possible)
|
// Get key size (if possible)
|
||||||
try {
|
try {
|
||||||
result.setKeySize(
|
result.setKeySize(((RSAPublicKey) cert.getPublicKey()).getModulus().bitLength());
|
||||||
((RSAPublicKey) cert.getPublicKey()).getModulus().bitLength());
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// If not RSA or error, set to 0
|
// If not RSA or error, set to 0
|
||||||
result.setKeySize(0);
|
result.setKeySize(0);
|
||||||
@@ -162,9 +152,7 @@ public class ValidateSignatureController {
|
|||||||
result.setKeyUsages(keyUsages);
|
result.setKeyUsages(keyUsages);
|
||||||
|
|
||||||
// Check if self-signed
|
// Check if self-signed
|
||||||
result.setSelfSigned(
|
result.setSelfSigned(cert.getSubjectX500Principal().equals(cert.getIssuerX500Principal()));
|
||||||
cert.getSubjectX500Principal()
|
|
||||||
.equals(cert.getIssuerX500Principal()));
|
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
result.setValid(false);
|
result.setValid(false);
|
||||||
|
|||||||
@@ -69,7 +69,6 @@ public class WatermarkController {
|
|||||||
float opacity = request.getOpacity();
|
float opacity = request.getOpacity();
|
||||||
int widthSpacer = request.getWidthSpacer();
|
int widthSpacer = request.getWidthSpacer();
|
||||||
int heightSpacer = request.getHeightSpacer();
|
int heightSpacer = request.getHeightSpacer();
|
||||||
String customColor = request.getCustomColor();
|
|
||||||
boolean convertPdfToImage = request.isConvertPDFToImage();
|
boolean convertPdfToImage = request.isConvertPDFToImage();
|
||||||
|
|
||||||
// Load the input PDF
|
// Load the input PDF
|
||||||
@@ -98,8 +97,7 @@ public class WatermarkController {
|
|||||||
widthSpacer,
|
widthSpacer,
|
||||||
heightSpacer,
|
heightSpacer,
|
||||||
fontSize,
|
fontSize,
|
||||||
alphabet,
|
alphabet);
|
||||||
customColor);
|
|
||||||
} else if ("image".equalsIgnoreCase(watermarkType)) {
|
} else if ("image".equalsIgnoreCase(watermarkType)) {
|
||||||
addImageWatermark(
|
addImageWatermark(
|
||||||
contentStream,
|
contentStream,
|
||||||
@@ -138,8 +136,7 @@ public class WatermarkController {
|
|||||||
int widthSpacer,
|
int widthSpacer,
|
||||||
int heightSpacer,
|
int heightSpacer,
|
||||||
float fontSize,
|
float fontSize,
|
||||||
String alphabet,
|
String alphabet)
|
||||||
String colorString)
|
|
||||||
throws IOException {
|
throws IOException {
|
||||||
String resourceDir = "";
|
String resourceDir = "";
|
||||||
PDFont font = new PDType1Font(Standard14Fonts.FontName.HELVETICA);
|
PDFont font = new PDType1Font(Standard14Fonts.FontName.HELVETICA);
|
||||||
@@ -176,18 +173,7 @@ public class WatermarkController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
contentStream.setFont(font, fontSize);
|
contentStream.setFont(font, fontSize);
|
||||||
|
contentStream.setNonStrokingColor(Color.LIGHT_GRAY);
|
||||||
Color redactColor;
|
|
||||||
try {
|
|
||||||
if (!colorString.startsWith("#")) {
|
|
||||||
colorString = "#" + colorString;
|
|
||||||
}
|
|
||||||
redactColor = Color.decode(colorString);
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
|
|
||||||
redactColor = Color.LIGHT_GRAY;
|
|
||||||
}
|
|
||||||
contentStream.setNonStrokingColor(redactColor);
|
|
||||||
|
|
||||||
String[] textLines = watermarkText.split("\\\\n");
|
String[] textLines = watermarkText.split("\\\\n");
|
||||||
float maxLineWidth = 0;
|
float maxLineWidth = 0;
|
||||||
|
|||||||
@@ -73,7 +73,6 @@ public class ApplicationProperties {
|
|||||||
private int loginAttemptCount;
|
private int loginAttemptCount;
|
||||||
private long loginResetTimeMinutes;
|
private long loginResetTimeMinutes;
|
||||||
private String loginMethod = "all";
|
private String loginMethod = "all";
|
||||||
private String customGlobalAPIKey;
|
|
||||||
|
|
||||||
public Boolean isAltLogin() {
|
public Boolean isAltLogin() {
|
||||||
return saml2.getEnabled() || oauth2.getEnabled();
|
return saml2.getEnabled() || oauth2.getEnabled();
|
||||||
@@ -286,7 +285,6 @@ public class ApplicationProperties {
|
|||||||
public static class AutomaticallyGenerated {
|
public static class AutomaticallyGenerated {
|
||||||
@ToString.Exclude private String key;
|
@ToString.Exclude private String key;
|
||||||
private String UUID;
|
private String UUID;
|
||||||
private String appVersion;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
|
|||||||
@@ -45,9 +45,6 @@ public class AddWatermarkRequest extends PDFFile {
|
|||||||
@Schema(description = "The height spacer between watermark elements", example = "50")
|
@Schema(description = "The height spacer between watermark elements", example = "50")
|
||||||
private int heightSpacer;
|
private int heightSpacer;
|
||||||
|
|
||||||
@Schema(description = "The color for watermark", defaultValue = "#d3d3d3")
|
|
||||||
private String customColor = "#d3d3d3";
|
|
||||||
|
|
||||||
@Schema(description = "Convert the redacted PDF to an image", defaultValue = "false")
|
@Schema(description = "Convert the redacted PDF to an image", defaultValue = "false")
|
||||||
private boolean convertPDFToImage;
|
private boolean convertPDFToImage;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,14 +17,15 @@ public class SignatureValidationResult {
|
|||||||
private boolean notExpired;
|
private boolean notExpired;
|
||||||
private boolean notRevoked;
|
private boolean notRevoked;
|
||||||
|
|
||||||
private String issuerDN; // Certificate issuer's Distinguished Name
|
private String issuerDN; // Certificate issuer's Distinguished Name
|
||||||
private String subjectDN; // Certificate subject's Distinguished Name
|
private String subjectDN; // Certificate subject's Distinguished Name
|
||||||
private String serialNumber; // Certificate serial number
|
private String serialNumber; // Certificate serial number
|
||||||
private String validFrom; // Certificate validity start date
|
private String validFrom; // Certificate validity start date
|
||||||
private String validUntil; // Certificate validity end date
|
private String validUntil; // Certificate validity end date
|
||||||
private String signatureAlgorithm; // Algorithm used for signing
|
private String signatureAlgorithm;// Algorithm used for signing
|
||||||
private int keySize; // Key size in bits
|
private int keySize; // Key size in bits
|
||||||
private String version; // Certificate version
|
private String version; // Certificate version
|
||||||
private List<String> keyUsages; // List of key usage purposes
|
private List<String> keyUsages; // List of key usage purposes
|
||||||
private boolean isSelfSigned; // Whether the certificate is self-signed
|
private boolean isSelfSigned; // Whether the certificate is self-signed
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package stirling.software.SPDF.service;
|
package stirling.software.SPDF.service;
|
||||||
|
|
||||||
|
import io.github.pixee.security.BoundedLineReader;
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
@@ -23,8 +24,6 @@ import java.util.Set;
|
|||||||
|
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import io.github.pixee.security.BoundedLineReader;
|
|
||||||
|
|
||||||
import jakarta.annotation.PostConstruct;
|
import jakarta.annotation.PostConstruct;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
|
|||||||
@@ -88,45 +88,15 @@ public class GeneralUtils {
|
|||||||
|
|
||||||
public static boolean isURLReachable(String urlStr) {
|
public static boolean isURLReachable(String urlStr) {
|
||||||
try {
|
try {
|
||||||
// Parse the URL
|
|
||||||
URL url = URI.create(urlStr).toURL();
|
URL url = URI.create(urlStr).toURL();
|
||||||
|
|
||||||
// Allow only http and https protocols
|
|
||||||
String protocol = url.getProtocol();
|
|
||||||
if (!protocol.equals("http") && !protocol.equals("https")) {
|
|
||||||
return false; // Disallow other protocols
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the host is a local address
|
|
||||||
String host = url.getHost();
|
|
||||||
if (isLocalAddress(host)) {
|
|
||||||
return false; // Exclude local addresses
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the URL is reachable
|
|
||||||
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
||||||
connection.setRequestMethod("HEAD");
|
connection.setRequestMethod("HEAD");
|
||||||
// connection.setConnectTimeout(5000); // Set connection timeout
|
|
||||||
// connection.setReadTimeout(5000); // Set read timeout
|
|
||||||
int responseCode = connection.getResponseCode();
|
int responseCode = connection.getResponseCode();
|
||||||
return (200 <= responseCode && responseCode <= 399);
|
return (200 <= responseCode && responseCode <= 399);
|
||||||
} catch (Exception e) {
|
} catch (MalformedURLException e) {
|
||||||
return false; // Return false in case of any exception
|
return false;
|
||||||
}
|
} catch (IOException e) {
|
||||||
}
|
return false;
|
||||||
|
|
||||||
private static boolean isLocalAddress(String host) {
|
|
||||||
try {
|
|
||||||
// Resolve DNS to IP address
|
|
||||||
InetAddress address = InetAddress.getByName(host);
|
|
||||||
|
|
||||||
// Check for local addresses
|
|
||||||
return address.isAnyLocalAddress() || // Matches 0.0.0.0 or similar
|
|
||||||
address.isLoopbackAddress() || // Matches 127.0.0.1 or ::1
|
|
||||||
address.isSiteLocalAddress() || // Matches private IPv4 ranges: 192.168.x.x, 10.x.x.x, 172.16.x.x to 172.31.x.x
|
|
||||||
address.getHostAddress().startsWith("fe80:"); // Matches link-local IPv6 addresses
|
|
||||||
} catch (Exception e) {
|
|
||||||
return false; // Return false for invalid or unresolved addresses
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -319,10 +289,6 @@ public class GeneralUtils {
|
|||||||
saveKeyToConfig(id, key, true);
|
saveKeyToConfig(id, key, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void saveKeyToConfig(String id, boolean key) throws IOException {
|
|
||||||
saveKeyToConfig(id, key, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void saveKeyToConfig(String id, String key, boolean autoGenerated)
|
public static void saveKeyToConfig(String id, String key, boolean autoGenerated)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
Path path = Paths.get("configs", "settings.yml"); // Target the configs/settings.yml
|
Path path = Paths.get("configs", "settings.yml"); // Target the configs/settings.yml
|
||||||
@@ -341,24 +307,6 @@ public class GeneralUtils {
|
|||||||
settingsYml.save();
|
settingsYml.save();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void saveKeyToConfig(String id, boolean key, boolean autoGenerated)
|
|
||||||
throws IOException {
|
|
||||||
Path path = Paths.get("configs", "settings.yml");
|
|
||||||
|
|
||||||
final YamlFile settingsYml = new YamlFile(path.toFile());
|
|
||||||
DumperOptions yamlOptionssettingsYml =
|
|
||||||
((SimpleYamlImplementation) settingsYml.getImplementation()).getDumperOptions();
|
|
||||||
yamlOptionssettingsYml.setSplitLines(false);
|
|
||||||
|
|
||||||
settingsYml.loadWithComments();
|
|
||||||
|
|
||||||
YamlFileWrapper writer = settingsYml.path(id).set(key);
|
|
||||||
if (autoGenerated) {
|
|
||||||
writer.comment("# Automatically Generated Settings (Do Not Edit Directly)");
|
|
||||||
}
|
|
||||||
settingsYml.save();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String generateMachineFingerprint() {
|
public static String generateMachineFingerprint() {
|
||||||
try {
|
try {
|
||||||
// Get the MAC address
|
// Get the MAC address
|
||||||
@@ -401,33 +349,4 @@ public class GeneralUtils {
|
|||||||
return "GenericID";
|
return "GenericID";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isVersionHigher(String currentVersion, String compareVersion) {
|
|
||||||
if (currentVersion == null || compareVersion == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Split versions into components
|
|
||||||
String[] current = currentVersion.split("\\.");
|
|
||||||
String[] compare = compareVersion.split("\\.");
|
|
||||||
|
|
||||||
// Get the length of the shorter version array
|
|
||||||
int length = Math.min(current.length, compare.length);
|
|
||||||
|
|
||||||
// Compare each component
|
|
||||||
for (int i = 0; i < length; i++) {
|
|
||||||
int currentPart = Integer.parseInt(current[i]);
|
|
||||||
int comparePart = Integer.parseInt(compare[i]);
|
|
||||||
|
|
||||||
if (currentPart > comparePart) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (currentPart < comparePart) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If all components so far are equal, the longer version is considered higher
|
|
||||||
return current.length > compare.length;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -965,16 +965,6 @@ multiTool.dragDropMessage=الصفحات المحددة
|
|||||||
multiTool.undo=تراجع
|
multiTool.undo=تراجع
|
||||||
multiTool.redo=إعادة إجراء
|
multiTool.redo=إعادة إجراء
|
||||||
|
|
||||||
#decrypt
|
|
||||||
decrypt.passwordPrompt=This file is password-protected. Please enter the password:
|
|
||||||
decrypt.cancelled=Operation cancelled for PDF: {0}
|
|
||||||
decrypt.noPassword=No password provided for encrypted PDF: {0}
|
|
||||||
decrypt.invalidPassword=Please try again with the correct password.
|
|
||||||
decrypt.invalidPasswordHeader=Incorrect password or unsupported encryption for PDF: {0}
|
|
||||||
decrypt.unexpectedError=There was an error processing the file. Please try again.
|
|
||||||
decrypt.serverError=Server error while decrypting: {0}
|
|
||||||
decrypt.success=File decrypted successfully.
|
|
||||||
|
|
||||||
#multiTool-advert
|
#multiTool-advert
|
||||||
multiTool-advert.message=هذه الميزة متوفرة في <a href="{0}">صفحة الأدوات المتعددة</a> لدينا. اطلع عليها للحصول على واجهة مستخدم محسّنة لكل صفحة وميزات إضافية!
|
multiTool-advert.message=هذه الميزة متوفرة في <a href="{0}">صفحة الأدوات المتعددة</a> لدينا. اطلع عليها للحصول على واجهة مستخدم محسّنة لكل صفحة وميزات إضافية!
|
||||||
|
|
||||||
@@ -1066,7 +1056,6 @@ addPassword.submit=تشفير
|
|||||||
#watermark
|
#watermark
|
||||||
watermark.title=إضافة علامة مائية
|
watermark.title=إضافة علامة مائية
|
||||||
watermark.header=إضافة علامة مائية
|
watermark.header=إضافة علامة مائية
|
||||||
watermark.customColor=لون نص مخصص
|
|
||||||
watermark.selectText.1=حدد PDF لإضافة العلامة المائية إليه:
|
watermark.selectText.1=حدد PDF لإضافة العلامة المائية إليه:
|
||||||
watermark.selectText.2=نص العلامة المائية:
|
watermark.selectText.2=نص العلامة المائية:
|
||||||
watermark.selectText.3=حجم الخط:
|
watermark.selectText.3=حجم الخط:
|
||||||
@@ -1291,6 +1280,7 @@ 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
|
||||||
#Validate Signature
|
#Validate Signature
|
||||||
validateSignature.title=Validate PDF Signatures
|
validateSignature.title=Validate PDF Signatures
|
||||||
validateSignature.header=Validate Digital Signatures
|
validateSignature.header=Validate Digital Signatures
|
||||||
|
|||||||
@@ -965,16 +965,6 @@ multiTool.dragDropMessage=Seçilmiş Səhifə(lər)
|
|||||||
multiTool.undo=Undo
|
multiTool.undo=Undo
|
||||||
multiTool.redo=Redo
|
multiTool.redo=Redo
|
||||||
|
|
||||||
#decrypt
|
|
||||||
decrypt.passwordPrompt=This file is password-protected. Please enter the password:
|
|
||||||
decrypt.cancelled=Operation cancelled for PDF: {0}
|
|
||||||
decrypt.noPassword=No password provided for encrypted PDF: {0}
|
|
||||||
decrypt.invalidPassword=Please try again with the correct password.
|
|
||||||
decrypt.invalidPasswordHeader=Incorrect password or unsupported encryption for PDF: {0}
|
|
||||||
decrypt.unexpectedError=There was an error processing the file. Please try again.
|
|
||||||
decrypt.serverError=Server error while decrypting: {0}
|
|
||||||
decrypt.success=File decrypted successfully.
|
|
||||||
|
|
||||||
#multiTool-advert
|
#multiTool-advert
|
||||||
multiTool-advert.message=Bu xüsusiyyət bizim <a href="{0}">multi-alət səhifə</a>mizdə də mövcuddur. Əlavə xüsusiyyətlər və səhifə-səhifə interfeys üçün sınaqdan keçirin!
|
multiTool-advert.message=Bu xüsusiyyət bizim <a href="{0}">multi-alət səhifə</a>mizdə də mövcuddur. Əlavə xüsusiyyətlər və səhifə-səhifə interfeys üçün sınaqdan keçirin!
|
||||||
|
|
||||||
@@ -1066,7 +1056,6 @@ addPassword.submit=Şifrlə
|
|||||||
#watermark
|
#watermark
|
||||||
watermark.title=Watermark Əlavə Et
|
watermark.title=Watermark Əlavə Et
|
||||||
watermark.header=Watermark Əlavə Et
|
watermark.header=Watermark Əlavə Et
|
||||||
watermark.customColor=Fərdi Mətn Rəngi
|
|
||||||
watermark.selectText.1=Watermark əlavə olunacaq PDF-i seç
|
watermark.selectText.1=Watermark əlavə olunacaq PDF-i seç
|
||||||
watermark.selectText.2=Watermark Mətni:
|
watermark.selectText.2=Watermark Mətni:
|
||||||
watermark.selectText.3=Şrift Ölçüsü:
|
watermark.selectText.3=Şrift Ölçüsü:
|
||||||
@@ -1291,6 +1280,7 @@ 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
|
||||||
#Validate Signature
|
#Validate Signature
|
||||||
validateSignature.title=Validate PDF Signatures
|
validateSignature.title=Validate PDF Signatures
|
||||||
validateSignature.header=Validate Digital Signatures
|
validateSignature.header=Validate Digital Signatures
|
||||||
|
|||||||
@@ -965,16 +965,6 @@ multiTool.dragDropMessage=Page(s) Selected
|
|||||||
multiTool.undo=Undo
|
multiTool.undo=Undo
|
||||||
multiTool.redo=Redo
|
multiTool.redo=Redo
|
||||||
|
|
||||||
#decrypt
|
|
||||||
decrypt.passwordPrompt=This file is password-protected. Please enter the password:
|
|
||||||
decrypt.cancelled=Operation cancelled for PDF: {0}
|
|
||||||
decrypt.noPassword=No password provided for encrypted PDF: {0}
|
|
||||||
decrypt.invalidPassword=Please try again with the correct password.
|
|
||||||
decrypt.invalidPasswordHeader=Incorrect password or unsupported encryption for PDF: {0}
|
|
||||||
decrypt.unexpectedError=There was an error processing the file. Please try again.
|
|
||||||
decrypt.serverError=Server error while decrypting: {0}
|
|
||||||
decrypt.success=File decrypted successfully.
|
|
||||||
|
|
||||||
#multiTool-advert
|
#multiTool-advert
|
||||||
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
|
||||||
@@ -1066,7 +1056,6 @@ addPassword.submit=Шифроване
|
|||||||
#watermark
|
#watermark
|
||||||
watermark.title=Добавяне на воден знак
|
watermark.title=Добавяне на воден знак
|
||||||
watermark.header=Добавяне на воден знак
|
watermark.header=Добавяне на воден знак
|
||||||
watermark.customColor=Персонализиран цвят на текста
|
|
||||||
watermark.selectText.1=Изберете PDF, към който да добавите воден знак:
|
watermark.selectText.1=Изберете PDF, към който да добавите воден знак:
|
||||||
watermark.selectText.2=Текст на воден знак:
|
watermark.selectText.2=Текст на воден знак:
|
||||||
watermark.selectText.3=Размер на шрифта:
|
watermark.selectText.3=Размер на шрифта:
|
||||||
@@ -1291,6 +1280,7 @@ 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
|
||||||
#Validate Signature
|
#Validate Signature
|
||||||
validateSignature.title=Validate PDF Signatures
|
validateSignature.title=Validate PDF Signatures
|
||||||
validateSignature.header=Validate Digital Signatures
|
validateSignature.header=Validate Digital Signatures
|
||||||
|
|||||||
@@ -965,16 +965,6 @@ multiTool.dragDropMessage=Page(s) Selected
|
|||||||
multiTool.undo=Undo
|
multiTool.undo=Undo
|
||||||
multiTool.redo=Redo
|
multiTool.redo=Redo
|
||||||
|
|
||||||
#decrypt
|
|
||||||
decrypt.passwordPrompt=This file is password-protected. Please enter the password:
|
|
||||||
decrypt.cancelled=Operation cancelled for PDF: {0}
|
|
||||||
decrypt.noPassword=No password provided for encrypted PDF: {0}
|
|
||||||
decrypt.invalidPassword=Please try again with the correct password.
|
|
||||||
decrypt.invalidPasswordHeader=Incorrect password or unsupported encryption for PDF: {0}
|
|
||||||
decrypt.unexpectedError=There was an error processing the file. Please try again.
|
|
||||||
decrypt.serverError=Server error while decrypting: {0}
|
|
||||||
decrypt.success=File decrypted successfully.
|
|
||||||
|
|
||||||
#multiTool-advert
|
#multiTool-advert
|
||||||
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
|
||||||
@@ -1066,7 +1056,6 @@ addPassword.submit=Encripta
|
|||||||
#watermark
|
#watermark
|
||||||
watermark.title=Afegir Marca d'Aigua
|
watermark.title=Afegir Marca d'Aigua
|
||||||
watermark.header=Afegir Marca d'Aigua
|
watermark.header=Afegir Marca d'Aigua
|
||||||
watermark.customColor=Color de Text Personalitzat
|
|
||||||
watermark.selectText.1=Selecciona el PDF per afegir la Marca d'Aigua:
|
watermark.selectText.1=Selecciona el PDF per afegir la Marca d'Aigua:
|
||||||
watermark.selectText.2=Text de la Marca d'Aigua
|
watermark.selectText.2=Text de la Marca d'Aigua
|
||||||
watermark.selectText.3=Mida de la Font:
|
watermark.selectText.3=Mida de la Font:
|
||||||
@@ -1291,6 +1280,7 @@ 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
|
||||||
#Validate Signature
|
#Validate Signature
|
||||||
validateSignature.title=Validate PDF Signatures
|
validateSignature.title=Validate PDF Signatures
|
||||||
validateSignature.header=Validate Digital Signatures
|
validateSignature.header=Validate Digital Signatures
|
||||||
|
|||||||
@@ -965,16 +965,6 @@ multiTool.dragDropMessage=Page(s) Selected
|
|||||||
multiTool.undo=Undo
|
multiTool.undo=Undo
|
||||||
multiTool.redo=Redo
|
multiTool.redo=Redo
|
||||||
|
|
||||||
#decrypt
|
|
||||||
decrypt.passwordPrompt=This file is password-protected. Please enter the password:
|
|
||||||
decrypt.cancelled=Operation cancelled for PDF: {0}
|
|
||||||
decrypt.noPassword=No password provided for encrypted PDF: {0}
|
|
||||||
decrypt.invalidPassword=Please try again with the correct password.
|
|
||||||
decrypt.invalidPasswordHeader=Incorrect password or unsupported encryption for PDF: {0}
|
|
||||||
decrypt.unexpectedError=There was an error processing the file. Please try again.
|
|
||||||
decrypt.serverError=Server error while decrypting: {0}
|
|
||||||
decrypt.success=File decrypted successfully.
|
|
||||||
|
|
||||||
#multiTool-advert
|
#multiTool-advert
|
||||||
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
|
||||||
@@ -1066,7 +1056,6 @@ addPassword.submit=Šifrovat
|
|||||||
#watermark
|
#watermark
|
||||||
watermark.title=Přidat vodoznak
|
watermark.title=Přidat vodoznak
|
||||||
watermark.header=Přidat vodoznak
|
watermark.header=Přidat vodoznak
|
||||||
watermark.customColor=Vlastní barva textu
|
|
||||||
watermark.selectText.1=Vyberte PDF, ke kterému chcete přidat vodoznak:
|
watermark.selectText.1=Vyberte PDF, ke kterému chcete přidat vodoznak:
|
||||||
watermark.selectText.2=Text vodoznaku:
|
watermark.selectText.2=Text vodoznaku:
|
||||||
watermark.selectText.3=Velikost písma:
|
watermark.selectText.3=Velikost písma:
|
||||||
@@ -1291,6 +1280,7 @@ 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
|
||||||
#Validate Signature
|
#Validate Signature
|
||||||
validateSignature.title=Validate PDF Signatures
|
validateSignature.title=Validate PDF Signatures
|
||||||
validateSignature.header=Validate Digital Signatures
|
validateSignature.header=Validate Digital Signatures
|
||||||
|
|||||||
@@ -965,16 +965,6 @@ multiTool.dragDropMessage=Page(s) Selected
|
|||||||
multiTool.undo=Undo
|
multiTool.undo=Undo
|
||||||
multiTool.redo=Redo
|
multiTool.redo=Redo
|
||||||
|
|
||||||
#decrypt
|
|
||||||
decrypt.passwordPrompt=This file is password-protected. Please enter the password:
|
|
||||||
decrypt.cancelled=Operation cancelled for PDF: {0}
|
|
||||||
decrypt.noPassword=No password provided for encrypted PDF: {0}
|
|
||||||
decrypt.invalidPassword=Please try again with the correct password.
|
|
||||||
decrypt.invalidPasswordHeader=Incorrect password or unsupported encryption for PDF: {0}
|
|
||||||
decrypt.unexpectedError=There was an error processing the file. Please try again.
|
|
||||||
decrypt.serverError=Server error while decrypting: {0}
|
|
||||||
decrypt.success=File decrypted successfully.
|
|
||||||
|
|
||||||
#multiTool-advert
|
#multiTool-advert
|
||||||
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
|
||||||
@@ -1066,7 +1056,6 @@ addPassword.submit=Kryptér
|
|||||||
#watermark
|
#watermark
|
||||||
watermark.title=Tilføj Vandmærke
|
watermark.title=Tilføj Vandmærke
|
||||||
watermark.header=Tilføj Vandmærke
|
watermark.header=Tilføj Vandmærke
|
||||||
watermark.customColor=Brugerdefineret Tekstfarve
|
|
||||||
watermark.selectText.1=Vælg PDF til at tilføje vandmærke:
|
watermark.selectText.1=Vælg PDF til at tilføje vandmærke:
|
||||||
watermark.selectText.2=Vandmærketekst:
|
watermark.selectText.2=Vandmærketekst:
|
||||||
watermark.selectText.3=Skriftstørrelse:
|
watermark.selectText.3=Skriftstørrelse:
|
||||||
@@ -1291,6 +1280,7 @@ 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
|
||||||
#Validate Signature
|
#Validate Signature
|
||||||
validateSignature.title=Validate PDF Signatures
|
validateSignature.title=Validate PDF Signatures
|
||||||
validateSignature.header=Validate Digital Signatures
|
validateSignature.header=Validate Digital Signatures
|
||||||
|
|||||||
@@ -146,8 +146,8 @@ navbar.search=Suche
|
|||||||
navbar.sections.organize=Organisieren
|
navbar.sections.organize=Organisieren
|
||||||
navbar.sections.convertTo=In PDF konvertieren
|
navbar.sections.convertTo=In PDF konvertieren
|
||||||
navbar.sections.convertFrom=Konvertieren von PDF
|
navbar.sections.convertFrom=Konvertieren von PDF
|
||||||
navbar.sections.security=Signieren und Sicherheit
|
navbar.sections.security=Zeichen und Sicherheit
|
||||||
navbar.sections.advance=Erweiterte Funktionen
|
navbar.sections.advance=Fortschrittlich
|
||||||
navbar.sections.edit=Anzeigen und Bearbeiten
|
navbar.sections.edit=Anzeigen und Bearbeiten
|
||||||
navbar.sections.popular=Beliebt
|
navbar.sections.popular=Beliebt
|
||||||
|
|
||||||
@@ -248,7 +248,7 @@ database.fileNullOrEmpty=Datei darf nicht null oder leer sein
|
|||||||
database.failedImportFile=Dateiimport fehlgeschlagen
|
database.failedImportFile=Dateiimport fehlgeschlagen
|
||||||
|
|
||||||
session.expired=Ihre Sitzung ist abgelaufen. Bitte laden Sie die Seite neu und versuchen Sie es erneut.
|
session.expired=Ihre Sitzung ist abgelaufen. Bitte laden Sie die Seite neu und versuchen Sie es erneut.
|
||||||
session.refreshPage=Seite aktualisieren
|
session.refreshPage=Refresh Page
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# HOME-PAGE #
|
# HOME-PAGE #
|
||||||
@@ -512,9 +512,9 @@ 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.title=Validate PDF Signature
|
||||||
home.validateSignature.desc=Digitale Signaturen und Zertifikate in PDF-Dokumenten überprüfen
|
home.validateSignature.desc=Verify digital signatures and certificates in PDF documents
|
||||||
validateSignature.tags=signature,verify,validate,pdf,digitale signatur,signatur validieren,überprüfen,Zertifikat,cert
|
validateSignature.tags=signature,verify,validate,pdf,certificate,digital signature,Validate Signature,Validate certificate
|
||||||
|
|
||||||
#replace-invert-color
|
#replace-invert-color
|
||||||
replace-color.title=Farbe Ersetzen-Invertieren
|
replace-color.title=Farbe Ersetzen-Invertieren
|
||||||
@@ -822,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=Zu allen Seiten hinzufügen
|
sign.addToAll=Add to all pages
|
||||||
sign.delete=Löschen
|
sign.delete=Delete
|
||||||
sign.first=Erste Seite
|
sign.first=First page
|
||||||
sign.last=Letzte Seite
|
sign.last=Last page
|
||||||
sign.next=Nächste Seite
|
sign.next=Next page
|
||||||
sign.previous=Vorherige Seite
|
sign.previous=Previous page
|
||||||
|
|
||||||
#repair
|
#repair
|
||||||
repair.title=Reparieren
|
repair.title=Reparieren
|
||||||
@@ -953,27 +953,17 @@ multiTool.deleteSelected=Auswahl löschen
|
|||||||
multiTool.downloadAll=Downloaden
|
multiTool.downloadAll=Downloaden
|
||||||
multiTool.downloadSelected=Auswahl downloaden
|
multiTool.downloadSelected=Auswahl downloaden
|
||||||
|
|
||||||
multiTool.insertPageBreak=Seitenumbruch einfügen
|
multiTool.insertPageBreak=Insert Page Break
|
||||||
multiTool.addFile=Datei hinzufügen
|
multiTool.addFile=Add File
|
||||||
multiTool.rotateLeft=Nach links drehen
|
multiTool.rotateLeft=Rotate Left
|
||||||
multiTool.rotateRight=Nach rechts drehen
|
multiTool.rotateRight=Rotate Right
|
||||||
multiTool.split=Teilen
|
multiTool.split=Split
|
||||||
multiTool.moveLeft=Nach links verschieben
|
multiTool.moveLeft=Move Left
|
||||||
multiTool.moveRight=Nach rechts verschieben
|
multiTool.moveRight=Move Right
|
||||||
multiTool.delete=Löschen
|
multiTool.delete=Delete
|
||||||
multiTool.dragDropMessage=Ausgewählte Seite(n)
|
multiTool.dragDropMessage=Page(s) Selected
|
||||||
multiTool.undo=Rückgängig machen
|
multiTool.undo=Undo
|
||||||
multiTool.redo=Wiederherstellen
|
multiTool.redo=Redo
|
||||||
|
|
||||||
#decrypt
|
|
||||||
decrypt.passwordPrompt=This file is password-protected. Please enter the password:
|
|
||||||
decrypt.cancelled=Operation cancelled for PDF: {0}
|
|
||||||
decrypt.noPassword=No password provided for encrypted PDF: {0}
|
|
||||||
decrypt.invalidPassword=Please try again with the correct password.
|
|
||||||
decrypt.invalidPasswordHeader=Incorrect password or unsupported encryption for PDF: {0}
|
|
||||||
decrypt.unexpectedError=There was an error processing the file. Please try again.
|
|
||||||
decrypt.serverError=Server error while decrypting: {0}
|
|
||||||
decrypt.success=File decrypted successfully.
|
|
||||||
|
|
||||||
#multiTool-advert
|
#multiTool-advert
|
||||||
multiTool-advert.message=Diese Funktion ist auch auf unserer <a href="{0}">PDF-Multitool-Seite</a> verfügbar. Probieren Sie sie aus, denn sie bietet eine verbesserte Benutzeroberfläche und zusätzliche Funktionen!
|
multiTool-advert.message=Diese Funktion ist auch auf unserer <a href="{0}">PDF-Multitool-Seite</a> verfügbar. Probieren Sie sie aus, denn sie bietet eine verbesserte Benutzeroberfläche und zusätzliche Funktionen!
|
||||||
@@ -1066,7 +1056,6 @@ addPassword.submit=Verschlüsseln
|
|||||||
#watermark
|
#watermark
|
||||||
watermark.title=Wasserzeichen hinzufügen
|
watermark.title=Wasserzeichen hinzufügen
|
||||||
watermark.header=Wasserzeichen hinzufügen
|
watermark.header=Wasserzeichen hinzufügen
|
||||||
watermark.customColor=Benutzerdefinierte Textfarbe
|
|
||||||
watermark.selectText.1=PDF auswählen, dem ein Wasserzeichen hinzugefügt werden soll:
|
watermark.selectText.1=PDF auswählen, dem ein Wasserzeichen hinzugefügt werden soll:
|
||||||
watermark.selectText.2=Wasserzeichen Text:
|
watermark.selectText.2=Wasserzeichen Text:
|
||||||
watermark.selectText.3=Schriftgröße:
|
watermark.selectText.3=Schriftgröße:
|
||||||
@@ -1279,49 +1268,50 @@ splitByChapters.desc.4=Duplikate erlauben: Wenn diese Option aktiviert ist, kön
|
|||||||
splitByChapters.submit=PDF teilen
|
splitByChapters.submit=PDF teilen
|
||||||
|
|
||||||
#File Chooser
|
#File Chooser
|
||||||
fileChooser.click=Klicken
|
fileChooser.click=Click
|
||||||
fileChooser.or=oder
|
fileChooser.or=or
|
||||||
fileChooser.dragAndDrop=Drag & Drop
|
fileChooser.dragAndDrop=Drag & Drop
|
||||||
fileChooser.hoveredDragAndDrop=Datei(en) hierhin Ziehen & Fallenlassen
|
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
||||||
|
|
||||||
#release notes
|
#release notes
|
||||||
releases.footer=Veröffentlichungen
|
releases.footer=Releases
|
||||||
releases.title=Versionshinweise
|
releases.title=Release Notes
|
||||||
releases.header=Versionshinweise
|
releases.header=Release Notes
|
||||||
releases.current.version=Aktuelle Version
|
releases.current.version=Current Release
|
||||||
releases.note=Versionshinweise sind nur auf Englisch verfügbar
|
releases.note=Release notes are only available in English
|
||||||
|
|
||||||
#Validate Signature
|
#Validate Signature
|
||||||
validateSignature.title=PDF-Signaturen überprüfen
|
#Validate Signature
|
||||||
validateSignature.header=Digitale Signaturen überprüfen
|
validateSignature.title=Validate PDF Signatures
|
||||||
validateSignature.selectPDF=Signierte PDF-Datei auswählen
|
validateSignature.header=Validate Digital Signatures
|
||||||
validateSignature.submit=Signaturen überprüfen
|
validateSignature.selectPDF=Select signed PDF file
|
||||||
validateSignature.results=Gültigkeitsprüfungsergebnisse
|
validateSignature.submit=Validate Signatures
|
||||||
|
validateSignature.results=Validation Results
|
||||||
validateSignature.status=Status
|
validateSignature.status=Status
|
||||||
validateSignature.signer=Unterzeichner
|
validateSignature.signer=Signer
|
||||||
validateSignature.date=Datum
|
validateSignature.date=Date
|
||||||
validateSignature.reason=Grund
|
validateSignature.reason=Reason
|
||||||
validateSignature.location=Ort
|
validateSignature.location=Location
|
||||||
validateSignature.noSignatures=Keine digitalen Signaturen in diesem Dokument gefunden
|
validateSignature.noSignatures=No digital signatures found in this document
|
||||||
validateSignature.status.valid=Gültig
|
validateSignature.status.valid=Valid
|
||||||
validateSignature.status.invalid=Ungültig
|
validateSignature.status.invalid=Invalid
|
||||||
validateSignature.chain.invalid=Zertifikatskettenprüfung fehlgeschlagen - kann die Identität des Unterzeichners nicht verifizieren
|
validateSignature.chain.invalid=Certificate chain validation failed - cannot verify signer's identity
|
||||||
validateSignature.trust.invalid=Zertifikat nicht im Truststore - Quelle kann nicht verifiziert werden
|
validateSignature.trust.invalid=Certificate not in trust store - source cannot be verified
|
||||||
validateSignature.cert.expired=Zertifikat ist abgelaufen
|
validateSignature.cert.expired=Certificate has expired
|
||||||
validateSignature.cert.revoked=Zertifikat wurde widerrufen
|
validateSignature.cert.revoked=Certificate has been revoked
|
||||||
validateSignature.signature.info=Signaturinformationen
|
validateSignature.signature.info=Signature Information
|
||||||
validateSignature.signature=Signatur
|
validateSignature.signature=Signature
|
||||||
validateSignature.signature.mathValid=Signatur ist mathematisch gültig ABER:
|
validateSignature.signature.mathValid=Signature is mathematically valid BUT:
|
||||||
validateSignature.selectCustomCert=Benutzerdefinierte Zertifikatsdatei X.509 (Optional)
|
validateSignature.selectCustomCert=Custom Certificate File X.509 (Optional)
|
||||||
validateSignature.cert.info=Zertifikat Details
|
validateSignature.cert.info=Certificate Details
|
||||||
validateSignature.cert.issuer=Aussteller
|
validateSignature.cert.issuer=Issuer
|
||||||
validateSignature.cert.subject=Betreff
|
validateSignature.cert.subject=Subject
|
||||||
validateSignature.cert.serialNumber=Seriennummer
|
validateSignature.cert.serialNumber=Serial Number
|
||||||
validateSignature.cert.validFrom=Gültig von
|
validateSignature.cert.validFrom=Valid From
|
||||||
validateSignature.cert.validUntil=Gültig bis
|
validateSignature.cert.validUntil=Valid Until
|
||||||
validateSignature.cert.algorithm=Algorithmus
|
validateSignature.cert.algorithm=Algorithm
|
||||||
validateSignature.cert.keySize=Schlüsselgröße
|
validateSignature.cert.keySize=Key Size
|
||||||
validateSignature.cert.version=Version
|
validateSignature.cert.version=Version
|
||||||
validateSignature.cert.keyUsage=Schlüsselverwendung
|
validateSignature.cert.keyUsage=Key Usage
|
||||||
validateSignature.cert.selfSigned=Selbstsigniert
|
validateSignature.cert.selfSigned=Self-Signed
|
||||||
validateSignature.cert.bits=bits
|
validateSignature.cert.bits=bits
|
||||||
|
|||||||
@@ -965,16 +965,6 @@ multiTool.dragDropMessage=Page(s) Selected
|
|||||||
multiTool.undo=Undo
|
multiTool.undo=Undo
|
||||||
multiTool.redo=Redo
|
multiTool.redo=Redo
|
||||||
|
|
||||||
#decrypt
|
|
||||||
decrypt.passwordPrompt=This file is password-protected. Please enter the password:
|
|
||||||
decrypt.cancelled=Operation cancelled for PDF: {0}
|
|
||||||
decrypt.noPassword=No password provided for encrypted PDF: {0}
|
|
||||||
decrypt.invalidPassword=Please try again with the correct password.
|
|
||||||
decrypt.invalidPasswordHeader=Incorrect password or unsupported encryption for PDF: {0}
|
|
||||||
decrypt.unexpectedError=There was an error processing the file. Please try again.
|
|
||||||
decrypt.serverError=Server error while decrypting: {0}
|
|
||||||
decrypt.success=File decrypted successfully.
|
|
||||||
|
|
||||||
#multiTool-advert
|
#multiTool-advert
|
||||||
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
|
||||||
@@ -1066,7 +1056,6 @@ addPassword.submit=Κρυπτογράφηση
|
|||||||
#watermark
|
#watermark
|
||||||
watermark.title=Προσθήκη Υδατογραφήματος
|
watermark.title=Προσθήκη Υδατογραφήματος
|
||||||
watermark.header=Προσθήκη Υδατογραφήματος
|
watermark.header=Προσθήκη Υδατογραφήματος
|
||||||
watermark.customColor=Προσαρμοσμένο χρώμα κειμένου
|
|
||||||
watermark.selectText.1=Επιλέξτε PDF για την προσθήκη του υδατογραφήματος:
|
watermark.selectText.1=Επιλέξτε PDF για την προσθήκη του υδατογραφήματος:
|
||||||
watermark.selectText.2=Κείμενο Υδατογραφήματος:
|
watermark.selectText.2=Κείμενο Υδατογραφήματος:
|
||||||
watermark.selectText.3=Μέγεθος Κειμένου:
|
watermark.selectText.3=Μέγεθος Κειμένου:
|
||||||
@@ -1291,6 +1280,7 @@ 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
|
||||||
#Validate Signature
|
#Validate Signature
|
||||||
validateSignature.title=Validate PDF Signatures
|
validateSignature.title=Validate PDF Signatures
|
||||||
validateSignature.header=Validate Digital Signatures
|
validateSignature.header=Validate Digital Signatures
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ joinDiscord=Join our Discord server
|
|||||||
seeDockerHub=See Docker Hub
|
seeDockerHub=See Docker Hub
|
||||||
visitGithub=Visit Github Repository
|
visitGithub=Visit Github Repository
|
||||||
donate=Donate
|
donate=Donate
|
||||||
color=Colour
|
color=Color
|
||||||
sponsor=Sponsor
|
sponsor=Sponsor
|
||||||
info=Info
|
info=Info
|
||||||
pro=Pro
|
pro=Pro
|
||||||
@@ -419,9 +419,9 @@ home.auto-rename.title=Auto Rename PDF File
|
|||||||
home.auto-rename.desc=Auto renames a PDF file based on its detected header
|
home.auto-rename.desc=Auto renames a PDF file based on its detected header
|
||||||
auto-rename.tags=auto-detect,header-based,organize,relabel
|
auto-rename.tags=auto-detect,header-based,organize,relabel
|
||||||
|
|
||||||
home.adjust-contrast.title=Adjust Colours/Contrast
|
home.adjust-contrast.title=Adjust Colors/Contrast
|
||||||
home.adjust-contrast.desc=Adjust Contrast, Saturation and Brightness of a PDF
|
home.adjust-contrast.desc=Adjust Contrast, Saturation and Brightness of a PDF
|
||||||
adjust-contrast.tags=color-correction,tune,modify,enhance,colour-correction
|
adjust-contrast.tags=color-correction,tune,modify,enhance
|
||||||
|
|
||||||
home.crop.title=Crop PDF
|
home.crop.title=Crop PDF
|
||||||
home.crop.desc=Crop a PDF to reduce its size (maintains text!)
|
home.crop.desc=Crop a PDF to reduce its size (maintains text!)
|
||||||
@@ -488,11 +488,11 @@ overlay-pdfs.tags=Overlay
|
|||||||
|
|
||||||
home.split-by-sections.title=Split PDF by Sections
|
home.split-by-sections.title=Split PDF by Sections
|
||||||
home.split-by-sections.desc=Divide each page of a PDF into smaller horizontal and vertical sections
|
home.split-by-sections.desc=Divide each page of a PDF into smaller horizontal and vertical sections
|
||||||
split-by-sections.tags=Section Split, Divide, Customize,Customise
|
split-by-sections.tags=Section Split, Divide, Customize
|
||||||
|
|
||||||
home.AddStampRequest.title=Add Stamp to PDF
|
home.AddStampRequest.title=Add Stamp to PDF
|
||||||
home.AddStampRequest.desc=Add text or add image stamps at set locations
|
home.AddStampRequest.desc=Add text or add image stamps at set locations
|
||||||
AddStampRequest.tags=Stamp, Add image, center image, Watermark, PDF, Embed, Customize,Customise
|
AddStampRequest.tags=Stamp, Add image, center image, Watermark, PDF, Embed, Customize
|
||||||
|
|
||||||
|
|
||||||
home.PDFToBook.title=PDF to Book
|
home.PDFToBook.title=PDF to Book
|
||||||
@@ -518,21 +518,21 @@ validateSignature.tags=signature,verify,validate,pdf,certificate,digital signatu
|
|||||||
|
|
||||||
#replace-invert-color
|
#replace-invert-color
|
||||||
replace-color.title=Advanced Colour options
|
replace-color.title=Advanced Colour options
|
||||||
replace-color.header=Replace-Invert Colour PDF
|
replace-color.header=Replace-Invert Color PDF
|
||||||
home.replaceColorPdf.title=Advanced Colour options
|
home.replaceColorPdf.title=Advanced Colour options
|
||||||
home.replaceColorPdf.desc=Replace colour for text and background in PDF and invert full colour of pdf to reduce file size
|
home.replaceColorPdf.desc=Replace color for text and background in PDF and invert full color of pdf to reduce file size
|
||||||
replaceColorPdf.tags=Replace Colour,Page operations,Back end,server side
|
replaceColorPdf.tags=Replace Color,Page operations,Back end,server side
|
||||||
replace-color.selectText.1=Replace or Invert colour Options
|
replace-color.selectText.1=Replace or Invert color Options
|
||||||
replace-color.selectText.2=Default(Default high contrast colours)
|
replace-color.selectText.2=Default(Default high contrast colors)
|
||||||
replace-color.selectText.3=Custom(Customised colours)
|
replace-color.selectText.3=Custom(Customized colors)
|
||||||
replace-color.selectText.4=Full-Invert(Invert all colours)
|
replace-color.selectText.4=Full-Invert(Invert all colors)
|
||||||
replace-color.selectText.5=High contrast colour options
|
replace-color.selectText.5=High contrast color options
|
||||||
replace-color.selectText.6=white text on black background
|
replace-color.selectText.6=white text on black background
|
||||||
replace-color.selectText.7=Black text on white background
|
replace-color.selectText.7=Black text on white background
|
||||||
replace-color.selectText.8=Yellow text on black background
|
replace-color.selectText.8=Yellow text on black background
|
||||||
replace-color.selectText.9=Green text on black background
|
replace-color.selectText.9=Green text on black background
|
||||||
replace-color.selectText.10=Choose text Colour
|
replace-color.selectText.10=Choose text Color
|
||||||
replace-color.selectText.11=Choose background Colour
|
replace-color.selectText.11=Choose background Color
|
||||||
replace-color.submit=Replace
|
replace-color.submit=Replace
|
||||||
|
|
||||||
|
|
||||||
@@ -655,7 +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 Colour
|
AddStampRequest.customColor=Custom Text Color
|
||||||
AddStampRequest.submit=Submit
|
AddStampRequest.submit=Submit
|
||||||
|
|
||||||
|
|
||||||
@@ -787,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 Colour 1:
|
compare.highlightColor.1=Highlight Color 1:
|
||||||
compare.highlightColor.2=Highlight Colour 2:
|
compare.highlightColor.2=Highlight Color 2:
|
||||||
compare.document.1=Document 1
|
compare.document.1=Document 1
|
||||||
compare.document.2=Document 2
|
compare.document.2=Document 2
|
||||||
compare.submit=Compare
|
compare.submit=Compare
|
||||||
@@ -846,7 +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 colour variation around the estimated background colour (default: 30).
|
ScannerImageSplit.selectText.4=Determines the range of color variation around the estimated background color (default: 30).
|
||||||
ScannerImageSplit.selectText.5=Minimum Area:
|
ScannerImageSplit.selectText.5=Minimum Area:
|
||||||
ScannerImageSplit.selectText.6=Sets the minimum area threshold for a photo (default: 10000).
|
ScannerImageSplit.selectText.6=Sets the minimum area threshold for a photo (default: 10000).
|
||||||
ScannerImageSplit.selectText.7=Minimum Contour Area:
|
ScannerImageSplit.selectText.7=Minimum Contour Area:
|
||||||
@@ -898,7 +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=Optimisation level:
|
compress.selectText.2=Optimization level:
|
||||||
compress.selectText.3=4 (Terrible for text images)
|
compress.selectText.3=4 (Terrible for text images)
|
||||||
compress.selectText.4=Auto mode - Auto adjusts quality to get PDF to exact size
|
compress.selectText.4=Auto mode - Auto adjusts quality to get PDF to exact size
|
||||||
compress.selectText.5=Expected PDF Size (e.g. 25MB, 10.8MB, 25KB)
|
compress.selectText.5=Expected PDF Size (e.g. 25MB, 10.8MB, 25KB)
|
||||||
@@ -965,16 +965,6 @@ multiTool.dragDropMessage=Page(s) Selected
|
|||||||
multiTool.undo=Undo
|
multiTool.undo=Undo
|
||||||
multiTool.redo=Redo
|
multiTool.redo=Redo
|
||||||
|
|
||||||
#decrypt
|
|
||||||
decrypt.passwordPrompt=This file is password-protected. Please enter the password:
|
|
||||||
decrypt.cancelled=Operation cancelled for PDF: {0}
|
|
||||||
decrypt.noPassword=No password provided for encrypted PDF: {0}
|
|
||||||
decrypt.invalidPassword=Please try again with the correct password.
|
|
||||||
decrypt.invalidPasswordHeader=Incorrect password or unsupported encryption for PDF: {0}
|
|
||||||
decrypt.unexpectedError=There was an error processing the file. Please try again.
|
|
||||||
decrypt.serverError=Server error while decrypting: {0}
|
|
||||||
decrypt.success=File decrypted successfully.
|
|
||||||
|
|
||||||
#multiTool-advert
|
#multiTool-advert
|
||||||
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
|
||||||
@@ -1066,7 +1056,6 @@ 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:
|
||||||
@@ -1291,6 +1280,7 @@ 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
|
||||||
#Validate Signature
|
#Validate Signature
|
||||||
validateSignature.title=Validate PDF Signatures
|
validateSignature.title=Validate PDF Signatures
|
||||||
validateSignature.header=Validate Digital Signatures
|
validateSignature.header=Validate Digital Signatures
|
||||||
|
|||||||
@@ -965,16 +965,6 @@ multiTool.dragDropMessage=Page(s) Selected
|
|||||||
multiTool.undo=Undo
|
multiTool.undo=Undo
|
||||||
multiTool.redo=Redo
|
multiTool.redo=Redo
|
||||||
|
|
||||||
#decrypt
|
|
||||||
decrypt.passwordPrompt=This file is password-protected. Please enter the password:
|
|
||||||
decrypt.cancelled=Operation cancelled for PDF: {0}
|
|
||||||
decrypt.noPassword=No password provided for encrypted PDF: {0}
|
|
||||||
decrypt.invalidPassword=Please try again with the correct password.
|
|
||||||
decrypt.invalidPasswordHeader=Incorrect password or unsupported encryption for PDF: {0}
|
|
||||||
decrypt.unexpectedError=There was an error processing the file. Please try again.
|
|
||||||
decrypt.serverError=Server error while decrypting: {0}
|
|
||||||
decrypt.success=File decrypted successfully.
|
|
||||||
|
|
||||||
#multiTool-advert
|
#multiTool-advert
|
||||||
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
|
||||||
@@ -1066,7 +1056,6 @@ 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:
|
||||||
@@ -1291,6 +1280,7 @@ 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
|
||||||
#Validate Signature
|
#Validate Signature
|
||||||
validateSignature.title=Validate PDF Signatures
|
validateSignature.title=Validate PDF Signatures
|
||||||
validateSignature.header=Validate Digital Signatures
|
validateSignature.header=Validate Digital Signatures
|
||||||
|
|||||||
@@ -965,16 +965,6 @@ multiTool.dragDropMessage=Page(s) Selected
|
|||||||
multiTool.undo=Undo
|
multiTool.undo=Undo
|
||||||
multiTool.redo=Redo
|
multiTool.redo=Redo
|
||||||
|
|
||||||
#decrypt
|
|
||||||
decrypt.passwordPrompt=This file is password-protected. Please enter the password:
|
|
||||||
decrypt.cancelled=Operation cancelled for PDF: {0}
|
|
||||||
decrypt.noPassword=No password provided for encrypted PDF: {0}
|
|
||||||
decrypt.invalidPassword=Please try again with the correct password.
|
|
||||||
decrypt.invalidPasswordHeader=Incorrect password or unsupported encryption for PDF: {0}
|
|
||||||
decrypt.unexpectedError=There was an error processing the file. Please try again.
|
|
||||||
decrypt.serverError=Server error while decrypting: {0}
|
|
||||||
decrypt.success=File decrypted successfully.
|
|
||||||
|
|
||||||
#multiTool-advert
|
#multiTool-advert
|
||||||
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
|
||||||
@@ -1066,7 +1056,6 @@ addPassword.submit=Encriptar
|
|||||||
#watermark
|
#watermark
|
||||||
watermark.title=Añadir marca de agua
|
watermark.title=Añadir marca de agua
|
||||||
watermark.header=Añadir marca de agua
|
watermark.header=Añadir marca de agua
|
||||||
watermark.customColor=Personalizar color de texto
|
|
||||||
watermark.selectText.1=Seleccionar PDF para añadir marca de agua:
|
watermark.selectText.1=Seleccionar PDF para añadir marca de agua:
|
||||||
watermark.selectText.2=Texto de la marca de agua:
|
watermark.selectText.2=Texto de la marca de agua:
|
||||||
watermark.selectText.3=Tamaño de la Fuente:
|
watermark.selectText.3=Tamaño de la Fuente:
|
||||||
@@ -1291,6 +1280,7 @@ 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
|
||||||
#Validate Signature
|
#Validate Signature
|
||||||
validateSignature.title=Validate PDF Signatures
|
validateSignature.title=Validate PDF Signatures
|
||||||
validateSignature.header=Validate Digital Signatures
|
validateSignature.header=Validate Digital Signatures
|
||||||
|
|||||||
@@ -965,16 +965,6 @@ multiTool.dragDropMessage=Page(s) Selected
|
|||||||
multiTool.undo=Undo
|
multiTool.undo=Undo
|
||||||
multiTool.redo=Redo
|
multiTool.redo=Redo
|
||||||
|
|
||||||
#decrypt
|
|
||||||
decrypt.passwordPrompt=This file is password-protected. Please enter the password:
|
|
||||||
decrypt.cancelled=Operation cancelled for PDF: {0}
|
|
||||||
decrypt.noPassword=No password provided for encrypted PDF: {0}
|
|
||||||
decrypt.invalidPassword=Please try again with the correct password.
|
|
||||||
decrypt.invalidPasswordHeader=Incorrect password or unsupported encryption for PDF: {0}
|
|
||||||
decrypt.unexpectedError=There was an error processing the file. Please try again.
|
|
||||||
decrypt.serverError=Server error while decrypting: {0}
|
|
||||||
decrypt.success=File decrypted successfully.
|
|
||||||
|
|
||||||
#multiTool-advert
|
#multiTool-advert
|
||||||
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
|
||||||
@@ -1066,7 +1056,6 @@ addPassword.submit=Enkriptatu
|
|||||||
#watermark
|
#watermark
|
||||||
watermark.title=Gehitu ur-marka
|
watermark.title=Gehitu ur-marka
|
||||||
watermark.header=Gehitu ur-marka
|
watermark.header=Gehitu ur-marka
|
||||||
watermark.customColor=Custom Text Color
|
|
||||||
watermark.selectText.1=Hautatu PDFa ur-marka gehitzeko:
|
watermark.selectText.1=Hautatu PDFa ur-marka gehitzeko:
|
||||||
watermark.selectText.2=Ur-markaren testua:
|
watermark.selectText.2=Ur-markaren testua:
|
||||||
watermark.selectText.3=Letra-tipoaren tamaina:
|
watermark.selectText.3=Letra-tipoaren tamaina:
|
||||||
@@ -1291,6 +1280,7 @@ 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
|
||||||
#Validate Signature
|
#Validate Signature
|
||||||
validateSignature.title=Validate PDF Signatures
|
validateSignature.title=Validate PDF Signatures
|
||||||
validateSignature.header=Validate Digital Signatures
|
validateSignature.header=Validate Digital Signatures
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -965,16 +965,6 @@ multiTool.dragDropMessage=Page(s) sélectionnées
|
|||||||
multiTool.undo=Undo
|
multiTool.undo=Undo
|
||||||
multiTool.redo=Redo
|
multiTool.redo=Redo
|
||||||
|
|
||||||
#decrypt
|
|
||||||
decrypt.passwordPrompt=This file is password-protected. Please enter the password:
|
|
||||||
decrypt.cancelled=Operation cancelled for PDF: {0}
|
|
||||||
decrypt.noPassword=No password provided for encrypted PDF: {0}
|
|
||||||
decrypt.invalidPassword=Please try again with the correct password.
|
|
||||||
decrypt.invalidPasswordHeader=Incorrect password or unsupported encryption for PDF: {0}
|
|
||||||
decrypt.unexpectedError=There was an error processing the file. Please try again.
|
|
||||||
decrypt.serverError=Server error while decrypting: {0}
|
|
||||||
decrypt.success=File decrypted successfully.
|
|
||||||
|
|
||||||
#multiTool-advert
|
#multiTool-advert
|
||||||
multiTool-advert.message=Cette fonctionnalité est aussi disponible dans la <a href="{0}">page de l'outil multifonction</a>. Allez-y pour une interface page par page améliorée et des fonctionnalités additionnelles !
|
multiTool-advert.message=Cette fonctionnalité est aussi disponible dans la <a href="{0}">page de l'outil multifonction</a>. Allez-y pour une interface page par page améliorée et des fonctionnalités additionnelles !
|
||||||
|
|
||||||
@@ -1066,7 +1056,6 @@ 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
|
||||||
@@ -1291,6 +1280,7 @@ 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
|
||||||
#Validate Signature
|
#Validate Signature
|
||||||
validateSignature.title=Validate PDF Signatures
|
validateSignature.title=Validate PDF Signatures
|
||||||
validateSignature.header=Validate Digital Signatures
|
validateSignature.header=Validate Digital Signatures
|
||||||
|
|||||||
@@ -965,16 +965,6 @@ multiTool.dragDropMessage=Page(s) Selected
|
|||||||
multiTool.undo=Undo
|
multiTool.undo=Undo
|
||||||
multiTool.redo=Redo
|
multiTool.redo=Redo
|
||||||
|
|
||||||
#decrypt
|
|
||||||
decrypt.passwordPrompt=This file is password-protected. Please enter the password:
|
|
||||||
decrypt.cancelled=Operation cancelled for PDF: {0}
|
|
||||||
decrypt.noPassword=No password provided for encrypted PDF: {0}
|
|
||||||
decrypt.invalidPassword=Please try again with the correct password.
|
|
||||||
decrypt.invalidPasswordHeader=Incorrect password or unsupported encryption for PDF: {0}
|
|
||||||
decrypt.unexpectedError=There was an error processing the file. Please try again.
|
|
||||||
decrypt.serverError=Server error while decrypting: {0}
|
|
||||||
decrypt.success=File decrypted successfully.
|
|
||||||
|
|
||||||
#multiTool-advert
|
#multiTool-advert
|
||||||
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
|
||||||
@@ -1066,7 +1056,6 @@ 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ó:
|
||||||
@@ -1291,6 +1280,7 @@ 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
|
||||||
#Validate Signature
|
#Validate Signature
|
||||||
validateSignature.title=Validate PDF Signatures
|
validateSignature.title=Validate PDF Signatures
|
||||||
validateSignature.header=Validate Digital Signatures
|
validateSignature.header=Validate Digital Signatures
|
||||||
|
|||||||
@@ -965,16 +965,6 @@ multiTool.dragDropMessage=Page(s) Selected
|
|||||||
multiTool.undo=Undo
|
multiTool.undo=Undo
|
||||||
multiTool.redo=Redo
|
multiTool.redo=Redo
|
||||||
|
|
||||||
#decrypt
|
|
||||||
decrypt.passwordPrompt=This file is password-protected. Please enter the password:
|
|
||||||
decrypt.cancelled=Operation cancelled for PDF: {0}
|
|
||||||
decrypt.noPassword=No password provided for encrypted PDF: {0}
|
|
||||||
decrypt.invalidPassword=Please try again with the correct password.
|
|
||||||
decrypt.invalidPasswordHeader=Incorrect password or unsupported encryption for PDF: {0}
|
|
||||||
decrypt.unexpectedError=There was an error processing the file. Please try again.
|
|
||||||
decrypt.serverError=Server error while decrypting: {0}
|
|
||||||
decrypt.success=File decrypted successfully.
|
|
||||||
|
|
||||||
#multiTool-advert
|
#multiTool-advert
|
||||||
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
|
||||||
@@ -1066,7 +1056,6 @@ addPassword.submit=एन्क्रिप्ट करें
|
|||||||
#watermark
|
#watermark
|
||||||
watermark.title=वॉटरमार्क जोड़ें
|
watermark.title=वॉटरमार्क जोड़ें
|
||||||
watermark.header=वॉटरमार्क जोड़ें
|
watermark.header=वॉटरमार्क जोड़ें
|
||||||
watermark.customColor=संवैधित टेक्स्ट रंग
|
|
||||||
watermark.selectText.1=वॉटरमार्क जोड़ने के लिए पीडीएफ चुनें:
|
watermark.selectText.1=वॉटरमार्क जोड़ने के लिए पीडीएफ चुनें:
|
||||||
watermark.selectText.2=वॉटरमार्क टेक्स्ट:
|
watermark.selectText.2=वॉटरमार्क टेक्स्ट:
|
||||||
watermark.selectText.3=फ़ॉन्ट साइज़:
|
watermark.selectText.3=फ़ॉन्ट साइज़:
|
||||||
@@ -1291,6 +1280,7 @@ 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
|
||||||
#Validate Signature
|
#Validate Signature
|
||||||
validateSignature.title=Validate PDF Signatures
|
validateSignature.title=Validate PDF Signatures
|
||||||
validateSignature.header=Validate Digital Signatures
|
validateSignature.header=Validate Digital Signatures
|
||||||
|
|||||||
@@ -965,16 +965,6 @@ multiTool.dragDropMessage=Page(s) Selected
|
|||||||
multiTool.undo=Undo
|
multiTool.undo=Undo
|
||||||
multiTool.redo=Redo
|
multiTool.redo=Redo
|
||||||
|
|
||||||
#decrypt
|
|
||||||
decrypt.passwordPrompt=This file is password-protected. Please enter the password:
|
|
||||||
decrypt.cancelled=Operation cancelled for PDF: {0}
|
|
||||||
decrypt.noPassword=No password provided for encrypted PDF: {0}
|
|
||||||
decrypt.invalidPassword=Please try again with the correct password.
|
|
||||||
decrypt.invalidPasswordHeader=Incorrect password or unsupported encryption for PDF: {0}
|
|
||||||
decrypt.unexpectedError=There was an error processing the file. Please try again.
|
|
||||||
decrypt.serverError=Server error while decrypting: {0}
|
|
||||||
decrypt.success=File decrypted successfully.
|
|
||||||
|
|
||||||
#multiTool-advert
|
#multiTool-advert
|
||||||
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
|
||||||
@@ -1066,7 +1056,6 @@ addPassword.submit=Šifriraj
|
|||||||
#watermark
|
#watermark
|
||||||
watermark.title=Dodaj vodeni žig
|
watermark.title=Dodaj vodeni žig
|
||||||
watermark.header=Dodaj vodeni žig
|
watermark.header=Dodaj vodeni žig
|
||||||
watermark.customColor=Prilagođena boja teksta
|
|
||||||
watermark.selectText.1=Izaberite PDF za dodavanje vodenog žiga:
|
watermark.selectText.1=Izaberite PDF za dodavanje vodenog žiga:
|
||||||
watermark.selectText.2=Tekst vodenog žiga:
|
watermark.selectText.2=Tekst vodenog žiga:
|
||||||
watermark.selectText.3=Veličina fonta:
|
watermark.selectText.3=Veličina fonta:
|
||||||
@@ -1291,6 +1280,7 @@ 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
|
||||||
#Validate Signature
|
#Validate Signature
|
||||||
validateSignature.title=Validate PDF Signatures
|
validateSignature.title=Validate PDF Signatures
|
||||||
validateSignature.header=Validate Digital Signatures
|
validateSignature.header=Validate Digital Signatures
|
||||||
|
|||||||
@@ -965,16 +965,6 @@ multiTool.dragDropMessage=Page(s) Selected
|
|||||||
multiTool.undo=Undo
|
multiTool.undo=Undo
|
||||||
multiTool.redo=Redo
|
multiTool.redo=Redo
|
||||||
|
|
||||||
#decrypt
|
|
||||||
decrypt.passwordPrompt=This file is password-protected. Please enter the password:
|
|
||||||
decrypt.cancelled=Operation cancelled for PDF: {0}
|
|
||||||
decrypt.noPassword=No password provided for encrypted PDF: {0}
|
|
||||||
decrypt.invalidPassword=Please try again with the correct password.
|
|
||||||
decrypt.invalidPasswordHeader=Incorrect password or unsupported encryption for PDF: {0}
|
|
||||||
decrypt.unexpectedError=There was an error processing the file. Please try again.
|
|
||||||
decrypt.serverError=Server error while decrypting: {0}
|
|
||||||
decrypt.success=File decrypted successfully.
|
|
||||||
|
|
||||||
#multiTool-advert
|
#multiTool-advert
|
||||||
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
|
||||||
@@ -1066,7 +1056,6 @@ 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:
|
||||||
@@ -1291,6 +1280,7 @@ 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
|
||||||
#Validate Signature
|
#Validate Signature
|
||||||
validateSignature.title=Validate PDF Signatures
|
validateSignature.title=Validate PDF Signatures
|
||||||
validateSignature.header=Validate Digital Signatures
|
validateSignature.header=Validate Digital Signatures
|
||||||
|
|||||||
@@ -965,16 +965,6 @@ multiTool.dragDropMessage=Page(s) Selected
|
|||||||
multiTool.undo=Undo
|
multiTool.undo=Undo
|
||||||
multiTool.redo=Redo
|
multiTool.redo=Redo
|
||||||
|
|
||||||
#decrypt
|
|
||||||
decrypt.passwordPrompt=This file is password-protected. Please enter the password:
|
|
||||||
decrypt.cancelled=Operation cancelled for PDF: {0}
|
|
||||||
decrypt.noPassword=No password provided for encrypted PDF: {0}
|
|
||||||
decrypt.invalidPassword=Please try again with the correct password.
|
|
||||||
decrypt.invalidPasswordHeader=Incorrect password or unsupported encryption for PDF: {0}
|
|
||||||
decrypt.unexpectedError=There was an error processing the file. Please try again.
|
|
||||||
decrypt.serverError=Server error while decrypting: {0}
|
|
||||||
decrypt.success=File decrypted successfully.
|
|
||||||
|
|
||||||
#multiTool-advert
|
#multiTool-advert
|
||||||
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
|
||||||
@@ -1066,7 +1056,6 @@ 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:
|
||||||
@@ -1291,6 +1280,7 @@ 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
|
||||||
#Validate Signature
|
#Validate Signature
|
||||||
validateSignature.title=Validate PDF Signatures
|
validateSignature.title=Validate PDF Signatures
|
||||||
validateSignature.header=Validate Digital Signatures
|
validateSignature.header=Validate Digital Signatures
|
||||||
|
|||||||
@@ -965,16 +965,6 @@ multiTool.dragDropMessage=Pagina(e) selezionata(e)
|
|||||||
multiTool.undo=Annulla
|
multiTool.undo=Annulla
|
||||||
multiTool.redo=Rifai
|
multiTool.redo=Rifai
|
||||||
|
|
||||||
#decrypt
|
|
||||||
decrypt.passwordPrompt=Questo file è protetto da password. Inserisci la password:
|
|
||||||
decrypt.cancelled=Operazione annullata per il PDF: {0}
|
|
||||||
decrypt.noPassword=Nessuna password fornita per il PDF crittografato: {0}
|
|
||||||
decrypt.invalidPassword=Riprova con la password corretta.
|
|
||||||
decrypt.invalidPasswordHeader=Password errata o crittografia non supportata per il PDF: {0}
|
|
||||||
decrypt.unexpectedError=Si è verificato un errore durante l'elaborazione del file. Riprova..
|
|
||||||
decrypt.serverError=Errore del server durante la decrittazione: {0}
|
|
||||||
decrypt.success=File decrittografato con successo.
|
|
||||||
|
|
||||||
#multiTool-advert
|
#multiTool-advert
|
||||||
multiTool-advert.message=Questa funzione è disponibile anche nella nostra <a href="{0}">pagina multi-strumento</a>. Scoprila per un'interfaccia utente pagina per pagina migliorata e funzionalità aggiuntive!
|
multiTool-advert.message=Questa funzione è disponibile anche nella nostra <a href="{0}">pagina multi-strumento</a>. Scoprila per un'interfaccia utente pagina per pagina migliorata e funzionalità aggiuntive!
|
||||||
|
|
||||||
@@ -1066,7 +1056,6 @@ addPassword.submit=Crittografa
|
|||||||
#watermark
|
#watermark
|
||||||
watermark.title=Aggiungi Filigrana
|
watermark.title=Aggiungi Filigrana
|
||||||
watermark.header=Aggiungi filigrana
|
watermark.header=Aggiungi filigrana
|
||||||
watermark.customColor=Colore testo personalizzato
|
|
||||||
watermark.selectText.1=Seleziona PDF a cui aggiungere la filigrana:
|
watermark.selectText.1=Seleziona PDF a cui aggiungere la filigrana:
|
||||||
watermark.selectText.2=Testo:
|
watermark.selectText.2=Testo:
|
||||||
watermark.selectText.3=Dimensione carattere:
|
watermark.selectText.3=Dimensione carattere:
|
||||||
@@ -1291,6 +1280,7 @@ 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
|
||||||
#Validate Signature
|
#Validate Signature
|
||||||
validateSignature.title=Validare le firme PDF
|
validateSignature.title=Validare le firme PDF
|
||||||
validateSignature.header=Convalidare le firme digitali
|
validateSignature.header=Convalidare le firme digitali
|
||||||
|
|||||||
@@ -56,12 +56,12 @@ userNotFoundMessage=ユーザーが見つかりません。
|
|||||||
incorrectPasswordMessage=現在のパスワードが正しくありません。
|
incorrectPasswordMessage=現在のパスワードが正しくありません。
|
||||||
usernameExistsMessage=新しいユーザー名はすでに存在します。
|
usernameExistsMessage=新しいユーザー名はすでに存在します。
|
||||||
invalidUsernameMessage=ユーザー名が無効です。ユーザー名には文字、数字、およびそれに続く特殊文字 @._+- のみを含めることができます。または、有効な電子メール アドレスである必要があります。
|
invalidUsernameMessage=ユーザー名が無効です。ユーザー名には文字、数字、およびそれに続く特殊文字 @._+- のみを含めることができます。または、有効な電子メール アドレスである必要があります。
|
||||||
invalidPasswordMessage=パスワードは空にすることはできません。また、先頭・末尾にスペースを含めることもできません。
|
invalidPasswordMessage=The password must not be empty and must not have spaces at the beginning or end.
|
||||||
confirmPasswordErrorMessage=新しいパスワードと新しいパスワードの確認は一致する必要があります。
|
confirmPasswordErrorMessage=新しいパスワードと新しいパスワードの確認は一致する必要があります。
|
||||||
deleteCurrentUserMessage=現在ログインしているユーザーは削除できません。
|
deleteCurrentUserMessage=現在ログインしているユーザーは削除できません。
|
||||||
deleteUsernameExistsMessage=そのユーザー名は存在しないため削除できません。
|
deleteUsernameExistsMessage=そのユーザー名は存在しないため削除できません。
|
||||||
downgradeCurrentUserMessage=現在のユーザーの役割をダウングレードできません
|
downgradeCurrentUserMessage=現在のユーザーの役割をダウングレードできません
|
||||||
disabledCurrentUserMessage=現在のユーザーを無効にすることはできません
|
disabledCurrentUserMessage=The current user cannot be disabled
|
||||||
downgradeCurrentUserLongMessage=現在のユーザーの役割をダウングレードできません。したがって、現在のユーザーは表示されません。
|
downgradeCurrentUserLongMessage=現在のユーザーの役割をダウングレードできません。したがって、現在のユーザーは表示されません。
|
||||||
userAlreadyExistsOAuthMessage=ユーザーは既にOAuth2ユーザーとして存在します。
|
userAlreadyExistsOAuthMessage=ユーザーは既にOAuth2ユーザーとして存在します。
|
||||||
userAlreadyExistsWebMessage=ユーザーは既にWebユーザーとして存在します。
|
userAlreadyExistsWebMessage=ユーザーは既にWebユーザーとして存在します。
|
||||||
@@ -76,12 +76,12 @@ donate=寄付する
|
|||||||
color=色
|
color=色
|
||||||
sponsor=スポンサー
|
sponsor=スポンサー
|
||||||
info=Info
|
info=Info
|
||||||
pro=pro
|
pro=Pro
|
||||||
page=ページ
|
page=Page
|
||||||
pages=ページ
|
pages=Pages
|
||||||
loading=読込中...
|
loading=Loading...
|
||||||
addToDoc=ドキュメントに追加
|
addToDoc=Add to Document
|
||||||
reset=リセット
|
reset=Reset
|
||||||
|
|
||||||
legal.privacy=プライバシーポリシー
|
legal.privacy=プライバシーポリシー
|
||||||
legal.terms=利用規約
|
legal.terms=利用規約
|
||||||
@@ -92,7 +92,7 @@ legal.impressum=著作権利者情報
|
|||||||
###############
|
###############
|
||||||
# Pipeline #
|
# Pipeline #
|
||||||
###############
|
###############
|
||||||
pipeline.header=パイプラインメニュー (Beta)
|
pipeline.header=パイプラインメニュー (Alpha)
|
||||||
pipeline.uploadButton=カスタムのアップロード
|
pipeline.uploadButton=カスタムのアップロード
|
||||||
pipeline.configureButton=設定
|
pipeline.configureButton=設定
|
||||||
pipeline.defaultOption=カスタム
|
pipeline.defaultOption=カスタム
|
||||||
@@ -117,21 +117,21 @@ pipelineOptions.validateButton=検証
|
|||||||
########################
|
########################
|
||||||
# ENTERPRISE EDITION #
|
# ENTERPRISE EDITION #
|
||||||
########################
|
########################
|
||||||
enterpriseEdition.button=Proにアップグレード
|
enterpriseEdition.button=Upgrade to Pro
|
||||||
enterpriseEdition.warning=この機能はProユーザーのみが利用できます。
|
enterpriseEdition.warning=This feature is only available to Pro users.
|
||||||
enterpriseEdition.yamlAdvert=Stirling PDF Proは、YAML構成ファイルやその他のSSO機能をサポートしています。
|
enterpriseEdition.yamlAdvert=Stirling PDF Pro supports YAML configuration files and other SSO features.
|
||||||
enterpriseEdition.ssoAdvert=より多くのユーザー管理機能をお探しですか? Stirling PDF Proをご覧ください
|
enterpriseEdition.ssoAdvert=Looking for more user management features? Check out Stirling PDF Pro
|
||||||
|
|
||||||
|
|
||||||
#################
|
#################
|
||||||
# Analytics #
|
# Analytics #
|
||||||
#################
|
#################
|
||||||
analytics.title=Stirling PDFをもっと良くしたいですか?
|
analytics.title=Do you want make Stirling PDF better?
|
||||||
analytics.paragraph1=Stirling PDFでは、製品の改善に役立つ分析機能をオプトインしています。個人情報やファイルの内容を追跡することはありません。
|
analytics.paragraph1=Stirling PDF has opt in analytics to help us improve the product. We do not track any personal information or file contents.
|
||||||
analytics.paragraph2=Stirling-PDFの成長を支援しユーザーをより深く理解できるように分析を有効にすることを検討してください。
|
analytics.paragraph2=Please consider enabling analytics to help Stirling-PDF grow and to allow us to understand our users better.
|
||||||
analytics.enable=分析を有効にする
|
analytics.enable=Enable analytics
|
||||||
analytics.disable=分析を無効にする
|
analytics.disable=Disable analytics
|
||||||
analytics.settings=config/settings.ymlファイルでアナリティクスの設定を変更できます。
|
analytics.settings=You can change the settings for analytics in the config/settings.yml file
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# NAVBAR #
|
# NAVBAR #
|
||||||
@@ -142,14 +142,14 @@ navbar.language=言語
|
|||||||
navbar.settings=設定
|
navbar.settings=設定
|
||||||
navbar.allTools=ツール
|
navbar.allTools=ツール
|
||||||
navbar.multiTool=マルチツール
|
navbar.multiTool=マルチツール
|
||||||
navbar.search=検索
|
navbar.search=Search
|
||||||
navbar.sections.organize=整理
|
navbar.sections.organize=整理
|
||||||
navbar.sections.convertTo=PDFへ変換
|
navbar.sections.convertTo=PDFへ変換
|
||||||
navbar.sections.convertFrom=PDFから変換
|
navbar.sections.convertFrom=PDFから変換
|
||||||
navbar.sections.security=署名とセキュリティ
|
navbar.sections.security=署名とセキュリティ
|
||||||
navbar.sections.advance=アドバンスド
|
navbar.sections.advance=アドバンスド
|
||||||
navbar.sections.edit=閲覧と編集
|
navbar.sections.edit=閲覧と編集
|
||||||
navbar.sections.popular=人気
|
navbar.sections.popular=Popular
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# SETTINGS #
|
# SETTINGS #
|
||||||
@@ -208,7 +208,7 @@ adminUserSettings.user=ユーザー
|
|||||||
adminUserSettings.addUser=新しいユーザを追加
|
adminUserSettings.addUser=新しいユーザを追加
|
||||||
adminUserSettings.deleteUser=ユーザの削除
|
adminUserSettings.deleteUser=ユーザの削除
|
||||||
adminUserSettings.confirmDeleteUser=ユーザを本当に削除しますか?
|
adminUserSettings.confirmDeleteUser=ユーザを本当に削除しますか?
|
||||||
adminUserSettings.confirmChangeUserStatus=ユーザーを無効/有効にする必要がありますか?
|
adminUserSettings.confirmChangeUserStatus=Should the user be disabled/enabled?
|
||||||
adminUserSettings.usernameInfo=ユーザー名には、文字、数字、および次の特殊文字 @._+- のみを含めることができます。または、有効な電子メール アドレスである必要があります。
|
adminUserSettings.usernameInfo=ユーザー名には、文字、数字、および次の特殊文字 @._+- のみを含めることができます。または、有効な電子メール アドレスである必要があります。
|
||||||
adminUserSettings.roles=役割
|
adminUserSettings.roles=役割
|
||||||
adminUserSettings.role=役割
|
adminUserSettings.role=役割
|
||||||
@@ -247,8 +247,8 @@ database.fileNotFound=ファイルが見つかりません
|
|||||||
database.fileNullOrEmpty=ファイルは null または空であってはなりません
|
database.fileNullOrEmpty=ファイルは null または空であってはなりません
|
||||||
database.failedImportFile=ファイルのインポートに失敗
|
database.failedImportFile=ファイルのインポートに失敗
|
||||||
|
|
||||||
session.expired=セッションが期限切れです。ページを更新してもう一度お試しください。
|
session.expired=Your session has expired. Please refresh the page and try again.
|
||||||
session.refreshPage=ページを更新
|
session.refreshPage=Refresh Page
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# HOME-PAGE #
|
# HOME-PAGE #
|
||||||
@@ -488,52 +488,52 @@ overlay-pdfs.tags=Overlay
|
|||||||
|
|
||||||
home.split-by-sections.title=PDFをセクションで分割
|
home.split-by-sections.title=PDFをセクションで分割
|
||||||
home.split-by-sections.desc=PDFの各ページを縦横に分割します。
|
home.split-by-sections.desc=PDFの各ページを縦横に分割します。
|
||||||
split-by-sections.tags=Section Split, Divide, Customize,Customise
|
split-by-sections.tags=Section Split, Divide, Customize
|
||||||
|
|
||||||
home.AddStampRequest.title=PDFにスタンプを追加
|
home.AddStampRequest.title=PDFにスタンプを追加
|
||||||
home.AddStampRequest.desc=設定した位置にテキストや画像のスタンプを追加できます
|
home.AddStampRequest.desc=設定した位置にテキストや画像のスタンプを追加できます
|
||||||
AddStampRequest.tags=Stamp, Add image, center image, Watermark, PDF, Embed, Customize,Customise
|
AddStampRequest.tags=Stamp, Add image, center image, Watermark, PDF, Embed, Customize
|
||||||
|
|
||||||
|
|
||||||
home.PDFToBook.title=PDFを書籍に変換
|
home.PDFToBook.title=PDFを書籍に変換
|
||||||
home.PDFToBook.desc=calibreを使用してPDFを書籍/コミック形式に変換します
|
home.PDFToBook.desc=calibreを使用してPDFを書籍/コミック形式に変換します
|
||||||
PDFToBook.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle,epub,mobi,azw3,docx,rtf,txt,html,lit,fb2,pdb,lrf
|
PDFToBook.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle
|
||||||
|
|
||||||
home.BookToPDF.title=PDFを書籍に変換
|
home.BookToPDF.title=PDFを書籍に変換
|
||||||
home.BookToPDF.desc=calibreを使用してPDFを書籍/コミック形式に変換します
|
home.BookToPDF.desc=calibreを使用してPDFを書籍/コミック形式に変換します
|
||||||
BookToPDF.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle,epub,mobi,azw3,docx,rtf,txt,html,lit,fb2,pdb,lrf
|
BookToPDF.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle
|
||||||
|
|
||||||
home.removeImagePdf.title=画像の削除
|
home.removeImagePdf.title=画像の削除
|
||||||
home.removeImagePdf.desc=PDFから画像を削除してファイルサイズを小さくします
|
home.removeImagePdf.desc=PDFから画像を削除してファイルサイズを小さくします
|
||||||
removeImagePdf.tags=Remove Image,Page operations,Back end,server side
|
removeImagePdf.tags=Remove Image,Page operations,Back end,server side
|
||||||
|
|
||||||
|
|
||||||
home.splitPdfByChapters.title=PDFをチャプターごとに分割
|
home.splitPdfByChapters.title=Split PDF by Chapters
|
||||||
home.splitPdfByChapters.desc=チャプターの構造に基づいてPDFを複数のファイルに分割します
|
home.splitPdfByChapters.desc=Split a PDF into multiple files based on its chapter structure.
|
||||||
splitPdfByChapters.tags=split,chapters,bookmarks,organize
|
splitPdfByChapters.tags=split,chapters,bookmarks,organize
|
||||||
|
|
||||||
home.validateSignature.title=PDF署名の検証
|
home.validateSignature.title=Validate PDF Signature
|
||||||
home.validateSignature.desc=PDF文書のデジタル署名と証明書を検証します
|
home.validateSignature.desc=Verify digital signatures and certificates in PDF documents
|
||||||
validateSignature.tags=signature,verify,validate,pdf,certificate,digital signature,Validate Signature,Validate certificate
|
validateSignature.tags=signature,verify,validate,pdf,certificate,digital signature,Validate Signature,Validate certificate
|
||||||
|
|
||||||
#replace-invert-color
|
#replace-invert-color
|
||||||
replace-color.title=色の置換・反転
|
replace-color.title=Replace-Invert-Color
|
||||||
replace-color.header=PDFの色の置換・反転
|
replace-color.header=Replace-Invert Color PDF
|
||||||
home.replaceColorPdf.title=色の置換と反転
|
home.replaceColorPdf.title=Replace and Invert Color
|
||||||
home.replaceColorPdf.desc=PDF内のテキストと背景の色を置き換え、PDFのフルカラーを反転してファイルサイズを縮小します。
|
home.replaceColorPdf.desc=Replace color for text and background in PDF and invert full color of pdf to reduce file size
|
||||||
replaceColorPdf.tags=Replace Color,Page operations,Back end,server side
|
replaceColorPdf.tags=Replace Color,Page operations,Back end,server side
|
||||||
replace-color.selectText.1=色の置換または反転オプション
|
replace-color.selectText.1=Replace or Invert color Options
|
||||||
replace-color.selectText.2=デフォルト(デフォルトの高コントラスト色)
|
replace-color.selectText.2=Default(Default high contrast colors)
|
||||||
replace-color.selectText.3=カスタム(カスタマイズされた色)
|
replace-color.selectText.3=Custom(Customized colors)
|
||||||
replace-color.selectText.4=フル反転(すべての色を反転)
|
replace-color.selectText.4=Full-Invert(Invert all colors)
|
||||||
replace-color.selectText.5=高コントラストカラーオプション
|
replace-color.selectText.5=High contrast color options
|
||||||
replace-color.selectText.6=黒背景に白文字
|
replace-color.selectText.6=white text on black background
|
||||||
replace-color.selectText.7=白背景に黒文字
|
replace-color.selectText.7=Black text on white background
|
||||||
replace-color.selectText.8=黒背景に黄色文字
|
replace-color.selectText.8=Yellow text on black background
|
||||||
replace-color.selectText.9=黒背景に緑文字
|
replace-color.selectText.9=Green text on black background
|
||||||
replace-color.selectText.10=テキストの色を選択
|
replace-color.selectText.10=Choose text Color
|
||||||
replace-color.selectText.11=背景色を選択
|
replace-color.selectText.11=Choose background Color
|
||||||
replace-color.submit=置換
|
replace-color.submit=Replace
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -560,9 +560,9 @@ login.oauth2AccessDenied=アクセス拒否
|
|||||||
login.oauth2InvalidTokenResponse=無効なトークン応答
|
login.oauth2InvalidTokenResponse=無効なトークン応答
|
||||||
login.oauth2InvalidIdToken=無効なIDトークン
|
login.oauth2InvalidIdToken=無効なIDトークン
|
||||||
login.userIsDisabled=ユーザーは非アクティブ化されており、現在このユーザー名でのログインはブロックされています。管理者に連絡してください。
|
login.userIsDisabled=ユーザーは非アクティブ化されており、現在このユーザー名でのログインはブロックされています。管理者に連絡してください。
|
||||||
login.alreadyLoggedIn=すでにログインしています
|
login.alreadyLoggedIn=You are already logged in to
|
||||||
login.alreadyLoggedIn2=デバイスからログアウトしてもう一度お試しください。
|
login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
|
||||||
login.toManySessions=アクティブなセッションが多すぎます
|
login.toManySessions=You have too many active sessions
|
||||||
|
|
||||||
#auto-redact
|
#auto-redact
|
||||||
autoRedact.title=自動塗りつぶし
|
autoRedact.title=自動塗りつぶし
|
||||||
@@ -578,8 +578,8 @@ autoRedact.submitButton=送信
|
|||||||
|
|
||||||
|
|
||||||
#showJS
|
#showJS
|
||||||
showJS.title=Javascriptを表示
|
showJS.title=JavaScriptを表示
|
||||||
showJS.header=Javascriptを表示
|
showJS.header=JavaScriptを表示
|
||||||
showJS.downloadJS=Javascriptをダウンロード
|
showJS.downloadJS=Javascriptをダウンロード
|
||||||
showJS.submit=表示
|
showJS.submit=表示
|
||||||
|
|
||||||
@@ -757,7 +757,7 @@ certSign.showSig=署名を表示
|
|||||||
certSign.reason=理由
|
certSign.reason=理由
|
||||||
certSign.location=場所
|
certSign.location=場所
|
||||||
certSign.name=名前
|
certSign.name=名前
|
||||||
certSign.showLogo=ロゴを表示
|
certSign.showLogo=Show Logo
|
||||||
certSign.submit=PDFに署名
|
certSign.submit=PDFに署名
|
||||||
|
|
||||||
|
|
||||||
@@ -792,9 +792,9 @@ compare.highlightColor.2=ハイライトカラー 2:
|
|||||||
compare.document.1=ドキュメント 1
|
compare.document.1=ドキュメント 1
|
||||||
compare.document.2=ドキュメント 2
|
compare.document.2=ドキュメント 2
|
||||||
compare.submit=比較
|
compare.submit=比較
|
||||||
compare.complex.message=提供された文書の一方または両方が大きなファイルであるため、比較の精度が低下する可能性があります。
|
compare.complex.message=One or both of the provided documents are large files, accuracy of comparison may be reduced
|
||||||
compare.large.file.message=提供された文書の1つまたは両方が大きすぎて処理できません
|
compare.large.file.message=One or Both of the provided documents are too large to process
|
||||||
compare.no.text.message=選択したPDFの1つまたは両方にテキストコンテンツがありません。比較するには、テキストを含むPDFを選択してください。
|
compare.no.text.message=One or both of the selected PDFs have no text content. Please choose PDFs with text for comparison.
|
||||||
|
|
||||||
#BookToPDF
|
#BookToPDF
|
||||||
BookToPDF.title=書籍やコミックをPDFに変換
|
BookToPDF.title=書籍やコミックをPDFに変換
|
||||||
@@ -803,8 +803,8 @@ BookToPDF.credit=calibreを使用
|
|||||||
BookToPDF.submit=変換
|
BookToPDF.submit=変換
|
||||||
|
|
||||||
#PDFToBook
|
#PDFToBook
|
||||||
PDFToBook.title=PDFを書籍に変換
|
PDFToBook.title=書籍をPDFに変換
|
||||||
PDFToBook.header=PDFを書籍に変換
|
PDFToBook.header=書籍をPDFに変換
|
||||||
PDFToBook.selectText.1=フォーマット
|
PDFToBook.selectText.1=フォーマット
|
||||||
PDFToBook.credit=calibreを使用
|
PDFToBook.credit=calibreを使用
|
||||||
PDFToBook.submit=変換
|
PDFToBook.submit=変換
|
||||||
@@ -817,17 +817,17 @@ sign.draw=署名を書く
|
|||||||
sign.text=テキスト入力
|
sign.text=テキスト入力
|
||||||
sign.clear=クリア
|
sign.clear=クリア
|
||||||
sign.add=追加
|
sign.add=追加
|
||||||
sign.saved=保存された署名
|
sign.saved=Saved Signatures
|
||||||
sign.save=署名を保存
|
sign.save=Save Signature
|
||||||
sign.personalSigs=個人署名
|
sign.personalSigs=Personal Signatures
|
||||||
sign.sharedSigs=共有署名
|
sign.sharedSigs=Shared Signatures
|
||||||
sign.noSavedSigs=保存された署名が見つかりません
|
sign.noSavedSigs=No saved signatures found
|
||||||
sign.addToAll=すべてのページに追加
|
sign.addToAll=Add to all pages
|
||||||
sign.delete=削除
|
sign.delete=Delete
|
||||||
sign.first=最初のページ
|
sign.first=First page
|
||||||
sign.last=最後のページ
|
sign.last=Last page
|
||||||
sign.next=次のページ
|
sign.next=Next page
|
||||||
sign.previous=前のページ
|
sign.previous=Previous page
|
||||||
|
|
||||||
#repair
|
#repair
|
||||||
repair.title=修復
|
repair.title=修復
|
||||||
@@ -944,39 +944,29 @@ pdfOrganiser.placeholder=(例:1,3,2または4-8,2,10-12または2n-1)
|
|||||||
multiTool.title=PDFマルチツール
|
multiTool.title=PDFマルチツール
|
||||||
multiTool.header=PDFマルチツール
|
multiTool.header=PDFマルチツール
|
||||||
multiTool.uploadPrompts=ファイル名
|
multiTool.uploadPrompts=ファイル名
|
||||||
multiTool.selectAll=すべて選択
|
multiTool.selectAll=Select All
|
||||||
multiTool.deselectAll=選択を解除
|
multiTool.deselectAll=Deselect All
|
||||||
multiTool.selectPages=ページ選択
|
multiTool.selectPages=Page Select
|
||||||
multiTool.selectedPages=選択したページ
|
multiTool.selectedPages=Selected Pages
|
||||||
multiTool.page=ページ
|
multiTool.page=Page
|
||||||
multiTool.deleteSelected=選択項目を削除
|
multiTool.deleteSelected=Delete Selected
|
||||||
multiTool.downloadAll=エクスポート
|
multiTool.downloadAll=Export
|
||||||
multiTool.downloadSelected=選択項目をエクスポート
|
multiTool.downloadSelected=Export Selected
|
||||||
|
|
||||||
multiTool.insertPageBreak=改ページを挿入
|
multiTool.insertPageBreak=Insert Page Break
|
||||||
multiTool.addFile=ファイルを追加
|
multiTool.addFile=Add File
|
||||||
multiTool.rotateLeft=左回転
|
multiTool.rotateLeft=Rotate Left
|
||||||
multiTool.rotateRight=右回転
|
multiTool.rotateRight=Rotate Right
|
||||||
multiTool.split=分割
|
multiTool.split=Split
|
||||||
multiTool.moveLeft=左に移動
|
multiTool.moveLeft=Move Left
|
||||||
multiTool.moveRight=右に移動
|
multiTool.moveRight=Move Right
|
||||||
multiTool.delete=削除
|
multiTool.delete=Delete
|
||||||
multiTool.dragDropMessage=選択されたページ
|
multiTool.dragDropMessage=Page(s) Selected
|
||||||
multiTool.undo=元に戻す
|
multiTool.undo=Undo
|
||||||
multiTool.redo=やり直す
|
multiTool.redo=Redo
|
||||||
|
|
||||||
#decrypt
|
|
||||||
decrypt.passwordPrompt=このファイルはパスワードで保護されています。パスワードを入力してください:
|
|
||||||
decrypt.cancelled=PDFの操作がキャンセルされました: {0}
|
|
||||||
decrypt.noPassword=暗号化されたPDFにパスワードが指定されていません: {0}
|
|
||||||
decrypt.invalidPassword=正しいパスワードでもう一度お試しください。
|
|
||||||
decrypt.invalidPasswordHeader=PDFのパスワードが正しくないか、暗号化がサポートされていません: {0}
|
|
||||||
decrypt.unexpectedError=ファイルの処理中にエラーが発生しました。もう一度お試しください。
|
|
||||||
decrypt.serverError=復号化中にサーバーエラーが発生しました: {0}
|
|
||||||
decrypt.success=ファイルの暗号化が正常に完了しました。
|
|
||||||
|
|
||||||
#multiTool-advert
|
#multiTool-advert
|
||||||
multiTool-advert.message=この機能は、<a href="{0}">マルチツール</a>でもご利用いただけます。強化されたページごとのUIと追加機能についてはこちらをご覧ください。
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
|
||||||
#view pdf
|
#view pdf
|
||||||
viewPdf.title=PDFを表示
|
viewPdf.title=PDFを表示
|
||||||
@@ -1066,7 +1056,6 @@ addPassword.submit=暗号化
|
|||||||
#watermark
|
#watermark
|
||||||
watermark.title=透かしの追加
|
watermark.title=透かしの追加
|
||||||
watermark.header=透かしの追加
|
watermark.header=透かしの追加
|
||||||
watermark.customColor=文字色のカスタム
|
|
||||||
watermark.selectText.1=透かしを追加するPDFを選択:
|
watermark.selectText.1=透かしを追加するPDFを選択:
|
||||||
watermark.selectText.2=透かしのテキスト:
|
watermark.selectText.2=透かしのテキスト:
|
||||||
watermark.selectText.3=文字サイズ:
|
watermark.selectText.3=文字サイズ:
|
||||||
@@ -1132,8 +1121,8 @@ pdfToPDFA.header=PDFをPDF/Aに変換
|
|||||||
pdfToPDFA.credit=本サービスはPDF/Aの変換にqpdfを使用しています。
|
pdfToPDFA.credit=本サービスはPDF/Aの変換にqpdfを使用しています。
|
||||||
pdfToPDFA.submit=変換
|
pdfToPDFA.submit=変換
|
||||||
pdfToPDFA.tip=現在、一度に複数の入力に対して機能しません
|
pdfToPDFA.tip=現在、一度に複数の入力に対して機能しません
|
||||||
pdfToPDFA.outputFormat=出力形式
|
pdfToPDFA.outputFormat=Output format
|
||||||
pdfToPDFA.pdfWithDigitalSignature=PDFにはデジタル署名が含まれています。これは次の手順で削除されます。
|
pdfToPDFA.pdfWithDigitalSignature=PDF にはデジタル署名が含まれています。これは次の手順で削除されます。
|
||||||
|
|
||||||
|
|
||||||
#PDFToWord
|
#PDFToWord
|
||||||
@@ -1238,8 +1227,8 @@ licenses.license=ライセンス
|
|||||||
survey.nav=アンケート
|
survey.nav=アンケート
|
||||||
survey.title=Stirling-PDFのアンケート
|
survey.title=Stirling-PDFのアンケート
|
||||||
survey.description=Stirling-PDFには追跡機能がないため、Stirling-PDFをより良くするために皆様の意見を聞かせてください!
|
survey.description=Stirling-PDFには追跡機能がないため、Stirling-PDFをより良くするために皆様の意見を聞かせてください!
|
||||||
survey.changes=Stirling-PDFは前回の調査から変更されました。詳細についてはこちらのブログ投稿をご覧ください。
|
survey.changes=Stirling-PDF has changed since the last survey! To find out more please check our blog post here:
|
||||||
survey.changes2=これらの変更により私たちは有償のビジネスサポートと資金援助を受けています
|
survey.changes2=With these changes we are getting paid business support and funding
|
||||||
survey.please=アンケートにご協力ください!
|
survey.please=アンケートにご協力ください!
|
||||||
survey.disabled=(アンケートのポップアップは、次の更新では無効になりますが、ページの下部に表示されます。)
|
survey.disabled=(アンケートのポップアップは、次の更新では無効になりますが、ページの下部に表示されます。)
|
||||||
survey.button=アンケートに答える
|
survey.button=アンケートに答える
|
||||||
@@ -1267,61 +1256,62 @@ removeImage.removeImage=画像の削除
|
|||||||
removeImage.submit=画像を削除
|
removeImage.submit=画像を削除
|
||||||
|
|
||||||
|
|
||||||
splitByChapters.title=PDFをチャプターごとに分割
|
splitByChapters.title=Split PDF by Chapters
|
||||||
splitByChapters.header=PDFをチャプターごとに分割
|
splitByChapters.header=Split PDF by Chapters
|
||||||
splitByChapters.bookmarkLevel=ブックマークレベル
|
splitByChapters.bookmarkLevel=Bookmark Level
|
||||||
splitByChapters.includeMetadata=メタデータを含める
|
splitByChapters.includeMetadata=Include Metadata
|
||||||
splitByChapters.allowDuplicates=重複を許可する
|
splitByChapters.allowDuplicates=Allow Duplicates
|
||||||
splitByChapters.desc.1=このツールは、チャプター構造に基づいてPDFファイルを複数のPDFに分割します。
|
splitByChapters.desc.1=This tool splits a PDF file into multiple PDFs based on its chapter structure.
|
||||||
splitByChapters.desc.2=ブックマークレベル:分割に使用するブックマークのレベルを選択します(最上位レベルの場合は0、第2レベルの場合は1など)。
|
splitByChapters.desc.2=Bookmark Level: Choose the level of bookmarks to use for splitting (0 for top-level, 1 for second-level, etc.).
|
||||||
splitByChapters.desc.3=メタデータを含める:チェックすると、元のPDFのメタデータが各分割PDFに含まれます。
|
splitByChapters.desc.3=Include Metadata: If checked, the original PDF's metadata will be included in each split PDF.
|
||||||
splitByChapters.desc.4=重複を許可:チェックすると同じページ上の複数のブックマークから個別のPDFを作成できます。
|
splitByChapters.desc.4=Allow Duplicates: If checked, allows multiple bookmarks on the same page to create separate PDFs.
|
||||||
splitByChapters.submit=PDFを分割
|
splitByChapters.submit=Split PDF
|
||||||
|
|
||||||
#File Chooser
|
#File Chooser
|
||||||
fileChooser.click=クリック
|
fileChooser.click=Click
|
||||||
fileChooser.or=または
|
fileChooser.or=or
|
||||||
fileChooser.dragAndDrop=ドラッグ&ドロップ
|
fileChooser.dragAndDrop=Drag & Drop
|
||||||
fileChooser.hoveredDragAndDrop=ファイルをここにドラッグ&ドロップ
|
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
||||||
|
|
||||||
#release notes
|
#release notes
|
||||||
releases.footer=リリース
|
releases.footer=Releases
|
||||||
releases.title=リリースノート
|
releases.title=Release Notes
|
||||||
releases.header=リリースノート
|
releases.header=Release Notes
|
||||||
releases.current.version=現在のリリース
|
releases.current.version=Current Release
|
||||||
releases.note=リリースノートは英語でのみで提供されています
|
releases.note=Release notes are only available in English
|
||||||
|
|
||||||
#Validate Signature
|
#Validate Signature
|
||||||
validateSignature.title=PDF署名の検証
|
#Validate Signature
|
||||||
validateSignature.header=デジタル署名の検証
|
validateSignature.title=Validate PDF Signatures
|
||||||
validateSignature.selectPDF=署名済みPDFファイルを選択
|
validateSignature.header=Validate Digital Signatures
|
||||||
validateSignature.submit=署名の検証
|
validateSignature.selectPDF=Select signed PDF file
|
||||||
validateSignature.results=検証結果
|
validateSignature.submit=Validate Signatures
|
||||||
validateSignature.status=状態
|
validateSignature.results=Validation Results
|
||||||
validateSignature.signer=署名者
|
validateSignature.status=Status
|
||||||
validateSignature.date=日付
|
validateSignature.signer=Signer
|
||||||
validateSignature.reason=理由
|
validateSignature.date=Date
|
||||||
validateSignature.location=場所
|
validateSignature.reason=Reason
|
||||||
validateSignature.noSignatures=この文書にはデジタル署名が見つかりません
|
validateSignature.location=Location
|
||||||
validateSignature.status.valid=有効
|
validateSignature.noSignatures=No digital signatures found in this document
|
||||||
validateSignature.status.invalid=無効
|
validateSignature.status.valid=Valid
|
||||||
validateSignature.chain.invalid=証明書チェーンの検証に失敗しました - 署名者の身元を確認できません
|
validateSignature.status.invalid=Invalid
|
||||||
validateSignature.trust.invalid=証明書が信頼ストアにありません - ソースを検証できません
|
validateSignature.chain.invalid=Certificate chain validation failed - cannot verify signer's identity
|
||||||
validateSignature.cert.expired=証明書の有効期限が切れています
|
validateSignature.trust.invalid=Certificate not in trust store - source cannot be verified
|
||||||
validateSignature.cert.revoked=証明書は取り消されました
|
validateSignature.cert.expired=Certificate has expired
|
||||||
validateSignature.signature.info=署名情報
|
validateSignature.cert.revoked=Certificate has been revoked
|
||||||
validateSignature.signature=署名
|
validateSignature.signature.info=Signature Information
|
||||||
validateSignature.signature.mathValid=署名は数学的には有効ですが:
|
validateSignature.signature=Signature
|
||||||
validateSignature.selectCustomCert=カスタム証明書ファイル X.509 (オプション)
|
validateSignature.signature.mathValid=Signature is mathematically valid BUT:
|
||||||
validateSignature.cert.info=証明書の詳細
|
validateSignature.selectCustomCert=Custom Certificate File X.509 (Optional)
|
||||||
validateSignature.cert.issuer=発行者
|
validateSignature.cert.info=Certificate Details
|
||||||
validateSignature.cert.subject=主題
|
validateSignature.cert.issuer=Issuer
|
||||||
validateSignature.cert.serialNumber=シリアルナンバー
|
validateSignature.cert.subject=Subject
|
||||||
validateSignature.cert.validFrom=有効開始日
|
validateSignature.cert.serialNumber=Serial Number
|
||||||
validateSignature.cert.validUntil=有効期限
|
validateSignature.cert.validFrom=Valid From
|
||||||
validateSignature.cert.algorithm=アルゴリズム
|
validateSignature.cert.validUntil=Valid Until
|
||||||
validateSignature.cert.keySize=キーサイズ
|
validateSignature.cert.algorithm=Algorithm
|
||||||
validateSignature.cert.version=バージョン
|
validateSignature.cert.keySize=Key Size
|
||||||
validateSignature.cert.keyUsage=キーの使用法
|
validateSignature.cert.version=Version
|
||||||
validateSignature.cert.selfSigned=自己署名
|
validateSignature.cert.keyUsage=Key Usage
|
||||||
validateSignature.cert.bits=ビット
|
validateSignature.cert.selfSigned=Self-Signed
|
||||||
|
validateSignature.cert.bits=bits
|
||||||
|
|||||||
@@ -965,16 +965,6 @@ multiTool.dragDropMessage=Page(s) Selected
|
|||||||
multiTool.undo=Undo
|
multiTool.undo=Undo
|
||||||
multiTool.redo=Redo
|
multiTool.redo=Redo
|
||||||
|
|
||||||
#decrypt
|
|
||||||
decrypt.passwordPrompt=This file is password-protected. Please enter the password:
|
|
||||||
decrypt.cancelled=Operation cancelled for PDF: {0}
|
|
||||||
decrypt.noPassword=No password provided for encrypted PDF: {0}
|
|
||||||
decrypt.invalidPassword=Please try again with the correct password.
|
|
||||||
decrypt.invalidPasswordHeader=Incorrect password or unsupported encryption for PDF: {0}
|
|
||||||
decrypt.unexpectedError=There was an error processing the file. Please try again.
|
|
||||||
decrypt.serverError=Server error while decrypting: {0}
|
|
||||||
decrypt.success=File decrypted successfully.
|
|
||||||
|
|
||||||
#multiTool-advert
|
#multiTool-advert
|
||||||
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
|
||||||
@@ -1066,7 +1056,6 @@ addPassword.submit=암호화
|
|||||||
#watermark
|
#watermark
|
||||||
watermark.title=워터마크 추가
|
watermark.title=워터마크 추가
|
||||||
watermark.header=워터마크 추가
|
watermark.header=워터마크 추가
|
||||||
watermark.customColor=사용자 정의 텍스트 색상
|
|
||||||
watermark.selectText.1=워터마크를 추가할 PDF 선택:
|
watermark.selectText.1=워터마크를 추가할 PDF 선택:
|
||||||
watermark.selectText.2=워터마크 텍스트:
|
watermark.selectText.2=워터마크 텍스트:
|
||||||
watermark.selectText.3=폰트 크기:
|
watermark.selectText.3=폰트 크기:
|
||||||
@@ -1291,6 +1280,7 @@ 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
|
||||||
#Validate Signature
|
#Validate Signature
|
||||||
validateSignature.title=Validate PDF Signatures
|
validateSignature.title=Validate PDF Signatures
|
||||||
validateSignature.header=Validate Digital Signatures
|
validateSignature.header=Validate Digital Signatures
|
||||||
|
|||||||
@@ -965,16 +965,6 @@ multiTool.dragDropMessage=Page(s) Selected
|
|||||||
multiTool.undo=Undo
|
multiTool.undo=Undo
|
||||||
multiTool.redo=Redo
|
multiTool.redo=Redo
|
||||||
|
|
||||||
#decrypt
|
|
||||||
decrypt.passwordPrompt=This file is password-protected. Please enter the password:
|
|
||||||
decrypt.cancelled=Operation cancelled for PDF: {0}
|
|
||||||
decrypt.noPassword=No password provided for encrypted PDF: {0}
|
|
||||||
decrypt.invalidPassword=Please try again with the correct password.
|
|
||||||
decrypt.invalidPasswordHeader=Incorrect password or unsupported encryption for PDF: {0}
|
|
||||||
decrypt.unexpectedError=There was an error processing the file. Please try again.
|
|
||||||
decrypt.serverError=Server error while decrypting: {0}
|
|
||||||
decrypt.success=File decrypted successfully.
|
|
||||||
|
|
||||||
#multiTool-advert
|
#multiTool-advert
|
||||||
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
|
||||||
@@ -1066,7 +1056,6 @@ addPassword.submit=Versleutelen
|
|||||||
#watermark
|
#watermark
|
||||||
watermark.title=Watermerk toevoegen
|
watermark.title=Watermerk toevoegen
|
||||||
watermark.header=Watermerk toevoegen
|
watermark.header=Watermerk toevoegen
|
||||||
watermark.customColor=Aangepaste tekstkleur
|
|
||||||
watermark.selectText.1=Selecteer PDF om watermerk toe te voegen:
|
watermark.selectText.1=Selecteer PDF om watermerk toe te voegen:
|
||||||
watermark.selectText.2=Watermerk tekst:
|
watermark.selectText.2=Watermerk tekst:
|
||||||
watermark.selectText.3=Tekengrootte:
|
watermark.selectText.3=Tekengrootte:
|
||||||
@@ -1291,6 +1280,7 @@ 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
|
||||||
#Validate Signature
|
#Validate Signature
|
||||||
validateSignature.title=Validate PDF Signatures
|
validateSignature.title=Validate PDF Signatures
|
||||||
validateSignature.header=Validate Digital Signatures
|
validateSignature.header=Validate Digital Signatures
|
||||||
|
|||||||
@@ -965,16 +965,6 @@ multiTool.dragDropMessage=Page(s) Selected
|
|||||||
multiTool.undo=Undo
|
multiTool.undo=Undo
|
||||||
multiTool.redo=Redo
|
multiTool.redo=Redo
|
||||||
|
|
||||||
#decrypt
|
|
||||||
decrypt.passwordPrompt=This file is password-protected. Please enter the password:
|
|
||||||
decrypt.cancelled=Operation cancelled for PDF: {0}
|
|
||||||
decrypt.noPassword=No password provided for encrypted PDF: {0}
|
|
||||||
decrypt.invalidPassword=Please try again with the correct password.
|
|
||||||
decrypt.invalidPasswordHeader=Incorrect password or unsupported encryption for PDF: {0}
|
|
||||||
decrypt.unexpectedError=There was an error processing the file. Please try again.
|
|
||||||
decrypt.serverError=Server error while decrypting: {0}
|
|
||||||
decrypt.success=File decrypted successfully.
|
|
||||||
|
|
||||||
#multiTool-advert
|
#multiTool-advert
|
||||||
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
|
||||||
@@ -1066,7 +1056,6 @@ addPassword.submit=Krypter
|
|||||||
#watermark
|
#watermark
|
||||||
watermark.title=Legg til vannmerke
|
watermark.title=Legg til vannmerke
|
||||||
watermark.header=Legg til vannmerke
|
watermark.header=Legg til vannmerke
|
||||||
watermark.customColor=Tilpasset Tekstfarge
|
|
||||||
watermark.selectText.1=Velg PDF-fil å legge til vannmerke på:
|
watermark.selectText.1=Velg PDF-fil å legge til vannmerke på:
|
||||||
watermark.selectText.2=Vannmerketekst:
|
watermark.selectText.2=Vannmerketekst:
|
||||||
watermark.selectText.3=Skriftstørrelse:
|
watermark.selectText.3=Skriftstørrelse:
|
||||||
@@ -1291,6 +1280,7 @@ 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
|
||||||
#Validate Signature
|
#Validate Signature
|
||||||
validateSignature.title=Validate PDF Signatures
|
validateSignature.title=Validate PDF Signatures
|
||||||
validateSignature.header=Validate Digital Signatures
|
validateSignature.header=Validate Digital Signatures
|
||||||
|
|||||||
12
src/main/resources/messages_pl_PL.properties
Normal file → Executable file
12
src/main/resources/messages_pl_PL.properties
Normal file → Executable file
@@ -965,16 +965,6 @@ multiTool.dragDropMessage=Page(s) Selected
|
|||||||
multiTool.undo=Undo
|
multiTool.undo=Undo
|
||||||
multiTool.redo=Redo
|
multiTool.redo=Redo
|
||||||
|
|
||||||
#decrypt
|
|
||||||
decrypt.passwordPrompt=This file is password-protected. Please enter the password:
|
|
||||||
decrypt.cancelled=Operation cancelled for PDF: {0}
|
|
||||||
decrypt.noPassword=No password provided for encrypted PDF: {0}
|
|
||||||
decrypt.invalidPassword=Please try again with the correct password.
|
|
||||||
decrypt.invalidPasswordHeader=Incorrect password or unsupported encryption for PDF: {0}
|
|
||||||
decrypt.unexpectedError=There was an error processing the file. Please try again.
|
|
||||||
decrypt.serverError=Server error while decrypting: {0}
|
|
||||||
decrypt.success=File decrypted successfully.
|
|
||||||
|
|
||||||
#multiTool-advert
|
#multiTool-advert
|
||||||
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
|
||||||
@@ -1066,7 +1056,6 @@ addPassword.submit=Zablokuj
|
|||||||
#watermark
|
#watermark
|
||||||
watermark.title=Dodaj znak wodny
|
watermark.title=Dodaj znak wodny
|
||||||
watermark.header=Dodaj znak wodny
|
watermark.header=Dodaj znak wodny
|
||||||
watermark.customColor=Własny kolor tekstu
|
|
||||||
watermark.selectText.1=Wybierz dokument PDF, do którego chcesz dodać znak wodny:
|
watermark.selectText.1=Wybierz dokument PDF, do którego chcesz dodać znak wodny:
|
||||||
watermark.selectText.2=Treść znaku wodnego:
|
watermark.selectText.2=Treść znaku wodnego:
|
||||||
watermark.selectText.3=Rozmiar czcionki:
|
watermark.selectText.3=Rozmiar czcionki:
|
||||||
@@ -1291,6 +1280,7 @@ 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
|
||||||
#Validate Signature
|
#Validate Signature
|
||||||
validateSignature.title=Validate PDF Signatures
|
validateSignature.title=Validate PDF Signatures
|
||||||
validateSignature.header=Validate Digital Signatures
|
validateSignature.header=Validate Digital Signatures
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -965,16 +965,6 @@ multiTool.dragDropMessage=Page(s) Selected
|
|||||||
multiTool.undo=Undo
|
multiTool.undo=Undo
|
||||||
multiTool.redo=Redo
|
multiTool.redo=Redo
|
||||||
|
|
||||||
#decrypt
|
|
||||||
decrypt.passwordPrompt=This file is password-protected. Please enter the password:
|
|
||||||
decrypt.cancelled=Operation cancelled for PDF: {0}
|
|
||||||
decrypt.noPassword=No password provided for encrypted PDF: {0}
|
|
||||||
decrypt.invalidPassword=Please try again with the correct password.
|
|
||||||
decrypt.invalidPasswordHeader=Incorrect password or unsupported encryption for PDF: {0}
|
|
||||||
decrypt.unexpectedError=There was an error processing the file. Please try again.
|
|
||||||
decrypt.serverError=Server error while decrypting: {0}
|
|
||||||
decrypt.success=File decrypted successfully.
|
|
||||||
|
|
||||||
#multiTool-advert
|
#multiTool-advert
|
||||||
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
|
||||||
@@ -1066,7 +1056,6 @@ 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
|
||||||
@@ -1291,6 +1280,7 @@ 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
|
||||||
#Validate Signature
|
#Validate Signature
|
||||||
validateSignature.title=Validate PDF Signatures
|
validateSignature.title=Validate PDF Signatures
|
||||||
validateSignature.header=Validate Digital Signatures
|
validateSignature.header=Validate Digital Signatures
|
||||||
|
|||||||
@@ -965,16 +965,6 @@ multiTool.dragDropMessage=Page(s) Selected
|
|||||||
multiTool.undo=Undo
|
multiTool.undo=Undo
|
||||||
multiTool.redo=Redo
|
multiTool.redo=Redo
|
||||||
|
|
||||||
#decrypt
|
|
||||||
decrypt.passwordPrompt=This file is password-protected. Please enter the password:
|
|
||||||
decrypt.cancelled=Operation cancelled for PDF: {0}
|
|
||||||
decrypt.noPassword=No password provided for encrypted PDF: {0}
|
|
||||||
decrypt.invalidPassword=Please try again with the correct password.
|
|
||||||
decrypt.invalidPasswordHeader=Incorrect password or unsupported encryption for PDF: {0}
|
|
||||||
decrypt.unexpectedError=There was an error processing the file. Please try again.
|
|
||||||
decrypt.serverError=Server error while decrypting: {0}
|
|
||||||
decrypt.success=File decrypted successfully.
|
|
||||||
|
|
||||||
#multiTool-advert
|
#multiTool-advert
|
||||||
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
|
||||||
@@ -1066,7 +1056,6 @@ addPassword.submit=Criptează
|
|||||||
#watermark
|
#watermark
|
||||||
watermark.title=Adaugă Filigran
|
watermark.title=Adaugă Filigran
|
||||||
watermark.header=Adaugă Filigran
|
watermark.header=Adaugă Filigran
|
||||||
watermark.customColor=Culoare Text Personalizată
|
|
||||||
watermark.selectText.1=Selectează PDF-ul la care să adaugi filigranul:
|
watermark.selectText.1=Selectează PDF-ul la care să adaugi filigranul:
|
||||||
watermark.selectText.2=Textul Filigranului:
|
watermark.selectText.2=Textul Filigranului:
|
||||||
watermark.selectText.3=Mărimea fontului:
|
watermark.selectText.3=Mărimea fontului:
|
||||||
@@ -1291,6 +1280,7 @@ 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
|
||||||
#Validate Signature
|
#Validate Signature
|
||||||
validateSignature.title=Validate PDF Signatures
|
validateSignature.title=Validate PDF Signatures
|
||||||
validateSignature.header=Validate Digital Signatures
|
validateSignature.header=Validate Digital Signatures
|
||||||
|
|||||||
@@ -965,16 +965,6 @@ multiTool.dragDropMessage=Page(s) Selected
|
|||||||
multiTool.undo=Undo
|
multiTool.undo=Undo
|
||||||
multiTool.redo=Redo
|
multiTool.redo=Redo
|
||||||
|
|
||||||
#decrypt
|
|
||||||
decrypt.passwordPrompt=This file is password-protected. Please enter the password:
|
|
||||||
decrypt.cancelled=Operation cancelled for PDF: {0}
|
|
||||||
decrypt.noPassword=No password provided for encrypted PDF: {0}
|
|
||||||
decrypt.invalidPassword=Please try again with the correct password.
|
|
||||||
decrypt.invalidPasswordHeader=Incorrect password or unsupported encryption for PDF: {0}
|
|
||||||
decrypt.unexpectedError=There was an error processing the file. Please try again.
|
|
||||||
decrypt.serverError=Server error while decrypting: {0}
|
|
||||||
decrypt.success=File decrypted successfully.
|
|
||||||
|
|
||||||
#multiTool-advert
|
#multiTool-advert
|
||||||
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
|
||||||
@@ -1066,7 +1056,6 @@ addPassword.submit=Шифровать
|
|||||||
#watermark
|
#watermark
|
||||||
watermark.title=Добавить водяной знак
|
watermark.title=Добавить водяной знак
|
||||||
watermark.header=Добавить водяной знак
|
watermark.header=Добавить водяной знак
|
||||||
watermark.customColor=Настроенный цвет текста
|
|
||||||
watermark.selectText.1=Выберите PDF, чтобы добавить водяной знак:
|
watermark.selectText.1=Выберите PDF, чтобы добавить водяной знак:
|
||||||
watermark.selectText.2=Текст водяного знака:
|
watermark.selectText.2=Текст водяного знака:
|
||||||
watermark.selectText.3=Размер шрифта:
|
watermark.selectText.3=Размер шрифта:
|
||||||
@@ -1291,6 +1280,7 @@ 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
|
||||||
#Validate Signature
|
#Validate Signature
|
||||||
validateSignature.title=Validate PDF Signatures
|
validateSignature.title=Validate PDF Signatures
|
||||||
validateSignature.header=Validate Digital Signatures
|
validateSignature.header=Validate Digital Signatures
|
||||||
|
|||||||
@@ -965,16 +965,6 @@ multiTool.dragDropMessage=Page(s) Selected
|
|||||||
multiTool.undo=Undo
|
multiTool.undo=Undo
|
||||||
multiTool.redo=Redo
|
multiTool.redo=Redo
|
||||||
|
|
||||||
#decrypt
|
|
||||||
decrypt.passwordPrompt=This file is password-protected. Please enter the password:
|
|
||||||
decrypt.cancelled=Operation cancelled for PDF: {0}
|
|
||||||
decrypt.noPassword=No password provided for encrypted PDF: {0}
|
|
||||||
decrypt.invalidPassword=Please try again with the correct password.
|
|
||||||
decrypt.invalidPasswordHeader=Incorrect password or unsupported encryption for PDF: {0}
|
|
||||||
decrypt.unexpectedError=There was an error processing the file. Please try again.
|
|
||||||
decrypt.serverError=Server error while decrypting: {0}
|
|
||||||
decrypt.success=File decrypted successfully.
|
|
||||||
|
|
||||||
#multiTool-advert
|
#multiTool-advert
|
||||||
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
|
||||||
@@ -1066,7 +1056,6 @@ addPassword.submit=Zašifrovať
|
|||||||
#watermark
|
#watermark
|
||||||
watermark.title=Pridať vodotlač
|
watermark.title=Pridať vodotlač
|
||||||
watermark.header=Pridať vodotlač
|
watermark.header=Pridať vodotlač
|
||||||
watermark.customColor=Vlastná farba textu
|
|
||||||
watermark.selectText.1=Vyberte PDF, do ktorého chcete pridať vodotlač:
|
watermark.selectText.1=Vyberte PDF, do ktorého chcete pridať vodotlač:
|
||||||
watermark.selectText.2=Text vodotlače:
|
watermark.selectText.2=Text vodotlače:
|
||||||
watermark.selectText.3=Veľkosť písma:
|
watermark.selectText.3=Veľkosť písma:
|
||||||
@@ -1291,6 +1280,7 @@ 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
|
||||||
#Validate Signature
|
#Validate Signature
|
||||||
validateSignature.title=Validate PDF Signatures
|
validateSignature.title=Validate PDF Signatures
|
||||||
validateSignature.header=Validate Digital Signatures
|
validateSignature.header=Validate Digital Signatures
|
||||||
|
|||||||
@@ -965,16 +965,6 @@ multiTool.dragDropMessage=Page(s) Selected
|
|||||||
multiTool.undo=Undo
|
multiTool.undo=Undo
|
||||||
multiTool.redo=Redo
|
multiTool.redo=Redo
|
||||||
|
|
||||||
#decrypt
|
|
||||||
decrypt.passwordPrompt=This file is password-protected. Please enter the password:
|
|
||||||
decrypt.cancelled=Operation cancelled for PDF: {0}
|
|
||||||
decrypt.noPassword=No password provided for encrypted PDF: {0}
|
|
||||||
decrypt.invalidPassword=Please try again with the correct password.
|
|
||||||
decrypt.invalidPasswordHeader=Incorrect password or unsupported encryption for PDF: {0}
|
|
||||||
decrypt.unexpectedError=There was an error processing the file. Please try again.
|
|
||||||
decrypt.serverError=Server error while decrypting: {0}
|
|
||||||
decrypt.success=File decrypted successfully.
|
|
||||||
|
|
||||||
#multiTool-advert
|
#multiTool-advert
|
||||||
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
|
||||||
@@ -1066,7 +1056,6 @@ addPassword.submit=Enkriptuj
|
|||||||
#watermark
|
#watermark
|
||||||
watermark.title=Dodaj vodeni žig
|
watermark.title=Dodaj vodeni žig
|
||||||
watermark.header=Dodaj vodeni žig
|
watermark.header=Dodaj vodeni žig
|
||||||
watermark.customColor=Custom Text Color
|
|
||||||
watermark.selectText.1=Izaberite PDF za dodavanje vodenog žiga:
|
watermark.selectText.1=Izaberite PDF za dodavanje vodenog žiga:
|
||||||
watermark.selectText.2=Tekst vodenog žiga:
|
watermark.selectText.2=Tekst vodenog žiga:
|
||||||
watermark.selectText.3=Veličina fonta:
|
watermark.selectText.3=Veličina fonta:
|
||||||
@@ -1291,6 +1280,7 @@ 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
|
||||||
#Validate Signature
|
#Validate Signature
|
||||||
validateSignature.title=Validate PDF Signatures
|
validateSignature.title=Validate PDF Signatures
|
||||||
validateSignature.header=Validate Digital Signatures
|
validateSignature.header=Validate Digital Signatures
|
||||||
|
|||||||
@@ -965,16 +965,6 @@ multiTool.dragDropMessage=Page(s) Selected
|
|||||||
multiTool.undo=Undo
|
multiTool.undo=Undo
|
||||||
multiTool.redo=Redo
|
multiTool.redo=Redo
|
||||||
|
|
||||||
#decrypt
|
|
||||||
decrypt.passwordPrompt=This file is password-protected. Please enter the password:
|
|
||||||
decrypt.cancelled=Operation cancelled for PDF: {0}
|
|
||||||
decrypt.noPassword=No password provided for encrypted PDF: {0}
|
|
||||||
decrypt.invalidPassword=Please try again with the correct password.
|
|
||||||
decrypt.invalidPasswordHeader=Incorrect password or unsupported encryption for PDF: {0}
|
|
||||||
decrypt.unexpectedError=There was an error processing the file. Please try again.
|
|
||||||
decrypt.serverError=Server error while decrypting: {0}
|
|
||||||
decrypt.success=File decrypted successfully.
|
|
||||||
|
|
||||||
#multiTool-advert
|
#multiTool-advert
|
||||||
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
|
||||||
@@ -1066,7 +1056,6 @@ 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:
|
||||||
@@ -1291,6 +1280,7 @@ 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
|
||||||
#Validate Signature
|
#Validate Signature
|
||||||
validateSignature.title=Validate PDF Signatures
|
validateSignature.title=Validate PDF Signatures
|
||||||
validateSignature.header=Validate Digital Signatures
|
validateSignature.header=Validate Digital Signatures
|
||||||
|
|||||||
@@ -965,16 +965,6 @@ multiTool.dragDropMessage=Page(s) Selected
|
|||||||
multiTool.undo=Undo
|
multiTool.undo=Undo
|
||||||
multiTool.redo=Redo
|
multiTool.redo=Redo
|
||||||
|
|
||||||
#decrypt
|
|
||||||
decrypt.passwordPrompt=This file is password-protected. Please enter the password:
|
|
||||||
decrypt.cancelled=Operation cancelled for PDF: {0}
|
|
||||||
decrypt.noPassword=No password provided for encrypted PDF: {0}
|
|
||||||
decrypt.invalidPassword=Please try again with the correct password.
|
|
||||||
decrypt.invalidPasswordHeader=Incorrect password or unsupported encryption for PDF: {0}
|
|
||||||
decrypt.unexpectedError=There was an error processing the file. Please try again.
|
|
||||||
decrypt.serverError=Server error while decrypting: {0}
|
|
||||||
decrypt.success=File decrypted successfully.
|
|
||||||
|
|
||||||
#multiTool-advert
|
#multiTool-advert
|
||||||
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
|
||||||
@@ -1066,7 +1056,6 @@ addPassword.submit=เข้ารหัส
|
|||||||
#watermark
|
#watermark
|
||||||
watermark.title=เพิ่มลายน้ำ
|
watermark.title=เพิ่มลายน้ำ
|
||||||
watermark.header=เพิ่มลายน้ำ
|
watermark.header=เพิ่มลายน้ำ
|
||||||
watermark.customColor=สีข้อความที่กำหนดเอง
|
|
||||||
watermark.selectText.1=เลือก PDF เพื่อเพิ่มลายน้ำ:
|
watermark.selectText.1=เลือก PDF เพื่อเพิ่มลายน้ำ:
|
||||||
watermark.selectText.2=ข้อความลายน้ำ:
|
watermark.selectText.2=ข้อความลายน้ำ:
|
||||||
watermark.selectText.3=ขนาดฟอนต์:
|
watermark.selectText.3=ขนาดฟอนต์:
|
||||||
@@ -1291,6 +1280,7 @@ 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
|
||||||
#Validate Signature
|
#Validate Signature
|
||||||
validateSignature.title=Validate PDF Signatures
|
validateSignature.title=Validate PDF Signatures
|
||||||
validateSignature.header=Validate Digital Signatures
|
validateSignature.header=Validate Digital Signatures
|
||||||
|
|||||||
@@ -965,16 +965,6 @@ multiTool.dragDropMessage=Page(s) Selected
|
|||||||
multiTool.undo=Undo
|
multiTool.undo=Undo
|
||||||
multiTool.redo=Redo
|
multiTool.redo=Redo
|
||||||
|
|
||||||
#decrypt
|
|
||||||
decrypt.passwordPrompt=This file is password-protected. Please enter the password:
|
|
||||||
decrypt.cancelled=Operation cancelled for PDF: {0}
|
|
||||||
decrypt.noPassword=No password provided for encrypted PDF: {0}
|
|
||||||
decrypt.invalidPassword=Please try again with the correct password.
|
|
||||||
decrypt.invalidPasswordHeader=Incorrect password or unsupported encryption for PDF: {0}
|
|
||||||
decrypt.unexpectedError=There was an error processing the file. Please try again.
|
|
||||||
decrypt.serverError=Server error while decrypting: {0}
|
|
||||||
decrypt.success=File decrypted successfully.
|
|
||||||
|
|
||||||
#multiTool-advert
|
#multiTool-advert
|
||||||
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
|
||||||
@@ -1066,7 +1056,6 @@ addPassword.submit=Şifrele
|
|||||||
#watermark
|
#watermark
|
||||||
watermark.title=Filigran Ekle
|
watermark.title=Filigran Ekle
|
||||||
watermark.header=Filigran Ekle
|
watermark.header=Filigran Ekle
|
||||||
watermark.customColor=Özel Metin Rengi
|
|
||||||
watermark.selectText.1=Filigran eklemek için PDF seçin:
|
watermark.selectText.1=Filigran eklemek için PDF seçin:
|
||||||
watermark.selectText.2=Filigran Metni:
|
watermark.selectText.2=Filigran Metni:
|
||||||
watermark.selectText.3=Yazı Boyutu:
|
watermark.selectText.3=Yazı Boyutu:
|
||||||
@@ -1291,6 +1280,7 @@ 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
|
||||||
#Validate Signature
|
#Validate Signature
|
||||||
validateSignature.title=Validate PDF Signatures
|
validateSignature.title=Validate PDF Signatures
|
||||||
validateSignature.header=Validate Digital Signatures
|
validateSignature.header=Validate Digital Signatures
|
||||||
|
|||||||
@@ -965,16 +965,6 @@ multiTool.dragDropMessage=Page(s) Selected
|
|||||||
multiTool.undo=Undo
|
multiTool.undo=Undo
|
||||||
multiTool.redo=Redo
|
multiTool.redo=Redo
|
||||||
|
|
||||||
#decrypt
|
|
||||||
decrypt.passwordPrompt=This file is password-protected. Please enter the password:
|
|
||||||
decrypt.cancelled=Operation cancelled for PDF: {0}
|
|
||||||
decrypt.noPassword=No password provided for encrypted PDF: {0}
|
|
||||||
decrypt.invalidPassword=Please try again with the correct password.
|
|
||||||
decrypt.invalidPasswordHeader=Incorrect password or unsupported encryption for PDF: {0}
|
|
||||||
decrypt.unexpectedError=There was an error processing the file. Please try again.
|
|
||||||
decrypt.serverError=Server error while decrypting: {0}
|
|
||||||
decrypt.success=File decrypted successfully.
|
|
||||||
|
|
||||||
#multiTool-advert
|
#multiTool-advert
|
||||||
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
|
||||||
@@ -1066,7 +1056,6 @@ addPassword.submit=Шифрувати
|
|||||||
#watermark
|
#watermark
|
||||||
watermark.title=Додати водяний знак
|
watermark.title=Додати водяний знак
|
||||||
watermark.header=Додати водяний знак
|
watermark.header=Додати водяний знак
|
||||||
watermark.customColor=Користувацький колір тексту
|
|
||||||
watermark.selectText.1=Виберіть PDF, щоб додати водяний знак:
|
watermark.selectText.1=Виберіть PDF, щоб додати водяний знак:
|
||||||
watermark.selectText.2=Текст водяного знаку:
|
watermark.selectText.2=Текст водяного знаку:
|
||||||
watermark.selectText.3=Розмір шрифту:
|
watermark.selectText.3=Розмір шрифту:
|
||||||
@@ -1291,6 +1280,7 @@ 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
|
||||||
#Validate Signature
|
#Validate Signature
|
||||||
validateSignature.title=Validate PDF Signatures
|
validateSignature.title=Validate PDF Signatures
|
||||||
validateSignature.header=Validate Digital Signatures
|
validateSignature.header=Validate Digital Signatures
|
||||||
|
|||||||
@@ -965,16 +965,6 @@ multiTool.dragDropMessage=Page(s) Selected
|
|||||||
multiTool.undo=Undo
|
multiTool.undo=Undo
|
||||||
multiTool.redo=Redo
|
multiTool.redo=Redo
|
||||||
|
|
||||||
#decrypt
|
|
||||||
decrypt.passwordPrompt=This file is password-protected. Please enter the password:
|
|
||||||
decrypt.cancelled=Operation cancelled for PDF: {0}
|
|
||||||
decrypt.noPassword=No password provided for encrypted PDF: {0}
|
|
||||||
decrypt.invalidPassword=Please try again with the correct password.
|
|
||||||
decrypt.invalidPasswordHeader=Incorrect password or unsupported encryption for PDF: {0}
|
|
||||||
decrypt.unexpectedError=There was an error processing the file. Please try again.
|
|
||||||
decrypt.serverError=Server error while decrypting: {0}
|
|
||||||
decrypt.success=File decrypted successfully.
|
|
||||||
|
|
||||||
#multiTool-advert
|
#multiTool-advert
|
||||||
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
|
||||||
@@ -1066,7 +1056,6 @@ addPassword.submit=Mã hóa
|
|||||||
#watermark
|
#watermark
|
||||||
watermark.title=Thêm hình mờ
|
watermark.title=Thêm hình mờ
|
||||||
watermark.header=Thêm hình mờ
|
watermark.header=Thêm hình mờ
|
||||||
watermark.customColor=Màu văn bản tùy chỉnh
|
|
||||||
watermark.selectText.1=Chọn PDF để thêm hình mờ:
|
watermark.selectText.1=Chọn PDF để thêm hình mờ:
|
||||||
watermark.selectText.2=Văn bản hình mờ:
|
watermark.selectText.2=Văn bản hình mờ:
|
||||||
watermark.selectText.3=Cỡ chữ:
|
watermark.selectText.3=Cỡ chữ:
|
||||||
@@ -1291,6 +1280,7 @@ 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
|
||||||
#Validate Signature
|
#Validate Signature
|
||||||
validateSignature.title=Validate PDF Signatures
|
validateSignature.title=Validate PDF Signatures
|
||||||
validateSignature.header=Validate Digital Signatures
|
validateSignature.header=Validate Digital Signatures
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -965,16 +965,6 @@ multiTool.dragDropMessage=Page(s) Selected
|
|||||||
multiTool.undo=Undo
|
multiTool.undo=Undo
|
||||||
multiTool.redo=Redo
|
multiTool.redo=Redo
|
||||||
|
|
||||||
#decrypt
|
|
||||||
decrypt.passwordPrompt=This file is password-protected. Please enter the password:
|
|
||||||
decrypt.cancelled=Operation cancelled for PDF: {0}
|
|
||||||
decrypt.noPassword=No password provided for encrypted PDF: {0}
|
|
||||||
decrypt.invalidPassword=Please try again with the correct password.
|
|
||||||
decrypt.invalidPasswordHeader=Incorrect password or unsupported encryption for PDF: {0}
|
|
||||||
decrypt.unexpectedError=There was an error processing the file. Please try again.
|
|
||||||
decrypt.serverError=Server error while decrypting: {0}
|
|
||||||
decrypt.success=File decrypted successfully.
|
|
||||||
|
|
||||||
#multiTool-advert
|
#multiTool-advert
|
||||||
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
|
||||||
@@ -1066,7 +1056,6 @@ addPassword.submit=加密
|
|||||||
#watermark
|
#watermark
|
||||||
watermark.title=新增浮水印
|
watermark.title=新增浮水印
|
||||||
watermark.header=新增浮水印
|
watermark.header=新增浮水印
|
||||||
watermark.customColor=自訂文字顏色
|
|
||||||
watermark.selectText.1=選擇要新增浮水印的 PDF:
|
watermark.selectText.1=選擇要新增浮水印的 PDF:
|
||||||
watermark.selectText.2=浮水印文字:
|
watermark.selectText.2=浮水印文字:
|
||||||
watermark.selectText.3=字型大小:
|
watermark.selectText.3=字型大小:
|
||||||
@@ -1291,6 +1280,7 @@ 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
|
||||||
#Validate Signature
|
#Validate Signature
|
||||||
validateSignature.title=Validate PDF Signatures
|
validateSignature.title=Validate PDF Signatures
|
||||||
validateSignature.header=Validate Digital Signatures
|
validateSignature.header=Validate Digital Signatures
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
|
|
||||||
security:
|
security:
|
||||||
enableLogin: false # set to 'true' to enable login
|
enableLogin: false # set to 'true' to enable login
|
||||||
csrfDisabled: false # set to 'true' to disable CSRF protection (not recommended for production)
|
csrfDisabled: true # set to 'true' to disable CSRF protection (not recommended for production)
|
||||||
loginAttemptCount: 5 # lock user account after 5 tries; when using e.g. Fail2Ban you can deactivate the function with -1
|
loginAttemptCount: 5 # lock user account after 5 tries; when using e.g. Fail2Ban you can deactivate the function with -1
|
||||||
loginResetTimeMinutes: 120 # lock account for 2 hours after x attempts
|
loginResetTimeMinutes: 120 # lock account for 2 hours after x attempts
|
||||||
loginMethod: all # Accepts values like 'all' and 'normal'(only Login with Username/Password), 'oauth2'(only Login with OAuth2) or 'saml2'(only Login with SAML2)
|
loginMethod: all # Accepts values like 'all' and 'normal'(only Login with Username/Password), 'oauth2'(only Login with OAuth2) or 'saml2'(only Login with SAML2)
|
||||||
@@ -102,7 +102,6 @@ metrics:
|
|||||||
AutomaticallyGenerated:
|
AutomaticallyGenerated:
|
||||||
key: example
|
key: example
|
||||||
UUID: example
|
UUID: example
|
||||||
appVersion: 0.35.0
|
|
||||||
|
|
||||||
processExecutor:
|
processExecutor:
|
||||||
sessionLimit: # Process executor instances limits
|
sessionLimit: # Process executor instances limits
|
||||||
|
|||||||
@@ -172,13 +172,6 @@
|
|||||||
"moduleLicense": "The Apache Software License, Version 2.0",
|
"moduleLicense": "The Apache Software License, Version 2.0",
|
||||||
"moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
|
"moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"moduleName": "com.google.code.gson:gson",
|
|
||||||
"moduleUrl": "https://github.com/google/gson",
|
|
||||||
"moduleVersion": "2.11.0",
|
|
||||||
"moduleLicense": "Apache-2.0",
|
|
||||||
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"moduleName": "com.google.errorprone:error_prone_annotations",
|
"moduleName": "com.google.errorprone:error_prone_annotations",
|
||||||
"moduleUrl": "https://errorprone.info/error_prone_annotations",
|
"moduleUrl": "https://errorprone.info/error_prone_annotations",
|
||||||
@@ -598,34 +591,6 @@
|
|||||||
"moduleLicense": "GPL2 w/ CPE",
|
"moduleLicense": "GPL2 w/ CPE",
|
||||||
"moduleLicenseUrl": "https://oss.oracle.com/licenses/CDDL+GPL-1.1"
|
"moduleLicenseUrl": "https://oss.oracle.com/licenses/CDDL+GPL-1.1"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"moduleName": "me.friwi:gluegen-rt",
|
|
||||||
"moduleUrl": "http://jogamp.org/gluegen/www/",
|
|
||||||
"moduleVersion": "v2.4.0-rc-20210111",
|
|
||||||
"moduleLicense": "BSD-4 License",
|
|
||||||
"moduleLicenseUrl": "http://www.spdx.org/licenses/BSD-4-Clause"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"moduleName": "me.friwi:jcef-api",
|
|
||||||
"moduleUrl": "https://bitbucket.org/chromiumembedded/java-cef/",
|
|
||||||
"moduleVersion": "jcef-99c2f7a+cef-127.3.1+g6cbb30e+chromium-127.0.6533.100",
|
|
||||||
"moduleLicense": "BSD License",
|
|
||||||
"moduleLicenseUrl": "https://bitbucket.org/chromiumembedded/java-cef/src/master/LICENSE.txt"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"moduleName": "me.friwi:jcefmaven",
|
|
||||||
"moduleUrl": "https://github.com/jcefmaven/jcefmaven/",
|
|
||||||
"moduleVersion": "127.3.1",
|
|
||||||
"moduleLicense": "Apache-2.0 License",
|
|
||||||
"moduleLicenseUrl": "https://github.com/jcefmaven/jcefmaven/blob/master/LICENSE"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"moduleName": "me.friwi:jogl-all",
|
|
||||||
"moduleUrl": "http://jogamp.org/jogl/www/",
|
|
||||||
"moduleVersion": "v2.4.0-rc-20210111",
|
|
||||||
"moduleLicense": "Ubuntu Font Licence 1.0",
|
|
||||||
"moduleLicenseUrl": "http://font.ubuntu.com/ufl/ubuntu-font-licence-1.0.txt"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"moduleName": "net.bytebuddy:byte-buddy",
|
"moduleName": "net.bytebuddy:byte-buddy",
|
||||||
"moduleVersion": "1.15.10",
|
"moduleVersion": "1.15.10",
|
||||||
@@ -666,13 +631,6 @@
|
|||||||
"moduleLicense": "Apache License, Version 2.0",
|
"moduleLicense": "Apache License, Version 2.0",
|
||||||
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
|
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"moduleName": "org.apache.commons:commons-compress",
|
|
||||||
"moduleUrl": "https://commons.apache.org/proper/commons-compress/",
|
|
||||||
"moduleVersion": "1.21",
|
|
||||||
"moduleLicense": "Apache License, Version 2.0",
|
|
||||||
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"moduleName": "org.apache.commons:commons-csv",
|
"moduleName": "org.apache.commons:commons-csv",
|
||||||
"moduleUrl": "https://commons.apache.org/proper/commons-csv/",
|
"moduleUrl": "https://commons.apache.org/proper/commons-csv/",
|
||||||
@@ -1121,30 +1079,6 @@
|
|||||||
"moduleLicense": "Eclipse Public License, Version 2.0",
|
"moduleLicense": "Eclipse Public License, Version 2.0",
|
||||||
"moduleLicenseUrl": "https://github.com/locationtech/jts/blob/master/LICENSE_EPLv2.txt"
|
"moduleLicenseUrl": "https://github.com/locationtech/jts/blob/master/LICENSE_EPLv2.txt"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"moduleName": "org.openjfx:javafx-base",
|
|
||||||
"moduleVersion": "21",
|
|
||||||
"moduleLicense": "GPLv2+CE",
|
|
||||||
"moduleLicenseUrl": "https://openjdk.java.net/legal/gplv2+ce.html"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"moduleName": "org.openjfx:javafx-controls",
|
|
||||||
"moduleVersion": "21",
|
|
||||||
"moduleLicense": "GPLv2+CE",
|
|
||||||
"moduleLicenseUrl": "https://openjdk.java.net/legal/gplv2+ce.html"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"moduleName": "org.openjfx:javafx-graphics",
|
|
||||||
"moduleVersion": "21",
|
|
||||||
"moduleLicense": "GPLv2+CE",
|
|
||||||
"moduleLicenseUrl": "https://openjdk.java.net/legal/gplv2+ce.html"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"moduleName": "org.openjfx:javafx-swing",
|
|
||||||
"moduleVersion": "21",
|
|
||||||
"moduleLicense": "GPLv2+CE",
|
|
||||||
"moduleLicenseUrl": "https://openjdk.java.net/legal/gplv2+ce.html"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"moduleName": "org.opensaml:opensaml-core",
|
"moduleName": "org.opensaml:opensaml-core",
|
||||||
"moduleVersion": "4.3.2",
|
"moduleVersion": "4.3.2",
|
||||||
@@ -1545,7 +1479,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"moduleName": "org.thymeleaf.extras:thymeleaf-extras-springsecurity5",
|
"moduleName": "org.thymeleaf.extras:thymeleaf-extras-springsecurity5",
|
||||||
"moduleVersion": "3.1.3.RELEASE",
|
"moduleVersion": "3.1.2.RELEASE",
|
||||||
"moduleLicense": "The Apache Software License, Version 2.0",
|
"moduleLicense": "The Apache Software License, Version 2.0",
|
||||||
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
|
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
|
||||||
},
|
},
|
||||||
@@ -1557,7 +1491,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"moduleName": "org.thymeleaf:thymeleaf-spring5",
|
"moduleName": "org.thymeleaf:thymeleaf-spring5",
|
||||||
"moduleVersion": "3.1.3.RELEASE",
|
"moduleVersion": "3.1.2.RELEASE",
|
||||||
"moduleLicense": "The Apache Software License, Version 2.0",
|
"moduleLicense": "The Apache Software License, Version 2.0",
|
||||||
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
|
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -65,7 +65,6 @@
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
margin: -20px;
|
margin: -20px;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
box-sizing:content-box;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.feature-group-container.animated-group {
|
.feature-group-container.animated-group {
|
||||||
|
|||||||
Binary file not shown.
@@ -1,219 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-ir" viewBox="0 0 640 480">
|
|
||||||
<defs>
|
|
||||||
<clipPath id="ir-a">
|
|
||||||
<path fill-opacity=".7" d="M-85.3 0h682.7v512H-85.3z"/>
|
|
||||||
</clipPath>
|
|
||||||
</defs>
|
|
||||||
<g fill-rule="evenodd" clip-path="url(#ir-a)" transform="translate(80)scale(.9375)">
|
|
||||||
<path fill="#fff" d="M-192 0h896v512h-896z"/>
|
|
||||||
<path fill="#da0000" d="M-192 343.8h896V512h-896z"/>
|
|
||||||
<g fill="#fff" stroke-width="1pt">
|
|
||||||
<path d="M-21.6 351h49v3.3h-49zm7.3 16.8h3.4v3.3h-3.4zm41.9 0v3.3h-9.8v-3.4zm5.2-16.8h3.4v20h-3.4z"/>
|
|
||||||
<path d="M52.4 367.7v3.4H33.8v-3.4zm-34.6-7.9H21v11.3h-3.3z"/>
|
|
||||||
<path d="M49.6 351H53v20h-3.4zm-8.4 0h3.3v20h-3.3zm-44.8 8v3.4h-18V359zm39.3 0v3.4h-18V359z"/>
|
|
||||||
<path d="M17.8 359.9H21V371h-3.3z"/>
|
|
||||||
<path d="M17.8 359.9H21V371h-3.3z"/>
|
|
||||||
<path d="M17.8 359.9H21V371h-3.3zm-39.3 0h3.3V371h-3.3zm28.8 0h3.4V371H7.3zm-14.3 0h3.4V371H-7z"/>
|
|
||||||
<path d="M9.6 367.7v3.4H-5.5v-3.4zm1-8.7v3.4H1V359z"/>
|
|
||||||
</g>
|
|
||||||
<g fill="#fff" stroke-width="1pt">
|
|
||||||
<path d="M-102.2 351h49v3.3h-49zm7.3 16.8h3.4v3.3H-95zm41.9 0v3.3h-9.8v-3.4zm5.2-16.8h3.4v20h-3.4z"/>
|
|
||||||
<path d="M-28.2 367.7v3.4h-18.6v-3.4zm-34.6-7.9h3.3v11.3h-3.3z"/>
|
|
||||||
<path d="M-31 351h3.4v20H-31zm-8.4 0h3.3v20h-3.3zm-44.8 8v3.4h-18V359zm39.3 0v3.4h-18V359z"/>
|
|
||||||
<path d="M-62.8 359.9h3.3V371h-3.3z"/>
|
|
||||||
<path d="M-62.8 359.9h3.3V371h-3.3z"/>
|
|
||||||
<path d="M-62.8 359.9h3.3V371h-3.3zm-39.3 0h3.3V371h-3.3zm28.8 0h3.3V371h-3.3zm-14.3 0h3.4V371h-3.4z"/>
|
|
||||||
<path d="M-71 367.7v3.4h-15v-3.4zm1-8.7v3.4h-9.6V359z"/>
|
|
||||||
</g>
|
|
||||||
<g fill="#fff" stroke-width="1pt">
|
|
||||||
<path d="M58.3 351h49v3.3h-49zm7.3 16.8H69v3.3h-3.4zm41.9 0v3.3h-9.8v-3.4zm5.3-16.8h3.4v20h-3.4z"/>
|
|
||||||
<path d="M132.3 367.7v3.4h-18.6v-3.4zm-34.6-7.9h3.4v11.3h-3.4z"/>
|
|
||||||
<path d="M129.5 351h3.4v20h-3.4zm-8.4 0h3.4v20H121zm-44.8 8v3.4h-18V359zm39.3 0v3.4h-18V359z"/>
|
|
||||||
<path d="M97.7 359.9h3.4V371h-3.4z"/>
|
|
||||||
<path d="M97.7 359.9h3.4V371h-3.4z"/>
|
|
||||||
<path d="M97.7 359.9h3.4V371h-3.4zm-39.3 0h3.4V371h-3.4zm28.8 0h3.4V371h-3.4zm-14.3 0h3.4V371h-3.4z"/>
|
|
||||||
<path d="M89.6 367.7v3.4H74.4v-3.4zm1-8.7v3.4H81V359z"/>
|
|
||||||
</g>
|
|
||||||
<g fill="#fff" stroke-width="1pt">
|
|
||||||
<path d="M622.7 351h49v3.3h-49zm7.3 16.8h3.4v3.3H630zm41.9 0v3.3H662v-3.4zm5.3-16.8h3.3v20h-3.4z"/>
|
|
||||||
<path d="M696.7 367.7v3.4H678v-3.4zm-34.6-7.9h3.4v11.3H662z"/>
|
|
||||||
<path d="M694 351h3.3v20h-3.4zm-8.5 0h3.4v20h-3.4zm-44.8 8v3.4h-18V359zm39.3 0v3.4h-18V359z"/>
|
|
||||||
<path d="M662 359.9h3.5V371H662z"/>
|
|
||||||
<path d="M662 359.9h3.5V371H662z"/>
|
|
||||||
<path d="M662 359.9h3.5V371H662zm-39.2 0h3.4V371h-3.4zm28.8 0h3.4V371h-3.4zm-14.3 0h3.4V371h-3.4z"/>
|
|
||||||
<path d="M654 367.7v3.4h-15.2v-3.4zm1-8.7v3.4h-9.6V359z"/>
|
|
||||||
</g>
|
|
||||||
<g fill="#fff" stroke-width="1pt">
|
|
||||||
<path d="M138.7 351h49.1v3.3h-49zm7.4 16.8h3.3v3.3h-3.3zm41.8 0v3.3h-9.8v-3.4zm5.3-16.8h3.4v20h-3.4z"/>
|
|
||||||
<path d="M212.8 367.7v3.4h-18.6v-3.4zm-34.7-7.9h3.4v11.3h-3.4z"/>
|
|
||||||
<path d="M210 351h3.4v20H210zm-8.5 0h3.4v20h-3.4zm-44.8 8v3.4h-17.9V359zm39.3 0v3.4h-17.9V359z"/>
|
|
||||||
<path d="M178.1 359.9h3.4V371h-3.4z"/>
|
|
||||||
<path d="M178.1 359.9h3.4V371h-3.4z"/>
|
|
||||||
<path d="M178.1 359.9h3.4V371h-3.4zm-39.3 0h3.4V371h-3.4zm28.8 0h3.4V371h-3.4zm-14.2 0h3.3V371h-3.3z"/>
|
|
||||||
<path d="M170 367.7v3.4h-15.1v-3.4zm1-8.7v3.4h-9.6V359z"/>
|
|
||||||
</g>
|
|
||||||
<g fill="#fff" stroke-width="1pt">
|
|
||||||
<path d="M219.5 351h49v3.3h-49zm7.3 16.8h3.4v3.3h-3.4zm41.9 0v3.3h-9.8v-3.4zM274 351h3.3v20H274z"/>
|
|
||||||
<path d="M293.5 367.7v3.4h-18.6v-3.4zm-34.6-7.9h3.4v11.3h-3.4z"/>
|
|
||||||
<path d="M290.7 351h3.4v20h-3.4zm-8.4 0h3.4v20h-3.4zm-44.8 8v3.4h-18V359zm39.3 0v3.4h-18V359z"/>
|
|
||||||
<path d="M258.9 359.9h3.4V371h-3.4z"/>
|
|
||||||
<path d="M258.9 359.9h3.4V371h-3.4z"/>
|
|
||||||
<path d="M258.9 359.9h3.4V371h-3.4zm-39.3 0h3.3V371h-3.3zm28.8 0h3.4V371h-3.4zm-14.3 0h3.4V371H234z"/>
|
|
||||||
<path d="M250.8 367.7v3.4h-15.2v-3.4zm1-8.7v3.4H242V359z"/>
|
|
||||||
</g>
|
|
||||||
<path fill="#239f40" d="M-192 0h896v168.2h-896z"/>
|
|
||||||
<g fill="#fff" stroke-width="1pt">
|
|
||||||
<path d="M300.7 351h49v3.3h-49zm7.3 16.8h3.4v3.3H308zm41.9 0v3.3H340v-3.4zm5.3-16.8h3.3v20h-3.3z"/>
|
|
||||||
<path d="M374.7 367.7v3.4h-18.6v-3.4zm-34.6-7.9h3.4v11.3H340z"/>
|
|
||||||
<path d="M372 351h3.3v20H372zm-8.5 0h3.4v20h-3.4zm-44.8 8v3.4h-18V359zm39.3 0v3.4h-18V359z"/>
|
|
||||||
<path d="M340 359.9h3.5V371H340z"/>
|
|
||||||
<path d="M340 359.9h3.5V371H340z"/>
|
|
||||||
<path d="M340 359.9h3.5V371H340zm-39.2 0h3.4V371h-3.4zm28.8 0h3.4V371h-3.4zm-14.3 0h3.4V371h-3.4z"/>
|
|
||||||
<path d="M332 367.7v3.4h-15.2v-3.4zm1-8.7v3.4h-9.6V359z"/>
|
|
||||||
</g>
|
|
||||||
<g fill="#fff" stroke-width="1pt">
|
|
||||||
<path d="M381.4 351h49v3.3h-49zm7.3 16.8h3.4v3.3h-3.4zm42 0v3.3h-9.9v-3.4zm5.2-16.8h3.4v20h-3.4z"/>
|
|
||||||
<path d="M455.4 367.7v3.4h-18.6v-3.4zm-34.6-7.9h3.4v11.3h-3.4z"/>
|
|
||||||
<path d="M452.7 351h3.3v20h-3.3zm-8.5 0h3.4v20h-3.4zm-44.8 8v3.4h-17.9V359zm39.3 0v3.4h-17.9V359z"/>
|
|
||||||
<path d="M420.8 359.9h3.4V371h-3.4z"/>
|
|
||||||
<path d="M420.8 359.9h3.4V371h-3.4z"/>
|
|
||||||
<path d="M420.8 359.9h3.4V371h-3.4zm-39.3 0h3.4V371h-3.4zm28.8 0h3.4V371h-3.4zm-14.3 0h3.4V371h-3.3z"/>
|
|
||||||
<path d="M412.7 367.7v3.4h-15.1v-3.4zm1-8.7v3.4H404V359z"/>
|
|
||||||
</g>
|
|
||||||
<g fill="#fff" stroke-width="1pt">
|
|
||||||
<path d="M462.2 351h49v3.3h-49zm7.3 16.8h3.4v3.3h-3.4zm41.9 0v3.3h-9.8v-3.4zm5.2-16.8h3.4v20h-3.4z"/>
|
|
||||||
<path d="M536.2 367.7v3.4h-18.6v-3.4zm-34.7-7.9h3.4v11.3h-3.4z"/>
|
|
||||||
<path d="M533.4 351h3.4v20h-3.4zm-8.4 0h3.3v20H525zm-44.8 8v3.4h-18V359zm39.3 0v3.4h-18V359z"/>
|
|
||||||
<path d="M501.6 359.9h3.3V371h-3.3z"/>
|
|
||||||
<path d="M501.6 359.9h3.3V371h-3.3z"/>
|
|
||||||
<path d="M501.6 359.9h3.3V371h-3.3zm-39.4 0h3.4V371h-3.4zm28.9 0h3.3V371h-3.3zm-14.3 0h3.4V371h-3.4z"/>
|
|
||||||
<path d="M493.4 367.7v3.4h-15.1v-3.4zm1-8.7v3.4h-9.6V359z"/>
|
|
||||||
</g>
|
|
||||||
<g fill="#fff" stroke-width="1pt">
|
|
||||||
<path d="M543.4 351h49v3.3h-49zm7.3 16.8h3.4v3.3h-3.4zm41.9 0v3.3h-9.8v-3.4zm5.2-16.8h3.4v20h-3.4z"/>
|
|
||||||
<path d="M617.4 367.7v3.4h-18.6v-3.4zm-34.6-7.9h3.3v11.3h-3.3z"/>
|
|
||||||
<path d="M614.6 351h3.4v20h-3.4zm-8.4 0h3.3v20h-3.3zm-44.8 8v3.4h-18V359zm39.3 0v3.4h-18V359z"/>
|
|
||||||
<path d="M582.8 359.9h3.3V371h-3.3z"/>
|
|
||||||
<path d="M582.8 359.9h3.3V371h-3.3z"/>
|
|
||||||
<path d="M582.8 359.9h3.3V371h-3.3zm-39.3 0h3.3V371h-3.3zm28.8 0h3.4V371h-3.4zm-14.3 0h3.4V371H558z"/>
|
|
||||||
<path d="M574.6 367.7v3.4h-15.1v-3.4zm1-8.7v3.4H566V359z"/>
|
|
||||||
</g>
|
|
||||||
<g fill="#fff" stroke-width="1pt">
|
|
||||||
<path d="M-183.8 351h49v3.3h-49zm7.3 16.8h3.4v3.3h-3.4zm42 0v3.3h-9.9v-3.4zm5.2-16.8h3.4v20h-3.4z"/>
|
|
||||||
<path d="M-109.8 367.7v3.4h-18.6v-3.4zm-34.6-7.9h3.4v11.3h-3.4z"/>
|
|
||||||
<path d="M-112.5 351h3.3v20h-3.3zm-8.5 0h3.4v20h-3.4zm-44.8 8v3.4h-17.9V359zm39.3 0v3.4h-17.9V359z"/>
|
|
||||||
<path d="M-144.4 359.9h3.4V371h-3.4z"/>
|
|
||||||
<path d="M-144.4 359.9h3.4V371h-3.4z"/>
|
|
||||||
<path d="M-144.4 359.9h3.4V371h-3.4zm-39.3 0h3.4V371h-3.4zm28.8 0h3.4V371h-3.4zm-14.3 0h3.4V371h-3.4z"/>
|
|
||||||
<path d="M-152.5 367.7v3.4h-15.2v-3.4zm1-8.7v3.4h-9.6V359z"/>
|
|
||||||
</g>
|
|
||||||
<g fill="#fff" stroke-width="1pt">
|
|
||||||
<path d="M-21.6 143.4h49v3.4h-49zm7.3 17h3.4v3.2h-3.4zm41.9-.2v3.4h-9.8v-3.4zm5.2-16.8h3.4v20.2h-3.4z"/>
|
|
||||||
<path d="M52.4 160.2v3.4H33.8v-3.4zm-34.6-7.9H21v11.3h-3.3z"/>
|
|
||||||
<path d="M49.6 143.4H53v20.2h-3.4zm-8.4 0h3.3v20.2h-3.3zm-44.8 8v3.4h-18v-3.3zm39.3 0v3.4h-18v-3.3z"/>
|
|
||||||
<path d="M17.8 152.3H21v11.3h-3.3z"/>
|
|
||||||
<path d="M17.8 152.3H21v11.3h-3.3z"/>
|
|
||||||
<path d="M17.8 152.3H21v11.3h-3.3zm-39.3 0h3.3v11.3h-3.3zm28.8 0h3.4v11.3H7.3zm-14.3 0h3.4v11.3H-7z"/>
|
|
||||||
<path d="M9.6 160.2v3.4H-5.5v-3.4zm1-8.7v3.3H1v-3.3z"/>
|
|
||||||
</g>
|
|
||||||
<g fill="#fff" stroke-width="1pt">
|
|
||||||
<path d="M-102.2 143.4h49v3.4h-49zm7.3 17h3.4v3.2H-95zm41.9-.2v3.4h-9.8v-3.4zm5.2-16.8h3.4v20.2h-3.4z"/>
|
|
||||||
<path d="M-28.2 160.2v3.4h-18.6v-3.4zm-34.6-7.9h3.3v11.3h-3.3z"/>
|
|
||||||
<path d="M-31 143.4h3.4v20.2H-31zm-8.4 0h3.3v20.2h-3.3zm-44.8 8v3.4h-18v-3.3zm39.3 0v3.4h-18v-3.3z"/>
|
|
||||||
<path d="M-62.8 152.3h3.3v11.3h-3.3z"/>
|
|
||||||
<path d="M-62.8 152.3h3.3v11.3h-3.3z"/>
|
|
||||||
<path d="M-62.8 152.3h3.3v11.3h-3.3zm-39.3 0h3.3v11.3h-3.3zm28.8 0h3.3v11.3h-3.3zm-14.3 0h3.4v11.3h-3.4z"/>
|
|
||||||
<path d="M-71 160.2v3.4h-15v-3.4zm1-8.7v3.3h-9.6v-3.3z"/>
|
|
||||||
</g>
|
|
||||||
<g fill="#fff" stroke-width="1pt">
|
|
||||||
<path d="M58.3 143.4h49v3.4h-49zm7.3 17H69v3.2h-3.4zm41.9-.2v3.4h-9.8v-3.4zm5.3-16.8h3.4v20.2h-3.4z"/>
|
|
||||||
<path d="M132.3 160.2v3.4h-18.6v-3.4zm-34.6-7.9h3.4v11.3h-3.4z"/>
|
|
||||||
<path d="M129.5 143.4h3.4v20.2h-3.4zm-8.4 0h3.4v20.2H121zm-44.8 8v3.4h-18v-3.3zm39.3 0v3.4h-18v-3.3z"/>
|
|
||||||
<path d="M97.7 152.3h3.4v11.3h-3.4z"/>
|
|
||||||
<path d="M97.7 152.3h3.4v11.3h-3.4z"/>
|
|
||||||
<path d="M97.7 152.3h3.4v11.3h-3.4zm-39.3 0h3.4v11.3h-3.4zm28.8 0h3.4v11.3h-3.4zm-14.3 0h3.4v11.3h-3.4z"/>
|
|
||||||
<path d="M89.6 160.2v3.4H74.4v-3.4zm1-8.7v3.3H81v-3.3z"/>
|
|
||||||
</g>
|
|
||||||
<g fill="#fff" stroke-width="1pt">
|
|
||||||
<path d="M622.7 143.4h49v3.4h-49zm7.3 17h3.4v3.2H630zm41.9-.2v3.4H662v-3.4zm5.3-16.8h3.3v20.2h-3.4z"/>
|
|
||||||
<path d="M696.7 160.2v3.4H678v-3.4zm-34.6-7.9h3.4v11.3H662z"/>
|
|
||||||
<path d="M694 143.4h3.3v20.2h-3.4zm-8.5 0h3.4v20.2h-3.4zm-44.8 8v3.4h-18v-3.3zm39.3 0v3.4h-18v-3.3z"/>
|
|
||||||
<path d="M662 152.3h3.5v11.3H662z"/>
|
|
||||||
<path d="M662 152.3h3.5v11.3H662z"/>
|
|
||||||
<path d="M662 152.3h3.5v11.3H662zm-39.2 0h3.4v11.3h-3.4zm28.8 0h3.4v11.3h-3.4zm-14.3 0h3.4v11.3h-3.4z"/>
|
|
||||||
<path d="M654 160.2v3.4h-15.2v-3.4zm1-8.7v3.3h-9.6v-3.3z"/>
|
|
||||||
</g>
|
|
||||||
<g fill="#fff" stroke-width="1pt">
|
|
||||||
<path d="M138.7 143.4h49.1v3.4h-49zm7.4 17h3.3v3.2h-3.3zm41.8-.2v3.4h-9.8v-3.4zm5.3-16.8h3.4v20.2h-3.4z"/>
|
|
||||||
<path d="M212.8 160.2v3.4h-18.6v-3.4zm-34.7-7.9h3.4v11.3h-3.4z"/>
|
|
||||||
<path d="M210 143.4h3.4v20.2H210zm-8.5 0h3.4v20.2h-3.4zm-44.8 8v3.4h-17.9v-3.3zm39.3 0v3.4h-17.9v-3.3z"/>
|
|
||||||
<path d="M178.1 152.3h3.4v11.3h-3.4z"/>
|
|
||||||
<path d="M178.1 152.3h3.4v11.3h-3.4z"/>
|
|
||||||
<path d="M178.1 152.3h3.4v11.3h-3.4zm-39.3 0h3.4v11.3h-3.4zm28.8 0h3.4v11.3h-3.4zm-14.2 0h3.3v11.3h-3.3z"/>
|
|
||||||
<path d="M170 160.2v3.4h-15.1v-3.4zm1-8.7v3.3h-9.6v-3.3z"/>
|
|
||||||
</g>
|
|
||||||
<g fill="#fff" stroke-width="1pt">
|
|
||||||
<path d="M219.5 143.4h49v3.4h-49zm7.3 17h3.4v3.2h-3.4zm41.9-.2v3.4h-9.8v-3.4zm5.3-16.8h3.3v20.2H274z"/>
|
|
||||||
<path d="M293.5 160.2v3.4h-18.6v-3.4zm-34.6-7.9h3.4v11.3h-3.4z"/>
|
|
||||||
<path d="M290.7 143.4h3.4v20.2h-3.4zm-8.4 0h3.4v20.2h-3.4zm-44.8 8v3.4h-18v-3.3zm39.3 0v3.4h-18v-3.3z"/>
|
|
||||||
<path d="M258.9 152.3h3.4v11.3h-3.4z"/>
|
|
||||||
<path d="M258.9 152.3h3.4v11.3h-3.4z"/>
|
|
||||||
<path d="M258.9 152.3h3.4v11.3h-3.4zm-39.3 0h3.3v11.3h-3.3zm28.8 0h3.4v11.3h-3.4zm-14.3 0h3.4v11.3H234z"/>
|
|
||||||
<path d="M250.8 160.2v3.4h-15.2v-3.4zm1-8.7v3.3H242v-3.3z"/>
|
|
||||||
</g>
|
|
||||||
<g fill="#fff" stroke-width="1pt">
|
|
||||||
<path d="M300.7 143.4h49v3.4h-49zm7.3 17h3.4v3.2H308zm41.9-.2v3.4H340v-3.4zm5.3-16.8h3.3v20.2h-3.3z"/>
|
|
||||||
<path d="M374.7 160.2v3.4h-18.6v-3.4zm-34.6-7.9h3.4v11.3H340z"/>
|
|
||||||
<path d="M372 143.4h3.3v20.2H372zm-8.5 0h3.4v20.2h-3.4zm-44.8 8v3.4h-18v-3.3zm39.3 0v3.4h-18v-3.3z"/>
|
|
||||||
<path d="M340 152.3h3.5v11.3H340z"/>
|
|
||||||
<path d="M340 152.3h3.5v11.3H340z"/>
|
|
||||||
<path d="M340 152.3h3.5v11.3H340zm-39.2 0h3.4v11.3h-3.4zm28.8 0h3.4v11.3h-3.4zm-14.3 0h3.4v11.3h-3.4z"/>
|
|
||||||
<path d="M332 160.2v3.4h-15.2v-3.4zm1-8.7v3.3h-9.6v-3.3z"/>
|
|
||||||
</g>
|
|
||||||
<g fill="#fff" stroke-width="1pt">
|
|
||||||
<path d="M381.4 143.4h49v3.4h-49zm7.3 17h3.4v3.2h-3.4zm42-.2v3.4h-9.9v-3.4zm5.2-16.8h3.4v20.2h-3.4z"/>
|
|
||||||
<path d="M455.4 160.2v3.4h-18.6v-3.4zm-34.6-7.9h3.4v11.3h-3.4z"/>
|
|
||||||
<path d="M452.7 143.4h3.3v20.2h-3.3zm-8.5 0h3.4v20.2h-3.4zm-44.8 8v3.4h-17.9v-3.3zm39.3 0v3.4h-17.9v-3.3z"/>
|
|
||||||
<path d="M420.8 152.3h3.4v11.3h-3.4z"/>
|
|
||||||
<path d="M420.8 152.3h3.4v11.3h-3.4z"/>
|
|
||||||
<path d="M420.8 152.3h3.4v11.3h-3.4zm-39.3 0h3.4v11.3h-3.4zm28.8 0h3.4v11.3h-3.4zm-14.3 0h3.4v11.3h-3.3z"/>
|
|
||||||
<path d="M412.7 160.2v3.4h-15.1v-3.4zm1-8.7v3.3H404v-3.3z"/>
|
|
||||||
</g>
|
|
||||||
<g fill="#fff" stroke-width="1pt">
|
|
||||||
<path d="M462.2 143.4h49v3.4h-49zm7.3 17h3.4v3.2h-3.4zm41.9-.2v3.4h-9.8v-3.4zm5.2-16.8h3.4v20.2h-3.4z"/>
|
|
||||||
<path d="M536.2 160.2v3.4h-18.6v-3.4zm-34.7-7.9h3.4v11.3h-3.4z"/>
|
|
||||||
<path d="M533.4 143.4h3.4v20.2h-3.4zm-8.4 0h3.3v20.2H525zm-44.8 8v3.4h-18v-3.3zm39.3 0v3.4h-18v-3.3z"/>
|
|
||||||
<path d="M501.6 152.3h3.3v11.3h-3.3z"/>
|
|
||||||
<path d="M501.6 152.3h3.3v11.3h-3.3z"/>
|
|
||||||
<path d="M501.6 152.3h3.3v11.3h-3.3zm-39.4 0h3.4v11.3h-3.4zm28.9 0h3.3v11.3h-3.3zm-14.3 0h3.4v11.3h-3.4z"/>
|
|
||||||
<path d="M493.4 160.2v3.4h-15.1v-3.4zm1-8.7v3.3h-9.6v-3.3z"/>
|
|
||||||
</g>
|
|
||||||
<g fill="#fff" stroke-width="1pt">
|
|
||||||
<path d="M543.4 143.4h49v3.4h-49zm7.3 17h3.4v3.2h-3.4zm41.9-.2v3.4h-9.8v-3.4zm5.2-16.8h3.4v20.2h-3.4z"/>
|
|
||||||
<path d="M617.4 160.2v3.4h-18.6v-3.4zm-34.6-7.9h3.3v11.3h-3.3z"/>
|
|
||||||
<path d="M614.6 143.4h3.4v20.2h-3.4zm-8.4 0h3.3v20.2h-3.3zm-44.8 8v3.4h-18v-3.3zm39.3 0v3.4h-18v-3.3z"/>
|
|
||||||
<path d="M582.8 152.3h3.3v11.3h-3.3z"/>
|
|
||||||
<path d="M582.8 152.3h3.3v11.3h-3.3z"/>
|
|
||||||
<path d="M582.8 152.3h3.3v11.3h-3.3zm-39.3 0h3.3v11.3h-3.3zm28.8 0h3.4v11.3h-3.4zm-14.3 0h3.4v11.3H558z"/>
|
|
||||||
<path d="M574.6 160.2v3.4h-15.1v-3.4zm1-8.7v3.3H566v-3.3z"/>
|
|
||||||
</g>
|
|
||||||
<g fill="#fff" stroke-width="1pt">
|
|
||||||
<path d="M-183.8 143.4h49v3.4h-49zm7.3 17h3.4v3.2h-3.4zm42-.2v3.4h-9.9v-3.4zm5.2-16.8h3.4v20.2h-3.4z"/>
|
|
||||||
<path d="M-109.8 160.2v3.4h-18.6v-3.4zm-34.6-7.9h3.4v11.3h-3.4z"/>
|
|
||||||
<path d="M-112.5 143.4h3.3v20.2h-3.3zm-8.5 0h3.4v20.2h-3.4zm-44.8 8v3.4h-17.9v-3.3zm39.3 0v3.4h-17.9v-3.3z"/>
|
|
||||||
<path d="M-144.4 152.3h3.4v11.3h-3.4z"/>
|
|
||||||
<path d="M-144.4 152.3h3.4v11.3h-3.4z"/>
|
|
||||||
<path d="M-144.4 152.3h3.4v11.3h-3.4zm-39.3 0h3.4v11.3h-3.4zm28.8 0h3.4v11.3h-3.4zm-14.3 0h3.4v11.3h-3.4z"/>
|
|
||||||
<path d="M-152.5 160.2v3.4h-15.2v-3.4zm1-8.7v3.3h-9.6v-3.3z"/>
|
|
||||||
</g>
|
|
||||||
<path fill="#d90000" d="M-68.8 339.5h6V350h-6zm160.5 0h6V350h-6zm-283.7 0h6V350h-6zm81.5 0h6V350h-6zm80.9 0h6V350h-6zm40 0h6V350h-6zm40.9 0h6V350h-6zm80.4 0h6V350h-6zm203 0h6.1V350h-6zm-162.1 0h6V350h-6zm40 0h6V350h-6zm40.5 0h6V350h-6zm40.4 0h6V350h-6zm323.2 0h6V350h-6zm-242.7 0h6V350h-6zm40.8 0h6V350h-6zm41.3 0h6V350h-6zm38.8 0h6V350h-6zm41.3 0h6V350h-6zm40.4 0h6V350h-6zm119.7 0h6V350h-6zm-38.8 0h6V350h-6zm-808.9 0h6V350h-6z"/>
|
|
||||||
<path fill="#239e3f" d="M-68.8 162.6h6v10.5h-6zm160.5 0h6v10.5h-6zm-283.7 0h6v10.5h-6zm81.5 0h6v10.5h-6zm80.9 0h6v10.5h-6zm40 0h6v10.5h-6zm40.9 0h6v10.5h-6zm80.4 0h6v10.5h-6zm203 0h6.1v10.5h-6zm-162.1 0h6v10.5h-6zm40 0h6v10.5h-6zm40.5 0h6v10.5h-6zm40.4 0h6v10.5h-6zm323.2 0h6v10.5h-6zm-242.7 0h6v10.5h-6zm40.8 0h6v10.5h-6zm41.3 0h6v10.5h-6zm38.8 0h6v10.5h-6zm41.3 0h6v10.5h-6zm40.4 0h6v10.5h-6zm119.7 0h6v10.5h-6zm-38.8 0h6v10.5h-6zm-808.9 0h6v10.5h-6z"/>
|
|
||||||
<g fill="#da0000">
|
|
||||||
<path d="M279.8 197.5c8.4 10.4 34.5 67.6-15.7 105.2-23.7 17.8-9 18.6-8.3 21.6 38-20.1 50.3-47.5 50-72-.2-24.4-13.2-46-26-54.8"/>
|
|
||||||
<path d="M284.8 194.8a73.3 73.3 0 0 1 15.7 112.4c27.2-6 62-86.4-15.7-112.4m-57.6 0a73.3 73.3 0 0 0-15.6 112.4c-27.3-6-62-86.4 15.6-112.4"/>
|
|
||||||
<path d="M232.2 197.5c-8.4 10.4-34.5 67.6 15.7 105.2 23.6 17.8 9 18.6 8.3 21.6-38-20.1-50.3-47.5-50-72 .2-24.4 13.2-46 26-54.8"/>
|
|
||||||
<path d="M304.2 319.1c-14.9.2-33.6-2-47.5-9.3 2.3 4.5 4.2 7.3 6.5 11.7 13.2 1.3 31.5 2.8 41-2.4m-95 0c14.9.2 33.6-2 47.5-9.3-2.3 4.5-4.2 7.3-6.5 11.7-13.2 1.3-31.5 2.8-41-2.4m27.3-138.7c3 8 10.9 9.2 19.3 4.5 6.2 3.6 15.7 3.9 19-4.1 2.5 19.8-18.3 15-19 11.2-7.8 7.5-22.2 3.2-19.3-11.6"/>
|
|
||||||
<path d="m256.4 331.6 7.8-9 1.1-120.1-9.3-8.2-9.3 7.8 1.9 121z"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 15 KiB |
@@ -1,131 +0,0 @@
|
|||||||
export class DecryptFile {
|
|
||||||
async decryptFile(file, requiresPassword) {
|
|
||||||
try {
|
|
||||||
async function getCsrfToken() {
|
|
||||||
const cookieValue = document.cookie
|
|
||||||
.split('; ')
|
|
||||||
.find((row) => row.startsWith('XSRF-TOKEN='))
|
|
||||||
?.split('=')[1];
|
|
||||||
|
|
||||||
if (cookieValue) {
|
|
||||||
return cookieValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const csrfElement = document.querySelector('input[name="_csrf"]');
|
|
||||||
return csrfElement ? csrfElement.value : null;
|
|
||||||
}
|
|
||||||
const csrfToken = await getCsrfToken();
|
|
||||||
const formData = new FormData();
|
|
||||||
formData.append('fileInput', file);
|
|
||||||
if (requiresPassword) {
|
|
||||||
const password = prompt(`${window.decrypt.passwordPrompt}`);
|
|
||||||
|
|
||||||
if (password === null) {
|
|
||||||
// User cancelled
|
|
||||||
console.error(`Password prompt cancelled for PDF: ${file.name}`);
|
|
||||||
return null; // No file to return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!password) {
|
|
||||||
// No password provided
|
|
||||||
console.error(`No password provided for encrypted PDF: ${file.name}`);
|
|
||||||
this.showErrorBanner(
|
|
||||||
`${window.decrypt.noPassword.replace('{0}', file.name)}`,
|
|
||||||
'',
|
|
||||||
`${window.decrypt.unexpectedError}`
|
|
||||||
);
|
|
||||||
return null; // No file to return
|
|
||||||
}
|
|
||||||
|
|
||||||
formData.append('password', password);
|
|
||||||
}
|
|
||||||
// Send decryption request
|
|
||||||
const response = await fetch('/api/v1/security/remove-password', {
|
|
||||||
method: 'POST',
|
|
||||||
body: formData,
|
|
||||||
headers: csrfToken ? {'X-XSRF-TOKEN': csrfToken} : undefined,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.ok) {
|
|
||||||
const decryptedBlob = await response.blob();
|
|
||||||
this.removeErrorBanner();
|
|
||||||
return new File([decryptedBlob], file.name, {type: 'application/pdf'});
|
|
||||||
} else {
|
|
||||||
const errorText = await response.text();
|
|
||||||
console.error(`${window.decrypt.invalidPassword} ${errorText}`);
|
|
||||||
this.showErrorBanner(
|
|
||||||
`${window.decrypt.invalidPassword}`,
|
|
||||||
errorText,
|
|
||||||
`${window.decrypt.invalidPasswordHeader.replace('{0}', file.name)}`
|
|
||||||
);
|
|
||||||
return null; // No file to return
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
// Handle network or unexpected errors
|
|
||||||
console.error(`Failed to decrypt PDF: ${file.name}`, error);
|
|
||||||
this.showErrorBanner(
|
|
||||||
`${window.decrypt.unexpectedError.replace('{0}', file.name)}`,
|
|
||||||
`${error.message || window.decrypt.unexpectedError}`,
|
|
||||||
error
|
|
||||||
);
|
|
||||||
return null; // No file to return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async checkFileEncrypted(file) {
|
|
||||||
try {
|
|
||||||
if (file.type !== 'application/pdf') {
|
|
||||||
return {isEncrypted: false, requiresPassword: false};
|
|
||||||
}
|
|
||||||
|
|
||||||
pdfjsLib.GlobalWorkerOptions.workerSrc = './pdfjs-legacy/pdf.worker.mjs';
|
|
||||||
const arrayBuffer = await file.arrayBuffer();
|
|
||||||
const arrayBufferForPdfLib = arrayBuffer.slice(0);
|
|
||||||
|
|
||||||
const loadingTask = pdfjsLib.getDocument({
|
|
||||||
data: arrayBuffer,
|
|
||||||
});
|
|
||||||
|
|
||||||
await loadingTask.promise;
|
|
||||||
|
|
||||||
try {
|
|
||||||
//Uses PDFLib.PDFDocument to check if unpassworded but encrypted
|
|
||||||
const pdfDoc = await PDFLib.PDFDocument.load(arrayBufferForPdfLib);
|
|
||||||
return {isEncrypted: false, requiresPassword: false};
|
|
||||||
} catch (error) {
|
|
||||||
if (error.message.includes('Input document to `PDFDocument.load` is encrypted')) {
|
|
||||||
return {isEncrypted: true, requiresPassword: false};
|
|
||||||
}
|
|
||||||
console.error('Error checking encryption:', error);
|
|
||||||
throw new Error('Failed to determine if the file is encrypted.');
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
if (error.name === 'PasswordException') {
|
|
||||||
if (error.code === pdfjsLib.PasswordResponses.NEED_PASSWORD) {
|
|
||||||
return {isEncrypted: true, requiresPassword: true};
|
|
||||||
} else if (error.code === pdfjsLib.PasswordResponses.INCORRECT_PASSWORD) {
|
|
||||||
return {isEncrypted: true, requiresPassword: false};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
console.error('Error checking encryption:', error);
|
|
||||||
throw new Error('Failed to determine if the file is encrypted.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
showErrorBanner(message, stackTrace, error) {
|
|
||||||
const errorContainer = document.getElementById('errorContainer');
|
|
||||||
errorContainer.style.display = 'block'; // Display the banner
|
|
||||||
errorContainer.querySelector('.alert-heading').textContent = error;
|
|
||||||
errorContainer.querySelector('p').textContent = message;
|
|
||||||
document.querySelector('#traceContent').textContent = stackTrace;
|
|
||||||
}
|
|
||||||
|
|
||||||
removeErrorBanner() {
|
|
||||||
const errorContainer = document.getElementById('errorContainer');
|
|
||||||
errorContainer.style.display = 'none'; // Hide the banner
|
|
||||||
errorContainer.querySelector('.alert-heading').textContent = '';
|
|
||||||
errorContainer.querySelector('p').textContent = '';
|
|
||||||
document.querySelector('#traceContent').textContent = '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
document.getElementById('download-pdf').addEventListener('click', async () => {
|
|
||||||
const modifiedPdf = await DraggableUtils.getOverlayedPdfDocument();
|
|
||||||
let decryptedFile = modifiedPdf;
|
|
||||||
let isEncrypted = false;
|
|
||||||
let requiresPassword = false;
|
|
||||||
await this.decryptFile
|
|
||||||
.checkFileEncrypted(decryptedFile)
|
|
||||||
.then((result) => {
|
|
||||||
isEncrypted = result.isEncrypted;
|
|
||||||
requiresPassword = result.requiresPassword;
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
console.error(error);
|
|
||||||
});
|
|
||||||
if (decryptedFile.type === 'application/pdf' && isEncrypted) {
|
|
||||||
decryptedFile = await this.decryptFile.decryptFile(decryptedFile, requiresPassword);
|
|
||||||
if (!decryptedFile) {
|
|
||||||
throw new Error('File decryption failed.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const modifiedPdfBytes = await modifiedPdf.save();
|
|
||||||
const blob = new Blob([modifiedPdfBytes], {type: 'application/pdf'});
|
|
||||||
const link = document.createElement('a');
|
|
||||||
link.href = URL.createObjectURL(blob);
|
|
||||||
link.download = originalFileName + '_signed.pdf';
|
|
||||||
link.click();
|
|
||||||
});
|
|
||||||
@@ -42,7 +42,7 @@
|
|||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
firstErrorOccurred = false;
|
firstErrorOccurred = false;
|
||||||
const url = this.action;
|
const url = this.action;
|
||||||
let files = $('#fileInput-input')[0].files;
|
const files = $('#fileInput-input')[0].files;
|
||||||
const formData = new FormData(this);
|
const formData = new FormData(this);
|
||||||
const submitButton = document.getElementById('submitBtn');
|
const submitButton = document.getElementById('submitBtn');
|
||||||
const showGameBtn = document.getElementById('show-game-btn');
|
const showGameBtn = document.getElementById('show-game-btn');
|
||||||
@@ -71,16 +71,6 @@
|
|||||||
}, 5000);
|
}, 5000);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (!url.includes('remove-password')) {
|
|
||||||
// Check if any PDF files are encrypted and handle decryption if necessary
|
|
||||||
const decryptedFiles = await checkAndDecryptFiles(url, files);
|
|
||||||
files = decryptedFiles;
|
|
||||||
// Append decrypted files to formData
|
|
||||||
decryptedFiles.forEach((file, index) => {
|
|
||||||
formData.set(`fileInput`, file);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
submitButton.textContent = 'Processing...';
|
submitButton.textContent = 'Processing...';
|
||||||
submitButton.disabled = true;
|
submitButton.disabled = true;
|
||||||
|
|
||||||
@@ -143,98 +133,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function checkAndDecryptFiles(url, files) {
|
|
||||||
const decryptedFiles = [];
|
|
||||||
pdfjsLib.GlobalWorkerOptions.workerSrc = './pdfjs-legacy/pdf.worker.mjs';
|
|
||||||
|
|
||||||
// Extract the base URL
|
|
||||||
const baseUrl = new URL(url);
|
|
||||||
let removePasswordUrl = `${baseUrl.origin}`;
|
|
||||||
|
|
||||||
// Check if there's a path before /api/
|
|
||||||
const apiIndex = baseUrl.pathname.indexOf('/api/');
|
|
||||||
if (apiIndex > 0) {
|
|
||||||
removePasswordUrl += baseUrl.pathname.substring(0, apiIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Append the new endpoint
|
|
||||||
removePasswordUrl += '/api/v1/security/remove-password';
|
|
||||||
|
|
||||||
console.log(`Remove password URL: ${removePasswordUrl}`);
|
|
||||||
|
|
||||||
for (const file of files) {
|
|
||||||
console.log(`Processing file: ${file.name}`);
|
|
||||||
if (file.type !== 'application/pdf') {
|
|
||||||
console.log(`Skipping non-PDF file: ${file.name}`);
|
|
||||||
decryptedFiles.push(file);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
const arrayBuffer = await file.arrayBuffer();
|
|
||||||
const loadingTask = pdfjsLib.getDocument({data: arrayBuffer});
|
|
||||||
|
|
||||||
console.log(`Attempting to load PDF: ${file.name}`);
|
|
||||||
const pdf = await loadingTask.promise;
|
|
||||||
console.log(`File is not encrypted: ${file.name}`);
|
|
||||||
decryptedFiles.push(file); // If no error, file is not encrypted
|
|
||||||
} catch (error) {
|
|
||||||
if (error.name === 'PasswordException' && error.code === 1) {
|
|
||||||
console.log(`PDF requires password: ${file.name}`, error);
|
|
||||||
console.log(`Attempting to remove password from PDF: ${file.name} with password.`);
|
|
||||||
const password = prompt(`${window.translations.decrypt.passwordPrompt}`);
|
|
||||||
|
|
||||||
if (!password) {
|
|
||||||
console.error(`No password provided for encrypted PDF: ${file.name}`);
|
|
||||||
showErrorBanner(
|
|
||||||
`${window.translations.decrypt.noPassword.replace('{0}', file.name)}`,
|
|
||||||
`${window.translations.decrypt.unexpectedError}`
|
|
||||||
);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Prepare FormData for the decryption request
|
|
||||||
const formData = new FormData();
|
|
||||||
formData.append('fileInput', file);
|
|
||||||
formData.append('password', password);
|
|
||||||
|
|
||||||
// Use handleSingleDownload to send the request
|
|
||||||
const decryptionResult = await fetch(removePasswordUrl, {method: 'POST', body: formData});
|
|
||||||
|
|
||||||
if (decryptionResult && decryptionResult.blob) {
|
|
||||||
const decryptedBlob = await decryptionResult.blob();
|
|
||||||
const decryptedFile = new File([decryptedBlob], file.name, {type: 'application/pdf'});
|
|
||||||
|
|
||||||
/* // Create a link element to download the file
|
|
||||||
const link = document.createElement('a');
|
|
||||||
link.href = URL.createObjectURL(decryptedBlob);
|
|
||||||
link.download = 'test.pdf';
|
|
||||||
document.body.appendChild(link);
|
|
||||||
link.click();
|
|
||||||
document.body.removeChild(link);
|
|
||||||
*/
|
|
||||||
decryptedFiles.push(decryptedFile);
|
|
||||||
console.log(`Successfully decrypted PDF: ${file.name}`);
|
|
||||||
} else {
|
|
||||||
throw new Error('Decryption failed: No valid response from server');
|
|
||||||
}
|
|
||||||
} catch (decryptError) {
|
|
||||||
console.error(`Failed to decrypt PDF: ${file.name}`, decryptError);
|
|
||||||
showErrorBanner(
|
|
||||||
`${window.translations.invalidPasswordHeader.replace('{0}', file.name)}`,
|
|
||||||
`${window.translations.invalidPassword}`
|
|
||||||
);
|
|
||||||
throw decryptError;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
console.log(`Error loading PDF: ${file.name}`, error);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return decryptedFiles;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleSingleDownload(url, formData, isMulti = false, isZip = false) {
|
async function handleSingleDownload(url, formData, isMulti = false, isZip = false) {
|
||||||
const startTime = performance.now();
|
const startTime = performance.now();
|
||||||
const file = formData.get('fileInput');
|
const file = formData.get('fileInput');
|
||||||
@@ -242,7 +140,7 @@
|
|||||||
let errorMessage = null;
|
let errorMessage = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await window.fetchWithCsrf(url, {method: 'POST', body: formData});
|
const response = await fetch(url, {method: 'POST', body: formData});
|
||||||
const contentType = response.headers.get('content-type');
|
const contentType = response.headers.get('content-type');
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
const DraggableUtils = {
|
const DraggableUtils = {
|
||||||
boxDragContainer: document.getElementById('box-drag-container'),
|
boxDragContainer: document.getElementById("box-drag-container"),
|
||||||
pdfCanvas: document.getElementById('pdf-canvas'),
|
pdfCanvas: document.getElementById("pdf-canvas"),
|
||||||
nextId: 0,
|
nextId: 0,
|
||||||
pdfDoc: null,
|
pdfDoc: null,
|
||||||
pageIndex: 0,
|
pageIndex: 0,
|
||||||
@@ -9,17 +9,19 @@ const DraggableUtils = {
|
|||||||
lastInteracted: null,
|
lastInteracted: null,
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
interact('.draggable-canvas')
|
interact(".draggable-canvas")
|
||||||
.draggable({
|
.draggable({
|
||||||
listeners: {
|
listeners: {
|
||||||
move: (event) => {
|
move: (event) => {
|
||||||
const target = event.target;
|
const target = event.target;
|
||||||
const x = (parseFloat(target.getAttribute('data-bs-x')) || 0) + event.dx;
|
const x = (parseFloat(target.getAttribute("data-bs-x")) || 0)
|
||||||
const y = (parseFloat(target.getAttribute('data-bs-y')) || 0) + event.dy;
|
+ event.dx;
|
||||||
|
const y = (parseFloat(target.getAttribute("data-bs-y")) || 0)
|
||||||
|
+ event.dy;
|
||||||
|
|
||||||
target.style.transform = `translate(${x}px, ${y}px)`;
|
target.style.transform = `translate(${x}px, ${y}px)`;
|
||||||
target.setAttribute('data-bs-x', x);
|
target.setAttribute("data-bs-x", x);
|
||||||
target.setAttribute('data-bs-y', y);
|
target.setAttribute("data-bs-y", y);
|
||||||
|
|
||||||
this.onInteraction(target);
|
this.onInteraction(target);
|
||||||
//update the last interacted element
|
//update the last interacted element
|
||||||
@@ -28,12 +30,12 @@ const DraggableUtils = {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
.resizable({
|
.resizable({
|
||||||
edges: {left: true, right: true, bottom: true, top: true},
|
edges: { left: true, right: true, bottom: true, top: true },
|
||||||
listeners: {
|
listeners: {
|
||||||
move: (event) => {
|
move: (event) => {
|
||||||
var target = event.target;
|
var target = event.target;
|
||||||
var x = parseFloat(target.getAttribute('data-bs-x')) || 0;
|
var x = parseFloat(target.getAttribute("data-bs-x")) || 0;
|
||||||
var y = parseFloat(target.getAttribute('data-bs-y')) || 0;
|
var y = parseFloat(target.getAttribute("data-bs-y")) || 0;
|
||||||
|
|
||||||
// check if control key is pressed
|
// check if control key is pressed
|
||||||
if (event.ctrlKey) {
|
if (event.ctrlKey) {
|
||||||
@@ -42,7 +44,8 @@ const DraggableUtils = {
|
|||||||
let width = event.rect.width;
|
let width = event.rect.width;
|
||||||
let height = event.rect.height;
|
let height = event.rect.height;
|
||||||
|
|
||||||
if (Math.abs(event.deltaRect.width) >= Math.abs(event.deltaRect.height)) {
|
if (Math.abs(event.deltaRect.width) >= Math.abs(
|
||||||
|
event.deltaRect.height)) {
|
||||||
height = width / aspectRatio;
|
height = width / aspectRatio;
|
||||||
} else {
|
} else {
|
||||||
width = height * aspectRatio;
|
width = height * aspectRatio;
|
||||||
@@ -52,18 +55,19 @@ const DraggableUtils = {
|
|||||||
event.rect.height = height;
|
event.rect.height = height;
|
||||||
}
|
}
|
||||||
|
|
||||||
target.style.width = event.rect.width + 'px';
|
target.style.width = event.rect.width + "px";
|
||||||
target.style.height = event.rect.height + 'px';
|
target.style.height = event.rect.height + "px";
|
||||||
|
|
||||||
// translate when resizing from top or left edges
|
// translate when resizing from top or left edges
|
||||||
x += event.deltaRect.left;
|
x += event.deltaRect.left;
|
||||||
y += event.deltaRect.top;
|
y += event.deltaRect.top;
|
||||||
|
|
||||||
target.style.transform = 'translate(' + x + 'px,' + y + 'px)';
|
target.style.transform = "translate(" + x + "px," + y + "px)";
|
||||||
|
|
||||||
target.setAttribute('data-bs-x', x);
|
target.setAttribute("data-bs-x", x);
|
||||||
target.setAttribute('data-bs-y', y);
|
target.setAttribute("data-bs-y", y);
|
||||||
target.textContent = Math.round(event.rect.width) + '\u00D7' + Math.round(event.rect.height);
|
target.textContent = Math.round(event.rect.width) + "\u00D7"
|
||||||
|
+ Math.round(event.rect.height);
|
||||||
|
|
||||||
this.onInteraction(target);
|
this.onInteraction(target);
|
||||||
},
|
},
|
||||||
@@ -71,7 +75,7 @@ const DraggableUtils = {
|
|||||||
|
|
||||||
modifiers: [
|
modifiers: [
|
||||||
interact.modifiers.restrictSize({
|
interact.modifiers.restrictSize({
|
||||||
min: {width: 5, height: 5},
|
min: { width: 5, height: 5 },
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
inertia: true,
|
inertia: true,
|
||||||
@@ -91,8 +95,8 @@ const DraggableUtils = {
|
|||||||
const stepY = target.offsetHeight * 0.05;
|
const stepY = target.offsetHeight * 0.05;
|
||||||
|
|
||||||
// Get the current x and y coordinates
|
// Get the current x and y coordinates
|
||||||
let x = parseFloat(target.getAttribute('data-bs-x')) || 0;
|
let x = (parseFloat(target.getAttribute('data-bs-x')) || 0);
|
||||||
let y = parseFloat(target.getAttribute('data-bs-y')) || 0;
|
let y = (parseFloat(target.getAttribute('data-bs-y')) || 0);
|
||||||
|
|
||||||
// Check which key was pressed and update the coordinates accordingly
|
// Check which key was pressed and update the coordinates accordingly
|
||||||
switch (event.key) {
|
switch (event.key) {
|
||||||
@@ -131,15 +135,15 @@ const DraggableUtils = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
createDraggableCanvas() {
|
createDraggableCanvas() {
|
||||||
const createdCanvas = document.createElement('canvas');
|
const createdCanvas = document.createElement("canvas");
|
||||||
createdCanvas.id = `draggable-canvas-${this.nextId++}`;
|
createdCanvas.id = `draggable-canvas-${this.nextId++}`;
|
||||||
createdCanvas.classList.add('draggable-canvas');
|
createdCanvas.classList.add("draggable-canvas");
|
||||||
|
|
||||||
const x = 0;
|
const x = 0;
|
||||||
const y = 20;
|
const y = 20;
|
||||||
createdCanvas.style.transform = `translate(${x}px, ${y}px)`;
|
createdCanvas.style.transform = `translate(${x}px, ${y}px)`;
|
||||||
createdCanvas.setAttribute('data-bs-x', x);
|
createdCanvas.setAttribute("data-bs-x", x);
|
||||||
createdCanvas.setAttribute('data-bs-y', y);
|
createdCanvas.setAttribute("data-bs-y", y);
|
||||||
|
|
||||||
//Click element in order to enable arrow keys
|
//Click element in order to enable arrow keys
|
||||||
createdCanvas.addEventListener('click', () => {
|
createdCanvas.addEventListener('click', () => {
|
||||||
@@ -182,29 +186,29 @@ const DraggableUtils = {
|
|||||||
newHeight = newHeight * scaleMultiplier;
|
newHeight = newHeight * scaleMultiplier;
|
||||||
}
|
}
|
||||||
|
|
||||||
createdCanvas.style.width = newWidth + 'px';
|
createdCanvas.style.width = newWidth + "px";
|
||||||
createdCanvas.style.height = newHeight + 'px';
|
createdCanvas.style.height = newHeight + "px";
|
||||||
|
|
||||||
var myContext = createdCanvas.getContext('2d');
|
var myContext = createdCanvas.getContext("2d");
|
||||||
myContext.drawImage(myImage, 0, 0);
|
myContext.drawImage(myImage, 0, 0);
|
||||||
resolve(createdCanvas);
|
resolve(createdCanvas);
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
deleteAllDraggableCanvases() {
|
deleteAllDraggableCanvases() {
|
||||||
this.boxDragContainer.querySelectorAll('.draggable-canvas').forEach((el) => el.remove());
|
this.boxDragContainer.querySelectorAll(".draggable-canvas").forEach((el) => el.remove());
|
||||||
},
|
},
|
||||||
async addAllPagesDraggableCanvas(element) {
|
async addAllPagesDraggableCanvas(element) {
|
||||||
if (element) {
|
if (element) {
|
||||||
let currentPage = this.pageIndex;
|
let currentPage = this.pageIndex
|
||||||
if (!this.elementAllPages.includes(element)) {
|
if (!this.elementAllPages.includes(element)) {
|
||||||
this.elementAllPages.push(element);
|
this.elementAllPages.push(element)
|
||||||
element.style.filter = 'sepia(1) hue-rotate(90deg) brightness(1.2)';
|
element.style.filter = 'sepia(1) hue-rotate(90deg) brightness(1.2)';
|
||||||
let newElement = {
|
let newElement = {
|
||||||
element: element,
|
"element": element,
|
||||||
offsetWidth: element.width,
|
"offsetWidth": element.width,
|
||||||
offsetHeight: element.height,
|
"offsetHeight": element.height
|
||||||
};
|
}
|
||||||
|
|
||||||
let pagesMap = this.documentsMap.get(this.pdfDoc);
|
let pagesMap = this.documentsMap.get(this.pdfDoc);
|
||||||
|
|
||||||
@@ -212,20 +216,21 @@ const DraggableUtils = {
|
|||||||
pagesMap = {};
|
pagesMap = {};
|
||||||
this.documentsMap.set(this.pdfDoc, pagesMap);
|
this.documentsMap.set(this.pdfDoc, pagesMap);
|
||||||
}
|
}
|
||||||
let page = this.pageIndex;
|
let page = this.pageIndex
|
||||||
|
|
||||||
for (let pageIndex = 0; pageIndex < this.pdfDoc.numPages; pageIndex++) {
|
for (let pageIndex = 0; pageIndex < this.pdfDoc.numPages; pageIndex++) {
|
||||||
|
|
||||||
if (pagesMap[`${pageIndex}-offsetWidth`]) {
|
if (pagesMap[`${pageIndex}-offsetWidth`]) {
|
||||||
if (!pagesMap[pageIndex].includes(newElement)) {
|
if (!pagesMap[pageIndex].includes(newElement)) {
|
||||||
pagesMap[pageIndex].push(newElement);
|
pagesMap[pageIndex].push(newElement);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
pagesMap[pageIndex] = [];
|
pagesMap[pageIndex] = []
|
||||||
pagesMap[pageIndex].push(newElement);
|
pagesMap[pageIndex].push(newElement)
|
||||||
pagesMap[`${pageIndex}-offsetWidth`] = pagesMap[`${page}-offsetWidth`];
|
pagesMap[`${pageIndex}-offsetWidth`] = pagesMap[`${page}-offsetWidth`];
|
||||||
pagesMap[`${pageIndex}-offsetHeight`] = pagesMap[`${page}-offsetHeight`];
|
pagesMap[`${pageIndex}-offsetHeight`] = pagesMap[`${page}-offsetHeight`];
|
||||||
}
|
}
|
||||||
await this.goToPage(pageIndex);
|
await this.goToPage(pageIndex)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const index = this.elementAllPages.indexOf(element);
|
const index = this.elementAllPages.indexOf(element);
|
||||||
@@ -242,17 +247,17 @@ const DraggableUtils = {
|
|||||||
for (let pageIndex = 0; pageIndex < this.pdfDoc.numPages; pageIndex++) {
|
for (let pageIndex = 0; pageIndex < this.pdfDoc.numPages; pageIndex++) {
|
||||||
if (pagesMap[`${pageIndex}-offsetWidth`] && pageIndex != currentPage) {
|
if (pagesMap[`${pageIndex}-offsetWidth`] && pageIndex != currentPage) {
|
||||||
const pageElements = pagesMap[pageIndex];
|
const pageElements = pagesMap[pageIndex];
|
||||||
pageElements.forEach((elementPage) => {
|
pageElements.forEach(elementPage => {
|
||||||
const elementIndex = pageElements.findIndex((elementPage) => elementPage['element'].id === element.id);
|
const elementIndex = pageElements.findIndex(elementPage => elementPage['element'].id === element.id);
|
||||||
if (elementIndex !== -1) {
|
if (elementIndex !== -1) {
|
||||||
pageElements.splice(elementIndex, 1);
|
pageElements.splice(elementIndex, 1);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
await this.goToPage(pageIndex);
|
await this.goToPage(pageIndex)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await this.goToPage(currentPage);
|
await this.goToPage(currentPage)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
deleteDraggableCanvas(element) {
|
deleteDraggableCanvas(element) {
|
||||||
@@ -266,7 +271,7 @@ const DraggableUtils = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
getLastInteracted() {
|
getLastInteracted() {
|
||||||
return this.boxDragContainer.querySelector('.draggable-canvas:last-of-type');
|
return this.boxDragContainer.querySelector(".draggable-canvas:last-of-type");
|
||||||
},
|
},
|
||||||
|
|
||||||
storePageContents() {
|
storePageContents() {
|
||||||
@@ -275,7 +280,7 @@ const DraggableUtils = {
|
|||||||
pagesMap = {};
|
pagesMap = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
const elements = [...this.boxDragContainer.querySelectorAll('.draggable-canvas')];
|
const elements = [...this.boxDragContainer.querySelectorAll(".draggable-canvas")];
|
||||||
const draggablesData = elements.map((el) => {
|
const draggablesData = elements.map((el) => {
|
||||||
return {
|
return {
|
||||||
element: el,
|
element: el,
|
||||||
@@ -286,8 +291,8 @@ const DraggableUtils = {
|
|||||||
elements.forEach((el) => this.boxDragContainer.removeChild(el));
|
elements.forEach((el) => this.boxDragContainer.removeChild(el));
|
||||||
|
|
||||||
pagesMap[this.pageIndex] = draggablesData;
|
pagesMap[this.pageIndex] = draggablesData;
|
||||||
pagesMap[this.pageIndex + '-offsetWidth'] = this.pdfCanvas.offsetWidth;
|
pagesMap[this.pageIndex + "-offsetWidth"] = this.pdfCanvas.offsetWidth;
|
||||||
pagesMap[this.pageIndex + '-offsetHeight'] = this.pdfCanvas.offsetHeight;
|
pagesMap[this.pageIndex + "-offsetHeight"] = this.pdfCanvas.offsetHeight;
|
||||||
|
|
||||||
this.documentsMap.set(this.pdfDoc, pagesMap);
|
this.documentsMap.set(this.pdfDoc, pagesMap);
|
||||||
},
|
},
|
||||||
@@ -324,8 +329,8 @@ const DraggableUtils = {
|
|||||||
|
|
||||||
// render the page onto the canvas
|
// render the page onto the canvas
|
||||||
var renderContext = {
|
var renderContext = {
|
||||||
canvasContext: this.pdfCanvas.getContext('2d'),
|
canvasContext: this.pdfCanvas.getContext("2d"),
|
||||||
viewport: page.getViewport({scale: 1}),
|
viewport: page.getViewport({ scale: 1 }),
|
||||||
};
|
};
|
||||||
await page.render(renderContext).promise;
|
await page.render(renderContext).promise;
|
||||||
|
|
||||||
@@ -353,7 +358,7 @@ const DraggableUtils = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
parseTransform(element) {},
|
parseTransform(element) { },
|
||||||
async getOverlayedPdfDocument() {
|
async getOverlayedPdfDocument() {
|
||||||
const pdfBytes = await this.pdfDoc.getData();
|
const pdfBytes = await this.pdfDoc.getData();
|
||||||
const pdfDocModified = await PDFLib.PDFDocument.load(pdfBytes, {
|
const pdfDocModified = await PDFLib.PDFDocument.load(pdfBytes, {
|
||||||
@@ -364,7 +369,7 @@ const DraggableUtils = {
|
|||||||
const pagesMap = this.documentsMap.get(this.pdfDoc);
|
const pagesMap = this.documentsMap.get(this.pdfDoc);
|
||||||
|
|
||||||
for (let pageIdx in pagesMap) {
|
for (let pageIdx in pagesMap) {
|
||||||
if (pageIdx.includes('offset')) {
|
if (pageIdx.includes("offset")) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
console.log(typeof pageIdx);
|
console.log(typeof pageIdx);
|
||||||
@@ -372,8 +377,9 @@ const DraggableUtils = {
|
|||||||
const page = pdfDocModified.getPage(parseInt(pageIdx));
|
const page = pdfDocModified.getPage(parseInt(pageIdx));
|
||||||
let draggablesData = pagesMap[pageIdx];
|
let draggablesData = pagesMap[pageIdx];
|
||||||
|
|
||||||
const offsetWidth = pagesMap[pageIdx + '-offsetWidth'];
|
const offsetWidth = pagesMap[pageIdx + "-offsetWidth"];
|
||||||
const offsetHeight = pagesMap[pageIdx + '-offsetHeight'];
|
const offsetHeight = pagesMap[pageIdx + "-offsetHeight"];
|
||||||
|
|
||||||
|
|
||||||
for (const draggableData of draggablesData) {
|
for (const draggableData of draggablesData) {
|
||||||
// embed the draggable canvas
|
// embed the draggable canvas
|
||||||
@@ -383,8 +389,8 @@ const DraggableUtils = {
|
|||||||
const pdfImageObject = await pdfDocModified.embedPng(draggableImgBytes);
|
const pdfImageObject = await pdfDocModified.embedPng(draggableImgBytes);
|
||||||
|
|
||||||
// calculate the position in the pdf document
|
// calculate the position in the pdf document
|
||||||
const tansform = draggableElement.style.transform.replace(/[^.,-\d]/g, '');
|
const tansform = draggableElement.style.transform.replace(/[^.,-\d]/g, "");
|
||||||
const transformComponents = tansform.split(',');
|
const transformComponents = tansform.split(",");
|
||||||
const draggablePositionPixels = {
|
const draggablePositionPixels = {
|
||||||
x: parseFloat(transformComponents[0]),
|
x: parseFloat(transformComponents[0]),
|
||||||
y: parseFloat(transformComponents[1]),
|
y: parseFloat(transformComponents[1]),
|
||||||
@@ -423,8 +429,9 @@ const DraggableUtils = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
//Defining the image if the page has a 0-degree angle
|
//Defining the image if the page has a 0-degree angle
|
||||||
let x = draggablePositionPdf.x;
|
let x = draggablePositionPdf.x
|
||||||
let y = heightAdjusted - draggablePositionPdf.y - draggablePositionPdf.height;
|
let y = heightAdjusted - draggablePositionPdf.y - draggablePositionPdf.height
|
||||||
|
|
||||||
|
|
||||||
//Defining the image position if it is at other angles
|
//Defining the image position if it is at other angles
|
||||||
if (normalizedAngle === 90) {
|
if (normalizedAngle === 90) {
|
||||||
@@ -444,7 +451,7 @@ const DraggableUtils = {
|
|||||||
y: y,
|
y: y,
|
||||||
width: draggablePositionPdf.width,
|
width: draggablePositionPdf.width,
|
||||||
height: draggablePositionPdf.height,
|
height: draggablePositionPdf.height,
|
||||||
rotate: rotation,
|
rotate: rotation
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -453,6 +460,6 @@ const DraggableUtils = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
DraggableUtils.init();
|
DraggableUtils.init();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ window.fetchWithCsrf = async function(url, options = {}) {
|
|||||||
if (cookieValue) {
|
if (cookieValue) {
|
||||||
return cookieValue;
|
return cookieValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const csrfElement = document.querySelector('input[name="_csrf"]');
|
const csrfElement = document.querySelector('input[name="_csrf"]');
|
||||||
return csrfElement ? csrfElement.value : null;
|
return csrfElement ? csrfElement.value : null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,20 @@
|
|||||||
import FileIconFactory from './file-icon-factory.js';
|
import FileIconFactory from "./file-icon-factory.js";
|
||||||
import FileUtils from './file-utils.js';
|
import FileUtils from "./file-utils.js";
|
||||||
import UUID from './uuid.js';
|
import UUID from './uuid.js';
|
||||||
import {DecryptFile} from './DecryptFiles.js';
|
|
||||||
let isScriptExecuted = false;
|
let isScriptExecuted = false;
|
||||||
if (!isScriptExecuted) {
|
if (!isScriptExecuted) {
|
||||||
isScriptExecuted = true;
|
isScriptExecuted = true;
|
||||||
document.addEventListener('DOMContentLoaded', function () {
|
document.addEventListener("DOMContentLoaded", function () {
|
||||||
document.querySelectorAll('.custom-file-chooser').forEach(setupFileInput);
|
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');
|
const inputContainerId = chooser.getAttribute('data-bs-element-container-id');
|
||||||
|
|
||||||
let inputContainer = document.getElementById(inputContainerId);
|
let inputContainer = document.getElementById(inputContainerId);
|
||||||
@@ -25,7 +26,7 @@ function setupFileInput(chooser) {
|
|||||||
inputContainer.addEventListener('click', (e) => {
|
inputContainer.addEventListener('click', (e) => {
|
||||||
let inputBtn = document.getElementById(elementId);
|
let inputBtn = document.getElementById(elementId);
|
||||||
inputBtn.click();
|
inputBtn.click();
|
||||||
});
|
})
|
||||||
|
|
||||||
const dragenterListener = function () {
|
const dragenterListener = function () {
|
||||||
dragCounter++;
|
dragCounter++;
|
||||||
@@ -62,7 +63,7 @@ function setupFileInput(chooser) {
|
|||||||
const files = dt.files;
|
const files = dt.files;
|
||||||
|
|
||||||
const fileInput = document.getElementById(elementId);
|
const fileInput = document.getElementById(elementId);
|
||||||
if (fileInput?.hasAttribute('multiple')) {
|
if (fileInput?.hasAttribute("multiple")) {
|
||||||
pushFileListTo(files, allFiles);
|
pushFileListTo(files, allFiles);
|
||||||
} else if (fileInput) {
|
} else if (fileInput) {
|
||||||
allFiles = [files[0]];
|
allFiles = [files[0]];
|
||||||
@@ -77,7 +78,7 @@ function setupFileInput(chooser) {
|
|||||||
|
|
||||||
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) {
|
function pushFileListTo(fileList, container) {
|
||||||
@@ -86,7 +87,7 @@ function setupFileInput(chooser) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
['dragenter', 'dragover', 'dragleave', 'drop'].forEach((eventName) => {
|
["dragenter", "dragover", "dragleave", "drop"].forEach((eventName) => {
|
||||||
document.body.addEventListener(eventName, preventDefaults, false);
|
document.body.addEventListener(eventName, preventDefaults, false);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -95,50 +96,37 @@ function setupFileInput(chooser) {
|
|||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
}
|
}
|
||||||
|
|
||||||
document.body.addEventListener('dragenter', dragenterListener);
|
document.body.addEventListener("dragenter", dragenterListener);
|
||||||
document.body.addEventListener('dragleave', dragleaveListener);
|
document.body.addEventListener("dragleave", dragleaveListener);
|
||||||
document.body.addEventListener('drop', dropListener);
|
document.body.addEventListener("drop", dropListener);
|
||||||
|
|
||||||
$('#' + elementId).on('change', async function (e) {
|
$("#" + elementId).on("change", function (e) {
|
||||||
let element = e.target;
|
let element = e.target;
|
||||||
const isDragAndDrop = e.detail?.source == 'drag-drop';
|
const isDragAndDrop = e.detail?.source == 'drag-drop';
|
||||||
|
|
||||||
if (element instanceof HTMLInputElement && element.hasAttribute('multiple')) {
|
if (element instanceof HTMLInputElement && element.hasAttribute("multiple")) {
|
||||||
allFiles = isDragAndDrop ? allFiles : [...allFiles, ...element.files];
|
allFiles = isDragAndDrop ? allFiles : [... allFiles, ... element.files];
|
||||||
} else {
|
} else {
|
||||||
allFiles = Array.from(isDragAndDrop ? allFiles : [element.files[0]]);
|
allFiles = Array.from(isDragAndDrop ? allFiles : [element.files[0]]);
|
||||||
}
|
}
|
||||||
allFiles = await Promise.all(
|
|
||||||
allFiles.map(async (file) => {
|
allFiles = allFiles.map(file => {
|
||||||
let decryptedFile = file;
|
if (!file.uniqueId) file.uniqueId = UUID.uuidv4();
|
||||||
try {
|
return file;
|
||||||
const decryptFile = new DecryptFile();
|
});
|
||||||
const {isEncrypted, requiresPassword} = await decryptFile.checkFileEncrypted(file);
|
|
||||||
if (file.type === 'application/pdf' && isEncrypted) {
|
|
||||||
decryptedFile = await decryptFile.decryptFile(file, requiresPassword);
|
|
||||||
if (!decryptedFile) throw new Error('File decryption failed.');
|
|
||||||
}
|
|
||||||
decryptedFile.uniqueId = UUID.uuidv4();
|
|
||||||
return decryptedFile;
|
|
||||||
} catch (error) {
|
|
||||||
console.error(`Error decrypting file: ${file.name}`, error);
|
|
||||||
if (!file.uniqueId) file.uniqueId = UUID.uuidv4();
|
|
||||||
return file;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
);
|
|
||||||
if (!isDragAndDrop) {
|
if (!isDragAndDrop) {
|
||||||
let dataTransfer = toDataTransfer(allFiles);
|
let dataTransfer = toDataTransfer(allFiles);
|
||||||
element.files = dataTransfer.files;
|
element.files = dataTransfer.files;
|
||||||
}
|
}
|
||||||
|
|
||||||
handleFileInputChange(this);
|
handleFileInputChange(this);
|
||||||
this.dispatchEvent(new CustomEvent('file-input-change', {bubbles: true, detail: {elementId, allFiles}}));
|
this.dispatchEvent(new CustomEvent("file-input-change", { bubbles: true }));
|
||||||
});
|
});
|
||||||
|
|
||||||
function toDataTransfer(files) {
|
function toDataTransfer(files) {
|
||||||
let dataTransfer = new DataTransfer();
|
let dataTransfer = new DataTransfer();
|
||||||
files.forEach((file) => dataTransfer.items.add(file));
|
files.forEach(file => dataTransfer.items.add(file));
|
||||||
return dataTransfer;
|
return dataTransfer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,7 +136,7 @@ function setupFileInput(chooser) {
|
|||||||
|
|
||||||
const filesInfo = files.map((f) => ({name: f.name, size: f.size, uniqueId: f.uniqueId}));
|
const filesInfo = files.map((f) => ({name: f.name, size: f.size, uniqueId: f.uniqueId}));
|
||||||
|
|
||||||
const selectedFilesContainer = $(inputContainer).siblings('.selected-files');
|
const selectedFilesContainer = $(inputContainer).siblings(".selected-files");
|
||||||
selectedFilesContainer.empty();
|
selectedFilesContainer.empty();
|
||||||
filesInfo.forEach((info) => {
|
filesInfo.forEach((info) => {
|
||||||
let fileContainerClasses = 'small-file-container d-flex flex-column justify-content-center align-items-center';
|
let fileContainerClasses = 'small-file-container d-flex flex-column justify-content-center align-items-center';
|
||||||
@@ -179,26 +167,28 @@ function setupFileInput(chooser) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function showOrHideSelectedFilesContainer(files) {
|
function showOrHideSelectedFilesContainer(files) {
|
||||||
if (files && files.length > 0) chooser.style.setProperty('--selected-files-display', 'flex');
|
if (files && files.length > 0)
|
||||||
else chooser.style.setProperty('--selected-files-display', 'none');
|
chooser.style.setProperty('--selected-files-display', 'flex');
|
||||||
|
else
|
||||||
|
chooser.style.setProperty('--selected-files-display', 'none');
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeFileListener(e) {
|
function removeFileListener(e) {
|
||||||
const fileId = e.target.getAttribute('data-file-id');
|
const fileId = (e.target).getAttribute('data-file-id');
|
||||||
|
|
||||||
let inputElement = document.getElementById(elementId);
|
let inputElement = document.getElementById(elementId);
|
||||||
removeFileById(fileId, inputElement);
|
removeFileById(fileId, inputElement);
|
||||||
|
|
||||||
showOrHideSelectedFilesContainer(allFiles);
|
showOrHideSelectedFilesContainer(allFiles);
|
||||||
|
|
||||||
inputElement.dispatchEvent(new CustomEvent('file-input-change', {bubbles: true}));
|
inputElement.dispatchEvent(new CustomEvent("file-input-change", { bubbles: true }));
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeFileById(fileId, inputElement) {
|
function removeFileById(fileId, inputElement) {
|
||||||
let fileContainer = document.getElementById(fileId);
|
let fileContainer = document.getElementById(fileId);
|
||||||
fileContainer.remove();
|
fileContainer.remove();
|
||||||
|
|
||||||
allFiles = allFiles.filter((v) => v.uniqueId != fileId);
|
allFiles = allFiles.filter(v => v.uniqueId != fileId);
|
||||||
let dataTransfer = toDataTransfer(allFiles);
|
let dataTransfer = toDataTransfer(allFiles);
|
||||||
|
|
||||||
if (inputElement) inputElement.files = dataTransfer.files;
|
if (inputElement) inputElement.files = dataTransfer.files;
|
||||||
@@ -217,19 +207,23 @@ function setupFileInput(chooser) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function createFileInfoContainer(info) {
|
function createFileInfoContainer(info) {
|
||||||
let fileInfoContainer = document.createElement('div');
|
let fileInfoContainer = document.createElement("div");
|
||||||
let fileInfoContainerClasses = 'file-info d-flex flex-column align-items-center justify-content-center';
|
let fileInfoContainerClasses = 'file-info d-flex flex-column align-items-center justify-content-center';
|
||||||
|
|
||||||
$(fileInfoContainer).addClass(fileInfoContainerClasses);
|
$(fileInfoContainer).addClass(fileInfoContainerClasses);
|
||||||
|
|
||||||
$(fileInfoContainer).append(`<div title="${info.name}">${info.name}</div>`);
|
$(fileInfoContainer).append(
|
||||||
|
`<div title="${info.name}">${info.name}</div>`
|
||||||
|
);
|
||||||
let fileSizeWithUnits = FileUtils.transformFileSize(info.size);
|
let fileSizeWithUnits = FileUtils.transformFileSize(info.size);
|
||||||
$(fileInfoContainer).append(`<div title="${info.size}">${fileSizeWithUnits}</div>`);
|
$(fileInfoContainer).append(
|
||||||
|
`<div title="${info.size}">${fileSizeWithUnits}</div>`
|
||||||
|
);
|
||||||
return fileInfoContainer;
|
return fileInfoContainer;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Listen for event of file being removed and the filter it out of the allFiles array
|
//Listen for event of file being removed and the filter it out of the allFiles array
|
||||||
document.addEventListener('fileRemoved', function (e) {
|
document.addEventListener("fileRemoved", function (e) {
|
||||||
const fileId = e.detail;
|
const fileId = e.detail;
|
||||||
let inputElement = document.getElementById(elementId);
|
let inputElement = document.getElementById(elementId);
|
||||||
removeFileById(fileId, inputElement);
|
removeFileById(fileId, inputElement);
|
||||||
|
|||||||
@@ -268,7 +268,7 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
const parent = header.parentNode;
|
const parent = header.parentNode;
|
||||||
const container = header.parentNode.querySelector(".feature-group-container");
|
const container = header.parentNode.querySelector(".feature-group-container");
|
||||||
if (parent.id !== "groupFavorites") {
|
if (parent.id !== "groupFavorites") {
|
||||||
container.style.maxHeight = container.scrollHeight + "px";
|
container.style.maxHeight = container.clientHeight + "px";
|
||||||
}
|
}
|
||||||
header.onclick = () => {
|
header.onclick = () => {
|
||||||
expandCollapseToggle(parent);
|
expandCollapseToggle(parent);
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import {SplitAllCommand} from './commands/split.js';
|
|||||||
import {UndoManager} from './UndoManager.js';
|
import {UndoManager} from './UndoManager.js';
|
||||||
import {PageBreakCommand} from './commands/page-break.js';
|
import {PageBreakCommand} from './commands/page-break.js';
|
||||||
import {AddFilesCommand} from './commands/add-page.js';
|
import {AddFilesCommand} from './commands/add-page.js';
|
||||||
import {DecryptFile} from '../DecryptFiles.js';
|
|
||||||
|
|
||||||
class PdfContainer {
|
class PdfContainer {
|
||||||
fileName;
|
fileName;
|
||||||
@@ -41,8 +40,6 @@ class PdfContainer {
|
|||||||
this.removeAllElements = this.removeAllElements.bind(this);
|
this.removeAllElements = this.removeAllElements.bind(this);
|
||||||
this.resetPages = this.resetPages.bind(this);
|
this.resetPages = this.resetPages.bind(this);
|
||||||
|
|
||||||
this.decryptFile = new DecryptFile();
|
|
||||||
|
|
||||||
this.undoManager = undoManager || new UndoManager();
|
this.undoManager = undoManager || new UndoManager();
|
||||||
|
|
||||||
this.pdfAdapters = pdfAdapters;
|
this.pdfAdapters = pdfAdapters;
|
||||||
@@ -168,6 +165,7 @@ class PdfContainer {
|
|||||||
input.click();
|
input.click();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async addFilesFromFiles(files, nextSiblingElement, pages) {
|
async addFilesFromFiles(files, nextSiblingElement, pages) {
|
||||||
this.fileName = files[0].name;
|
this.fileName = files[0].name;
|
||||||
for (var i = 0; i < files.length; i++) {
|
for (var i = 0; i < files.length; i++) {
|
||||||
@@ -175,37 +173,17 @@ class PdfContainer {
|
|||||||
let processingTime,
|
let processingTime,
|
||||||
errorMessage = null,
|
errorMessage = null,
|
||||||
pageCount = 0;
|
pageCount = 0;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let decryptedFile = files[i];
|
const file = files[i];
|
||||||
let isEncrypted = false;
|
if (file.type === 'application/pdf') {
|
||||||
let requiresPassword = false;
|
const {renderer, pdfDocument} = await this.loadFile(file);
|
||||||
await this.decryptFile
|
|
||||||
.checkFileEncrypted(decryptedFile)
|
|
||||||
.then((result) => {
|
|
||||||
isEncrypted = result.isEncrypted;
|
|
||||||
requiresPassword = result.requiresPassword;
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
console.error(error);
|
|
||||||
});
|
|
||||||
if (decryptedFile.type === 'application/pdf' && isEncrypted) {
|
|
||||||
decryptedFile = await this.decryptFile.decryptFile(decryptedFile, requiresPassword);
|
|
||||||
if (!decryptedFile) {
|
|
||||||
throw new Error('File decryption failed.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (decryptedFile.type === 'application/pdf') {
|
|
||||||
const {renderer, pdfDocument} = await this.loadFile(decryptedFile);
|
|
||||||
pageCount = renderer.pageCount || 0;
|
pageCount = renderer.pageCount || 0;
|
||||||
pages = await this.addPdfFile(renderer, pdfDocument, nextSiblingElement, pages);
|
pages = await this.addPdfFile(renderer, pdfDocument, nextSiblingElement, pages);
|
||||||
} else if (decryptedFile.type.startsWith('image/')) {
|
} else if (file.type.startsWith('image/')) {
|
||||||
pages = await this.addImageFile(decryptedFile, nextSiblingElement, pages);
|
pages = await this.addImageFile(file, nextSiblingElement, pages);
|
||||||
}
|
}
|
||||||
|
|
||||||
processingTime = Date.now() - startTime;
|
processingTime = Date.now() - startTime;
|
||||||
this.captureFileProcessingEvent(true, decryptedFile, processingTime, null, pageCount);
|
this.captureFileProcessingEvent(true, file, processingTime, null, pageCount);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
processingTime = Date.now() - startTime;
|
processingTime = Date.now() - startTime;
|
||||||
errorMessage = error.message || 'Unknown error';
|
errorMessage = error.message || 'Unknown error';
|
||||||
@@ -216,7 +194,6 @@ class PdfContainer {
|
|||||||
document.querySelectorAll('.enable-on-file').forEach((element) => {
|
document.querySelectorAll('.enable-on-file').forEach((element) => {
|
||||||
element.disabled = false;
|
element.disabled = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
return pages;
|
return pages;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,47 +0,0 @@
|
|||||||
document.getElementById('download-pdf').addEventListener('click', async () => {
|
|
||||||
const modifiedPdf = await DraggableUtils.getOverlayedPdfDocument();
|
|
||||||
const modifiedPdfBytes = await modifiedPdf.save();
|
|
||||||
const blob = new Blob([modifiedPdfBytes], {type: 'application/pdf'});
|
|
||||||
const link = document.createElement('a');
|
|
||||||
link.href = URL.createObjectURL(blob);
|
|
||||||
link.download = originalFileName + '_addedImage.pdf';
|
|
||||||
link.click();
|
|
||||||
});
|
|
||||||
let originalFileName = '';
|
|
||||||
document.querySelector('input[name=pdf-upload]').addEventListener('change', async (event) => {
|
|
||||||
const fileInput = event.target;
|
|
||||||
fileInput.addEventListener('file-input-change', async (e) => {
|
|
||||||
const {allFiles} = e.detail;
|
|
||||||
if (allFiles && allFiles.length > 0) {
|
|
||||||
const file = allFiles[0];
|
|
||||||
originalFileName = file.name.replace(/\.[^/.]+$/, '');
|
|
||||||
const pdfData = await file.arrayBuffer();
|
|
||||||
pdfjsLib.GlobalWorkerOptions.workerSrc = './pdfjs-legacy/pdf.worker.mjs';
|
|
||||||
const pdfDoc = await pdfjsLib.getDocument({data: pdfData}).promise;
|
|
||||||
await DraggableUtils.renderPage(pdfDoc, 0);
|
|
||||||
|
|
||||||
document.querySelectorAll('.show-on-file-selected').forEach((el) => {
|
|
||||||
el.style.cssText = '';
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
|
||||||
document.querySelectorAll('.show-on-file-selected').forEach((el) => {
|
|
||||||
el.style.cssText = 'display:none !important';
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
const imageUpload = document.querySelector('input[name=image-upload]');
|
|
||||||
imageUpload.addEventListener('change', (e) => {
|
|
||||||
if (!e.target.files) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for (const imageFile of e.target.files) {
|
|
||||||
var reader = new FileReader();
|
|
||||||
reader.readAsDataURL(imageFile);
|
|
||||||
reader.onloadend = function (e) {
|
|
||||||
DraggableUtils.createDraggableCanvasFromUrl(e.target.result);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
@@ -1,253 +0,0 @@
|
|||||||
var canvas = document.getElementById('contrast-pdf-canvas');
|
|
||||||
var context = canvas.getContext('2d');
|
|
||||||
var originalImageData = null;
|
|
||||||
var allPages = [];
|
|
||||||
var pdfDoc = null;
|
|
||||||
var pdf = null; // This is the current PDF document
|
|
||||||
|
|
||||||
async function renderPDFAndSaveOriginalImageData(file) {
|
|
||||||
var fileReader = new FileReader();
|
|
||||||
fileReader.onload = async function () {
|
|
||||||
var data = new Uint8Array(this.result);
|
|
||||||
pdfjsLib.GlobalWorkerOptions.workerSrc = './pdfjs-legacy/pdf.worker.mjs';
|
|
||||||
pdf = await pdfjsLib.getDocument({data: data}).promise;
|
|
||||||
|
|
||||||
// Get the number of pages in the PDF
|
|
||||||
var numPages = pdf.numPages;
|
|
||||||
allPages = Array.from({length: numPages}, (_, i) => i + 1);
|
|
||||||
|
|
||||||
// Create a new PDF document
|
|
||||||
pdfDoc = await PDFLib.PDFDocument.create();
|
|
||||||
// Render the first page in the viewer
|
|
||||||
await renderPageAndAdjustImageProperties(1);
|
|
||||||
document.getElementById('sliders-container').style.display = 'block';
|
|
||||||
};
|
|
||||||
fileReader.readAsArrayBuffer(file);
|
|
||||||
}
|
|
||||||
|
|
||||||
// This function is now async and returns a promise
|
|
||||||
function renderPageAndAdjustImageProperties(pageNum) {
|
|
||||||
return new Promise(async function (resolve, reject) {
|
|
||||||
var page = await pdf.getPage(pageNum);
|
|
||||||
var scale = 1.5;
|
|
||||||
var viewport = page.getViewport({scale: scale});
|
|
||||||
|
|
||||||
canvas.height = viewport.height;
|
|
||||||
canvas.width = viewport.width;
|
|
||||||
|
|
||||||
var renderContext = {
|
|
||||||
canvasContext: context,
|
|
||||||
viewport: viewport,
|
|
||||||
};
|
|
||||||
|
|
||||||
var renderTask = page.render(renderContext);
|
|
||||||
renderTask.promise.then(function () {
|
|
||||||
originalImageData = context.getImageData(0, 0, canvas.width, canvas.height);
|
|
||||||
adjustImageProperties();
|
|
||||||
resolve();
|
|
||||||
});
|
|
||||||
canvas.classList.add('fixed-shadow-canvas');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function adjustImageProperties() {
|
|
||||||
var contrast = parseFloat(document.getElementById('contrast-slider').value);
|
|
||||||
var brightness = parseFloat(document.getElementById('brightness-slider').value);
|
|
||||||
var saturation = parseFloat(document.getElementById('saturation-slider').value);
|
|
||||||
|
|
||||||
contrast /= 100; // normalize to range [0, 2]
|
|
||||||
brightness /= 100; // normalize to range [0, 2]
|
|
||||||
saturation /= 100; // normalize to range [0, 2]
|
|
||||||
|
|
||||||
if (originalImageData) {
|
|
||||||
var newImageData = context.createImageData(originalImageData.width, originalImageData.height);
|
|
||||||
newImageData.data.set(originalImageData.data);
|
|
||||||
|
|
||||||
for (var i = 0; i < newImageData.data.length; i += 4) {
|
|
||||||
var r = newImageData.data[i];
|
|
||||||
var g = newImageData.data[i + 1];
|
|
||||||
var b = newImageData.data[i + 2];
|
|
||||||
// Adjust contrast
|
|
||||||
r = adjustContrastForPixel(r, contrast);
|
|
||||||
g = adjustContrastForPixel(g, contrast);
|
|
||||||
b = adjustContrastForPixel(b, contrast);
|
|
||||||
// Adjust brightness
|
|
||||||
r = adjustBrightnessForPixel(r, brightness);
|
|
||||||
g = adjustBrightnessForPixel(g, brightness);
|
|
||||||
b = adjustBrightnessForPixel(b, brightness);
|
|
||||||
// Adjust saturation
|
|
||||||
var rgb = adjustSaturationForPixel(r, g, b, saturation);
|
|
||||||
newImageData.data[i] = rgb[0];
|
|
||||||
newImageData.data[i + 1] = rgb[1];
|
|
||||||
newImageData.data[i + 2] = rgb[2];
|
|
||||||
}
|
|
||||||
context.putImageData(newImageData, 0, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function rgbToHsl(r, g, b) {
|
|
||||||
(r /= 255), (g /= 255), (b /= 255);
|
|
||||||
|
|
||||||
var max = Math.max(r, g, b),
|
|
||||||
min = Math.min(r, g, b);
|
|
||||||
var h,
|
|
||||||
s,
|
|
||||||
l = (max + min) / 2;
|
|
||||||
|
|
||||||
if (max === min) {
|
|
||||||
h = s = 0; // achromatic
|
|
||||||
} else {
|
|
||||||
var d = max - min;
|
|
||||||
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
|
||||||
|
|
||||||
switch (max) {
|
|
||||||
case r:
|
|
||||||
h = (g - b) / d + (g < b ? 6 : 0);
|
|
||||||
break;
|
|
||||||
case g:
|
|
||||||
h = (b - r) / d + 2;
|
|
||||||
break;
|
|
||||||
case b:
|
|
||||||
h = (r - g) / d + 4;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
h /= 6;
|
|
||||||
}
|
|
||||||
|
|
||||||
return [h, s, l];
|
|
||||||
}
|
|
||||||
|
|
||||||
function hslToRgb(h, s, l) {
|
|
||||||
var r, g, b;
|
|
||||||
|
|
||||||
if (s === 0) {
|
|
||||||
r = g = b = l; // achromatic
|
|
||||||
} else {
|
|
||||||
var hue2rgb = function hue2rgb(p, q, t) {
|
|
||||||
if (t < 0) t += 1;
|
|
||||||
if (t > 1) t -= 1;
|
|
||||||
if (t < 1 / 6) return p + (q - p) * 6 * t;
|
|
||||||
if (t < 1 / 2) return q;
|
|
||||||
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
|
|
||||||
return p;
|
|
||||||
};
|
|
||||||
|
|
||||||
var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
|
||||||
var p = 2 * l - q;
|
|
||||||
|
|
||||||
r = hue2rgb(p, q, h + 1 / 3);
|
|
||||||
g = hue2rgb(p, q, h);
|
|
||||||
b = hue2rgb(p, q, h - 1 / 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
return [r * 255, g * 255, b * 255];
|
|
||||||
}
|
|
||||||
|
|
||||||
function adjustContrastForPixel(pixel, contrast) {
|
|
||||||
// Normalize to range [-0.5, 0.5]
|
|
||||||
var normalized = pixel / 255 - 0.5;
|
|
||||||
|
|
||||||
// Apply contrast
|
|
||||||
normalized *= contrast;
|
|
||||||
|
|
||||||
// Denormalize back to [0, 255]
|
|
||||||
return (normalized + 0.5) * 255;
|
|
||||||
}
|
|
||||||
|
|
||||||
function clamp(value, min, max) {
|
|
||||||
return Math.min(Math.max(value, min), max);
|
|
||||||
}
|
|
||||||
|
|
||||||
function adjustSaturationForPixel(r, g, b, saturation) {
|
|
||||||
var hsl = rgbToHsl(r, g, b);
|
|
||||||
|
|
||||||
// Adjust saturation
|
|
||||||
hsl[1] = clamp(hsl[1] * saturation, 0, 1);
|
|
||||||
|
|
||||||
// Convert back to RGB
|
|
||||||
var rgb = hslToRgb(hsl[0], hsl[1], hsl[2]);
|
|
||||||
|
|
||||||
// Return adjusted RGB values
|
|
||||||
return rgb;
|
|
||||||
}
|
|
||||||
|
|
||||||
function adjustBrightnessForPixel(pixel, brightness) {
|
|
||||||
return Math.max(0, Math.min(255, pixel * brightness));
|
|
||||||
}
|
|
||||||
let inputFileName = '';
|
|
||||||
async function downloadPDF() {
|
|
||||||
for (var i = 0; i < allPages.length; i++) {
|
|
||||||
await renderPageAndAdjustImageProperties(allPages[i]);
|
|
||||||
const pngImageBytes = canvas.toDataURL('image/png');
|
|
||||||
const pngImage = await pdfDoc.embedPng(pngImageBytes);
|
|
||||||
const pngDims = pngImage.scale(1);
|
|
||||||
|
|
||||||
// Create a blank page matching the dimensions of the image
|
|
||||||
const page = pdfDoc.addPage([pngDims.width, pngDims.height]);
|
|
||||||
|
|
||||||
// Draw the PNG image
|
|
||||||
page.drawImage(pngImage, {
|
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
width: pngDims.width,
|
|
||||||
height: pngDims.height,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Serialize the PDFDocument to bytes (a Uint8Array)
|
|
||||||
const pdfBytes = await pdfDoc.save();
|
|
||||||
|
|
||||||
// Create a Blob
|
|
||||||
const blob = new Blob([pdfBytes.buffer], {type: 'application/pdf'});
|
|
||||||
|
|
||||||
// Create download link
|
|
||||||
const downloadLink = document.createElement('a');
|
|
||||||
downloadLink.href = URL.createObjectURL(blob);
|
|
||||||
let newFileName = inputFileName ? inputFileName.replace('.pdf', '') : 'download';
|
|
||||||
newFileName += '_adjusted_color.pdf';
|
|
||||||
|
|
||||||
downloadLink.download = newFileName;
|
|
||||||
downloadLink.click();
|
|
||||||
|
|
||||||
// After download, reset the viewer and clear stored data
|
|
||||||
allPages = []; // Clear the pages
|
|
||||||
originalImageData = null; // Clear the image data
|
|
||||||
|
|
||||||
// Go back to page 1 and render it in the viewer
|
|
||||||
if (pdf !== null) {
|
|
||||||
renderPageAndAdjustImageProperties(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Event listeners
|
|
||||||
document.getElementById('fileInput-input').addEventListener('change', function (e) {
|
|
||||||
const fileInput = event.target;
|
|
||||||
fileInput.addEventListener('file-input-change', async (e) => {
|
|
||||||
const {allFiles} = e.detail;
|
|
||||||
if (allFiles && allFiles.length > 0) {
|
|
||||||
const file = allFiles[0];
|
|
||||||
inputFileName = file.name;
|
|
||||||
renderPDFAndSaveOriginalImageData(file);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
document.getElementById('contrast-slider').addEventListener('input', function () {
|
|
||||||
document.getElementById('contrast-val').textContent = this.value;
|
|
||||||
adjustImageProperties();
|
|
||||||
});
|
|
||||||
|
|
||||||
document.getElementById('brightness-slider').addEventListener('input', function () {
|
|
||||||
document.getElementById('brightness-val').textContent = this.value;
|
|
||||||
adjustImageProperties();
|
|
||||||
});
|
|
||||||
|
|
||||||
document.getElementById('saturation-slider').addEventListener('input', function () {
|
|
||||||
document.getElementById('saturation-val').textContent = this.value;
|
|
||||||
adjustImageProperties();
|
|
||||||
});
|
|
||||||
|
|
||||||
document.getElementById('download-button').addEventListener('click', function () {
|
|
||||||
downloadPDF();
|
|
||||||
});
|
|
||||||
@@ -1,150 +0,0 @@
|
|||||||
const deleteAllCheckbox = document.querySelector('#deleteAll');
|
|
||||||
let inputs = document.querySelectorAll('input');
|
|
||||||
const customMetadataDiv = document.getElementById('customMetadata');
|
|
||||||
const otherMetadataEntriesDiv = document.getElementById('otherMetadataEntries');
|
|
||||||
|
|
||||||
deleteAllCheckbox.addEventListener('change', function (event) {
|
|
||||||
inputs.forEach((input) => {
|
|
||||||
// If it's the deleteAllCheckbox or any file input, skip
|
|
||||||
if (input === deleteAllCheckbox || input.type === 'file') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Disable or enable based on the checkbox state
|
|
||||||
input.disabled = deleteAllCheckbox.checked;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
const customModeCheckbox = document.getElementById('customModeCheckbox');
|
|
||||||
const addMetadataBtn = document.getElementById('addMetadataBtn');
|
|
||||||
const customMetadataFormContainer = document.getElementById('customMetadataEntries');
|
|
||||||
var count = 1;
|
|
||||||
const fileInput = document.querySelector('#fileInput-input');
|
|
||||||
const authorInput = document.querySelector('#author');
|
|
||||||
const creationDateInput = document.querySelector('#creationDate');
|
|
||||||
const creatorInput = document.querySelector('#creator');
|
|
||||||
const keywordsInput = document.querySelector('#keywords');
|
|
||||||
const modificationDateInput = document.querySelector('#modificationDate');
|
|
||||||
const producerInput = document.querySelector('#producer');
|
|
||||||
const subjectInput = document.querySelector('#subject');
|
|
||||||
const titleInput = document.querySelector('#title');
|
|
||||||
const trappedInput = document.querySelector('#trapped');
|
|
||||||
var lastPDFFileMeta = null;
|
|
||||||
var lastPDFFile = null;
|
|
||||||
|
|
||||||
fileInput.addEventListener('change', async function () {
|
|
||||||
fileInput.addEventListener('file-input-change', async (e) => {
|
|
||||||
const {allFiles} = e.detail;
|
|
||||||
if (allFiles && allFiles.length > 0) {
|
|
||||||
const file = allFiles[0];
|
|
||||||
while (otherMetadataEntriesDiv.firstChild) {
|
|
||||||
otherMetadataEntriesDiv.removeChild(otherMetadataEntriesDiv.firstChild);
|
|
||||||
}
|
|
||||||
while (customMetadataFormContainer.firstChild) {
|
|
||||||
customMetadataFormContainer.removeChild(customMetadataFormContainer.firstChild);
|
|
||||||
}
|
|
||||||
var url = URL.createObjectURL(file);
|
|
||||||
pdfjsLib.GlobalWorkerOptions.workerSrc = './pdfjs-legacy/pdf.worker.mjs';
|
|
||||||
const pdf = await pdfjsLib.getDocument(url).promise;
|
|
||||||
const pdfMetadata = await pdf.getMetadata();
|
|
||||||
lastPDFFile = pdfMetadata?.info;
|
|
||||||
console.log(pdfMetadata);
|
|
||||||
if (!pdfMetadata?.info?.Custom || pdfMetadata?.info?.Custom.size == 0) {
|
|
||||||
customModeCheckbox.disabled = true;
|
|
||||||
customModeCheckbox.checked = false;
|
|
||||||
} else {
|
|
||||||
customModeCheckbox.disabled = false;
|
|
||||||
}
|
|
||||||
authorInput.value = pdfMetadata?.info?.Author;
|
|
||||||
creationDateInput.value = convertDateFormat(pdfMetadata?.info?.CreationDate);
|
|
||||||
creatorInput.value = pdfMetadata?.info?.Creator;
|
|
||||||
keywordsInput.value = pdfMetadata?.info?.Keywords;
|
|
||||||
modificationDateInput.value = convertDateFormat(pdfMetadata?.info?.ModDate);
|
|
||||||
producerInput.value = pdfMetadata?.info?.Producer;
|
|
||||||
subjectInput.value = pdfMetadata?.info?.Subject;
|
|
||||||
titleInput.value = pdfMetadata?.info?.Title;
|
|
||||||
console.log(pdfMetadata?.info);
|
|
||||||
const trappedValue = pdfMetadata?.info?.Trapped;
|
|
||||||
// Get all options in the select element
|
|
||||||
const options = trappedInput.options;
|
|
||||||
// Loop through all options to find the one with a matching value
|
|
||||||
for (let i = 0; i < options.length; i++) {
|
|
||||||
if (options[i].value === trappedValue) {
|
|
||||||
options[i].selected = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
addExtra();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
addMetadataBtn.addEventListener('click', () => {
|
|
||||||
const keyInput = document.createElement('input');
|
|
||||||
keyInput.type = 'text';
|
|
||||||
keyInput.placeholder = 'Key';
|
|
||||||
keyInput.className = 'form-control';
|
|
||||||
keyInput.name = `allRequestParams[customKey${count}]`;
|
|
||||||
|
|
||||||
const valueInput = document.createElement('input');
|
|
||||||
valueInput.type = 'text';
|
|
||||||
valueInput.placeholder = 'Value';
|
|
||||||
valueInput.className = 'form-control';
|
|
||||||
valueInput.name = `allRequestParams[customValue${count}]`;
|
|
||||||
count = count + 1;
|
|
||||||
|
|
||||||
const formGroup = document.createElement('div');
|
|
||||||
formGroup.className = 'mb-3';
|
|
||||||
formGroup.appendChild(keyInput);
|
|
||||||
formGroup.appendChild(valueInput);
|
|
||||||
|
|
||||||
customMetadataFormContainer.appendChild(formGroup);
|
|
||||||
});
|
|
||||||
function convertDateFormat(dateTimeString) {
|
|
||||||
if (!dateTimeString || dateTimeString.length < 17) {
|
|
||||||
return dateTimeString;
|
|
||||||
}
|
|
||||||
|
|
||||||
const year = dateTimeString.substring(2, 6);
|
|
||||||
const month = dateTimeString.substring(6, 8);
|
|
||||||
const day = dateTimeString.substring(8, 10);
|
|
||||||
const hour = dateTimeString.substring(10, 12);
|
|
||||||
const minute = dateTimeString.substring(12, 14);
|
|
||||||
const second = dateTimeString.substring(14, 16);
|
|
||||||
|
|
||||||
return year + '/' + month + '/' + day + ' ' + hour + ':' + minute + ':' + second;
|
|
||||||
}
|
|
||||||
|
|
||||||
function addExtra() {
|
|
||||||
const event = document.getElementById('customModeCheckbox');
|
|
||||||
if (event.checked && lastPDFFile.Custom != null) {
|
|
||||||
customMetadataDiv.style.display = 'block';
|
|
||||||
for (const [key, value] of Object.entries(lastPDFFile.Custom)) {
|
|
||||||
if (
|
|
||||||
key === 'Author' ||
|
|
||||||
key === 'CreationDate' ||
|
|
||||||
key === 'Creator' ||
|
|
||||||
key === 'Keywords' ||
|
|
||||||
key === 'ModDate' ||
|
|
||||||
key === 'Producer' ||
|
|
||||||
key === 'Subject' ||
|
|
||||||
key === 'Title' ||
|
|
||||||
key === 'Trapped'
|
|
||||||
) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const entryDiv = document.createElement('div');
|
|
||||||
entryDiv.className = 'mb-3';
|
|
||||||
entryDiv.innerHTML = `<div class="mb-3"><label class="form-check-label" for="${key}">${key}:</label><input name="${key}" value="${value}" type="text" class="form-control" id="${key}"></div>`;
|
|
||||||
otherMetadataEntriesDiv.appendChild(entryDiv);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
customMetadataDiv.style.display = 'none';
|
|
||||||
while (otherMetadataEntriesDiv.firstChild) {
|
|
||||||
otherMetadataEntriesDiv.removeChild(otherMetadataEntriesDiv.firstChild);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
customModeCheckbox.addEventListener('change', (event) => {
|
|
||||||
addExtra();
|
|
||||||
});
|
|
||||||
@@ -1,159 +0,0 @@
|
|||||||
let pdfCanvas = document.getElementById('cropPdfCanvas');
|
|
||||||
let overlayCanvas = document.getElementById('overlayCanvas');
|
|
||||||
let canvasesContainer = document.getElementById('canvasesContainer');
|
|
||||||
canvasesContainer.style.display = 'none';
|
|
||||||
let containerRect = canvasesContainer.getBoundingClientRect();
|
|
||||||
|
|
||||||
let context = pdfCanvas.getContext('2d');
|
|
||||||
let overlayContext = overlayCanvas.getContext('2d');
|
|
||||||
|
|
||||||
overlayCanvas.width = pdfCanvas.width;
|
|
||||||
overlayCanvas.height = pdfCanvas.height;
|
|
||||||
|
|
||||||
let isDrawing = false; // New flag to check if drawing is ongoing
|
|
||||||
|
|
||||||
let cropForm = document.getElementById('cropForm');
|
|
||||||
let fileInput = document.getElementById('fileInput-input');
|
|
||||||
let xInput = document.getElementById('x');
|
|
||||||
let yInput = document.getElementById('y');
|
|
||||||
let widthInput = document.getElementById('width');
|
|
||||||
let heightInput = document.getElementById('height');
|
|
||||||
|
|
||||||
let pdfDoc = null;
|
|
||||||
let currentPage = 1;
|
|
||||||
let totalPages = 0;
|
|
||||||
|
|
||||||
let startX = 0;
|
|
||||||
let startY = 0;
|
|
||||||
let rectWidth = 0;
|
|
||||||
let rectHeight = 0;
|
|
||||||
|
|
||||||
let pageScale = 1; // The scale which the pdf page renders
|
|
||||||
let timeId = null; // timeout id for resizing canvases event
|
|
||||||
|
|
||||||
function renderPageFromFile(file) {
|
|
||||||
if (file.type === 'application/pdf') {
|
|
||||||
let reader = new FileReader();
|
|
||||||
reader.onload = function (ev) {
|
|
||||||
let typedArray = new Uint8Array(reader.result);
|
|
||||||
pdfjsLib.GlobalWorkerOptions.workerSrc = './pdfjs-legacy/pdf.worker.mjs';
|
|
||||||
pdfjsLib.getDocument(typedArray).promise.then(function (pdf) {
|
|
||||||
pdfDoc = pdf;
|
|
||||||
totalPages = pdf.numPages;
|
|
||||||
renderPage(currentPage);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
reader.readAsArrayBuffer(file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
window.addEventListener('resize', function () {
|
|
||||||
clearTimeout(timeId);
|
|
||||||
|
|
||||||
timeId = setTimeout(function () {
|
|
||||||
if (fileInput.files.length == 0) return;
|
|
||||||
let canvasesContainer = document.getElementById('canvasesContainer');
|
|
||||||
let containerRect = canvasesContainer.getBoundingClientRect();
|
|
||||||
|
|
||||||
context.clearRect(0, 0, pdfCanvas.width, pdfCanvas.height);
|
|
||||||
|
|
||||||
overlayContext.clearRect(0, 0, overlayCanvas.width, overlayCanvas.height);
|
|
||||||
|
|
||||||
pdfCanvas.width = containerRect.width;
|
|
||||||
pdfCanvas.height = containerRect.height;
|
|
||||||
|
|
||||||
overlayCanvas.width = containerRect.width;
|
|
||||||
overlayCanvas.height = containerRect.height;
|
|
||||||
|
|
||||||
let file = fileInput.files[0];
|
|
||||||
renderPageFromFile(file);
|
|
||||||
}, 1000);
|
|
||||||
});
|
|
||||||
|
|
||||||
fileInput.addEventListener('change', function (e) {
|
|
||||||
fileInput.addEventListener('file-input-change', async (e) => {
|
|
||||||
const {allFiles} = e.detail;
|
|
||||||
if (allFiles && allFiles.length > 0) {
|
|
||||||
canvasesContainer.style.display = 'block'; // set for visual purposes
|
|
||||||
let file = allFiles[0];
|
|
||||||
renderPageFromFile(file);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
cropForm.addEventListener('submit', function (e) {
|
|
||||||
if (xInput.value == '' && yInput.value == '' && widthInput.value == '' && heightInput.value == '') {
|
|
||||||
// Ορίστε συντεταγμένες για ολόκληρη την επιφάνεια του PDF
|
|
||||||
xInput.value = 0;
|
|
||||||
yInput.value = 0;
|
|
||||||
widthInput.value = containerRect.width;
|
|
||||||
heightInput.value = containerRect.height;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
overlayCanvas.addEventListener('mousedown', function (e) {
|
|
||||||
// Clear previously drawn rectangle on the main canvas
|
|
||||||
context.clearRect(0, 0, pdfCanvas.width, pdfCanvas.height);
|
|
||||||
renderPage(currentPage); // Re-render the PDF
|
|
||||||
|
|
||||||
// Clear the overlay canvas to ensure old drawings are removed
|
|
||||||
overlayContext.clearRect(0, 0, overlayCanvas.width, overlayCanvas.height);
|
|
||||||
|
|
||||||
startX = e.offsetX;
|
|
||||||
startY = e.offsetY;
|
|
||||||
isDrawing = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
overlayCanvas.addEventListener('mousemove', function (e) {
|
|
||||||
if (!isDrawing) return;
|
|
||||||
overlayContext.clearRect(0, 0, overlayCanvas.width, overlayCanvas.height); // Clear previous rectangle
|
|
||||||
|
|
||||||
rectWidth = e.offsetX - startX;
|
|
||||||
rectHeight = e.offsetY - startY;
|
|
||||||
overlayContext.strokeStyle = 'red';
|
|
||||||
overlayContext.strokeRect(startX, startY, rectWidth, rectHeight);
|
|
||||||
});
|
|
||||||
|
|
||||||
overlayCanvas.addEventListener('mouseup', function (e) {
|
|
||||||
isDrawing = false;
|
|
||||||
|
|
||||||
rectWidth = e.offsetX - startX;
|
|
||||||
rectHeight = e.offsetY - startY;
|
|
||||||
|
|
||||||
let flippedY = pdfCanvas.height - e.offsetY;
|
|
||||||
|
|
||||||
xInput.value = startX / pageScale;
|
|
||||||
yInput.value = flippedY / pageScale;
|
|
||||||
widthInput.value = rectWidth / pageScale;
|
|
||||||
heightInput.value = rectHeight / pageScale;
|
|
||||||
|
|
||||||
// Draw the final rectangle on the main canvas
|
|
||||||
context.strokeStyle = 'red';
|
|
||||||
context.strokeRect(startX, startY, rectWidth, rectHeight);
|
|
||||||
|
|
||||||
overlayContext.clearRect(0, 0, overlayCanvas.width, overlayCanvas.height); // Clear the overlay
|
|
||||||
});
|
|
||||||
|
|
||||||
function renderPage(pageNumber) {
|
|
||||||
pdfDoc.getPage(pageNumber).then(function (page) {
|
|
||||||
let canvasesContainer = document.getElementById('canvasesContainer');
|
|
||||||
let containerRect = canvasesContainer.getBoundingClientRect();
|
|
||||||
|
|
||||||
pageScale = containerRect.width / page.getViewport({scale: 1}).width; // The new scale
|
|
||||||
|
|
||||||
let viewport = page.getViewport({scale: containerRect.width / page.getViewport({scale: 1}).width});
|
|
||||||
|
|
||||||
canvasesContainer.width = viewport.width;
|
|
||||||
canvasesContainer.height = viewport.height;
|
|
||||||
|
|
||||||
pdfCanvas.width = viewport.width;
|
|
||||||
pdfCanvas.height = viewport.height;
|
|
||||||
|
|
||||||
overlayCanvas.width = viewport.width; // Match overlay canvas size with PDF canvas
|
|
||||||
overlayCanvas.height = viewport.height;
|
|
||||||
|
|
||||||
let renderContext = {canvasContext: context, viewport: viewport};
|
|
||||||
page.render(renderContext);
|
|
||||||
pdfCanvas.classList.add('shadow-canvas');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -1,138 +0,0 @@
|
|||||||
let pdfCanvas = document.getElementById('cropPdfCanvas');
|
|
||||||
let overlayCanvas = document.getElementById('overlayCanvas');
|
|
||||||
let canvasesContainer = document.getElementById('canvasesContainer');
|
|
||||||
canvasesContainer.style.display = 'none';
|
|
||||||
// let paginationBtnContainer = ;
|
|
||||||
|
|
||||||
let context = pdfCanvas.getContext('2d');
|
|
||||||
let overlayContext = overlayCanvas.getContext('2d');
|
|
||||||
|
|
||||||
let btn1Object = document.getElementById('previous-page-btn');
|
|
||||||
let btn2Object = document.getElementById('next-page-btn');
|
|
||||||
overlayCanvas.width = pdfCanvas.width;
|
|
||||||
overlayCanvas.height = pdfCanvas.height;
|
|
||||||
|
|
||||||
let fileInput = document.getElementById('fileInput-input');
|
|
||||||
|
|
||||||
let file;
|
|
||||||
|
|
||||||
let pdfDoc = null;
|
|
||||||
let pageId = document.getElementById('pageId');
|
|
||||||
let currentPage = 1;
|
|
||||||
let totalPages = 0;
|
|
||||||
|
|
||||||
let startX = 0;
|
|
||||||
let startY = 0;
|
|
||||||
let rectWidth = 0;
|
|
||||||
let rectHeight = 0;
|
|
||||||
|
|
||||||
let timeId = null; // timeout id for resizing canvases event
|
|
||||||
|
|
||||||
btn1Object.addEventListener('click', function (e) {
|
|
||||||
if (currentPage !== 1) {
|
|
||||||
currentPage = currentPage - 1;
|
|
||||||
pageId.value = currentPage;
|
|
||||||
|
|
||||||
if (file.type === 'application/pdf') {
|
|
||||||
let reader = new FileReader();
|
|
||||||
reader.onload = function (ev) {
|
|
||||||
let typedArray = new Uint8Array(reader.result);
|
|
||||||
pdfjsLib.GlobalWorkerOptions.workerSrc = './pdfjs-legacy/pdf.worker.mjs';
|
|
||||||
pdfjsLib.getDocument(typedArray).promise.then(function (pdf) {
|
|
||||||
pdfDoc = pdf;
|
|
||||||
totalPages = pdf.numPages;
|
|
||||||
renderPage(currentPage);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
reader.readAsArrayBuffer(file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
btn2Object.addEventListener('click', function (e) {
|
|
||||||
if (currentPage !== totalPages) {
|
|
||||||
currentPage = currentPage + 1;
|
|
||||||
pageId.value = currentPage;
|
|
||||||
|
|
||||||
if (file.type === 'application/pdf') {
|
|
||||||
let reader = new FileReader();
|
|
||||||
reader.onload = function (ev) {
|
|
||||||
let typedArray = new Uint8Array(reader.result);
|
|
||||||
pdfjsLib.GlobalWorkerOptions.workerSrc = './pdfjs-legacy/pdf.worker.mjs';
|
|
||||||
pdfjsLib.getDocument(typedArray).promise.then(function (pdf) {
|
|
||||||
pdfDoc = pdf;
|
|
||||||
totalPages = pdf.numPages;
|
|
||||||
renderPage(currentPage);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
reader.readAsArrayBuffer(file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
function renderPageFromFile(file) {
|
|
||||||
if (file.type === 'application/pdf') {
|
|
||||||
let reader = new FileReader();
|
|
||||||
reader.onload = function (ev) {
|
|
||||||
let typedArray = new Uint8Array(reader.result);
|
|
||||||
pdfjsLib.GlobalWorkerOptions.workerSrc = './pdfjs-legacy/pdf.worker.mjs';
|
|
||||||
pdfjsLib.getDocument(typedArray).promise.then(function (pdf) {
|
|
||||||
pdfDoc = pdf;
|
|
||||||
totalPages = pdf.numPages;
|
|
||||||
renderPage(currentPage);
|
|
||||||
});
|
|
||||||
pageId.value = currentPage;
|
|
||||||
};
|
|
||||||
reader.readAsArrayBuffer(file);
|
|
||||||
document.getElementById('pagination-button-container').style.display = 'flex';
|
|
||||||
document.getElementById('instruction-text').style.display = 'block';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
window.addEventListener('resize', function () {
|
|
||||||
clearTimeout(timeId);
|
|
||||||
timeId = setTimeout(function () {
|
|
||||||
if (fileInput.files.length == 0) return;
|
|
||||||
let canvasesContainer = document.getElementById('canvasesContainer');
|
|
||||||
let containerRect = canvasesContainer.getBoundingClientRect();
|
|
||||||
|
|
||||||
context.clearRect(0, 0, pdfCanvas.width, pdfCanvas.height);
|
|
||||||
|
|
||||||
overlayContext.clearRect(0, 0, overlayCanvas.width, overlayCanvas.height);
|
|
||||||
|
|
||||||
pdfCanvas.width = containerRect.width;
|
|
||||||
pdfCanvas.height = containerRect.height;
|
|
||||||
|
|
||||||
overlayCanvas.width = containerRect.width;
|
|
||||||
overlayCanvas.height = containerRect.height;
|
|
||||||
|
|
||||||
let file = fileInput.files[0];
|
|
||||||
renderPageFromFile(file);
|
|
||||||
}, 1000);
|
|
||||||
});
|
|
||||||
|
|
||||||
fileInput.addEventListener('change', function (e) {
|
|
||||||
fileInput.addEventListener('file-input-change', async (e) => {
|
|
||||||
const {allFiles} = e.detail;
|
|
||||||
if (allFiles && allFiles.length > 0) {
|
|
||||||
canvasesContainer.style.display = 'block'; // set for visual purposes
|
|
||||||
file = e.target.files[0];
|
|
||||||
renderPageFromFile(file);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
function renderPage(pageNumber) {
|
|
||||||
pdfDoc.getPage(pageNumber).then(function (page) {
|
|
||||||
let viewport = page.getViewport({scale: 1.0});
|
|
||||||
pdfCanvas.width = viewport.width;
|
|
||||||
pdfCanvas.height = viewport.height;
|
|
||||||
|
|
||||||
overlayCanvas.width = viewport.width; // Match overlay canvas size with PDF canvas
|
|
||||||
overlayCanvas.height = viewport.height;
|
|
||||||
|
|
||||||
let renderContext = {canvasContext: context, viewport: viewport};
|
|
||||||
page.render(renderContext);
|
|
||||||
pdfCanvas.classList.add('shadow-canvas');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -1,213 +0,0 @@
|
|||||||
window.toggleSignatureView = toggleSignatureView;
|
|
||||||
window.previewSignature = previewSignature;
|
|
||||||
window.addSignatureFromPreview = addSignatureFromPreview;
|
|
||||||
window.addDraggableFromPad = addDraggableFromPad;
|
|
||||||
window.addDraggableFromText = addDraggableFromText;
|
|
||||||
window.goToFirstOrLastPage = goToFirstOrLastPage;
|
|
||||||
|
|
||||||
let currentPreviewSrc = null;
|
|
||||||
|
|
||||||
function toggleSignatureView() {
|
|
||||||
const gridView = document.getElementById('gridView');
|
|
||||||
const listView = document.getElementById('listView');
|
|
||||||
const gridText = document.querySelector('.grid-view-text');
|
|
||||||
const listText = document.querySelector('.list-view-text');
|
|
||||||
|
|
||||||
if (gridView.style.display !== 'none') {
|
|
||||||
gridView.style.display = 'none';
|
|
||||||
listView.style.display = 'block';
|
|
||||||
gridText.style.display = 'none';
|
|
||||||
listText.style.display = 'inline';
|
|
||||||
} else {
|
|
||||||
gridView.style.display = 'block';
|
|
||||||
listView.style.display = 'none';
|
|
||||||
gridText.style.display = 'inline';
|
|
||||||
listText.style.display = 'none';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function previewSignature(element) {
|
|
||||||
const src = element.dataset.src;
|
|
||||||
currentPreviewSrc = src;
|
|
||||||
|
|
||||||
const filename = element.querySelector('.signature-list-name').textContent;
|
|
||||||
|
|
||||||
const previewImage = document.getElementById('previewImage');
|
|
||||||
const previewFileName = document.getElementById('previewFileName');
|
|
||||||
|
|
||||||
previewImage.src = src;
|
|
||||||
previewFileName.textContent = filename;
|
|
||||||
|
|
||||||
const modal = new bootstrap.Modal(document.getElementById('signaturePreview'));
|
|
||||||
modal.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
function addSignatureFromPreview() {
|
|
||||||
if (currentPreviewSrc) {
|
|
||||||
DraggableUtils.createDraggableCanvasFromUrl(currentPreviewSrc);
|
|
||||||
bootstrap.Modal.getInstance(document.getElementById('signaturePreview')).hide();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let originalFileName = '';
|
|
||||||
document.querySelector('input[name=pdf-upload]').addEventListener('change', async (event) => {
|
|
||||||
const fileInput = event.target;
|
|
||||||
fileInput.addEventListener('file-input-change', async (e) => {
|
|
||||||
const {allFiles} = e.detail;
|
|
||||||
if (allFiles && allFiles.length > 0) {
|
|
||||||
const file = allFiles[0];
|
|
||||||
originalFileName = file.name.replace(/\.[^/.]+$/, '');
|
|
||||||
const pdfData = await file.arrayBuffer();
|
|
||||||
pdfjsLib.GlobalWorkerOptions.workerSrc = './pdfjs-legacy/pdf.worker.mjs';
|
|
||||||
const pdfDoc = await pdfjsLib.getDocument({data: pdfData}).promise;
|
|
||||||
await DraggableUtils.renderPage(pdfDoc, 0);
|
|
||||||
|
|
||||||
document.querySelectorAll('.show-on-file-selected').forEach((el) => {
|
|
||||||
el.style.cssText = '';
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
|
||||||
document.querySelectorAll('.show-on-file-selected').forEach((el) => {
|
|
||||||
el.style.cssText = 'display:none !important';
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
const imageUpload = document.querySelector('input[name=image-upload]');
|
|
||||||
imageUpload.addEventListener('change', (e) => {
|
|
||||||
if (!e.target.files) return;
|
|
||||||
for (const imageFile of e.target.files) {
|
|
||||||
var reader = new FileReader();
|
|
||||||
reader.readAsDataURL(imageFile);
|
|
||||||
reader.onloadend = function (e) {
|
|
||||||
DraggableUtils.createDraggableCanvasFromUrl(e.target.result);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const signaturePadCanvas = document.getElementById('drawing-pad-canvas');
|
|
||||||
const signaturePad = new SignaturePad(signaturePadCanvas, {
|
|
||||||
minWidth: 1,
|
|
||||||
maxWidth: 2,
|
|
||||||
penColor: 'black',
|
|
||||||
});
|
|
||||||
|
|
||||||
function addDraggableFromPad() {
|
|
||||||
if (signaturePad.isEmpty()) return;
|
|
||||||
const startTime = Date.now();
|
|
||||||
const croppedDataUrl = getCroppedCanvasDataUrl(signaturePadCanvas);
|
|
||||||
console.log(Date.now() - startTime);
|
|
||||||
DraggableUtils.createDraggableCanvasFromUrl(croppedDataUrl);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getCroppedCanvasDataUrl(canvas) {
|
|
||||||
let originalCtx = canvas.getContext('2d');
|
|
||||||
let originalWidth = canvas.width;
|
|
||||||
let originalHeight = canvas.height;
|
|
||||||
let imageData = originalCtx.getImageData(0, 0, originalWidth, originalHeight);
|
|
||||||
|
|
||||||
let minX = originalWidth + 1,
|
|
||||||
maxX = -1,
|
|
||||||
minY = originalHeight + 1,
|
|
||||||
maxY = -1,
|
|
||||||
x = 0,
|
|
||||||
y = 0,
|
|
||||||
currentPixelColorValueIndex;
|
|
||||||
|
|
||||||
for (y = 0; y < originalHeight; y++) {
|
|
||||||
for (x = 0; x < originalWidth; x++) {
|
|
||||||
currentPixelColorValueIndex = (y * originalWidth + x) * 4;
|
|
||||||
let currentPixelAlphaValue = imageData.data[currentPixelColorValueIndex + 3];
|
|
||||||
if (currentPixelAlphaValue > 0) {
|
|
||||||
if (minX > x) minX = x;
|
|
||||||
if (maxX < x) maxX = x;
|
|
||||||
if (minY > y) minY = y;
|
|
||||||
if (maxY < y) maxY = y;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let croppedWidth = maxX - minX;
|
|
||||||
let croppedHeight = maxY - minY;
|
|
||||||
if (croppedWidth < 0 || croppedHeight < 0) return null;
|
|
||||||
let cuttedImageData = originalCtx.getImageData(minX, minY, croppedWidth, croppedHeight);
|
|
||||||
|
|
||||||
let croppedCanvas = document.createElement('canvas'),
|
|
||||||
croppedCtx = croppedCanvas.getContext('2d');
|
|
||||||
|
|
||||||
croppedCanvas.width = croppedWidth;
|
|
||||||
croppedCanvas.height = croppedHeight;
|
|
||||||
croppedCtx.putImageData(cuttedImageData, 0, 0);
|
|
||||||
|
|
||||||
return croppedCanvas.toDataURL();
|
|
||||||
}
|
|
||||||
|
|
||||||
function resizeCanvas() {
|
|
||||||
var ratio = Math.max(window.devicePixelRatio || 1, 1);
|
|
||||||
var additionalFactor = 10;
|
|
||||||
|
|
||||||
signaturePadCanvas.width = signaturePadCanvas.offsetWidth * ratio * additionalFactor;
|
|
||||||
signaturePadCanvas.height = signaturePadCanvas.offsetHeight * ratio * additionalFactor;
|
|
||||||
signaturePadCanvas.getContext('2d').scale(ratio * additionalFactor, ratio * additionalFactor);
|
|
||||||
|
|
||||||
signaturePad.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
new IntersectionObserver((entries, observer) => {
|
|
||||||
if (entries.some((entry) => entry.intersectionRatio > 0)) {
|
|
||||||
resizeCanvas();
|
|
||||||
}
|
|
||||||
}).observe(signaturePadCanvas);
|
|
||||||
|
|
||||||
new ResizeObserver(resizeCanvas).observe(signaturePadCanvas);
|
|
||||||
|
|
||||||
function addDraggableFromText() {
|
|
||||||
const sigText = document.getElementById('sigText').value;
|
|
||||||
const font = document.querySelector('select[name=font]').value;
|
|
||||||
const fontSize = 100;
|
|
||||||
|
|
||||||
const canvas = document.createElement('canvas');
|
|
||||||
const ctx = canvas.getContext('2d');
|
|
||||||
ctx.font = `${fontSize}px ${font}`;
|
|
||||||
const textWidth = ctx.measureText(sigText).width;
|
|
||||||
const textHeight = fontSize;
|
|
||||||
|
|
||||||
let paragraphs = sigText.split(/\r?\n/);
|
|
||||||
|
|
||||||
canvas.width = textWidth;
|
|
||||||
canvas.height = paragraphs.length * textHeight * 1.35; // for tails
|
|
||||||
ctx.font = `${fontSize}px ${font}`;
|
|
||||||
|
|
||||||
ctx.textBaseline = 'top';
|
|
||||||
|
|
||||||
let y = 0;
|
|
||||||
|
|
||||||
paragraphs.forEach((paragraph) => {
|
|
||||||
ctx.fillText(paragraph, 0, y);
|
|
||||||
y += fontSize;
|
|
||||||
});
|
|
||||||
|
|
||||||
const dataURL = canvas.toDataURL();
|
|
||||||
DraggableUtils.createDraggableCanvasFromUrl(dataURL);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function goToFirstOrLastPage(page) {
|
|
||||||
if (page) {
|
|
||||||
const lastPage = DraggableUtils.pdfDoc.numPages;
|
|
||||||
await DraggableUtils.goToPage(lastPage - 1);
|
|
||||||
} else {
|
|
||||||
await DraggableUtils.goToPage(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
document.getElementById('download-pdf').addEventListener('click', async () => {
|
|
||||||
const modifiedPdf = await DraggableUtils.getOverlayedPdfDocument();
|
|
||||||
const modifiedPdfBytes = await modifiedPdf.save();
|
|
||||||
const blob = new Blob([modifiedPdfBytes], {type: 'application/pdf'});
|
|
||||||
const link = document.createElement('a');
|
|
||||||
link.href = URL.createObjectURL(blob);
|
|
||||||
link.download = originalFileName + '_signed.pdf';
|
|
||||||
link.click();
|
|
||||||
});
|
|
||||||
@@ -196,7 +196,7 @@
|
|||||||
/*<![CDATA[*/
|
/*<![CDATA[*/
|
||||||
const urlGetApiKey = /*[[@{/api/v1/user/get-api-key}]]*/ "/api/v1/user/get-api-key";
|
const urlGetApiKey = /*[[@{/api/v1/user/get-api-key}]]*/ "/api/v1/user/get-api-key";
|
||||||
/*]]>*/
|
/*]]>*/
|
||||||
let response = await window.fetchWithCsrf(urlGetApiKey, { method: 'POST' });
|
let response = await fetch(urlGetApiKey, { method: 'POST' });
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
let apiKey = await response.text();
|
let apiKey = await response.text();
|
||||||
manageUIState(apiKey);
|
manageUIState(apiKey);
|
||||||
@@ -213,7 +213,7 @@
|
|||||||
/*<![CDATA[*/
|
/*<![CDATA[*/
|
||||||
const urlUpdateApiKey = /*[[@{/api/v1/user/update-api-key}]]*/ "/api/v1/user/update-api-key";
|
const urlUpdateApiKey = /*[[@{/api/v1/user/update-api-key}]]*/ "/api/v1/user/update-api-key";
|
||||||
/*]]>*/
|
/*]]>*/
|
||||||
let response = await window.fetchWithCsrf(urlUpdateApiKey, { method: 'POST' });
|
let response = await fetch(urlUpdateApiKey, { method: 'POST' });
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
let apiKey = await response.text();
|
let apiKey = await response.text();
|
||||||
manageUIState(apiKey);
|
manageUIState(apiKey);
|
||||||
|
|||||||
@@ -34,8 +34,140 @@
|
|||||||
<canvas id="overlayCanvas" style="position: absolute; top: 0; left: 0; z-index: 2; width: 100%"></canvas>
|
<canvas id="overlayCanvas" style="position: absolute; top: 0; left: 0; z-index: 2; width: 100%"></canvas>
|
||||||
</div>
|
</div>
|
||||||
<script type="module" th:src="@{'/pdfjs-legacy/pdf.mjs'}"></script>
|
<script type="module" th:src="@{'/pdfjs-legacy/pdf.mjs'}"></script>
|
||||||
<script th:src="@{'/js/thirdParty/pdf-lib.min.js'}"></script>
|
<script>
|
||||||
<script type="module" th:src="@{'/js/pages/pdf-to-csv.js'}">
|
let pdfCanvas = document.getElementById('cropPdfCanvas');
|
||||||
|
let overlayCanvas = document.getElementById('overlayCanvas');
|
||||||
|
let canvasesContainer = document.getElementById('canvasesContainer');
|
||||||
|
canvasesContainer.style.display = "none";
|
||||||
|
// let paginationBtnContainer = ;
|
||||||
|
|
||||||
|
let context = pdfCanvas.getContext('2d');
|
||||||
|
let overlayContext = overlayCanvas.getContext('2d');
|
||||||
|
|
||||||
|
let btn1Object = document.getElementById('previous-page-btn');
|
||||||
|
let btn2Object = document.getElementById('next-page-btn');
|
||||||
|
overlayCanvas.width = pdfCanvas.width;
|
||||||
|
overlayCanvas.height = pdfCanvas.height;
|
||||||
|
|
||||||
|
let fileInput = document.getElementById('fileInput-input');
|
||||||
|
|
||||||
|
let file;
|
||||||
|
|
||||||
|
let pdfDoc = null;
|
||||||
|
let pageId = document.getElementById('pageId');
|
||||||
|
let currentPage = 1;
|
||||||
|
let totalPages = 0;
|
||||||
|
|
||||||
|
let startX = 0;
|
||||||
|
let startY = 0;
|
||||||
|
let rectWidth = 0;
|
||||||
|
let rectHeight = 0;
|
||||||
|
|
||||||
|
let timeId = null; // timeout id for resizing canvases event
|
||||||
|
|
||||||
|
btn1Object.addEventListener('click',function (e){
|
||||||
|
if (currentPage !== 1) {
|
||||||
|
currentPage = currentPage - 1;
|
||||||
|
pageId.value = currentPage;
|
||||||
|
|
||||||
|
if (file.type === 'application/pdf') {
|
||||||
|
let reader = new FileReader();
|
||||||
|
reader.onload = function (ev) {
|
||||||
|
let typedArray = new Uint8Array(reader.result);
|
||||||
|
pdfjsLib.GlobalWorkerOptions.workerSrc = './pdfjs-legacy/pdf.worker.mjs'
|
||||||
|
pdfjsLib.getDocument(typedArray).promise.then(function (pdf) {
|
||||||
|
pdfDoc = pdf;
|
||||||
|
totalPages = pdf.numPages;
|
||||||
|
renderPage(currentPage);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
reader.readAsArrayBuffer(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
btn2Object.addEventListener('click',function (e){
|
||||||
|
if (currentPage !== totalPages){
|
||||||
|
currentPage=currentPage+1;
|
||||||
|
pageId.value = currentPage;
|
||||||
|
|
||||||
|
if (file.type === 'application/pdf') {
|
||||||
|
let reader = new FileReader();
|
||||||
|
reader.onload = function(ev) {
|
||||||
|
let typedArray = new Uint8Array(reader.result);
|
||||||
|
pdfjsLib.GlobalWorkerOptions.workerSrc = './pdfjs-legacy/pdf.worker.mjs'
|
||||||
|
pdfjsLib.getDocument(typedArray).promise.then(function(pdf) {
|
||||||
|
pdfDoc = pdf;
|
||||||
|
totalPages = pdf.numPages;
|
||||||
|
renderPage(currentPage);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
reader.readAsArrayBuffer(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function renderPageFromFile(file) {
|
||||||
|
if (file.type === 'application/pdf') {
|
||||||
|
let reader = new FileReader();
|
||||||
|
reader.onload = function (ev) {
|
||||||
|
let typedArray = new Uint8Array(reader.result);
|
||||||
|
pdfjsLib.GlobalWorkerOptions.workerSrc = './pdfjs-legacy/pdf.worker.mjs';
|
||||||
|
pdfjsLib.getDocument(typedArray).promise.then(function (pdf) {
|
||||||
|
pdfDoc = pdf;
|
||||||
|
totalPages = pdf.numPages;
|
||||||
|
renderPage(currentPage);
|
||||||
|
});
|
||||||
|
pageId.value = currentPage;
|
||||||
|
};
|
||||||
|
reader.readAsArrayBuffer(file);
|
||||||
|
document.getElementById("pagination-button-container").style.display = "flex";
|
||||||
|
document.getElementById("instruction-text").style.display = "block";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener("resize", function() {
|
||||||
|
clearTimeout(timeId);
|
||||||
|
timeId = setTimeout(function () {
|
||||||
|
if (fileInput.files.length == 0) return;
|
||||||
|
let canvasesContainer = document.getElementById('canvasesContainer');
|
||||||
|
let containerRect = canvasesContainer.getBoundingClientRect();
|
||||||
|
|
||||||
|
context.clearRect(0, 0, pdfCanvas.width, pdfCanvas.height);
|
||||||
|
|
||||||
|
overlayContext.clearRect(0, 0, overlayCanvas.width, overlayCanvas.height);
|
||||||
|
|
||||||
|
pdfCanvas.width = containerRect.width;
|
||||||
|
pdfCanvas.height = containerRect.height;
|
||||||
|
|
||||||
|
overlayCanvas.width = containerRect.width;
|
||||||
|
overlayCanvas.height = containerRect.height;
|
||||||
|
|
||||||
|
let file = fileInput.files[0];
|
||||||
|
renderPageFromFile(file);
|
||||||
|
}, 1000);
|
||||||
|
});
|
||||||
|
|
||||||
|
fileInput.addEventListener('change', function(e) {
|
||||||
|
canvasesContainer.style.display = "block"; // set for visual purposes
|
||||||
|
file = e.target.files[0];
|
||||||
|
renderPageFromFile(file);
|
||||||
|
});
|
||||||
|
|
||||||
|
function renderPage(pageNumber) {
|
||||||
|
pdfDoc.getPage(pageNumber).then(function(page) {
|
||||||
|
let viewport = page.getViewport({ scale: 1.0 });
|
||||||
|
pdfCanvas.width = viewport.width;
|
||||||
|
pdfCanvas.height = viewport.height;
|
||||||
|
|
||||||
|
overlayCanvas.width = viewport.width; // Match overlay canvas size with PDF canvas
|
||||||
|
overlayCanvas.height = viewport.height;
|
||||||
|
|
||||||
|
let renderContext = { canvasContext: context, viewport: viewport };
|
||||||
|
page.render(renderContext);
|
||||||
|
pdfCanvas.classList.add("shadow-canvas");
|
||||||
|
});
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -32,7 +32,163 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script type="module" th:src="@{'/pdfjs-legacy/pdf.mjs'}"></script>
|
<script type="module" th:src="@{'/pdfjs-legacy/pdf.mjs'}"></script>
|
||||||
<script type="module" th:src="@{'/js/pages/crop.js'}"></script>
|
<script>
|
||||||
|
let pdfCanvas = document.getElementById('cropPdfCanvas');
|
||||||
|
let overlayCanvas = document.getElementById('overlayCanvas');
|
||||||
|
let canvasesContainer = document.getElementById('canvasesContainer');
|
||||||
|
canvasesContainer.style.display = "none";
|
||||||
|
let containerRect = canvasesContainer.getBoundingClientRect();
|
||||||
|
|
||||||
|
let context = pdfCanvas.getContext('2d');
|
||||||
|
let overlayContext = overlayCanvas.getContext('2d');
|
||||||
|
|
||||||
|
overlayCanvas.width = pdfCanvas.width;
|
||||||
|
overlayCanvas.height = pdfCanvas.height;
|
||||||
|
|
||||||
|
let isDrawing = false; // New flag to check if drawing is ongoing
|
||||||
|
|
||||||
|
let cropForm = document.getElementById('cropForm');
|
||||||
|
let fileInput = document.getElementById('fileInput-input');
|
||||||
|
let xInput = document.getElementById('x');
|
||||||
|
let yInput = document.getElementById('y');
|
||||||
|
let widthInput = document.getElementById('width');
|
||||||
|
let heightInput = document.getElementById('height');
|
||||||
|
|
||||||
|
let pdfDoc = null;
|
||||||
|
let currentPage = 1;
|
||||||
|
let totalPages = 0;
|
||||||
|
|
||||||
|
let startX = 0;
|
||||||
|
let startY = 0;
|
||||||
|
let rectWidth = 0;
|
||||||
|
let rectHeight = 0;
|
||||||
|
|
||||||
|
|
||||||
|
let pageScale = 1; // The scale which the pdf page renders
|
||||||
|
let timeId = null; // timeout id for resizing canvases event
|
||||||
|
|
||||||
|
function renderPageFromFile(file) {
|
||||||
|
if (file.type === 'application/pdf') {
|
||||||
|
let reader = new FileReader();
|
||||||
|
reader.onload = function(ev) {
|
||||||
|
let typedArray = new Uint8Array(reader.result);
|
||||||
|
pdfjsLib.GlobalWorkerOptions.workerSrc = './pdfjs-legacy/pdf.worker.mjs'
|
||||||
|
pdfjsLib.getDocument(typedArray).promise.then(function(pdf) {
|
||||||
|
pdfDoc = pdf;
|
||||||
|
totalPages = pdf.numPages;
|
||||||
|
renderPage(currentPage);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
reader.readAsArrayBuffer(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener("resize", function() {
|
||||||
|
clearTimeout(timeId);
|
||||||
|
|
||||||
|
timeId = setTimeout(function () {
|
||||||
|
if (fileInput.files.length == 0) return;
|
||||||
|
let canvasesContainer = document.getElementById('canvasesContainer');
|
||||||
|
let containerRect = canvasesContainer.getBoundingClientRect();
|
||||||
|
|
||||||
|
context.clearRect(0, 0, pdfCanvas.width, pdfCanvas.height);
|
||||||
|
|
||||||
|
overlayContext.clearRect(0, 0, overlayCanvas.width, overlayCanvas.height);
|
||||||
|
|
||||||
|
pdfCanvas.width = containerRect.width;
|
||||||
|
pdfCanvas.height = containerRect.height;
|
||||||
|
|
||||||
|
overlayCanvas.width = containerRect.width;
|
||||||
|
overlayCanvas.height = containerRect.height;
|
||||||
|
|
||||||
|
let file = fileInput.files[0];
|
||||||
|
renderPageFromFile(file);
|
||||||
|
}, 1000);
|
||||||
|
});
|
||||||
|
|
||||||
|
fileInput.addEventListener('change', function(e) {
|
||||||
|
canvasesContainer.style.display = "block"; // set for visual purposes
|
||||||
|
let file = e.target.files[0];
|
||||||
|
renderPageFromFile(file);
|
||||||
|
});
|
||||||
|
|
||||||
|
cropForm.addEventListener('submit', function(e) {
|
||||||
|
if (xInput.value == "" && yInput.value == "" && widthInput.value == "" && heightInput.value == "") {
|
||||||
|
// Ορίστε συντεταγμένες για ολόκληρη την επιφάνεια του PDF
|
||||||
|
xInput.value = 0;
|
||||||
|
yInput.value = 0;
|
||||||
|
widthInput.value = containerRect.width;
|
||||||
|
heightInput.value = containerRect.height;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
overlayCanvas.addEventListener('mousedown', function(e) {
|
||||||
|
// Clear previously drawn rectangle on the main canvas
|
||||||
|
context.clearRect(0, 0, pdfCanvas.width, pdfCanvas.height);
|
||||||
|
renderPage(currentPage); // Re-render the PDF
|
||||||
|
|
||||||
|
// Clear the overlay canvas to ensure old drawings are removed
|
||||||
|
overlayContext.clearRect(0, 0, overlayCanvas.width, overlayCanvas.height);
|
||||||
|
|
||||||
|
startX = e.offsetX;
|
||||||
|
startY = e.offsetY;
|
||||||
|
isDrawing = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
overlayCanvas.addEventListener('mousemove', function(e) {
|
||||||
|
if (!isDrawing) return;
|
||||||
|
overlayContext.clearRect(0, 0, overlayCanvas.width, overlayCanvas.height); // Clear previous rectangle
|
||||||
|
|
||||||
|
rectWidth = e.offsetX - startX;
|
||||||
|
rectHeight = e.offsetY - startY;
|
||||||
|
overlayContext.strokeStyle = 'red';
|
||||||
|
overlayContext.strokeRect(startX, startY, rectWidth, rectHeight);
|
||||||
|
});
|
||||||
|
|
||||||
|
overlayCanvas.addEventListener('mouseup', function(e) {
|
||||||
|
isDrawing = false;
|
||||||
|
|
||||||
|
rectWidth = e.offsetX - startX;
|
||||||
|
rectHeight = e.offsetY - startY;
|
||||||
|
|
||||||
|
let flippedY = pdfCanvas.height - e.offsetY;
|
||||||
|
|
||||||
|
xInput.value = startX / pageScale;
|
||||||
|
yInput.value = flippedY / pageScale;
|
||||||
|
widthInput.value = rectWidth / pageScale;
|
||||||
|
heightInput.value = rectHeight /pageScale;
|
||||||
|
|
||||||
|
// Draw the final rectangle on the main canvas
|
||||||
|
context.strokeStyle = 'red';
|
||||||
|
context.strokeRect(startX, startY, rectWidth, rectHeight);
|
||||||
|
|
||||||
|
overlayContext.clearRect(0, 0, overlayCanvas.width, overlayCanvas.height); // Clear the overlay
|
||||||
|
});
|
||||||
|
|
||||||
|
function renderPage(pageNumber) {
|
||||||
|
pdfDoc.getPage(pageNumber).then(function(page) {
|
||||||
|
let canvasesContainer = document.getElementById('canvasesContainer');
|
||||||
|
let containerRect = canvasesContainer.getBoundingClientRect();
|
||||||
|
|
||||||
|
pageScale = containerRect.width / page.getViewport({ scale: 1 }).width; // The new scale
|
||||||
|
|
||||||
|
let viewport = page.getViewport({ scale: containerRect.width / page.getViewport({ scale: 1 }).width });
|
||||||
|
|
||||||
|
canvasesContainer.width =viewport.width;
|
||||||
|
canvasesContainer.height = viewport.height;
|
||||||
|
|
||||||
|
pdfCanvas.width = viewport.width;
|
||||||
|
pdfCanvas.height = viewport.height;
|
||||||
|
|
||||||
|
overlayCanvas.width = viewport.width; // Match overlay canvas size with PDF canvas
|
||||||
|
overlayCanvas.height = viewport.height;
|
||||||
|
|
||||||
|
let renderContext = { canvasContext: context, viewport: viewport };
|
||||||
|
page.render(renderContext);
|
||||||
|
pdfCanvas.classList.add("shadow-canvas");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
|
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
<p th:text="#{error.contactTip}"></p>
|
<p th:text="#{error.contactTip}"></p>
|
||||||
<div id="button-group">
|
<div id="button-group">
|
||||||
<a href="https://github.com/Stirling-Tools/Stirling-PDF/issues" id="github-button" class="btn btn-primary" target="_blank" th:text="#{error.github}"></a>
|
<a href="https://github.com/Stirling-Tools/Stirling-PDF/issues" id="github-button" class="btn btn-primary" target="_blank" th:text="#{error.github}"></a>
|
||||||
<a href="https://discord.gg/HYmhKj45pU" id="discord-button" class="btn btn-primary" target="_blank" th:text="#{joinDiscord}"></a>
|
<a href="https://discord.gg/Cn8pWhQRxZ" id="discord-button" class="btn btn-primary" target="_blank" th:text="#{joinDiscord}"></a>
|
||||||
</div>
|
</div>
|
||||||
<a th:href="@{'/'}" id="home-button" class="home-button btn btn-primary" th:text="#{goHomepage}"></a>
|
<a th:href="@{'/'}" id="home-button" class="home-button btn btn-primary" th:text="#{goHomepage}"></a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
<script>
|
<script>
|
||||||
window.stirlingPDF = window.stirlingPDF || {};
|
window.stirlingPDF = window.stirlingPDF || {};
|
||||||
</script>
|
</script>
|
||||||
<script th:src="@{'/js/fetch-utils.js'}"></script>
|
|
||||||
<!-- jQuery -->
|
<!-- jQuery -->
|
||||||
<script th:src="@{'/js/thirdParty/jquery.min.js'}"></script>
|
<script th:src="@{'/js/thirdParty/jquery.min.js'}"></script>
|
||||||
<script th:src="@{'/js/thirdParty/jquery.validate.min.js'}"></script>
|
<script th:src="@{'/js/thirdParty/jquery.validate.min.js'}"></script>
|
||||||
@@ -203,17 +203,7 @@
|
|||||||
</script>
|
</script>
|
||||||
<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>
|
||||||
<script>
|
|
||||||
window.decrypt = {
|
|
||||||
passwordPrompt: '[[#{decrypt.passwordPrompt}]]',
|
|
||||||
cancelled: '[[#{decrypt.cancelled}]]',
|
|
||||||
noPassword: '[[#{decrypt.noPassword}]]',
|
|
||||||
invalidPassword: '[[#{decrypt.invalidPassword}]]',
|
|
||||||
invalidPasswordHeader: '[[#{decrypt.invalidPasswordHeader}]]',
|
|
||||||
unexpectedError: '[[#{decrypt.unexpectedError}]]',
|
|
||||||
serverError: '[[#{decrypt.serverError}]]',
|
|
||||||
success: '[[#{decrypt.success}]]',
|
|
||||||
};</script>
|
|
||||||
<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="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 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}">
|
<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}">
|
||||||
<label class="file-input-btn d-none">
|
<label class="file-input-btn d-none">
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<!-- Buttons to submit a ticket on GitHub and join Discord server -->
|
<!-- Buttons to submit a ticket on GitHub and join Discord server -->
|
||||||
<a href="https://github.com/Stirling-Tools/Stirling-PDF/issues" id="github-button" target="_blank" th:text="#{error.github}"></a>
|
<a href="https://github.com/Stirling-Tools/Stirling-PDF/issues" id="github-button" target="_blank" th:text="#{error.github}"></a>
|
||||||
<a href="https://discord.gg/HYmhKj45pU" id="discord-button" target="_blank" th:text="#{joinDiscord}"></a>
|
<a href="https://discord.gg/Cn8pWhQRxZ" id="discord-button" target="_blank" th:text="#{joinDiscord}"></a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script>
|
<script>
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user