Compare commits
5 Commits
v0.34.0
...
Frooodle-p
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d19d87b8f0 | ||
|
|
a22ce69bc3 | ||
|
|
59b60ebfb8 | ||
|
|
b9a014b5c7 | ||
|
|
9e30918aae |
117
.github/scripts/check_language_properties.py
vendored
117
.github/scripts/check_language_properties.py
vendored
@@ -9,9 +9,8 @@ The script also provides functionality to update the translation files to match
|
||||
adjusting the format.
|
||||
|
||||
Usage:
|
||||
python check_language_properties.py --reference-file <path_to_reference_file> --branch <branch_name> [--actor <actor_name>] [--files <list_of_changed_files>]
|
||||
python script_name.py --reference-file <path_to_reference_file> --branch <branch_name> [--files <list_of_changed_files>]
|
||||
"""
|
||||
|
||||
import copy
|
||||
import glob
|
||||
import os
|
||||
@@ -19,10 +18,6 @@ import argparse
|
||||
import re
|
||||
|
||||
|
||||
# Maximum size for properties files (e.g., 200 KB)
|
||||
MAX_FILE_SIZE = 200 * 1024
|
||||
|
||||
|
||||
def parse_properties_file(file_path):
|
||||
"""Parses a .properties file and returns a list of objects (including comments, empty lines, and line numbers)."""
|
||||
properties_list = []
|
||||
@@ -100,7 +95,7 @@ def write_json_file(file_path, updated_properties):
|
||||
def update_missing_keys(reference_file, file_list, branch=""):
|
||||
reference_properties = parse_properties_file(reference_file)
|
||||
for file_path in file_list:
|
||||
basename_current_file = os.path.basename(os.path.join(branch, file_path))
|
||||
basename_current_file = os.path.basename(branch + file_path)
|
||||
if (
|
||||
basename_current_file == os.path.basename(reference_file)
|
||||
or not file_path.endswith(".properties")
|
||||
@@ -108,7 +103,7 @@ def update_missing_keys(reference_file, file_list, branch=""):
|
||||
):
|
||||
continue
|
||||
|
||||
current_properties = parse_properties_file(os.path.join(branch, file_path))
|
||||
current_properties = parse_properties_file(branch + file_path)
|
||||
updated_properties = []
|
||||
for ref_entry in reference_properties:
|
||||
ref_entry_copy = copy.deepcopy(ref_entry)
|
||||
@@ -119,79 +114,60 @@ def update_missing_keys(reference_file, file_list, branch=""):
|
||||
if ref_entry_copy["key"] == current_entry["key"]:
|
||||
ref_entry_copy["value"] = current_entry["value"]
|
||||
updated_properties.append(ref_entry_copy)
|
||||
write_json_file(os.path.join(branch, file_path), updated_properties)
|
||||
write_json_file(branch + file_path, updated_properties)
|
||||
|
||||
|
||||
def check_for_missing_keys(reference_file, file_list, branch):
|
||||
update_missing_keys(reference_file, file_list, branch)
|
||||
update_missing_keys(reference_file, file_list, branch + "/")
|
||||
|
||||
|
||||
def read_properties(file_path):
|
||||
if os.path.isfile(file_path) and os.path.exists(file_path):
|
||||
with open(file_path, "r", encoding="utf-8") as file:
|
||||
return file.read().splitlines()
|
||||
return [""]
|
||||
with open(file_path, "r", encoding="utf-8") as file:
|
||||
return file.read().splitlines()
|
||||
|
||||
|
||||
def check_for_differences(reference_file, file_list, branch, actor):
|
||||
def check_for_differences(reference_file, file_list, branch):
|
||||
reference_branch = reference_file.split("/")[0]
|
||||
basename_reference_file = os.path.basename(reference_file)
|
||||
|
||||
report = []
|
||||
report.append(f"#### 🔄 Reference Branch: `{reference_branch}`")
|
||||
report.append(
|
||||
f"### 📋 Checking with the file `{basename_reference_file}` from the `{reference_branch}` - Checking the `{branch}`"
|
||||
)
|
||||
reference_lines = read_properties(reference_file)
|
||||
has_differences = False
|
||||
|
||||
only_reference_file = True
|
||||
|
||||
file_arr = file_list
|
||||
|
||||
if len(file_list) == 1:
|
||||
file_arr = file_list[0].split()
|
||||
base_dir = os.path.abspath(os.path.join(os.getcwd(), "src", "main", "resources"))
|
||||
|
||||
for file_path in file_arr:
|
||||
absolute_path = os.path.abspath(file_path)
|
||||
# Verify that file is within the expected directory
|
||||
if not absolute_path.startswith(base_dir):
|
||||
raise ValueError(f"Unsafe file found: {file_path}")
|
||||
# Verify file size before processing
|
||||
if os.path.getsize(os.path.join(branch, file_path)) > MAX_FILE_SIZE:
|
||||
raise ValueError(
|
||||
f"The file {file_path} is too large and could pose a security risk."
|
||||
)
|
||||
|
||||
basename_current_file = os.path.basename(os.path.join(branch, file_path))
|
||||
for file_path in file_list:
|
||||
basename_current_file = os.path.basename(branch + "/" + file_path)
|
||||
if (
|
||||
basename_current_file == basename_reference_file
|
||||
or not file_path.startswith(
|
||||
os.path.join("src", "main", "resources", "messages_")
|
||||
)
|
||||
or not file_path.endswith(".properties")
|
||||
or not basename_current_file.startswith("messages_")
|
||||
):
|
||||
continue
|
||||
only_reference_file = False
|
||||
report.append(f"#### 📃 **File Check:** `{basename_current_file}`")
|
||||
current_lines = read_properties(os.path.join(branch, file_path))
|
||||
report.append(f"#### 🗂️ **Checking File:** `{basename_current_file}`...")
|
||||
current_lines = read_properties(branch + "/" + file_path)
|
||||
reference_line_count = len(reference_lines)
|
||||
current_line_count = len(current_lines)
|
||||
|
||||
if reference_line_count != current_line_count:
|
||||
report.append("")
|
||||
report.append("1. **Test Status:** ❌ **_Failed_**")
|
||||
report.append(" - **Issue:**")
|
||||
report.append("- **Test 1 Status:** ❌ Failed")
|
||||
has_differences = True
|
||||
if reference_line_count > current_line_count:
|
||||
report.append(
|
||||
f" - **_Mismatched line count_**: {reference_line_count} (reference) vs {current_line_count} (current). Comments, empty lines, or translation strings are missing."
|
||||
f" - **Issue:** Missing lines! Comments, empty lines, or translation strings are missing. Details: {reference_line_count} (reference) vs {current_line_count} (current)."
|
||||
)
|
||||
elif reference_line_count < current_line_count:
|
||||
report.append(
|
||||
f" - **_Too many lines_**: {reference_line_count} (reference) vs {current_line_count} (current). Please verify if there is an additional line that needs to be removed."
|
||||
f" - **Issue:** Too many lines! Check your translation files! Details: {reference_line_count} (reference) vs {current_line_count} (current)."
|
||||
)
|
||||
# update_missing_keys(reference_file, [file_path], branch + "/")
|
||||
else:
|
||||
report.append("1. **Test Status:** ✅ **_Passed_**")
|
||||
report.append("- **Test 1 Status:** ✅ Passed")
|
||||
|
||||
# Check for missing or extra keys
|
||||
current_keys = []
|
||||
@@ -216,42 +192,32 @@ def check_for_differences(reference_file, file_list, branch, actor):
|
||||
has_differences = True
|
||||
missing_keys_str = "`, `".join(missing_keys_list)
|
||||
extra_keys_str = "`, `".join(extra_keys_list)
|
||||
report.append("2. **Test Status:** ❌ **_Failed_**")
|
||||
report.append(" - **Issue:**")
|
||||
report.append("- **Test 2 Status:** ❌ Failed")
|
||||
if missing_keys_list:
|
||||
spaces_keys_list = []
|
||||
for key in missing_keys_list:
|
||||
if " " in key:
|
||||
spaces_keys_list.append(key)
|
||||
if spaces_keys_list:
|
||||
spaces_keys_str = "`, `".join(spaces_keys_list)
|
||||
report.append(
|
||||
f" - **_Keys containing unnecessary spaces_**: `{spaces_keys_str}`!"
|
||||
)
|
||||
report.append(
|
||||
f" - **_Extra keys in `{basename_current_file}`_**: `{missing_keys_str}` that are not present in **_`{basename_reference_file}`_**."
|
||||
f" - **Issue:** There are keys in ***{basename_current_file}*** `{missing_keys_str}` that are not present in ***{basename_reference_file}***!"
|
||||
)
|
||||
if extra_keys_list:
|
||||
report.append(
|
||||
f" - **_Missing keys in `{basename_reference_file}`_**: `{extra_keys_str}` that are not present in **_`{basename_current_file}`_**."
|
||||
f" - **Issue:** There are keys in ***{basename_reference_file}*** `{extra_keys_str}` that are not present in ***{basename_current_file}***!"
|
||||
)
|
||||
# update_missing_keys(reference_file, [file_path], branch + "/")
|
||||
else:
|
||||
report.append("2. **Test Status:** ✅ **_Passed_**")
|
||||
report.append("- **Test 2 Status:** ✅ Passed")
|
||||
# if has_differences:
|
||||
# report.append("")
|
||||
# report.append(f"#### 🚧 ***{basename_current_file}*** will be corrected...")
|
||||
report.append("")
|
||||
report.append("---")
|
||||
report.append("")
|
||||
# update_file_list = glob.glob(branch + "/src/**/messages_*.properties", recursive=True)
|
||||
# update_missing_keys(reference_file, update_file_list)
|
||||
# report.append("---")
|
||||
# report.append("")
|
||||
if has_differences:
|
||||
report.append("## ❌ Overall Check Status: **_Failed_**")
|
||||
report.append("")
|
||||
report.append(
|
||||
f"@{actor} please check your translation if it conforms to the standard. Follow the format of [messages_en_GB.properties](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/src/main/resources/messages_en_GB.properties)"
|
||||
)
|
||||
else:
|
||||
report.append("## ✅ Overall Check Status: **_Success_**")
|
||||
report.append("")
|
||||
report.append(
|
||||
f"Thanks @{actor} for your help in keeping the translations up to date."
|
||||
)
|
||||
|
||||
if not only_reference_file:
|
||||
print("\n".join(report))
|
||||
@@ -259,11 +225,6 @@ def check_for_differences(reference_file, file_list, branch, actor):
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description="Find missing keys")
|
||||
parser.add_argument(
|
||||
"--actor",
|
||||
required=False,
|
||||
help="Actor from PR.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--reference-file",
|
||||
required=True,
|
||||
@@ -283,21 +244,11 @@ if __name__ == "__main__":
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
# Sanitize --actor input to avoid injection attacks
|
||||
if args.actor:
|
||||
args.actor = re.sub(r"[^a-zA-Z0-9_\\-]", "", args.actor)
|
||||
|
||||
# Sanitize --branch input to avoid injection attacks
|
||||
if args.branch:
|
||||
args.branch = re.sub(r"[^a-zA-Z0-9\\-]", "", args.branch)
|
||||
|
||||
file_list = args.files
|
||||
if file_list is None:
|
||||
file_list = glob.glob(
|
||||
os.path.join(
|
||||
os.getcwd(), "src", "main", "resources", "messages_*.properties"
|
||||
)
|
||||
os.getcwd() + "/src/**/messages_*.properties", recursive=True
|
||||
)
|
||||
update_missing_keys(args.reference_file, file_list)
|
||||
else:
|
||||
check_for_differences(args.reference_file, file_list, args.branch, args.actor)
|
||||
check_for_differences(args.reference_file, file_list, args.branch)
|
||||
|
||||
81
.github/workflows/check_properties.yml
vendored
81
.github/workflows/check_properties.yml
vendored
@@ -6,10 +6,14 @@ on:
|
||||
paths:
|
||||
- "src/main/resources/messages_*.properties"
|
||||
push:
|
||||
branches: ["main"]
|
||||
paths:
|
||||
- "src/main/resources/messages_en_GB.properties"
|
||||
|
||||
# Permissions required for the workflow
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
check-files:
|
||||
if: github.event_name == 'pull_request_target'
|
||||
@@ -50,7 +54,10 @@ jobs:
|
||||
echo "Getting list of changed files from PR..."
|
||||
gh pr view ${{ github.event.pull_request.number }} --json files -q ".files[].path" | grep -E '^src/main/resources/messages_[a-zA-Z_]+\.properties$' > ../changed_files.txt
|
||||
cd ..
|
||||
|
||||
echo "Setting branch path..."
|
||||
BRANCH_PATH="pr-branch"
|
||||
|
||||
echo "BRANCH_PATH=${BRANCH_PATH}" >> $GITHUB_ENV
|
||||
echo "Processing changed files..."
|
||||
mapfile -t CHANGED_FILES < changed_files.txt
|
||||
|
||||
@@ -58,6 +65,7 @@ jobs:
|
||||
echo "CHANGED_FILES=${CHANGED_FILES_STR}" >> $GITHUB_ENV
|
||||
|
||||
echo "Changed files: ${CHANGED_FILES_STR}"
|
||||
echo "Branch: ${BRANCH_PATH}"
|
||||
|
||||
- name: Determine reference file
|
||||
id: determine-file
|
||||
@@ -79,40 +87,31 @@ jobs:
|
||||
run: |
|
||||
echo "Running Python script to check files..."
|
||||
python main-branch/.github/scripts/check_language_properties.py \
|
||||
--actor ${{ github.event.pull_request.user.login }} \
|
||||
--reference-file "${REFERENCE_FILE}" \
|
||||
--branch pr-branch \
|
||||
--files "${CHANGED_FILES[@]}" > result.txt || true
|
||||
--branch "${BRANCH_PATH}" \
|
||||
--files ${CHANGED_FILES} > failure.txt || true
|
||||
|
||||
- name: Capture output
|
||||
id: capture-output
|
||||
run: |
|
||||
if [ -f result.txt ] && [ -s result.txt ]; then
|
||||
echo "Test, capturing output..."
|
||||
SCRIPT_OUTPUT=$(cat result.txt)
|
||||
echo "SCRIPT_OUTPUT<<EOF" >> $GITHUB_ENV
|
||||
echo "$SCRIPT_OUTPUT" >> $GITHUB_ENV
|
||||
if [ -f failure.txt ] && [ -s failure.txt ]; then
|
||||
echo "Test failed, capturing output..."
|
||||
ERROR_OUTPUT=$(cat failure.txt)
|
||||
echo "ERROR_OUTPUT<<EOF" >> $GITHUB_ENV
|
||||
echo "$ERROR_OUTPUT" >> $GITHUB_ENV
|
||||
echo "EOF" >> $GITHUB_ENV
|
||||
echo "${SCRIPT_OUTPUT}"
|
||||
|
||||
# Set FAIL_JOB to true if SCRIPT_OUTPUT contains ❌
|
||||
if [[ "$SCRIPT_OUTPUT" == *"❌"* ]]; then
|
||||
echo "FAIL_JOB=true" >> $GITHUB_ENV
|
||||
else
|
||||
echo "FAIL_JOB=false" >> $GITHUB_ENV
|
||||
fi
|
||||
echo "${ERROR_OUTPUT}"
|
||||
else
|
||||
echo "No update found."
|
||||
echo "SCRIPT_OUTPUT=" >> $GITHUB_ENV
|
||||
echo "FAIL_JOB=false" >> $GITHUB_ENV
|
||||
echo "No errors found."
|
||||
echo "ERROR_OUTPUT=" >> $GITHUB_ENV
|
||||
fi
|
||||
|
||||
- name: Post comment on PR
|
||||
if: env.SCRIPT_OUTPUT != ''
|
||||
if: env.ERROR_OUTPUT != ''
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const { GITHUB_REPOSITORY, SCRIPT_OUTPUT } = process.env;
|
||||
const { GITHUB_REPOSITORY, ERROR_OUTPUT } = process.env;
|
||||
const [repoOwner, repoName] = GITHUB_REPOSITORY.split('/');
|
||||
const prNumber = context.issue.number;
|
||||
|
||||
@@ -124,7 +123,7 @@ jobs:
|
||||
});
|
||||
|
||||
const comment = comments.data.find(c => c.body.includes("## 🚀 Translation Verification Summary"));
|
||||
|
||||
|
||||
// Only allow the action user to update comments
|
||||
const expectedActor = "github-actions[bot]";
|
||||
|
||||
@@ -134,7 +133,7 @@ jobs:
|
||||
owner: repoOwner,
|
||||
repo: repoName,
|
||||
comment_id: comment.id,
|
||||
body: `## 🚀 Translation Verification Summary\n\n\n${SCRIPT_OUTPUT}\n`
|
||||
body: `## 🚀 Translation Verification Summary\n\n\n${ERROR_OUTPUT}\n`
|
||||
});
|
||||
console.log("Updated existing comment.");
|
||||
} else if (!comment) {
|
||||
@@ -143,24 +142,33 @@ jobs:
|
||||
owner: repoOwner,
|
||||
repo: repoName,
|
||||
issue_number: prNumber,
|
||||
body: `## 🚀 Translation Verification Summary\n\n\n${SCRIPT_OUTPUT}\n`
|
||||
body: `## 🚀 Translation Verification Summary\n\n\n${ERROR_OUTPUT}\n`
|
||||
});
|
||||
console.log("Created new comment.");
|
||||
} else {
|
||||
console.log("Comment update attempt denied. Actor does not match.");
|
||||
}
|
||||
|
||||
- name: Fail job if errors found
|
||||
if: env.FAIL_JOB == 'true'
|
||||
run: |
|
||||
echo "Failing the job because errors were detected."
|
||||
exit 1
|
||||
# - name: Set up git config
|
||||
# run: |
|
||||
# git config --global user.name "github-actions[bot]"
|
||||
# git config --global user.email "github-actions[bot]@users.noreply.github.com"
|
||||
|
||||
# - name: Add translation keys
|
||||
# run: |
|
||||
# cd ${{ env.BRANCH_PATH }}
|
||||
# git add src/main/resources/messages_*.properties
|
||||
# git diff --staged --quiet || echo "CHANGES_DETECTED=true" >> $GITHUB_ENV
|
||||
# git commit -m "Update translation files" || echo "No changes to commit"
|
||||
# - name: Push
|
||||
# if: env.CHANGES_DETECTED == 'true'
|
||||
# run: |
|
||||
# cd pr-branch
|
||||
# git remote set-url origin https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.event.pull_request.head.repo.full_name }}.git
|
||||
# git push origin ${{ github.head_ref }} || echo "Push failed: possibly no changes to push"
|
||||
|
||||
update-translations-main:
|
||||
if: github.event_name == 'push'
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
@@ -192,7 +200,7 @@ jobs:
|
||||
- name: Create Pull Request
|
||||
id: cpr
|
||||
if: env.CHANGES_DETECTED == 'true'
|
||||
uses: peter-evans/create-pull-request@v7
|
||||
uses: peter-evans/create-pull-request@v6
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
commit-message: "Update translation files"
|
||||
@@ -201,8 +209,6 @@ jobs:
|
||||
signoff: true
|
||||
branch: update_translation_files
|
||||
title: "Update translation files"
|
||||
add-paths: |
|
||||
src/main/resources/messages_*.properties
|
||||
body: |
|
||||
Auto-generated by [create-pull-request][1]
|
||||
|
||||
@@ -210,4 +216,3 @@ jobs:
|
||||
labels: Translation
|
||||
draft: false
|
||||
delete-branch: true
|
||||
sign-commits: true
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Build the application
|
||||
FROM gradle:8.11-jdk17 AS build
|
||||
FROM gradle:8.7-jdk17 AS build
|
||||
|
||||
# Set the working directory
|
||||
WORKDIR /app
|
||||
|
||||
69
README.md
69
README.md
@@ -186,47 +186,46 @@ Certain functionality like `Sign` supports pre-saved files stored at `/customFil
|
||||
|
||||
## Supported Languages
|
||||
|
||||
Stirling-PDF currently supports 37 languages!
|
||||
Stirling-PDF currently supports 36 languages!
|
||||
|
||||
| Language | Progress |
|
||||
| -------------------------------------------- | -------------------------------------- |
|
||||
| Arabic (العربية) (ar_AR) |  |
|
||||
| Azerbaijani (Azərbaycan Dili) (az_AZ) |  |
|
||||
| Basque (Euskara) (eu_ES) |  |
|
||||
| Bulgarian (Български) (bg_BG) |  |
|
||||
| Catalan (Català) (ca_CA) |  |
|
||||
| Croatian (Hrvatski) (hr_HR) |  |
|
||||
| Czech (Česky) (cs_CZ) |  |
|
||||
| Danish (Dansk) (da_DK) |  |
|
||||
| Dutch (Nederlands) (nl_NL) |  |
|
||||
| Arabic (العربية) (ar_AR) |  |
|
||||
| Basque (Euskara) (eu_ES) |  |
|
||||
| Bulgarian (Български) (bg_BG) |  |
|
||||
| Catalan (Català) (ca_CA) |  |
|
||||
| Croatian (Hrvatski) (hr_HR) |  |
|
||||
| Czech (Česky) (cs_CZ) |  |
|
||||
| Danish (Dansk) (da_DK) |  |
|
||||
| Dutch (Nederlands) (nl_NL) |  |
|
||||
| English (English) (en_GB) |  |
|
||||
| English (US) (en_US) |  |
|
||||
| French (Français) (fr_FR) |  |
|
||||
| German (Deutsch) (de_DE) |  |
|
||||
| Greek (Ελληνικά) (el_GR) |  |
|
||||
| Hindi (हिंदी) (hi_IN) |  |
|
||||
| Hungarian (Magyar) (hu_HU) |  |
|
||||
| Indonesian (Bahasa Indonesia) (id_ID) |  |
|
||||
| Irish (Gaeilge) (ga_IE) |  |
|
||||
| French (Français) (fr_FR) |  |
|
||||
| German (Deutsch) (de_DE) |  |
|
||||
| Greek (Ελληνικά) (el_GR) |  |
|
||||
| Hindi (हिंदी) (hi_IN) |  |
|
||||
| Hungarian (Magyar) (hu_HU) |  |
|
||||
| Indonesian (Bahasa Indonesia) (id_ID) |  |
|
||||
| Irish (Gaeilge) (ga_IE) |  |
|
||||
| Italian (Italiano) (it_IT) |  |
|
||||
| Japanese (日本語) (ja_JP) |  |
|
||||
| Korean (한국어) (ko_KR) |  |
|
||||
| Norwegian (Norsk) (no_NB) |  |
|
||||
| Polish (Polski) (pl_PL) |  |
|
||||
| Portuguese (Português) (pt_PT) |  |
|
||||
| Portuguese Brazilian (Português) (pt_BR) |  |
|
||||
| Romanian (Română) (ro_RO) |  |
|
||||
| Russian (Русский) (ru_RU) |  |
|
||||
| Serbian Latin alphabet (Srpski) (sr_LATN_RS) |  |
|
||||
| Simplified Chinese (简体中文) (zh_CN) |  |
|
||||
| Slovakian (Slovensky) (sk_SK) |  |
|
||||
| Spanish (Español) (es_ES) |  |
|
||||
| Swedish (Svenska) (sv_SE) |  |
|
||||
| Thai (ไทย) (th_TH) |  |
|
||||
| Traditional Chinese (繁體中文) (zh_TW) |  |
|
||||
| Turkish (Türkçe) (tr_TR) |  |
|
||||
| Ukrainian (Українська) (uk_UA) |  |
|
||||
| Vietnamese (Tiếng Việt) (vi_VN) |  |
|
||||
| Japanese (日本語) (ja_JP) |  |
|
||||
| Korean (한국어) (ko_KR) |  |
|
||||
| Norwegian (Norsk) (no_NB) |  |
|
||||
| Polish (Polski) (pl_PL) |  |
|
||||
| Portuguese (Português) (pt_PT) |  |
|
||||
| Portuguese Brazilian (Português) (pt_BR) |  |
|
||||
| Romanian (Română) (ro_RO) |  |
|
||||
| Russian (Русский) (ru_RU) |  |
|
||||
| Serbian Latin alphabet (Srpski) (sr_LATN_RS) |  |
|
||||
| Simplified Chinese (简体中文) (zh_CN) |  |
|
||||
| Slovakian (Slovensky) (sk_SK) |  |
|
||||
| Spanish (Español) (es_ES) |  |
|
||||
| Swedish (Svenska) (sv_SE) |  |
|
||||
| Thai (ไทย) (th_TH) |  |
|
||||
| Traditional Chinese (繁體中文) (zh_TW) |  |
|
||||
| Turkish (Türkçe) (tr_TR) |  |
|
||||
| Ukrainian (Українська) (uk_UA) |  |
|
||||
| Vietnamese (Tiếng Việt) (vi_VN) |  |
|
||||
|
||||
## Contributing (Creating Issues, Translations, Fixing Bugs, etc.)
|
||||
|
||||
|
||||
26
build.gradle
26
build.gradle
@@ -1,6 +1,6 @@
|
||||
plugins {
|
||||
id "java"
|
||||
id "org.springframework.boot" version "3.4.0"
|
||||
id "org.springframework.boot" version "3.3.5"
|
||||
id "io.spring.dependency-management" version "1.1.6"
|
||||
id "org.springdoc.openapi-gradle-plugin" version "1.8.0"
|
||||
id "io.swagger.swaggerhub" version "1.3.2"
|
||||
@@ -15,16 +15,16 @@ plugins {
|
||||
import com.github.jk1.license.render.*
|
||||
|
||||
ext {
|
||||
springBootVersion = "3.4.0"
|
||||
springBootVersion = "3.3.5"
|
||||
pdfboxVersion = "3.0.3"
|
||||
logbackVersion = "1.5.7"
|
||||
imageioVersion = "3.12.0"
|
||||
lombokVersion = "1.18.36"
|
||||
bouncycastleVersion = "1.79"
|
||||
bouncycastleVersion = "1.78.1"
|
||||
}
|
||||
|
||||
group = "stirling.software"
|
||||
version = "0.34.0"
|
||||
version = "0.33.1"
|
||||
|
||||
java {
|
||||
// 17 is lowest but we support and recommend 21
|
||||
@@ -121,7 +121,7 @@ configurations.all {
|
||||
}
|
||||
dependencies {
|
||||
//security updates
|
||||
implementation "org.springframework:spring-webmvc:6.2.0"
|
||||
implementation "org.springframework:spring-webmvc:6.1.14"
|
||||
|
||||
implementation("io.github.pixee:java-security-toolkit:1.2.0")
|
||||
|
||||
@@ -143,10 +143,11 @@ dependencies {
|
||||
implementation "org.springframework.boot:spring-boot-starter-data-jpa:$springBootVersion"
|
||||
implementation "org.springframework.boot:spring-boot-starter-oauth2-client:$springBootVersion"
|
||||
|
||||
implementation 'org.springframework.security:spring-security-saml2-service-provider:6.4.1'
|
||||
implementation 'org.springframework.security:spring-security-saml2-service-provider:6.3.4'
|
||||
implementation 'com.unboundid.product.scim2:scim2-sdk-client:2.3.5'
|
||||
// Don't upgrade h2database
|
||||
runtimeOnly "com.h2database:h2:2.3.232"
|
||||
//2.2.x requires rebuild of DB file.. need migration path
|
||||
runtimeOnly "com.h2database:h2:2.1.214"
|
||||
// implementation "com.h2database:h2:2.2.224"
|
||||
constraints {
|
||||
implementation "org.opensaml:opensaml-core"
|
||||
implementation "org.opensaml:opensaml-saml-api"
|
||||
@@ -202,19 +203,12 @@ dependencies {
|
||||
exclude group: "commons-logging", module: "commons-logging"
|
||||
}
|
||||
|
||||
// https://mvnrepository.com/artifact/technology.tabula/tabula
|
||||
implementation ('technology.tabula:tabula:1.0.5') {
|
||||
exclude group: "org.slf4j", module: "slf4j-simple"
|
||||
exclude group: "org.bouncycastle", module: "bcprov-jdk15on"
|
||||
exclude group: "com.google.code.gson", module: "gson"
|
||||
}
|
||||
|
||||
implementation 'org.apache.pdfbox:jbig2-imageio:3.0.4'
|
||||
|
||||
implementation "org.bouncycastle:bcprov-jdk18on:$bouncycastleVersion"
|
||||
implementation "org.bouncycastle:bcpkix-jdk18on:$bouncycastleVersion"
|
||||
implementation "org.springframework.boot:spring-boot-starter-actuator:$springBootVersion"
|
||||
implementation "io.micrometer:micrometer-core:1.14.1"
|
||||
implementation "io.micrometer:micrometer-core:1.13.6"
|
||||
implementation group: "com.google.zxing", name: "core", version: "3.5.3"
|
||||
// https://mvnrepository.com/artifact/org.commonmark/commonmark
|
||||
implementation "org.commonmark:commonmark:0.24.0"
|
||||
|
||||
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,5 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
@@ -3,11 +3,6 @@ ignore = [
|
||||
'language.direction',
|
||||
]
|
||||
|
||||
[az_AZ]
|
||||
ignore = [
|
||||
'language.direction',
|
||||
]
|
||||
|
||||
[bg_BG]
|
||||
ignore = [
|
||||
'language.direction',
|
||||
|
||||
@@ -452,7 +452,7 @@ public class SecurityConfiguration {
|
||||
RelyingPartyRegistration rp =
|
||||
RelyingPartyRegistration.withRegistrationId(samlConf.getRegistrationId())
|
||||
.signingX509Credentials((c) -> c.add(signingCredential))
|
||||
.assertingPartyMetadata(
|
||||
.assertingPartyDetails(
|
||||
(details) ->
|
||||
details.entityId(samlConf.getIdpIssuer())
|
||||
.singleSignOnServiceLocation(
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
package stirling.software.SPDF.controller.api.converters;
|
||||
|
||||
import java.io.StringWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.csv.CSVFormat;
|
||||
import org.apache.commons.csv.QuoteMode;
|
||||
import org.apache.pdfbox.Loader;
|
||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||
import org.apache.pdfbox.pdmodel.PDPage;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.ContentDisposition;
|
||||
@@ -18,36 +18,79 @@ import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.opencsv.CSVWriter;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
||||
import stirling.software.SPDF.controller.api.CropController;
|
||||
import stirling.software.SPDF.controller.api.strippers.PDFTableStripper;
|
||||
import stirling.software.SPDF.model.api.extract.PDFFilePage;
|
||||
import stirling.software.SPDF.pdf.FlexibleCSVWriter;
|
||||
import technology.tabula.ObjectExtractor;
|
||||
import technology.tabula.Page;
|
||||
import technology.tabula.Table;
|
||||
import technology.tabula.extractors.SpreadsheetExtractionAlgorithm;
|
||||
import technology.tabula.writers.Writer;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/convert")
|
||||
@Tag(name = "Convert", description = "Convert APIs")
|
||||
public class ExtractCSVController {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(ExtractCSVController.class);
|
||||
private static final Logger logger = LoggerFactory.getLogger(CropController.class);
|
||||
|
||||
@PostMapping(value = "/pdf/csv", consumes = "multipart/form-data")
|
||||
@Operation(summary = "Extracts a CSV document from a PDF", description = "This operation takes an input PDF file and returns CSV file of whole page. Input:PDF Output:CSV Type:SISO")
|
||||
@Operation(
|
||||
summary = "Extracts a CSV document from a PDF",
|
||||
description =
|
||||
"This operation takes an input PDF file and returns CSV file of whole page. Input:PDF Output:CSV Type:SISO")
|
||||
public ResponseEntity<String> PdfToCsv(@ModelAttribute PDFFilePage form) throws Exception {
|
||||
StringWriter writer = new StringWriter();
|
||||
|
||||
ArrayList<String> tableData = new ArrayList<>();
|
||||
int columnsCount = 0;
|
||||
|
||||
try (PDDocument document = Loader.loadPDF(form.getFileInput().getBytes())) {
|
||||
CSVFormat format = CSVFormat.EXCEL.builder().setEscape('"').setQuoteMode(QuoteMode.ALL).build();
|
||||
Writer csvWriter = new FlexibleCSVWriter(format);
|
||||
SpreadsheetExtractionAlgorithm sea = new SpreadsheetExtractionAlgorithm();
|
||||
try (ObjectExtractor extractor = new ObjectExtractor(document)) {
|
||||
Page page = extractor.extract(form.getPageId());
|
||||
List<Table> tables = sea.extract(page);
|
||||
csvWriter.write(writer, tables);
|
||||
final double res = 72; // PDF units are at 72 DPI
|
||||
PDFTableStripper stripper = new PDFTableStripper();
|
||||
PDPage pdPage = document.getPage(form.getPageId() - 1);
|
||||
stripper.extractTable(pdPage);
|
||||
columnsCount = stripper.getColumns();
|
||||
for (int c = 0; c < columnsCount; ++c) {
|
||||
for (int r = 0; r < stripper.getRows(); ++r) {
|
||||
tableData.add(stripper.getText(r, c));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ArrayList<String> notEmptyColumns = new ArrayList<>();
|
||||
|
||||
for (String item : tableData) {
|
||||
if (!item.trim().isEmpty()) {
|
||||
notEmptyColumns.add(item);
|
||||
} else {
|
||||
columnsCount--;
|
||||
}
|
||||
}
|
||||
|
||||
List<String> fullTable =
|
||||
notEmptyColumns.stream()
|
||||
.map(
|
||||
(entity) ->
|
||||
entity.replace('\n', ' ')
|
||||
.replace('\r', ' ')
|
||||
.trim()
|
||||
.replaceAll("\\s{2,}", "|"))
|
||||
.toList();
|
||||
|
||||
int rowsCount = fullTable.get(0).split("\\|").length;
|
||||
|
||||
ArrayList<String> headersList = getTableHeaders(columnsCount, fullTable);
|
||||
ArrayList<String> recordList = getRecordsList(rowsCount, fullTable);
|
||||
|
||||
if (headersList.size() == 0 && recordList.size() == 0) {
|
||||
throw new Exception("No table detected, no headers or records found");
|
||||
}
|
||||
|
||||
StringWriter writer = new StringWriter();
|
||||
try (CSVWriter csvWriter = new CSVWriter(writer)) {
|
||||
csvWriter.writeNext(headersList.toArray(new String[0]));
|
||||
for (String record : recordList) {
|
||||
csvWriter.writeNext(record.split("\\|"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,12 +99,41 @@ public class ExtractCSVController {
|
||||
ContentDisposition.builder("attachment")
|
||||
.filename(
|
||||
form.getFileInput()
|
||||
.getOriginalFilename()
|
||||
.replaceFirst("[.][^.]+$", "")
|
||||
.getOriginalFilename()
|
||||
.replaceFirst("[.][^.]+$", "")
|
||||
+ "_extracted.csv")
|
||||
.build());
|
||||
headers.setContentType(MediaType.parseMediaType("text/csv"));
|
||||
|
||||
return ResponseEntity.ok().headers(headers).body(writer.toString());
|
||||
}
|
||||
|
||||
private ArrayList<String> getRecordsList(int rowsCounts, List<String> items) {
|
||||
ArrayList<String> recordsList = new ArrayList<>();
|
||||
|
||||
for (int b = 1; b < rowsCounts; b++) {
|
||||
StringBuilder strbldr = new StringBuilder();
|
||||
|
||||
for (int i = 0; i < items.size(); i++) {
|
||||
String[] parts = items.get(i).split("\\|");
|
||||
strbldr.append(parts[b]);
|
||||
if (i != items.size() - 1) {
|
||||
strbldr.append("|");
|
||||
}
|
||||
}
|
||||
recordsList.add(strbldr.toString());
|
||||
}
|
||||
|
||||
return recordsList;
|
||||
}
|
||||
|
||||
private ArrayList<String> getTableHeaders(int columnsCount, List<String> items) {
|
||||
ArrayList<String> resultList = new ArrayList<>();
|
||||
for (int i = 0; i < columnsCount; i++) {
|
||||
String[] parts = items.get(i).split("\\|");
|
||||
resultList.add(parts[0]);
|
||||
}
|
||||
|
||||
return resultList;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,10 +98,10 @@ public class CertSignController {
|
||||
|
||||
public CreateSignature(KeyStore keystore, char[] pin)
|
||||
throws KeyStoreException,
|
||||
UnrecoverableKeyException,
|
||||
NoSuchAlgorithmException,
|
||||
IOException,
|
||||
CertificateException {
|
||||
UnrecoverableKeyException,
|
||||
NoSuchAlgorithmException,
|
||||
IOException,
|
||||
CertificateException {
|
||||
super(keystore, pin);
|
||||
ClassPathResource resource = new ClassPathResource("static/images/signature.png");
|
||||
try (InputStream is = resource.getInputStream()) {
|
||||
@@ -160,7 +160,8 @@ public class CertSignController {
|
||||
extState.setNonStrokingAlphaConstant(0.5f);
|
||||
cs.setGraphicsStateParameters(extState);
|
||||
cs.transform(Matrix.getScaleInstance(0.08f, 0.08f));
|
||||
PDImageXObject img = PDImageXObject.createFromFileByExtension(logoFile, doc);
|
||||
PDImageXObject img =
|
||||
PDImageXObject.createFromFileByExtension(logoFile, doc);
|
||||
cs.drawImage(img, 100, 0);
|
||||
cs.restoreGraphicsState();
|
||||
}
|
||||
@@ -208,7 +209,10 @@ public class CertSignController {
|
||||
}
|
||||
|
||||
@PostMapping(consumes = "multipart/form-data", value = "/cert-sign")
|
||||
@Operation(summary = "Sign PDF with a Digital Certificate", description = "This endpoint accepts a PDF file, a digital certificate and related information to sign the PDF. It then returns the digitally signed PDF file. Input:PDF Output:PDF Type:SISO")
|
||||
@Operation(
|
||||
summary = "Sign PDF with a Digital Certificate",
|
||||
description =
|
||||
"This endpoint accepts a PDF file, a digital certificate and related information to sign the PDF. It then returns the digitally signed PDF file. Input:PDF Output:PDF Type:SISO")
|
||||
public ResponseEntity<byte[]> signPDFWithCert(@ModelAttribute SignPDFWithCertRequest request)
|
||||
throws Exception {
|
||||
MultipartFile pdf = request.getFileInput();
|
||||
@@ -238,7 +242,7 @@ public class CertSignController {
|
||||
PrivateKey privateKey = getPrivateKeyFromPEM(privateKeyFile.getBytes(), password);
|
||||
Certificate cert = (Certificate) getCertificateFromPEM(certFile.getBytes());
|
||||
ks.setKeyEntry(
|
||||
"alias", privateKey, password.toCharArray(), new Certificate[] { cert });
|
||||
"alias", privateKey, password.toCharArray(), new Certificate[] {cert});
|
||||
break;
|
||||
case "PKCS12":
|
||||
ks = KeyStore.getInstance("PKCS12");
|
||||
@@ -310,19 +314,22 @@ public class CertSignController {
|
||||
|
||||
private PrivateKey getPrivateKeyFromPEM(byte[] pemBytes, String password)
|
||||
throws IOException, OperatorCreationException, PKCSException {
|
||||
try (PEMParser pemParser = new PEMParser(new InputStreamReader(new ByteArrayInputStream(pemBytes)))) {
|
||||
try (PEMParser pemParser =
|
||||
new PEMParser(new InputStreamReader(new ByteArrayInputStream(pemBytes)))) {
|
||||
Object pemObject = pemParser.readObject();
|
||||
JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC");
|
||||
PrivateKeyInfo pkInfo;
|
||||
if (pemObject instanceof PKCS8EncryptedPrivateKeyInfo) {
|
||||
InputDecryptorProvider decProv = new JceOpenSSLPKCS8DecryptorProviderBuilder()
|
||||
.build(password.toCharArray());
|
||||
InputDecryptorProvider decProv =
|
||||
new JceOpenSSLPKCS8DecryptorProviderBuilder().build(password.toCharArray());
|
||||
pkInfo = ((PKCS8EncryptedPrivateKeyInfo) pemObject).decryptPrivateKeyInfo(decProv);
|
||||
} else if (pemObject instanceof PEMEncryptedKeyPair) {
|
||||
PEMDecryptorProvider decProv = new JcePEMDecryptorProviderBuilder().build(password.toCharArray());
|
||||
pkInfo = ((PEMEncryptedKeyPair) pemObject)
|
||||
.decryptKeyPair(decProv)
|
||||
.getPrivateKeyInfo();
|
||||
PEMDecryptorProvider decProv =
|
||||
new JcePEMDecryptorProviderBuilder().build(password.toCharArray());
|
||||
pkInfo =
|
||||
((PEMEncryptedKeyPair) pemObject)
|
||||
.decryptKeyPair(decProv)
|
||||
.getPrivateKeyInfo();
|
||||
} else {
|
||||
pkInfo = ((PEMKeyPair) pemObject).getPrivateKeyInfo();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,327 @@
|
||||
package stirling.software.SPDF.controller.api.strippers;
|
||||
|
||||
import java.awt.Shape;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.Writer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.fontbox.util.BoundingBox;
|
||||
import org.apache.pdfbox.pdmodel.PDPage;
|
||||
import org.apache.pdfbox.pdmodel.common.PDRectangle;
|
||||
import org.apache.pdfbox.pdmodel.font.PDFont;
|
||||
import org.apache.pdfbox.pdmodel.font.PDType3Font;
|
||||
import org.apache.pdfbox.text.PDFTextStripper;
|
||||
import org.apache.pdfbox.text.PDFTextStripperByArea;
|
||||
import org.apache.pdfbox.text.TextPosition;
|
||||
|
||||
/**
|
||||
* Class to extract tabular data from a PDF. Works by making a first pass of the page to group all
|
||||
* nearby text items together, and then inferring a 2D grid from these regions. Each table cell is
|
||||
* then extracted using a PDFTextStripperByArea object.
|
||||
*
|
||||
* <p>Works best when headers are included in the detected region, to ensure representative text in
|
||||
* every column.
|
||||
*
|
||||
* <p>Based upon DrawPrintTextLocations PDFBox example
|
||||
* (https://svn.apache.org/viewvc/pdfbox/trunk/examples/src/main/java/org/apache/pdfbox/examples/util/DrawPrintTextLocations.java)
|
||||
*
|
||||
* @author Beldaz
|
||||
*/
|
||||
public class PDFTableStripper extends PDFTextStripper {
|
||||
|
||||
/**
|
||||
* This will print the documents data, for each table cell.
|
||||
*
|
||||
* @param args The command line arguments.
|
||||
* @throws IOException If there is an error parsing the document.
|
||||
*/
|
||||
/*
|
||||
* Used in methods derived from DrawPrintTextLocations
|
||||
*/
|
||||
private AffineTransform flipAT;
|
||||
|
||||
private AffineTransform rotateAT;
|
||||
|
||||
/** Regions updated by calls to writeString */
|
||||
private Set<Rectangle2D> boxes;
|
||||
|
||||
// Border to allow when finding intersections
|
||||
private double dx = 1.0; // This value works for me, feel free to tweak (or add setter)
|
||||
private double dy = 0.000; // Rows of text tend to overlap, so need to extend
|
||||
|
||||
/** Region in which to find table (otherwise whole page) */
|
||||
private Rectangle2D regionArea;
|
||||
|
||||
/** Number of rows in inferred table */
|
||||
private int nRows = 0;
|
||||
|
||||
/** Number of columns in inferred table */
|
||||
private int nCols = 0;
|
||||
|
||||
/** This is the object that does the text extraction */
|
||||
private PDFTextStripperByArea regionStripper;
|
||||
|
||||
/**
|
||||
* 1D intervals - used for calculateTableRegions()
|
||||
*
|
||||
* @author Beldaz
|
||||
*/
|
||||
public static class Interval {
|
||||
double start;
|
||||
double end;
|
||||
|
||||
public Interval(double start, double end) {
|
||||
this.start = start;
|
||||
this.end = end;
|
||||
}
|
||||
|
||||
public void add(Interval col) {
|
||||
if (col.start < start) start = col.start;
|
||||
if (col.end > end) end = col.end;
|
||||
}
|
||||
|
||||
public static void addTo(Interval x, LinkedList<Interval> columns) {
|
||||
int p = 0;
|
||||
Iterator<Interval> it = columns.iterator();
|
||||
// Find where x should go
|
||||
while (it.hasNext()) {
|
||||
Interval col = it.next();
|
||||
if (x.end >= col.start) {
|
||||
if (x.start <= col.end) { // overlaps
|
||||
x.add(col);
|
||||
it.remove();
|
||||
}
|
||||
break;
|
||||
}
|
||||
++p;
|
||||
}
|
||||
while (it.hasNext()) {
|
||||
Interval col = it.next();
|
||||
if (x.start > col.end) break;
|
||||
x.add(col);
|
||||
it.remove();
|
||||
}
|
||||
columns.add(p, x);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiate a new PDFTableStripper object.
|
||||
*
|
||||
* @throws IOException If there is an error loading the properties.
|
||||
*/
|
||||
public PDFTableStripper() throws IOException {
|
||||
super.setShouldSeparateByBeads(false);
|
||||
regionStripper = new PDFTextStripperByArea();
|
||||
regionStripper.setSortByPosition(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the region to group text by.
|
||||
*
|
||||
* @param rect The rectangle area to retrieve the text from.
|
||||
*/
|
||||
public void setRegion(Rectangle2D rect) {
|
||||
regionArea = rect;
|
||||
}
|
||||
|
||||
public int getRows() {
|
||||
return nRows;
|
||||
}
|
||||
|
||||
public int getColumns() {
|
||||
return nCols;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the text for the region, this should be called after extractTable().
|
||||
*
|
||||
* @return The text that was identified in that region.
|
||||
*/
|
||||
public String getText(int row, int col) {
|
||||
return regionStripper.getTextForRegion("el" + col + "x" + row);
|
||||
}
|
||||
|
||||
public void extractTable(PDPage pdPage) throws IOException {
|
||||
setStartPage(getCurrentPageNo());
|
||||
setEndPage(getCurrentPageNo());
|
||||
|
||||
boxes = new HashSet<Rectangle2D>();
|
||||
// flip y-axis
|
||||
flipAT = new AffineTransform();
|
||||
flipAT.translate(0, pdPage.getBBox().getHeight());
|
||||
flipAT.scale(1, -1);
|
||||
|
||||
// page may be rotated
|
||||
rotateAT = new AffineTransform();
|
||||
int rotation = pdPage.getRotation();
|
||||
if (rotation != 0) {
|
||||
PDRectangle mediaBox = pdPage.getMediaBox();
|
||||
switch (rotation) {
|
||||
case 90:
|
||||
rotateAT.translate(mediaBox.getHeight(), 0);
|
||||
break;
|
||||
case 270:
|
||||
rotateAT.translate(0, mediaBox.getWidth());
|
||||
break;
|
||||
case 180:
|
||||
rotateAT.translate(mediaBox.getWidth(), mediaBox.getHeight());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
rotateAT.rotate(Math.toRadians(rotation));
|
||||
}
|
||||
// Trigger processing of the document so that writeString is called.
|
||||
try (Writer dummy = new OutputStreamWriter(new ByteArrayOutputStream())) {
|
||||
super.output = dummy;
|
||||
super.processPage(pdPage);
|
||||
}
|
||||
|
||||
Rectangle2D[][] regions = calculateTableRegions();
|
||||
|
||||
// System.err.println("Drawing " + nCols + "x" + nRows + "="+ nRows*nCols + "
|
||||
// regions");
|
||||
for (int i = 0; i < nCols; ++i) {
|
||||
for (int j = 0; j < nRows; ++j) {
|
||||
final Rectangle2D region = regions[i][j];
|
||||
regionStripper.addRegion("el" + i + "x" + j, region);
|
||||
}
|
||||
}
|
||||
|
||||
regionStripper.extractRegions(pdPage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Infer a rectangular grid of regions from the boxes field.
|
||||
*
|
||||
* @return 2D array of table regions (as Rectangle2D objects). Note that some of these regions
|
||||
* may have no content.
|
||||
*/
|
||||
private Rectangle2D[][] calculateTableRegions() {
|
||||
|
||||
// Build up a list of all table regions, based upon the populated
|
||||
// regions of boxes field. Treats the horizontal and vertical extents
|
||||
// of each box as distinct
|
||||
LinkedList<Interval> columns = new LinkedList<Interval>();
|
||||
LinkedList<Interval> rows = new LinkedList<Interval>();
|
||||
|
||||
for (Rectangle2D box : boxes) {
|
||||
Interval x = new Interval(box.getMinX(), box.getMaxX());
|
||||
Interval y = new Interval(box.getMinY(), box.getMaxY());
|
||||
|
||||
Interval.addTo(x, columns);
|
||||
Interval.addTo(y, rows);
|
||||
}
|
||||
|
||||
nRows = rows.size();
|
||||
nCols = columns.size();
|
||||
Rectangle2D[][] regions = new Rectangle2D[nCols][nRows];
|
||||
int i = 0;
|
||||
// Label regions from top left, rather than the transformed orientation
|
||||
for (Interval column : columns) {
|
||||
int j = 0;
|
||||
for (Interval row : rows) {
|
||||
regions[nCols - i - 1][nRows - j - 1] =
|
||||
new Rectangle2D.Double(
|
||||
column.start,
|
||||
row.start,
|
||||
column.end - column.start,
|
||||
row.end - row.start);
|
||||
++j;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
|
||||
return regions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register each character's bounding box, updating boxes field to maintain a list of all
|
||||
* distinct groups of characters.
|
||||
*
|
||||
* <p>Overrides the default functionality of PDFTextStripper. Most of this is taken from
|
||||
* DrawPrintTextLocations.java, with extra steps at end of main loop
|
||||
*/
|
||||
@Override
|
||||
protected void writeString(String string, List<TextPosition> textPositions) throws IOException {
|
||||
for (TextPosition text : textPositions) {
|
||||
// glyph space -> user space
|
||||
// note: text.getTextMatrix() is *not* the Text Matrix, it's the Text Rendering Matrix
|
||||
AffineTransform at = text.getTextMatrix().createAffineTransform();
|
||||
PDFont font = text.getFont();
|
||||
BoundingBox bbox = font.getBoundingBox();
|
||||
|
||||
// advance width, bbox height (glyph space)
|
||||
float xadvance =
|
||||
font.getWidth(text.getCharacterCodes()[0]); // todo: should iterate all chars
|
||||
Rectangle2D.Float rect =
|
||||
new Rectangle2D.Float(0, bbox.getLowerLeftY(), xadvance, bbox.getHeight());
|
||||
|
||||
if (font instanceof PDType3Font) {
|
||||
// bbox and font matrix are unscaled
|
||||
at.concatenate(font.getFontMatrix().createAffineTransform());
|
||||
} else {
|
||||
// bbox and font matrix are already scaled to 1000
|
||||
at.scale(1 / 1000f, 1 / 1000f);
|
||||
}
|
||||
Shape s = at.createTransformedShape(rect);
|
||||
s = flipAT.createTransformedShape(s);
|
||||
s = rotateAT.createTransformedShape(s);
|
||||
|
||||
//
|
||||
// Merge character's bounding box with boxes field
|
||||
//
|
||||
Rectangle2D bounds = s.getBounds2D();
|
||||
// Pad sides to detect almost touching boxes
|
||||
Rectangle2D hitbox = bounds.getBounds2D();
|
||||
hitbox.add(bounds.getMinX() - dx, bounds.getMinY() - dy);
|
||||
hitbox.add(bounds.getMaxX() + dx, bounds.getMaxY() + dy);
|
||||
|
||||
// Find all overlapping boxes
|
||||
List<Rectangle2D> intersectList = new ArrayList<Rectangle2D>();
|
||||
for (Rectangle2D box : boxes) {
|
||||
if (box.intersects(hitbox)) {
|
||||
intersectList.add(box);
|
||||
}
|
||||
}
|
||||
|
||||
// Combine all touching boxes and update
|
||||
// (NOTE: Potentially this could leave some overlapping boxes un-merged,
|
||||
// but it's sufficient for now and get's fixed up in calculateTableRegions)
|
||||
for (Rectangle2D box : intersectList) {
|
||||
bounds.add(box);
|
||||
boxes.remove(box);
|
||||
}
|
||||
boxes.add(bounds);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method does nothing in this derived class, because beads and regions are incompatible.
|
||||
* Beads are ignored when stripping by area.
|
||||
*
|
||||
* @param aShouldSeparateByBeads The new grouping of beads.
|
||||
*/
|
||||
@Override
|
||||
public final void setShouldSeparateByBeads(boolean aShouldSeparateByBeads) {}
|
||||
|
||||
/** Adapted from PDFTextStripperByArea {@inheritDoc} */
|
||||
@Override
|
||||
protected void processTextPosition(TextPosition text) {
|
||||
if (regionArea != null && !regionArea.contains(text.getX(), text.getY())) {
|
||||
// skip character
|
||||
} else {
|
||||
super.processTextPosition(text);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -34,9 +34,7 @@ public class DatabaseWebController {
|
||||
}
|
||||
|
||||
List<FileInfo> backupList = databaseBackupHelper.getBackupList();
|
||||
model.addAttribute("backupFiles", backupList);
|
||||
|
||||
model.addAttribute("databaseVersion", databaseBackupHelper.getH2Version());
|
||||
model.addAttribute("systemUpdate", backupList);
|
||||
|
||||
return "database";
|
||||
}
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
package stirling.software.SPDF.pdf;
|
||||
|
||||
import org.apache.commons.csv.CSVFormat;
|
||||
|
||||
import technology.tabula.writers.CSVWriter;
|
||||
|
||||
public class FlexibleCSVWriter extends CSVWriter {
|
||||
|
||||
public FlexibleCSVWriter() {
|
||||
super();
|
||||
}
|
||||
|
||||
public FlexibleCSVWriter(CSVFormat csvFormat) {
|
||||
super(csvFormat);
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,7 @@ public class MetricsAggregatorService {
|
||||
this.postHogService = postHogService;
|
||||
}
|
||||
|
||||
@Scheduled(fixedRate = 1800000) // Run every 30 minutes
|
||||
@Scheduled(fixedRate = 900000) // Run every 15 minutes
|
||||
public void aggregateAndSendMetrics() {
|
||||
Map<String, Object> metrics = new HashMap<>();
|
||||
Search.in(meterRegistry)
|
||||
|
||||
@@ -15,7 +15,6 @@ import java.util.TimeZone;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.posthog.java.PostHog;
|
||||
@@ -27,25 +26,19 @@ import stirling.software.SPDF.model.ApplicationProperties;
|
||||
public class PostHogService {
|
||||
private final PostHog postHog;
|
||||
private final String uniqueId;
|
||||
private final String appVersion;
|
||||
private final ApplicationProperties applicationProperties;
|
||||
private final UserServiceInterface userService;
|
||||
private final Environment env;
|
||||
|
||||
|
||||
@Autowired
|
||||
public PostHogService(
|
||||
PostHog postHog,
|
||||
@Qualifier("UUID") String uuid,
|
||||
@Qualifier("appVersion") String appVersion,
|
||||
ApplicationProperties applicationProperties,
|
||||
@Autowired(required = false) UserServiceInterface userService,
|
||||
Environment env) {
|
||||
@Autowired(required = false) UserServiceInterface userService) {
|
||||
this.postHog = postHog;
|
||||
this.uniqueId = uuid;
|
||||
this.appVersion = appVersion;
|
||||
this.applicationProperties = applicationProperties;
|
||||
this.userService = userService;
|
||||
this.env = env;
|
||||
captureSystemInfo();
|
||||
}
|
||||
|
||||
@@ -71,16 +64,6 @@ public class PostHogService {
|
||||
Map<String, Object> metrics = new HashMap<>();
|
||||
|
||||
try {
|
||||
//Application version
|
||||
metrics.put("app_version", appVersion);
|
||||
String deploymentType = "JAR"; // default
|
||||
if ("true".equalsIgnoreCase(env.getProperty("BROWSER_OPEN"))) {
|
||||
deploymentType = "EXE";
|
||||
} else if (isRunningInDocker()) {
|
||||
deploymentType = "DOCKER";
|
||||
}
|
||||
metrics.put("deployment_type", deploymentType);
|
||||
|
||||
// System info
|
||||
metrics.put("os_name", System.getProperty("os.name"));
|
||||
metrics.put("os_version", System.getProperty("os.version"));
|
||||
@@ -149,6 +132,7 @@ public class PostHogService {
|
||||
|
||||
// Docker detection and stats
|
||||
boolean isDocker = isRunningInDocker();
|
||||
metrics.put("is_docker", isDocker);
|
||||
if (isDocker) {
|
||||
metrics.put("docker_metrics", getDockerMetrics());
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ spring.mvc.async.request-timeout=${SYSTEM_CONNECTIONTIMEOUTMILLISECONDS:1200000}
|
||||
#spring.thymeleaf.prefix=file:/customFiles/templates/,classpath:/templates/
|
||||
#spring.thymeleaf.cache=false
|
||||
|
||||
spring.datasource.url=jdbc:h2:file:./configs/stirling-pdf-DB-2.3.232;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
|
||||
spring.datasource.url=jdbc:h2:file:./configs/stirling-pdf-DB;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
|
||||
spring.datasource.driver-class-name=org.h2.Driver
|
||||
spring.datasource.username=sa
|
||||
spring.datasource.password=
|
||||
|
||||
@@ -81,11 +81,11 @@ page=صفحة
|
||||
pages=صفحات
|
||||
loading=جارٍ التحميل...
|
||||
addToDoc=إضافة إلى المستند
|
||||
reset=إعداة ضبط
|
||||
reset=Reset
|
||||
|
||||
legal.privacy=سياسة الخصوصية
|
||||
legal.terms=شروط الاستخدام
|
||||
legal.accessibility=إمكانية الوصول
|
||||
legal.accessibility=Accessibility
|
||||
legal.cookie=سياسة ملفات تعريف الارتباط
|
||||
legal.impressum=بيان الهوية
|
||||
|
||||
@@ -119,8 +119,8 @@ pipelineOptions.validateButton=تحقق
|
||||
########################
|
||||
enterpriseEdition.button=ترقية إلى محترف
|
||||
enterpriseEdition.warning=هذه الخاصية متوفرة فقط للمستخدمين المحترفين.
|
||||
enterpriseEdition.yamlAdvert=يدعم Stirling PDF Pro ملفات الإعدادات YAML وميزات SSO أخرى
|
||||
enterpriseEdition.ssoAdvert=هل تبحث عن المزيد من ميزات إدارة المستخدمين؟ اطلع على Stirling PDF Pro
|
||||
enterpriseEdition.yamlAdvert=Stirling PDF Pro supports YAML configuration files and other SSO features.
|
||||
enterpriseEdition.ssoAdvert=Looking for more user management features? Check out Stirling PDF Pro
|
||||
|
||||
|
||||
#################
|
||||
@@ -142,7 +142,7 @@ navbar.language=اللغات
|
||||
navbar.settings=إعدادات
|
||||
navbar.allTools=أدوات
|
||||
navbar.multiTool=أدوات متعددة
|
||||
navbar.search=البحث
|
||||
navbar.search=Search
|
||||
navbar.sections.organize=تنظيم
|
||||
navbar.sections.convertTo=تحويل الى PDF
|
||||
navbar.sections.convertFrom=تحويل من PDF
|
||||
@@ -247,8 +247,8 @@ database.fileNotFound=لم يتم العثور على الملف
|
||||
database.fileNullOrEmpty=يجب ألا يكون الملف فارغًا أو خاليًا
|
||||
database.failedImportFile=فشل استيراد الملف
|
||||
|
||||
session.expired=لقد انتهت جلستك. يرجى تحديث الصفحة والمحاولة مرة أخرى
|
||||
session.refreshPage=تحديث الصفحة
|
||||
session.expired=Your session has expired. Please refresh the page and try again.
|
||||
session.refreshPage=Refresh Page
|
||||
|
||||
#############
|
||||
# HOME-PAGE #
|
||||
@@ -513,15 +513,15 @@ home.splitPdfByChapters.desc=قسم مستند PDF إلى ملفات متعدد
|
||||
splitPdfByChapters.tags=تجزئة، فصول، علامات تبويب، تنظيم
|
||||
|
||||
#replace-invert-color
|
||||
replace-color.title=إستبدال-عكس اللون
|
||||
replace-color.header=استبدال-عكس لون PDF
|
||||
home.replaceColorPdf.title=إستبدال و عكس الألوان
|
||||
replace-color.title=Replace-Invert-Color
|
||||
replace-color.header=استبدال-إلغاء مirro لون PDF
|
||||
home.replaceColorPdf.title=Replace and Invert Color
|
||||
home.replaceColorPdf.desc=استبدال الألوان للنصوص والخلفيات في المستندات PDF وإلغاء تعكير اللون الكامل للمستند لتقليل حجم الملف
|
||||
replaceColorPdf.tags=استبدال اللون، عمليات الصفحة، الخلفية، جانب الخادم
|
||||
replace-color.selectText.1=خيارات استبدال أو عكس الألوان
|
||||
replace-color.selectText.2=افتراضي(ألوان التباين العالي الافتراضية)
|
||||
replace-color.selectText.1=خيارات استبدال-إلغاء مirro لون
|
||||
replace-color.selectText.2=Default(Default high contrast colors)
|
||||
replace-color.selectText.3=خصيصة (ألوان شخصية)
|
||||
replace-color.selectText.4=عكس كامل(عكس جميع الألوان)
|
||||
replace-color.selectText.4=Full-Invert(Invert all colors)
|
||||
replace-color.selectText.5=خيارات ألوان التباين العالي
|
||||
replace-color.selectText.6=نص أبيض على خلفية سوداء
|
||||
replace-color.selectText.7=نص أسود على خلفية بيضاء
|
||||
@@ -818,12 +818,7 @@ sign.save=حفظ توقيع
|
||||
sign.personalSigs=توقيعات شخصية
|
||||
sign.sharedSigs=توقيعات مشتركة
|
||||
sign.noSavedSigs=لم يتم العثور على توقيعات محفوظة
|
||||
sign.addToAll=Add to all pages
|
||||
sign.delete=Delete
|
||||
sign.first=First page
|
||||
sign.last=Last page
|
||||
sign.next=Next page
|
||||
sign.previous=Previous page
|
||||
|
||||
|
||||
#repair
|
||||
repair.title=إصلاح
|
||||
@@ -940,27 +935,17 @@ pdfOrganiser.placeholder=(مثال: 1,3,2 أو 4-8,2,10-12 أو 2n-1)
|
||||
multiTool.title=أداة متعددة PDF
|
||||
multiTool.header=أداة متعددة PDF
|
||||
multiTool.uploadPrompts=اسم الملف
|
||||
multiTool.selectAll=تحديد الكل
|
||||
multiTool.deselectAll=إلغاء تحديد الكل
|
||||
multiTool.selectPages=تحديد الصفحة
|
||||
multiTool.selectedPages=الصفحات المحددة
|
||||
multiTool.page=صفحة
|
||||
multiTool.deleteSelected=حذف المحدد
|
||||
multiTool.downloadAll=تصدير
|
||||
multiTool.downloadSelected=تصدير المحدد
|
||||
|
||||
multiTool.insertPageBreak=إدراج فاصل صفحات
|
||||
multiTool.addFile=إضافة ملف
|
||||
multiTool.rotateLeft=تدوير إلى اليسار
|
||||
multiTool.rotateRight=تدوير إلى اليمين
|
||||
multiTool.split=تقسيم
|
||||
multiTool.moveLeft=تحريك إلى اليسار
|
||||
multiTool.moveRight=تحريك إلى اليمين
|
||||
multiTool.delete=حذف
|
||||
multiTool.dragDropMessage=الصفحات المحددة
|
||||
multiTool.selectAll=Select All
|
||||
multiTool.deselectAll=Deselect All
|
||||
multiTool.selectPages=Page Select
|
||||
multiTool.selectedPages=Selected Pages
|
||||
multiTool.page=Page
|
||||
multiTool.deleteSelected=Delete Selected
|
||||
multiTool.downloadAll=Export
|
||||
multiTool.downloadSelected=Export Selected
|
||||
|
||||
#multiTool-advert
|
||||
multiTool-advert.message=هذه الميزة متوفرة في <a href="{0}">صفحة الأدوات المتعددة</a> لدينا. اطلع عليها للحصول على واجهة مستخدم محسّنة لكل صفحة وميزات إضافية!
|
||||
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
|
||||
viewPdf.title=عرض PDF
|
||||
@@ -1254,9 +1239,9 @@ splitByChapters.title=تجزئة المستند حسب الفصول
|
||||
splitByChapters.header=تجزئة المستند حسب الفصول
|
||||
splitByChapters.bookmarkLevel=مستوى العلامات التذكارية
|
||||
splitByChapters.includeMetadata=شامل البيانات المرفقة
|
||||
splitByChapters.allowDuplicates=السماح بالتكرار
|
||||
splitByChapters.desc.1=هذه الأداة تقوم بتقسيم ملف PDF إلى عدة ملفات PDF استناداً إلى بنية فصوله
|
||||
splitByChapters.desc.2=مستوى الإشارة المرجعية: اختر مستوى الإشارات المرجعية التي تريد استخدامها للتقسيم (0 للمستوى الأعلى، 1 للمستوى الثاني، وما إلى ذلك)
|
||||
splitByChapters.allowDuplicates=Allow Duplicates
|
||||
splitByChapters.desc.1=This tool splits a PDF file into multiple PDFs based on its chapter structure.
|
||||
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 مجزأ.
|
||||
splitByChapters.desc.4=سماح بالتكرار: إذا تم اختياره، يسمح بوجود معاينات متعددة في الصفحة نفسها لخلق ملفات PDF منفصلة.
|
||||
splitByChapters.submit=تقطيع ملف PDF
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -818,12 +818,7 @@ sign.save=Save Signature
|
||||
sign.personalSigs=Personal Signatures
|
||||
sign.sharedSigs=Shared Signatures
|
||||
sign.noSavedSigs=No saved signatures found
|
||||
sign.addToAll=Add to all pages
|
||||
sign.delete=Delete
|
||||
sign.first=First page
|
||||
sign.last=Last page
|
||||
sign.next=Next page
|
||||
sign.previous=Previous page
|
||||
|
||||
|
||||
#repair
|
||||
repair.title=Поправи
|
||||
@@ -949,16 +944,6 @@ multiTool.deleteSelected=Delete Selected
|
||||
multiTool.downloadAll=Export
|
||||
multiTool.downloadSelected=Export Selected
|
||||
|
||||
multiTool.insertPageBreak=Insert Page Break
|
||||
multiTool.addFile=Add File
|
||||
multiTool.rotateLeft=Rotate Left
|
||||
multiTool.rotateRight=Rotate Right
|
||||
multiTool.split=Split
|
||||
multiTool.moveLeft=Move Left
|
||||
multiTool.moveRight=Move Right
|
||||
multiTool.delete=Delete
|
||||
multiTool.dragDropMessage=Page(s) Selected
|
||||
|
||||
#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!
|
||||
|
||||
|
||||
@@ -818,12 +818,7 @@ sign.save=Desa Signatura
|
||||
sign.personalSigs=Signatures Personals
|
||||
sign.sharedSigs=Signatures Compartides
|
||||
sign.noSavedSigs=No s'han trobat signatures desades
|
||||
sign.addToAll=Add to all pages
|
||||
sign.delete=Delete
|
||||
sign.first=First page
|
||||
sign.last=Last page
|
||||
sign.next=Next page
|
||||
sign.previous=Previous page
|
||||
|
||||
|
||||
#repair
|
||||
repair.title=Reparar
|
||||
@@ -949,16 +944,6 @@ multiTool.deleteSelected=Delete Selected
|
||||
multiTool.downloadAll=Export
|
||||
multiTool.downloadSelected=Export Selected
|
||||
|
||||
multiTool.insertPageBreak=Insert Page Break
|
||||
multiTool.addFile=Add File
|
||||
multiTool.rotateLeft=Rotate Left
|
||||
multiTool.rotateRight=Rotate Right
|
||||
multiTool.split=Split
|
||||
multiTool.moveLeft=Move Left
|
||||
multiTool.moveRight=Move Right
|
||||
multiTool.delete=Delete
|
||||
multiTool.dragDropMessage=Page(s) Selected
|
||||
|
||||
#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!
|
||||
|
||||
|
||||
@@ -818,12 +818,7 @@ sign.save=Uložit podpis
|
||||
sign.personalSigs=Osobní podpisy
|
||||
sign.sharedSigs=Sdílené podpisy
|
||||
sign.noSavedSigs=Nenašly se žádné uložené podpisy
|
||||
sign.addToAll=Add to all pages
|
||||
sign.delete=Delete
|
||||
sign.first=First page
|
||||
sign.last=Last page
|
||||
sign.next=Next page
|
||||
sign.previous=Previous page
|
||||
|
||||
|
||||
#repair
|
||||
repair.title=Opravit
|
||||
@@ -949,16 +944,6 @@ multiTool.deleteSelected=Delete Selected
|
||||
multiTool.downloadAll=Export
|
||||
multiTool.downloadSelected=Export Selected
|
||||
|
||||
multiTool.insertPageBreak=Insert Page Break
|
||||
multiTool.addFile=Add File
|
||||
multiTool.rotateLeft=Rotate Left
|
||||
multiTool.rotateRight=Rotate Right
|
||||
multiTool.split=Split
|
||||
multiTool.moveLeft=Move Left
|
||||
multiTool.moveRight=Move Right
|
||||
multiTool.delete=Delete
|
||||
multiTool.dragDropMessage=Page(s) Selected
|
||||
|
||||
#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!
|
||||
|
||||
|
||||
@@ -818,12 +818,7 @@ sign.save=Gem Signatur
|
||||
sign.personalSigs=Personlige Signaturer
|
||||
sign.sharedSigs=Delte Signaturer
|
||||
sign.noSavedSigs=Ingen Gemte Signaturer Fundet
|
||||
sign.addToAll=Add to all pages
|
||||
sign.delete=Delete
|
||||
sign.first=First page
|
||||
sign.last=Last page
|
||||
sign.next=Next page
|
||||
sign.previous=Previous page
|
||||
|
||||
|
||||
#repair
|
||||
repair.title=Reparér
|
||||
@@ -949,16 +944,6 @@ multiTool.deleteSelected=Delete Selected
|
||||
multiTool.downloadAll=Export
|
||||
multiTool.downloadSelected=Export Selected
|
||||
|
||||
multiTool.insertPageBreak=Insert Page Break
|
||||
multiTool.addFile=Add File
|
||||
multiTool.rotateLeft=Rotate Left
|
||||
multiTool.rotateRight=Rotate Right
|
||||
multiTool.split=Split
|
||||
multiTool.moveLeft=Move Left
|
||||
multiTool.moveRight=Move Right
|
||||
multiTool.delete=Delete
|
||||
multiTool.dragDropMessage=Page(s) Selected
|
||||
|
||||
#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!
|
||||
|
||||
|
||||
@@ -81,7 +81,7 @@ page=Seite
|
||||
pages=Seiten
|
||||
loading=Laden...
|
||||
addToDoc=In Dokument hinzufügen
|
||||
reset=Zurücksetzen
|
||||
reset=Reset
|
||||
|
||||
legal.privacy=Datenschutz
|
||||
legal.terms=AGB
|
||||
@@ -818,12 +818,7 @@ sign.save=Signature speichern
|
||||
sign.personalSigs=Persönliche Signaturen
|
||||
sign.sharedSigs=Geteilte Signaturen
|
||||
sign.noSavedSigs=Es wurden keine gespeicherten Signaturen gefunden
|
||||
sign.addToAll=Add to all pages
|
||||
sign.delete=Delete
|
||||
sign.first=First page
|
||||
sign.last=Last page
|
||||
sign.next=Next page
|
||||
sign.previous=Previous page
|
||||
|
||||
|
||||
#repair
|
||||
repair.title=Reparieren
|
||||
@@ -949,18 +944,8 @@ multiTool.deleteSelected=Auswahl löschen
|
||||
multiTool.downloadAll=Downloaden
|
||||
multiTool.downloadSelected=Auswahl downloaden
|
||||
|
||||
multiTool.insertPageBreak=Insert Page Break
|
||||
multiTool.addFile=Add File
|
||||
multiTool.rotateLeft=Rotate Left
|
||||
multiTool.rotateRight=Rotate Right
|
||||
multiTool.split=Split
|
||||
multiTool.moveLeft=Move Left
|
||||
multiTool.moveRight=Move Right
|
||||
multiTool.delete=Delete
|
||||
multiTool.dragDropMessage=Page(s) Selected
|
||||
|
||||
#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=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
|
||||
viewPdf.title=PDF anzeigen
|
||||
|
||||
@@ -818,12 +818,7 @@ sign.save=Αποθήκευση Αλιάσης
|
||||
sign.personalSigs=Προσωπικές Αλιάσεις
|
||||
sign.sharedSigs=Μεταδότες Αλιάσεις
|
||||
sign.noSavedSigs=Δεν βρέθηκαν αποθηκευμένες αλιάσεις
|
||||
sign.addToAll=Add to all pages
|
||||
sign.delete=Delete
|
||||
sign.first=First page
|
||||
sign.last=Last page
|
||||
sign.next=Next page
|
||||
sign.previous=Previous page
|
||||
|
||||
|
||||
#repair
|
||||
repair.title=Επιδιόρθωση
|
||||
@@ -949,16 +944,6 @@ multiTool.deleteSelected=Delete Selected
|
||||
multiTool.downloadAll=Export
|
||||
multiTool.downloadSelected=Export Selected
|
||||
|
||||
multiTool.insertPageBreak=Insert Page Break
|
||||
multiTool.addFile=Add File
|
||||
multiTool.rotateLeft=Rotate Left
|
||||
multiTool.rotateRight=Rotate Right
|
||||
multiTool.split=Split
|
||||
multiTool.moveLeft=Move Left
|
||||
multiTool.moveRight=Move Right
|
||||
multiTool.delete=Delete
|
||||
multiTool.dragDropMessage=Page(s) Selected
|
||||
|
||||
#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!
|
||||
|
||||
|
||||
@@ -818,12 +818,7 @@ sign.save=Save Signature
|
||||
sign.personalSigs=Personal Signatures
|
||||
sign.sharedSigs=Shared Signatures
|
||||
sign.noSavedSigs=No saved signatures found
|
||||
sign.addToAll=Add to all pages
|
||||
sign.delete=Delete
|
||||
sign.first=First page
|
||||
sign.last=Last page
|
||||
sign.next=Next page
|
||||
sign.previous=Previous page
|
||||
|
||||
|
||||
#repair
|
||||
repair.title=Repair
|
||||
@@ -949,16 +944,6 @@ multiTool.deleteSelected=Delete Selected
|
||||
multiTool.downloadAll=Export
|
||||
multiTool.downloadSelected=Export Selected
|
||||
|
||||
multiTool.insertPageBreak=Insert Page Break
|
||||
multiTool.addFile=Add File
|
||||
multiTool.rotateLeft=Rotate Left
|
||||
multiTool.rotateRight=Rotate Right
|
||||
multiTool.split=Split
|
||||
multiTool.moveLeft=Move Left
|
||||
multiTool.moveRight=Move Right
|
||||
multiTool.delete=Delete
|
||||
multiTool.dragDropMessage=Page(s) Selected
|
||||
|
||||
#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!
|
||||
|
||||
|
||||
@@ -818,12 +818,7 @@ sign.save=Save Signature
|
||||
sign.personalSigs=Personal Signatures
|
||||
sign.sharedSigs=Shared Signatures
|
||||
sign.noSavedSigs=No saved signatures found
|
||||
sign.addToAll=Add to all pages
|
||||
sign.delete=Delete
|
||||
sign.first=First page
|
||||
sign.last=Last page
|
||||
sign.next=Next page
|
||||
sign.previous=Previous page
|
||||
|
||||
|
||||
#repair
|
||||
repair.title=Repair
|
||||
@@ -949,16 +944,6 @@ multiTool.deleteSelected=Delete Selected
|
||||
multiTool.downloadAll=Export
|
||||
multiTool.downloadSelected=Export Selected
|
||||
|
||||
multiTool.insertPageBreak=Insert Page Break
|
||||
multiTool.addFile=Add File
|
||||
multiTool.rotateLeft=Rotate Left
|
||||
multiTool.rotateRight=Rotate Right
|
||||
multiTool.split=Split
|
||||
multiTool.moveLeft=Move Left
|
||||
multiTool.moveRight=Move Right
|
||||
multiTool.delete=Delete
|
||||
multiTool.dragDropMessage=Page(s) Selected
|
||||
|
||||
#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!
|
||||
|
||||
|
||||
@@ -818,12 +818,7 @@ sign.save=Guardar Firma
|
||||
sign.personalSigs=Firmas Personales
|
||||
sign.sharedSigs=Firmas compartidas
|
||||
sign.noSavedSigs=No se encontraron firmas guardadas
|
||||
sign.addToAll=Add to all pages
|
||||
sign.delete=Delete
|
||||
sign.first=First page
|
||||
sign.last=Last page
|
||||
sign.next=Next page
|
||||
sign.previous=Previous page
|
||||
|
||||
|
||||
#repair
|
||||
repair.title=Reparar
|
||||
@@ -949,16 +944,6 @@ multiTool.deleteSelected=Delete Selected
|
||||
multiTool.downloadAll=Export
|
||||
multiTool.downloadSelected=Export Selected
|
||||
|
||||
multiTool.insertPageBreak=Insert Page Break
|
||||
multiTool.addFile=Add File
|
||||
multiTool.rotateLeft=Rotate Left
|
||||
multiTool.rotateRight=Rotate Right
|
||||
multiTool.split=Split
|
||||
multiTool.moveLeft=Move Left
|
||||
multiTool.moveRight=Move Right
|
||||
multiTool.delete=Delete
|
||||
multiTool.dragDropMessage=Page(s) Selected
|
||||
|
||||
#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!
|
||||
|
||||
|
||||
@@ -818,12 +818,7 @@ sign.save=Save Signature
|
||||
sign.personalSigs=Personal Signatures
|
||||
sign.sharedSigs=Shared Signatures
|
||||
sign.noSavedSigs=No saved signatures found
|
||||
sign.addToAll=Add to all pages
|
||||
sign.delete=Delete
|
||||
sign.first=First page
|
||||
sign.last=Last page
|
||||
sign.next=Next page
|
||||
sign.previous=Previous page
|
||||
|
||||
|
||||
#repair
|
||||
repair.title=Konpondu
|
||||
@@ -949,16 +944,6 @@ multiTool.deleteSelected=Delete Selected
|
||||
multiTool.downloadAll=Export
|
||||
multiTool.downloadSelected=Export Selected
|
||||
|
||||
multiTool.insertPageBreak=Insert Page Break
|
||||
multiTool.addFile=Add File
|
||||
multiTool.rotateLeft=Rotate Left
|
||||
multiTool.rotateRight=Rotate Right
|
||||
multiTool.split=Split
|
||||
multiTool.moveLeft=Move Left
|
||||
multiTool.moveRight=Move Right
|
||||
multiTool.delete=Delete
|
||||
multiTool.dragDropMessage=Page(s) Selected
|
||||
|
||||
#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!
|
||||
|
||||
|
||||
@@ -56,8 +56,8 @@ userNotFoundMessage=Utilisateur non trouvé.
|
||||
incorrectPasswordMessage=Le mot de passe actuel est incorrect.
|
||||
usernameExistsMessage=Le nouveau nom d'utilisateur existe déjà.
|
||||
invalidUsernameMessage=Nom d'utilisateur invalide, le nom d'utilisateur ne peut contenir que des lettres, des chiffres et les caractères spéciaux suivants @._+- ou doit être une adresse e-mail valide.
|
||||
invalidPasswordMessage=Le mot de passe ne peut pas être vide et ne doit pas contenir d'espaces au début ou à la fin.
|
||||
confirmPasswordErrorMessage=Le nouveau mot de passe et sa confirmation doivent être identiques.
|
||||
invalidPasswordMessage=Le mot de passe ne peut pas être vide et ne doit pas contenir d'espaces au début ou en fin.
|
||||
confirmPasswordErrorMessage=Nouveau Mot de passe et Confirmer le Nouveau Mot de passe doivent correspondre.
|
||||
deleteCurrentUserMessage=Impossible de supprimer l'utilisateur actuellement connecté.
|
||||
deleteUsernameExistsMessage=Le nom d'utilisateur n'existe pas et ne peut pas être supprimé.
|
||||
downgradeCurrentUserMessage=Impossible de rétrograder le rôle de l'utilisateur actuel.
|
||||
@@ -81,7 +81,7 @@ page=Page
|
||||
pages=Pages
|
||||
loading=Chargement...
|
||||
addToDoc=Ajouter au Document
|
||||
reset=Réinitialiser
|
||||
reset=Reset
|
||||
|
||||
legal.privacy=Politique de Confidentialité
|
||||
legal.terms=Conditions Générales
|
||||
@@ -142,7 +142,7 @@ navbar.language=Langues
|
||||
navbar.settings=Paramètres
|
||||
navbar.allTools=Outils
|
||||
navbar.multiTool=Outils Multiples
|
||||
navbar.search=Rechercher
|
||||
navbar.search=Search
|
||||
navbar.sections.organize=Organisation
|
||||
navbar.sections.convertTo=Convertir en PDF
|
||||
navbar.sections.convertFrom=Convertir depuis PDF
|
||||
@@ -813,17 +813,12 @@ sign.draw=Dessiner une signature
|
||||
sign.text=Saisir de texte
|
||||
sign.clear=Effacer
|
||||
sign.add=Ajouter
|
||||
sign.saved=Sceaux enregistrées
|
||||
sign.saved=Saved Signatures
|
||||
sign.save=Enregistrer le sceau
|
||||
sign.personalSigs=Sceaux personnels
|
||||
sign.sharedSigs=Sceaux partagés
|
||||
sign.noSavedSigs=Aucun sceau enregistré trouvé
|
||||
sign.addToAll=Add to all pages
|
||||
sign.delete=Delete
|
||||
sign.first=First page
|
||||
sign.last=Last page
|
||||
sign.next=Next page
|
||||
sign.previous=Previous page
|
||||
|
||||
|
||||
#repair
|
||||
repair.title=Réparer
|
||||
@@ -940,27 +935,17 @@ pdfOrganiser.placeholder=(par exemple 1,3,2 ou 4-8,2,10-12 ou 2n-1)
|
||||
multiTool.title=Outil multifonction PDF
|
||||
multiTool.header=Outil multifonction PDF
|
||||
multiTool.uploadPrompts=Nom du fichier
|
||||
multiTool.selectAll=Tout sélectionner
|
||||
multiTool.deselectAll=Tout déselectionner
|
||||
multiTool.selectPages=Sélection des pages
|
||||
multiTool.selectedPages=Pages sélectionnées
|
||||
multiTool.selectAll=Select All
|
||||
multiTool.deselectAll=Deselect All
|
||||
multiTool.selectPages=Page Select
|
||||
multiTool.selectedPages=Selected Pages
|
||||
multiTool.page=Page
|
||||
multiTool.deleteSelected=Supprimer la sélection
|
||||
multiTool.downloadAll=Exporter
|
||||
multiTool.downloadSelected=Exporter la sélection
|
||||
|
||||
multiTool.insertPageBreak=Insérer un saut de page
|
||||
multiTool.addFile=Ajouter un fichier
|
||||
multiTool.rotateLeft=Rotation vers la gauche
|
||||
multiTool.rotateRight=Rotation vers la droite
|
||||
multiTool.split=Diviser
|
||||
multiTool.moveLeft=Déplacer vers la gauche
|
||||
multiTool.moveRight=Déplacer vers la droite
|
||||
multiTool.delete=Supprimer
|
||||
multiTool.dragDropMessage=Page(s) sélectionnées
|
||||
multiTool.deleteSelected=Delete Selected
|
||||
multiTool.downloadAll=Export
|
||||
multiTool.downloadSelected=Export Selected
|
||||
|
||||
#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=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
|
||||
viewPdf.title=Visualiser un PDF
|
||||
|
||||
@@ -818,12 +818,7 @@ sign.save=Save Signature
|
||||
sign.personalSigs=Personal Signatures
|
||||
sign.sharedSigs=Shared Signatures
|
||||
sign.noSavedSigs=No saved signatures found
|
||||
sign.addToAll=Add to all pages
|
||||
sign.delete=Delete
|
||||
sign.first=First page
|
||||
sign.last=Last page
|
||||
sign.next=Next page
|
||||
sign.previous=Previous page
|
||||
|
||||
|
||||
#repair
|
||||
repair.title=Deisiúchán
|
||||
@@ -949,16 +944,6 @@ multiTool.deleteSelected=Delete Selected
|
||||
multiTool.downloadAll=Export
|
||||
multiTool.downloadSelected=Export Selected
|
||||
|
||||
multiTool.insertPageBreak=Insert Page Break
|
||||
multiTool.addFile=Add File
|
||||
multiTool.rotateLeft=Rotate Left
|
||||
multiTool.rotateRight=Rotate Right
|
||||
multiTool.split=Split
|
||||
multiTool.moveLeft=Move Left
|
||||
multiTool.moveRight=Move Right
|
||||
multiTool.delete=Delete
|
||||
multiTool.dragDropMessage=Page(s) Selected
|
||||
|
||||
#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!
|
||||
|
||||
|
||||
@@ -818,12 +818,7 @@ sign.save=प्रदर्शन बचाएं
|
||||
sign.personalSigs=मौजूदा प्रदर्शन
|
||||
sign.sharedSigs=साझेदार प्रदर्शन
|
||||
sign.noSavedSigs=कोई भी संवर्तित प्रदर्शन नहीं मौजूद है
|
||||
sign.addToAll=Add to all pages
|
||||
sign.delete=Delete
|
||||
sign.first=First page
|
||||
sign.last=Last page
|
||||
sign.next=Next page
|
||||
sign.previous=Previous page
|
||||
|
||||
|
||||
#repair
|
||||
repair.title=मरम्मत
|
||||
@@ -949,16 +944,6 @@ multiTool.deleteSelected=Delete Selected
|
||||
multiTool.downloadAll=Export
|
||||
multiTool.downloadSelected=Export Selected
|
||||
|
||||
multiTool.insertPageBreak=Insert Page Break
|
||||
multiTool.addFile=Add File
|
||||
multiTool.rotateLeft=Rotate Left
|
||||
multiTool.rotateRight=Rotate Right
|
||||
multiTool.split=Split
|
||||
multiTool.moveLeft=Move Left
|
||||
multiTool.moveRight=Move Right
|
||||
multiTool.delete=Delete
|
||||
multiTool.dragDropMessage=Page(s) Selected
|
||||
|
||||
#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!
|
||||
|
||||
|
||||
@@ -818,12 +818,7 @@ sign.save=Sačuvaj potpisnu oznaku
|
||||
sign.personalSigs=Osobni potpisi
|
||||
sign.sharedSigs=Dijeljeni potpisi
|
||||
sign.noSavedSigs=Nema sacuvanih potpisa pronađenih
|
||||
sign.addToAll=Add to all pages
|
||||
sign.delete=Delete
|
||||
sign.first=First page
|
||||
sign.last=Last page
|
||||
sign.next=Next page
|
||||
sign.previous=Previous page
|
||||
|
||||
|
||||
#repair
|
||||
repair.title=Popravi
|
||||
@@ -949,16 +944,6 @@ multiTool.deleteSelected=Delete Selected
|
||||
multiTool.downloadAll=Export
|
||||
multiTool.downloadSelected=Export Selected
|
||||
|
||||
multiTool.insertPageBreak=Insert Page Break
|
||||
multiTool.addFile=Add File
|
||||
multiTool.rotateLeft=Rotate Left
|
||||
multiTool.rotateRight=Rotate Right
|
||||
multiTool.split=Split
|
||||
multiTool.moveLeft=Move Left
|
||||
multiTool.moveRight=Move Right
|
||||
multiTool.delete=Delete
|
||||
multiTool.dragDropMessage=Page(s) Selected
|
||||
|
||||
#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!
|
||||
|
||||
|
||||
@@ -818,12 +818,7 @@ sign.save=Aláíráshoz mentés
|
||||
sign.personalSigs=Személyi aláíráshoz
|
||||
sign.sharedSigs=Megosztott aláíráshoz
|
||||
sign.noSavedSigs=Nincsenek mentett aláírások találat
|
||||
sign.addToAll=Add to all pages
|
||||
sign.delete=Delete
|
||||
sign.first=First page
|
||||
sign.last=Last page
|
||||
sign.next=Next page
|
||||
sign.previous=Previous page
|
||||
|
||||
|
||||
#repair
|
||||
repair.title=Javítás
|
||||
@@ -949,16 +944,6 @@ multiTool.deleteSelected=Delete Selected
|
||||
multiTool.downloadAll=Export
|
||||
multiTool.downloadSelected=Export Selected
|
||||
|
||||
multiTool.insertPageBreak=Insert Page Break
|
||||
multiTool.addFile=Add File
|
||||
multiTool.rotateLeft=Rotate Left
|
||||
multiTool.rotateRight=Rotate Right
|
||||
multiTool.split=Split
|
||||
multiTool.moveLeft=Move Left
|
||||
multiTool.moveRight=Move Right
|
||||
multiTool.delete=Delete
|
||||
multiTool.dragDropMessage=Page(s) Selected
|
||||
|
||||
#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!
|
||||
|
||||
|
||||
@@ -818,12 +818,7 @@ sign.save=Simpan Tanda Tangan
|
||||
sign.personalSigs=Tanda Tangan Pribadi
|
||||
sign.sharedSigs=Tanda Tangan Berbagi
|
||||
sign.noSavedSigs=Tidak ditemukan tanda tangan yang disimpan
|
||||
sign.addToAll=Add to all pages
|
||||
sign.delete=Delete
|
||||
sign.first=First page
|
||||
sign.last=Last page
|
||||
sign.next=Next page
|
||||
sign.previous=Previous page
|
||||
|
||||
|
||||
#repair
|
||||
repair.title=Perbaiki
|
||||
@@ -949,16 +944,6 @@ multiTool.deleteSelected=Delete Selected
|
||||
multiTool.downloadAll=Export
|
||||
multiTool.downloadSelected=Export Selected
|
||||
|
||||
multiTool.insertPageBreak=Insert Page Break
|
||||
multiTool.addFile=Add File
|
||||
multiTool.rotateLeft=Rotate Left
|
||||
multiTool.rotateRight=Rotate Right
|
||||
multiTool.split=Split
|
||||
multiTool.moveLeft=Move Left
|
||||
multiTool.moveRight=Move Right
|
||||
multiTool.delete=Delete
|
||||
multiTool.dragDropMessage=Page(s) Selected
|
||||
|
||||
#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!
|
||||
|
||||
|
||||
@@ -818,12 +818,7 @@ sign.save=Firma salvata
|
||||
sign.personalSigs=Firme personali
|
||||
sign.sharedSigs=Firme condivise
|
||||
sign.noSavedSigs=Nessuna firma salvata trovata
|
||||
sign.addToAll=Aggiungi a tutte le pagine
|
||||
sign.delete=Elimina
|
||||
sign.first=Prima pagina
|
||||
sign.last=Ultima pagina
|
||||
sign.next=Prossima pagina
|
||||
sign.previous=Pagina precedente
|
||||
|
||||
|
||||
#repair
|
||||
repair.title=Ripara
|
||||
@@ -949,16 +944,6 @@ multiTool.deleteSelected=Elimina selezionata
|
||||
multiTool.downloadAll=Esporta
|
||||
multiTool.downloadSelected=Esporta selezionata
|
||||
|
||||
multiTool.insertPageBreak=Inserisci interruzione di pagina
|
||||
multiTool.addFile=Aggiungi file
|
||||
multiTool.rotateLeft=Ruota a sinistra
|
||||
multiTool.rotateRight=Ruota a destra
|
||||
multiTool.split=Dividi
|
||||
multiTool.moveLeft=Sposta a sinistra
|
||||
multiTool.moveRight=Sposta a destra
|
||||
multiTool.delete=Elimina
|
||||
multiTool.dragDropMessage=Pagina(e) selezionata(e)
|
||||
|
||||
#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!
|
||||
|
||||
|
||||
@@ -818,12 +818,7 @@ sign.save=Save Signature
|
||||
sign.personalSigs=Personal Signatures
|
||||
sign.sharedSigs=Shared Signatures
|
||||
sign.noSavedSigs=No saved signatures found
|
||||
sign.addToAll=Add to all pages
|
||||
sign.delete=Delete
|
||||
sign.first=First page
|
||||
sign.last=Last page
|
||||
sign.next=Next page
|
||||
sign.previous=Previous page
|
||||
|
||||
|
||||
#repair
|
||||
repair.title=修復
|
||||
@@ -949,16 +944,6 @@ multiTool.deleteSelected=Delete Selected
|
||||
multiTool.downloadAll=Export
|
||||
multiTool.downloadSelected=Export Selected
|
||||
|
||||
multiTool.insertPageBreak=Insert Page Break
|
||||
multiTool.addFile=Add File
|
||||
multiTool.rotateLeft=Rotate Left
|
||||
multiTool.rotateRight=Rotate Right
|
||||
multiTool.split=Split
|
||||
multiTool.moveLeft=Move Left
|
||||
multiTool.moveRight=Move Right
|
||||
multiTool.delete=Delete
|
||||
multiTool.dragDropMessage=Page(s) Selected
|
||||
|
||||
#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!
|
||||
|
||||
|
||||
@@ -818,12 +818,7 @@ sign.save=서명 저장
|
||||
sign.personalSigs=개인용 서명
|
||||
sign.sharedSigs=공유용 서명
|
||||
sign.noSavedSigs=저장된 서명이 없습니다
|
||||
sign.addToAll=Add to all pages
|
||||
sign.delete=Delete
|
||||
sign.first=First page
|
||||
sign.last=Last page
|
||||
sign.next=Next page
|
||||
sign.previous=Previous page
|
||||
|
||||
|
||||
#repair
|
||||
repair.title=복구
|
||||
@@ -949,16 +944,6 @@ multiTool.deleteSelected=Delete Selected
|
||||
multiTool.downloadAll=Export
|
||||
multiTool.downloadSelected=Export Selected
|
||||
|
||||
multiTool.insertPageBreak=Insert Page Break
|
||||
multiTool.addFile=Add File
|
||||
multiTool.rotateLeft=Rotate Left
|
||||
multiTool.rotateRight=Rotate Right
|
||||
multiTool.split=Split
|
||||
multiTool.moveLeft=Move Left
|
||||
multiTool.moveRight=Move Right
|
||||
multiTool.delete=Delete
|
||||
multiTool.dragDropMessage=Page(s) Selected
|
||||
|
||||
#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!
|
||||
|
||||
|
||||
@@ -818,12 +818,7 @@ sign.save=Opslaan Signatuur
|
||||
sign.personalSigs=Persoonlijke Signatuuren
|
||||
sign.sharedSigs=Gedeelde Signatuuren
|
||||
sign.noSavedSigs=Geen opgeslagen signatuuren gevonden
|
||||
sign.addToAll=Add to all pages
|
||||
sign.delete=Delete
|
||||
sign.first=First page
|
||||
sign.last=Last page
|
||||
sign.next=Next page
|
||||
sign.previous=Previous page
|
||||
|
||||
|
||||
#repair
|
||||
repair.title=Repareren
|
||||
@@ -949,16 +944,6 @@ multiTool.deleteSelected=Delete Selected
|
||||
multiTool.downloadAll=Export
|
||||
multiTool.downloadSelected=Export Selected
|
||||
|
||||
multiTool.insertPageBreak=Insert Page Break
|
||||
multiTool.addFile=Add File
|
||||
multiTool.rotateLeft=Rotate Left
|
||||
multiTool.rotateRight=Rotate Right
|
||||
multiTool.split=Split
|
||||
multiTool.moveLeft=Move Left
|
||||
multiTool.moveRight=Move Right
|
||||
multiTool.delete=Delete
|
||||
multiTool.dragDropMessage=Page(s) Selected
|
||||
|
||||
#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!
|
||||
|
||||
|
||||
@@ -818,12 +818,7 @@ sign.save=Save Signature
|
||||
sign.personalSigs=Personal Signatures
|
||||
sign.sharedSigs=Shared Signatures
|
||||
sign.noSavedSigs=No saved signatures found
|
||||
sign.addToAll=Add to all pages
|
||||
sign.delete=Delete
|
||||
sign.first=First page
|
||||
sign.last=Last page
|
||||
sign.next=Next page
|
||||
sign.previous=Previous page
|
||||
|
||||
|
||||
#repair
|
||||
repair.title=Reparer
|
||||
@@ -949,16 +944,6 @@ multiTool.deleteSelected=Delete Selected
|
||||
multiTool.downloadAll=Export
|
||||
multiTool.downloadSelected=Export Selected
|
||||
|
||||
multiTool.insertPageBreak=Insert Page Break
|
||||
multiTool.addFile=Add File
|
||||
multiTool.rotateLeft=Rotate Left
|
||||
multiTool.rotateRight=Rotate Right
|
||||
multiTool.split=Split
|
||||
multiTool.moveLeft=Move Left
|
||||
multiTool.moveRight=Move Right
|
||||
multiTool.delete=Delete
|
||||
multiTool.dragDropMessage=Page(s) Selected
|
||||
|
||||
#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!
|
||||
|
||||
|
||||
@@ -818,12 +818,7 @@ sign.save=Save Signature
|
||||
sign.personalSigs=Personal Signatures
|
||||
sign.sharedSigs=Shared Signatures
|
||||
sign.noSavedSigs=No saved signatures found
|
||||
sign.addToAll=Add to all pages
|
||||
sign.delete=Delete
|
||||
sign.first=First page
|
||||
sign.last=Last page
|
||||
sign.next=Next page
|
||||
sign.previous=Previous page
|
||||
|
||||
|
||||
#repair
|
||||
repair.title=Napraw
|
||||
@@ -949,16 +944,6 @@ multiTool.deleteSelected=Delete Selected
|
||||
multiTool.downloadAll=Export
|
||||
multiTool.downloadSelected=Export Selected
|
||||
|
||||
multiTool.insertPageBreak=Insert Page Break
|
||||
multiTool.addFile=Add File
|
||||
multiTool.rotateLeft=Rotate Left
|
||||
multiTool.rotateRight=Rotate Right
|
||||
multiTool.split=Split
|
||||
multiTool.moveLeft=Move Left
|
||||
multiTool.moveRight=Move Right
|
||||
multiTool.delete=Delete
|
||||
multiTool.dragDropMessage=Page(s) Selected
|
||||
|
||||
#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!
|
||||
|
||||
|
||||
@@ -818,12 +818,7 @@ sign.save=Salvar Assinatura
|
||||
sign.personalSigs=Assinaturas Pessoais
|
||||
sign.sharedSigs=Assinaturas Compartilhadas
|
||||
sign.noSavedSigs=Nenhuma assinatura salva encontrada
|
||||
sign.addToAll=Add to all pages
|
||||
sign.delete=Delete
|
||||
sign.first=First page
|
||||
sign.last=Last page
|
||||
sign.next=Next page
|
||||
sign.previous=Previous page
|
||||
|
||||
|
||||
#repair
|
||||
repair.title=Reparar
|
||||
@@ -949,16 +944,6 @@ multiTool.deleteSelected=Delete Selected
|
||||
multiTool.downloadAll=Export
|
||||
multiTool.downloadSelected=Export Selected
|
||||
|
||||
multiTool.insertPageBreak=Insert Page Break
|
||||
multiTool.addFile=Add File
|
||||
multiTool.rotateLeft=Rotate Left
|
||||
multiTool.rotateRight=Rotate Right
|
||||
multiTool.split=Split
|
||||
multiTool.moveLeft=Move Left
|
||||
multiTool.moveRight=Move Right
|
||||
multiTool.delete=Delete
|
||||
multiTool.dragDropMessage=Page(s) Selected
|
||||
|
||||
#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!
|
||||
|
||||
|
||||
@@ -818,12 +818,7 @@ sign.save=Guardar Assinatura
|
||||
sign.personalSigs=Assinaturas Pessoais
|
||||
sign.sharedSigs=Assinaturas Compartilhadas
|
||||
sign.noSavedSigs=Nenhuma assinatura guardada encontrada
|
||||
sign.addToAll=Add to all pages
|
||||
sign.delete=Delete
|
||||
sign.first=First page
|
||||
sign.last=Last page
|
||||
sign.next=Next page
|
||||
sign.previous=Previous page
|
||||
|
||||
|
||||
#repair
|
||||
repair.title=Reparar
|
||||
@@ -949,16 +944,6 @@ multiTool.deleteSelected=Delete Selected
|
||||
multiTool.downloadAll=Export
|
||||
multiTool.downloadSelected=Export Selected
|
||||
|
||||
multiTool.insertPageBreak=Insert Page Break
|
||||
multiTool.addFile=Add File
|
||||
multiTool.rotateLeft=Rotate Left
|
||||
multiTool.rotateRight=Rotate Right
|
||||
multiTool.split=Split
|
||||
multiTool.moveLeft=Move Left
|
||||
multiTool.moveRight=Move Right
|
||||
multiTool.delete=Delete
|
||||
multiTool.dragDropMessage=Page(s) Selected
|
||||
|
||||
#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!
|
||||
|
||||
|
||||
@@ -818,12 +818,7 @@ sign.save=Save Signature
|
||||
sign.personalSigs=Personal Signatures
|
||||
sign.sharedSigs=Shared Signatures
|
||||
sign.noSavedSigs=No saved signatures found
|
||||
sign.addToAll=Add to all pages
|
||||
sign.delete=Delete
|
||||
sign.first=First page
|
||||
sign.last=Last page
|
||||
sign.next=Next page
|
||||
sign.previous=Previous page
|
||||
|
||||
|
||||
#repair
|
||||
repair.title=Repară
|
||||
@@ -949,16 +944,6 @@ multiTool.deleteSelected=Delete Selected
|
||||
multiTool.downloadAll=Export
|
||||
multiTool.downloadSelected=Export Selected
|
||||
|
||||
multiTool.insertPageBreak=Insert Page Break
|
||||
multiTool.addFile=Add File
|
||||
multiTool.rotateLeft=Rotate Left
|
||||
multiTool.rotateRight=Rotate Right
|
||||
multiTool.split=Split
|
||||
multiTool.moveLeft=Move Left
|
||||
multiTool.moveRight=Move Right
|
||||
multiTool.delete=Delete
|
||||
multiTool.dragDropMessage=Page(s) Selected
|
||||
|
||||
#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!
|
||||
|
||||
|
||||
@@ -818,12 +818,7 @@ sign.save=Сохранить подпись
|
||||
sign.personalSigs=Личные подписи
|
||||
sign.sharedSigs=Общие подписи
|
||||
sign.noSavedSigs=Найдено ни одной сохраненной подписи
|
||||
sign.addToAll=Add to all pages
|
||||
sign.delete=Delete
|
||||
sign.first=First page
|
||||
sign.last=Last page
|
||||
sign.next=Next page
|
||||
sign.previous=Previous page
|
||||
|
||||
|
||||
#repair
|
||||
repair.title=Ремонт
|
||||
@@ -949,16 +944,6 @@ multiTool.deleteSelected=Delete Selected
|
||||
multiTool.downloadAll=Export
|
||||
multiTool.downloadSelected=Export Selected
|
||||
|
||||
multiTool.insertPageBreak=Insert Page Break
|
||||
multiTool.addFile=Add File
|
||||
multiTool.rotateLeft=Rotate Left
|
||||
multiTool.rotateRight=Rotate Right
|
||||
multiTool.split=Split
|
||||
multiTool.moveLeft=Move Left
|
||||
multiTool.moveRight=Move Right
|
||||
multiTool.delete=Delete
|
||||
multiTool.dragDropMessage=Page(s) Selected
|
||||
|
||||
#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!
|
||||
|
||||
|
||||
@@ -818,12 +818,7 @@ sign.save=Save Signature
|
||||
sign.personalSigs=Personal Signatures
|
||||
sign.sharedSigs=Shared Signatures
|
||||
sign.noSavedSigs=No saved signatures found
|
||||
sign.addToAll=Add to all pages
|
||||
sign.delete=Delete
|
||||
sign.first=First page
|
||||
sign.last=Last page
|
||||
sign.next=Next page
|
||||
sign.previous=Previous page
|
||||
|
||||
|
||||
#repair
|
||||
repair.title=Opraviť
|
||||
@@ -949,16 +944,6 @@ multiTool.deleteSelected=Delete Selected
|
||||
multiTool.downloadAll=Export
|
||||
multiTool.downloadSelected=Export Selected
|
||||
|
||||
multiTool.insertPageBreak=Insert Page Break
|
||||
multiTool.addFile=Add File
|
||||
multiTool.rotateLeft=Rotate Left
|
||||
multiTool.rotateRight=Rotate Right
|
||||
multiTool.split=Split
|
||||
multiTool.moveLeft=Move Left
|
||||
multiTool.moveRight=Move Right
|
||||
multiTool.delete=Delete
|
||||
multiTool.dragDropMessage=Page(s) Selected
|
||||
|
||||
#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!
|
||||
|
||||
|
||||
@@ -818,12 +818,7 @@ sign.save=Save Signature
|
||||
sign.personalSigs=Personal Signatures
|
||||
sign.sharedSigs=Shared Signatures
|
||||
sign.noSavedSigs=No saved signatures found
|
||||
sign.addToAll=Add to all pages
|
||||
sign.delete=Delete
|
||||
sign.first=First page
|
||||
sign.last=Last page
|
||||
sign.next=Next page
|
||||
sign.previous=Previous page
|
||||
|
||||
|
||||
#repair
|
||||
repair.title=Popravi
|
||||
@@ -949,16 +944,6 @@ multiTool.deleteSelected=Delete Selected
|
||||
multiTool.downloadAll=Export
|
||||
multiTool.downloadSelected=Export Selected
|
||||
|
||||
multiTool.insertPageBreak=Insert Page Break
|
||||
multiTool.addFile=Add File
|
||||
multiTool.rotateLeft=Rotate Left
|
||||
multiTool.rotateRight=Rotate Right
|
||||
multiTool.split=Split
|
||||
multiTool.moveLeft=Move Left
|
||||
multiTool.moveRight=Move Right
|
||||
multiTool.delete=Delete
|
||||
multiTool.dragDropMessage=Page(s) Selected
|
||||
|
||||
#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!
|
||||
|
||||
|
||||
@@ -818,12 +818,7 @@ sign.save=Spara signatur
|
||||
sign.personalSigs=Personliga signaturer
|
||||
sign.sharedSigs=Delade signaturer
|
||||
sign.noSavedSigs=Inga sparade signaturer hittades
|
||||
sign.addToAll=Add to all pages
|
||||
sign.delete=Delete
|
||||
sign.first=First page
|
||||
sign.last=Last page
|
||||
sign.next=Next page
|
||||
sign.previous=Previous page
|
||||
|
||||
|
||||
#repair
|
||||
repair.title=Reparera
|
||||
@@ -949,16 +944,6 @@ multiTool.deleteSelected=Delete Selected
|
||||
multiTool.downloadAll=Export
|
||||
multiTool.downloadSelected=Export Selected
|
||||
|
||||
multiTool.insertPageBreak=Insert Page Break
|
||||
multiTool.addFile=Add File
|
||||
multiTool.rotateLeft=Rotate Left
|
||||
multiTool.rotateRight=Rotate Right
|
||||
multiTool.split=Split
|
||||
multiTool.moveLeft=Move Left
|
||||
multiTool.moveRight=Move Right
|
||||
multiTool.delete=Delete
|
||||
multiTool.dragDropMessage=Page(s) Selected
|
||||
|
||||
#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!
|
||||
|
||||
|
||||
@@ -818,12 +818,7 @@ sign.save=บันทึกลายเซ็น
|
||||
sign.personalSigs=ลายเซ็นส่วนตัว
|
||||
sign.sharedSigs=ลายเซ็นร่วม
|
||||
sign.noSavedSigs=ไม่พบลายเซ็นที่บันทึกไว้
|
||||
sign.addToAll=Add to all pages
|
||||
sign.delete=Delete
|
||||
sign.first=First page
|
||||
sign.last=Last page
|
||||
sign.next=Next page
|
||||
sign.previous=Previous page
|
||||
|
||||
|
||||
#repair
|
||||
repair.title=ซ่อมแซม
|
||||
@@ -949,16 +944,6 @@ multiTool.deleteSelected=Delete Selected
|
||||
multiTool.downloadAll=Export
|
||||
multiTool.downloadSelected=Export Selected
|
||||
|
||||
multiTool.insertPageBreak=Insert Page Break
|
||||
multiTool.addFile=Add File
|
||||
multiTool.rotateLeft=Rotate Left
|
||||
multiTool.rotateRight=Rotate Right
|
||||
multiTool.split=Split
|
||||
multiTool.moveLeft=Move Left
|
||||
multiTool.moveRight=Move Right
|
||||
multiTool.delete=Delete
|
||||
multiTool.dragDropMessage=Page(s) Selected
|
||||
|
||||
#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!
|
||||
|
||||
|
||||
@@ -818,12 +818,7 @@ sign.save=Save Signature
|
||||
sign.personalSigs=Personal Signatures
|
||||
sign.sharedSigs=Shared Signatures
|
||||
sign.noSavedSigs=No saved signatures found
|
||||
sign.addToAll=Add to all pages
|
||||
sign.delete=Delete
|
||||
sign.first=First page
|
||||
sign.last=Last page
|
||||
sign.next=Next page
|
||||
sign.previous=Previous page
|
||||
|
||||
|
||||
#repair
|
||||
repair.title=Onar
|
||||
@@ -949,16 +944,6 @@ multiTool.deleteSelected=Delete Selected
|
||||
multiTool.downloadAll=Export
|
||||
multiTool.downloadSelected=Export Selected
|
||||
|
||||
multiTool.insertPageBreak=Insert Page Break
|
||||
multiTool.addFile=Add File
|
||||
multiTool.rotateLeft=Rotate Left
|
||||
multiTool.rotateRight=Rotate Right
|
||||
multiTool.split=Split
|
||||
multiTool.moveLeft=Move Left
|
||||
multiTool.moveRight=Move Right
|
||||
multiTool.delete=Delete
|
||||
multiTool.dragDropMessage=Page(s) Selected
|
||||
|
||||
#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!
|
||||
|
||||
|
||||
@@ -818,12 +818,7 @@ sign.save=Save Signature
|
||||
sign.personalSigs=Personal Signatures
|
||||
sign.sharedSigs=Shared Signatures
|
||||
sign.noSavedSigs=No saved signatures found
|
||||
sign.addToAll=Add to all pages
|
||||
sign.delete=Delete
|
||||
sign.first=First page
|
||||
sign.last=Last page
|
||||
sign.next=Next page
|
||||
sign.previous=Previous page
|
||||
|
||||
|
||||
#repair
|
||||
repair.title=Ремонт
|
||||
@@ -949,16 +944,6 @@ multiTool.deleteSelected=Delete Selected
|
||||
multiTool.downloadAll=Export
|
||||
multiTool.downloadSelected=Export Selected
|
||||
|
||||
multiTool.insertPageBreak=Insert Page Break
|
||||
multiTool.addFile=Add File
|
||||
multiTool.rotateLeft=Rotate Left
|
||||
multiTool.rotateRight=Rotate Right
|
||||
multiTool.split=Split
|
||||
multiTool.moveLeft=Move Left
|
||||
multiTool.moveRight=Move Right
|
||||
multiTool.delete=Delete
|
||||
multiTool.dragDropMessage=Page(s) Selected
|
||||
|
||||
#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!
|
||||
|
||||
|
||||
@@ -818,12 +818,7 @@ sign.save=Save Signature
|
||||
sign.personalSigs=Personal Signatures
|
||||
sign.sharedSigs=Shared Signatures
|
||||
sign.noSavedSigs=No saved signatures found
|
||||
sign.addToAll=Add to all pages
|
||||
sign.delete=Delete
|
||||
sign.first=First page
|
||||
sign.last=Last page
|
||||
sign.next=Next page
|
||||
sign.previous=Previous page
|
||||
|
||||
|
||||
#repair
|
||||
repair.title=Sửa chữa
|
||||
@@ -949,16 +944,6 @@ multiTool.deleteSelected=Delete Selected
|
||||
multiTool.downloadAll=Export
|
||||
multiTool.downloadSelected=Export Selected
|
||||
|
||||
multiTool.insertPageBreak=Insert Page Break
|
||||
multiTool.addFile=Add File
|
||||
multiTool.rotateLeft=Rotate Left
|
||||
multiTool.rotateRight=Rotate Right
|
||||
multiTool.split=Split
|
||||
multiTool.moveLeft=Move Left
|
||||
multiTool.moveRight=Move Right
|
||||
multiTool.delete=Delete
|
||||
multiTool.dragDropMessage=Page(s) Selected
|
||||
|
||||
#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!
|
||||
|
||||
|
||||
@@ -818,12 +818,7 @@ sign.save=Save Signature
|
||||
sign.personalSigs=Personal Signatures
|
||||
sign.sharedSigs=Shared Signatures
|
||||
sign.noSavedSigs=No saved signatures found
|
||||
sign.addToAll=Add to all pages
|
||||
sign.delete=Delete
|
||||
sign.first=First page
|
||||
sign.last=Last page
|
||||
sign.next=Next page
|
||||
sign.previous=Previous page
|
||||
|
||||
|
||||
#repair
|
||||
repair.title=修复
|
||||
@@ -949,16 +944,6 @@ multiTool.deleteSelected=Delete Selected
|
||||
multiTool.downloadAll=Export
|
||||
multiTool.downloadSelected=Export Selected
|
||||
|
||||
multiTool.insertPageBreak=Insert Page Break
|
||||
multiTool.addFile=Add File
|
||||
multiTool.rotateLeft=Rotate Left
|
||||
multiTool.rotateRight=Rotate Right
|
||||
multiTool.split=Split
|
||||
multiTool.moveLeft=Move Left
|
||||
multiTool.moveRight=Move Right
|
||||
multiTool.delete=Delete
|
||||
multiTool.dragDropMessage=Page(s) Selected
|
||||
|
||||
#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!
|
||||
|
||||
|
||||
@@ -818,12 +818,7 @@ sign.save=儲存簽章
|
||||
sign.personalSigs=個人簽章
|
||||
sign.sharedSigs=共用簽章
|
||||
sign.noSavedSigs=尚未儲存任何簽章
|
||||
sign.addToAll=Add to all pages
|
||||
sign.delete=Delete
|
||||
sign.first=First page
|
||||
sign.last=Last page
|
||||
sign.next=Next page
|
||||
sign.previous=Previous page
|
||||
|
||||
|
||||
#repair
|
||||
repair.title=修復
|
||||
@@ -949,16 +944,6 @@ multiTool.deleteSelected=Delete Selected
|
||||
multiTool.downloadAll=Export
|
||||
multiTool.downloadSelected=Export Selected
|
||||
|
||||
multiTool.insertPageBreak=Insert Page Break
|
||||
multiTool.addFile=Add File
|
||||
multiTool.rotateLeft=Rotate Left
|
||||
multiTool.rotateRight=Rotate Right
|
||||
multiTool.split=Split
|
||||
multiTool.moveLeft=Move Left
|
||||
multiTool.moveRight=Move Right
|
||||
multiTool.delete=Delete
|
||||
multiTool.dragDropMessage=Page(s) Selected
|
||||
|
||||
#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!
|
||||
|
||||
|
||||
@@ -3,14 +3,14 @@
|
||||
{
|
||||
"moduleName": "ch.qos.logback:logback-classic",
|
||||
"moduleUrl": "http://www.qos.ch",
|
||||
"moduleVersion": "1.5.12",
|
||||
"moduleVersion": "1.5.11",
|
||||
"moduleLicense": "GNU Lesser General Public License",
|
||||
"moduleLicenseUrl": "http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html"
|
||||
},
|
||||
{
|
||||
"moduleName": "ch.qos.logback:logback-core",
|
||||
"moduleUrl": "http://www.qos.ch",
|
||||
"moduleVersion": "1.5.12",
|
||||
"moduleVersion": "1.5.11",
|
||||
"moduleLicense": "GNU Lesser General Public License",
|
||||
"moduleLicenseUrl": "http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html"
|
||||
},
|
||||
@@ -45,77 +45,77 @@
|
||||
{
|
||||
"moduleName": "com.fasterxml.jackson.core:jackson-annotations",
|
||||
"moduleUrl": "https://github.com/FasterXML/jackson",
|
||||
"moduleVersion": "2.18.1",
|
||||
"moduleVersion": "2.17.2",
|
||||
"moduleLicense": "The Apache Software License, Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
|
||||
},
|
||||
{
|
||||
"moduleName": "com.fasterxml.jackson.core:jackson-core",
|
||||
"moduleUrl": "https://github.com/FasterXML/jackson-core",
|
||||
"moduleVersion": "2.18.1",
|
||||
"moduleVersion": "2.17.2",
|
||||
"moduleLicense": "The Apache Software License, Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
|
||||
},
|
||||
{
|
||||
"moduleName": "com.fasterxml.jackson.core:jackson-databind",
|
||||
"moduleUrl": "https://github.com/FasterXML/jackson",
|
||||
"moduleVersion": "2.18.1",
|
||||
"moduleVersion": "2.17.2",
|
||||
"moduleLicense": "The Apache Software License, Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
|
||||
},
|
||||
{
|
||||
"moduleName": "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml",
|
||||
"moduleUrl": "https://github.com/FasterXML/jackson-dataformats-text",
|
||||
"moduleVersion": "2.18.1",
|
||||
"moduleVersion": "2.17.2",
|
||||
"moduleLicense": "The Apache Software License, Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
|
||||
},
|
||||
{
|
||||
"moduleName": "com.fasterxml.jackson.datatype:jackson-datatype-jdk8",
|
||||
"moduleUrl": "https://github.com/FasterXML/jackson-modules-java8/jackson-datatype-jdk8",
|
||||
"moduleVersion": "2.18.1",
|
||||
"moduleVersion": "2.17.2",
|
||||
"moduleLicense": "The Apache Software License, Version 2.0",
|
||||
"moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
|
||||
},
|
||||
{
|
||||
"moduleName": "com.fasterxml.jackson.datatype:jackson-datatype-jsr310",
|
||||
"moduleUrl": "https://github.com/FasterXML/jackson-modules-java8/jackson-datatype-jsr310",
|
||||
"moduleVersion": "2.18.1",
|
||||
"moduleVersion": "2.17.2",
|
||||
"moduleLicense": "The Apache Software License, Version 2.0",
|
||||
"moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
|
||||
},
|
||||
{
|
||||
"moduleName": "com.fasterxml.jackson.jaxrs:jackson-jaxrs-base",
|
||||
"moduleUrl": "https://github.com/FasterXML/jackson-jaxrs-providers/jackson-jaxrs-base",
|
||||
"moduleVersion": "2.18.1",
|
||||
"moduleVersion": "2.17.2",
|
||||
"moduleLicense": "The Apache Software License, Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
|
||||
},
|
||||
{
|
||||
"moduleName": "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider",
|
||||
"moduleUrl": "https://github.com/FasterXML/jackson-jaxrs-providers/jackson-jaxrs-json-provider",
|
||||
"moduleVersion": "2.18.1",
|
||||
"moduleVersion": "2.17.2",
|
||||
"moduleLicense": "The Apache Software License, Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
|
||||
},
|
||||
{
|
||||
"moduleName": "com.fasterxml.jackson.module:jackson-module-jaxb-annotations",
|
||||
"moduleUrl": "https://github.com/FasterXML/jackson-modules-base",
|
||||
"moduleVersion": "2.18.1",
|
||||
"moduleVersion": "2.17.2",
|
||||
"moduleLicense": "The Apache Software License, Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
|
||||
},
|
||||
{
|
||||
"moduleName": "com.fasterxml.jackson.module:jackson-module-parameter-names",
|
||||
"moduleUrl": "https://github.com/FasterXML/jackson-modules-java8/jackson-module-parameter-names",
|
||||
"moduleVersion": "2.18.1",
|
||||
"moduleVersion": "2.17.2",
|
||||
"moduleLicense": "The Apache Software License, Version 2.0",
|
||||
"moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
|
||||
},
|
||||
{
|
||||
"moduleName": "com.fasterxml.jackson:jackson-bom",
|
||||
"moduleUrl": "https://github.com/FasterXML/jackson-bom",
|
||||
"moduleVersion": "2.18.1",
|
||||
"moduleVersion": "2.17.2",
|
||||
"moduleLicense": "The Apache Software License, Version 2.0",
|
||||
"moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
|
||||
},
|
||||
@@ -146,18 +146,6 @@
|
||||
"moduleLicense": "GNU General Public License v3.0",
|
||||
"moduleLicenseUrl": "https://api.github.com/licenses/gpl-3.0"
|
||||
},
|
||||
{
|
||||
"moduleName": "com.github.jai-imageio:jai-imageio-core",
|
||||
"moduleUrl": "https://github.com/jai-imageio/jai-imageio-core",
|
||||
"moduleVersion": "1.4.0",
|
||||
"moduleLicense": "LICENSE.txt"
|
||||
},
|
||||
{
|
||||
"moduleName": "com.github.jai-imageio:jai-imageio-jpeg2000",
|
||||
"moduleUrl": "https://github.com/jai-imageio/jai-imageio-jpeg2000",
|
||||
"moduleVersion": "1.4.0",
|
||||
"moduleLicense": "LICENSE-JJ2000.txt, LICENSE-Sun.txt"
|
||||
},
|
||||
{
|
||||
"moduleName": "com.github.stephenc.jcip:jcip-annotations",
|
||||
"moduleUrl": "http://stephenc.github.com/jcip-annotations",
|
||||
@@ -174,22 +162,21 @@
|
||||
},
|
||||
{
|
||||
"moduleName": "com.google.errorprone:error_prone_annotations",
|
||||
"moduleUrl": "https://errorprone.info/error_prone_annotations",
|
||||
"moduleVersion": "2.28.0",
|
||||
"moduleVersion": "2.11.0",
|
||||
"moduleLicense": "Apache 2.0",
|
||||
"moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
|
||||
},
|
||||
{
|
||||
"moduleName": "com.google.guava:failureaccess",
|
||||
"moduleUrl": "https://github.com/google/guava/",
|
||||
"moduleVersion": "1.0.2",
|
||||
"moduleVersion": "1.0.1",
|
||||
"moduleLicense": "The Apache Software License, Version 2.0",
|
||||
"moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
|
||||
},
|
||||
{
|
||||
"moduleName": "com.google.guava:guava",
|
||||
"moduleUrl": "https://github.com/google/guava/",
|
||||
"moduleVersion": "33.3.1-jre",
|
||||
"moduleVersion": "31.1-jre",
|
||||
"moduleLicense": "Apache License, Version 2.0",
|
||||
"moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
|
||||
},
|
||||
@@ -202,8 +189,8 @@
|
||||
{
|
||||
"moduleName": "com.google.j2objc:j2objc-annotations",
|
||||
"moduleUrl": "https://github.com/google/j2objc/",
|
||||
"moduleVersion": "3.0.0",
|
||||
"moduleLicense": "Apache License, Version 2.0",
|
||||
"moduleVersion": "1.3",
|
||||
"moduleLicense": "The Apache Software License, Version 2.0",
|
||||
"moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
|
||||
},
|
||||
{
|
||||
@@ -235,7 +222,7 @@
|
||||
{
|
||||
"moduleName": "com.h2database:h2",
|
||||
"moduleUrl": "https://h2database.com",
|
||||
"moduleVersion": "2.3.232",
|
||||
"moduleVersion": "2.1.214",
|
||||
"moduleLicense": "MPL 2.0",
|
||||
"moduleLicenseUrl": "https://www.mozilla.org/en-US/MPL/2.0/"
|
||||
},
|
||||
@@ -383,17 +370,10 @@
|
||||
"moduleLicense": "Apache License, Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
|
||||
},
|
||||
{
|
||||
"moduleName": "commons-cli:commons-cli",
|
||||
"moduleUrl": "http://commons.apache.org/proper/commons-cli/",
|
||||
"moduleVersion": "1.4",
|
||||
"moduleLicense": "Apache License, Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
|
||||
},
|
||||
{
|
||||
"moduleName": "commons-codec:commons-codec",
|
||||
"moduleUrl": "https://commons.apache.org/proper/commons-codec/",
|
||||
"moduleVersion": "1.17.1",
|
||||
"moduleVersion": "1.16.1",
|
||||
"moduleLicense": "Apache-2.0",
|
||||
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
|
||||
},
|
||||
@@ -413,10 +393,10 @@
|
||||
},
|
||||
{
|
||||
"moduleName": "commons-logging:commons-logging",
|
||||
"moduleUrl": "https://commons.apache.org/proper/commons-logging/",
|
||||
"moduleVersion": "1.3.3",
|
||||
"moduleLicense": "Apache-2.0",
|
||||
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
|
||||
"moduleUrl": "http://jakarta.apache.org/commons/logging/",
|
||||
"moduleVersion": "1.0.4",
|
||||
"moduleLicense": "The Apache Software License, Version 2.0",
|
||||
"moduleLicenseUrl": "/LICENSE.txt"
|
||||
},
|
||||
{
|
||||
"moduleName": "io.dropwizard.metrics:metrics-core",
|
||||
@@ -434,34 +414,34 @@
|
||||
{
|
||||
"moduleName": "io.micrometer:micrometer-commons",
|
||||
"moduleUrl": "https://github.com/micrometer-metrics/micrometer",
|
||||
"moduleVersion": "1.14.1",
|
||||
"moduleVersion": "1.13.6",
|
||||
"moduleLicense": "The Apache Software License, Version 2.0",
|
||||
"moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
|
||||
},
|
||||
{
|
||||
"moduleName": "io.micrometer:micrometer-core",
|
||||
"moduleUrl": "https://github.com/micrometer-metrics/micrometer",
|
||||
"moduleVersion": "1.14.1",
|
||||
"moduleVersion": "1.13.6",
|
||||
"moduleLicense": "The Apache Software License, Version 2.0",
|
||||
"moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
|
||||
},
|
||||
{
|
||||
"moduleName": "io.micrometer:micrometer-jakarta9",
|
||||
"moduleUrl": "https://github.com/micrometer-metrics/micrometer",
|
||||
"moduleVersion": "1.14.1",
|
||||
"moduleVersion": "1.13.6",
|
||||
"moduleLicense": "The Apache Software License, Version 2.0",
|
||||
"moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
|
||||
},
|
||||
{
|
||||
"moduleName": "io.micrometer:micrometer-observation",
|
||||
"moduleUrl": "https://github.com/micrometer-metrics/micrometer",
|
||||
"moduleVersion": "1.14.1",
|
||||
"moduleVersion": "1.13.6",
|
||||
"moduleLicense": "The Apache Software License, Version 2.0",
|
||||
"moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
|
||||
},
|
||||
{
|
||||
"moduleName": "io.smallrye:jandex",
|
||||
"moduleVersion": "3.2.0",
|
||||
"moduleVersion": "3.1.2",
|
||||
"moduleLicense": "Apache License, Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
|
||||
},
|
||||
@@ -593,7 +573,7 @@
|
||||
},
|
||||
{
|
||||
"moduleName": "net.bytebuddy:byte-buddy",
|
||||
"moduleVersion": "1.15.10",
|
||||
"moduleVersion": "1.14.19",
|
||||
"moduleLicense": "Apache License, Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
|
||||
},
|
||||
@@ -631,17 +611,10 @@
|
||||
"moduleLicense": "Apache License, Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.apache.commons:commons-csv",
|
||||
"moduleUrl": "https://commons.apache.org/proper/commons-csv/",
|
||||
"moduleVersion": "1.9.0",
|
||||
"moduleLicense": "Apache License, Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.apache.commons:commons-lang3",
|
||||
"moduleUrl": "https://commons.apache.org/proper/commons-lang/",
|
||||
"moduleVersion": "3.17.0",
|
||||
"moduleVersion": "3.14.0",
|
||||
"moduleLicense": "Apache-2.0",
|
||||
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
|
||||
},
|
||||
@@ -668,13 +641,13 @@
|
||||
},
|
||||
{
|
||||
"moduleName": "org.apache.logging.log4j:log4j-api",
|
||||
"moduleVersion": "2.24.1",
|
||||
"moduleVersion": "2.23.1",
|
||||
"moduleLicense": "Apache-2.0",
|
||||
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.apache.logging.log4j:log4j-to-slf4j",
|
||||
"moduleVersion": "2.24.1",
|
||||
"moduleVersion": "2.23.1",
|
||||
"moduleLicense": "Apache-2.0",
|
||||
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
|
||||
},
|
||||
@@ -722,7 +695,7 @@
|
||||
{
|
||||
"moduleName": "org.apache.tomcat.embed:tomcat-embed-el",
|
||||
"moduleUrl": "https://tomcat.apache.org/",
|
||||
"moduleVersion": "10.1.33",
|
||||
"moduleVersion": "10.1.31",
|
||||
"moduleLicense": "Apache License, Version 2.0",
|
||||
"moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
|
||||
},
|
||||
@@ -760,52 +733,31 @@
|
||||
"moduleLicense": "The Apache Software License, Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.bouncycastle:bcmail-jdk15on",
|
||||
"moduleUrl": "https://www.bouncycastle.org/java.html",
|
||||
"moduleVersion": "1.69",
|
||||
"moduleLicense": "Bouncy Castle Licence",
|
||||
"moduleLicenseUrl": "https://www.bouncycastle.org/licence.html"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.bouncycastle:bcpkix-jdk15on",
|
||||
"moduleUrl": "https://www.bouncycastle.org/java.html",
|
||||
"moduleVersion": "1.69",
|
||||
"moduleLicense": "Bouncy Castle Licence",
|
||||
"moduleLicenseUrl": "https://www.bouncycastle.org/licence.html"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.bouncycastle:bcpkix-jdk18on",
|
||||
"moduleUrl": "https://www.bouncycastle.org/java.html",
|
||||
"moduleVersion": "1.79",
|
||||
"moduleVersion": "1.78.1",
|
||||
"moduleLicense": "Bouncy Castle Licence",
|
||||
"moduleLicenseUrl": "https://www.bouncycastle.org/licence.html"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.bouncycastle:bcprov-jdk18on",
|
||||
"moduleUrl": "https://www.bouncycastle.org/java.html",
|
||||
"moduleVersion": "1.79",
|
||||
"moduleLicense": "Bouncy Castle Licence",
|
||||
"moduleLicenseUrl": "https://www.bouncycastle.org/licence.html"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.bouncycastle:bcutil-jdk15on",
|
||||
"moduleUrl": "https://www.bouncycastle.org/java.html",
|
||||
"moduleVersion": "1.69",
|
||||
"moduleVersion": "1.78.1",
|
||||
"moduleLicense": "Bouncy Castle Licence",
|
||||
"moduleLicenseUrl": "https://www.bouncycastle.org/licence.html"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.bouncycastle:bcutil-jdk18on",
|
||||
"moduleUrl": "https://www.bouncycastle.org/java.html",
|
||||
"moduleVersion": "1.79",
|
||||
"moduleVersion": "1.78.1",
|
||||
"moduleLicense": "Bouncy Castle Licence",
|
||||
"moduleLicenseUrl": "https://www.bouncycastle.org/licence.html"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.checkerframework:checker-qual",
|
||||
"moduleUrl": "https://checkerframework.org/",
|
||||
"moduleVersion": "3.43.0",
|
||||
"moduleUrl": "https://checkerframework.org",
|
||||
"moduleVersion": "3.12.0",
|
||||
"moduleLicense": "The MIT License",
|
||||
"moduleLicenseUrl": "http://opensource.org/licenses/MIT"
|
||||
},
|
||||
@@ -838,182 +790,182 @@
|
||||
{
|
||||
"moduleName": "org.eclipse.jetty.ee10.websocket:jetty-ee10-websocket-jakarta-client",
|
||||
"moduleUrl": "https://jetty.org/",
|
||||
"moduleVersion": "12.0.15",
|
||||
"moduleVersion": "12.0.14",
|
||||
"moduleLicense": "Eclipse Public License - Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.eclipse.jetty.ee10.websocket:jetty-ee10-websocket-jakarta-common",
|
||||
"moduleUrl": "https://jetty.org/",
|
||||
"moduleVersion": "12.0.15",
|
||||
"moduleVersion": "12.0.14",
|
||||
"moduleLicense": "Eclipse Public License - Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.eclipse.jetty.ee10.websocket:jetty-ee10-websocket-jakarta-server",
|
||||
"moduleUrl": "https://jetty.org/",
|
||||
"moduleVersion": "12.0.15",
|
||||
"moduleVersion": "12.0.14",
|
||||
"moduleLicense": "Eclipse Public License - Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.eclipse.jetty.ee10.websocket:jetty-ee10-websocket-jetty-server",
|
||||
"moduleUrl": "https://jetty.org/",
|
||||
"moduleVersion": "12.0.15",
|
||||
"moduleVersion": "12.0.14",
|
||||
"moduleLicense": "Eclipse Public License - Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.eclipse.jetty.ee10.websocket:jetty-ee10-websocket-servlet",
|
||||
"moduleUrl": "https://jetty.org/",
|
||||
"moduleVersion": "12.0.15",
|
||||
"moduleVersion": "12.0.14",
|
||||
"moduleLicense": "Eclipse Public License - Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.eclipse.jetty.ee10:jetty-ee10-annotations",
|
||||
"moduleUrl": "https://jetty.org/",
|
||||
"moduleVersion": "12.0.15",
|
||||
"moduleVersion": "12.0.14",
|
||||
"moduleLicense": "Eclipse Public License - Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.eclipse.jetty.ee10:jetty-ee10-plus",
|
||||
"moduleUrl": "https://jetty.org/",
|
||||
"moduleVersion": "12.0.15",
|
||||
"moduleVersion": "12.0.14",
|
||||
"moduleLicense": "Eclipse Public License - Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.eclipse.jetty.ee10:jetty-ee10-servlet",
|
||||
"moduleUrl": "https://jetty.org/",
|
||||
"moduleVersion": "12.0.15",
|
||||
"moduleVersion": "12.0.14",
|
||||
"moduleLicense": "Eclipse Public License - Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.eclipse.jetty.ee10:jetty-ee10-servlets",
|
||||
"moduleUrl": "https://jetty.org/",
|
||||
"moduleVersion": "12.0.15",
|
||||
"moduleVersion": "12.0.14",
|
||||
"moduleLicense": "Eclipse Public License - Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.eclipse.jetty.ee10:jetty-ee10-webapp",
|
||||
"moduleUrl": "https://jetty.org/",
|
||||
"moduleVersion": "12.0.15",
|
||||
"moduleVersion": "12.0.14",
|
||||
"moduleLicense": "Eclipse Public License - Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.eclipse.jetty.websocket:jetty-websocket-core-client",
|
||||
"moduleUrl": "https://jetty.org/",
|
||||
"moduleVersion": "12.0.15",
|
||||
"moduleVersion": "12.0.14",
|
||||
"moduleLicense": "Eclipse Public License - Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.eclipse.jetty.websocket:jetty-websocket-core-common",
|
||||
"moduleUrl": "https://jetty.org/",
|
||||
"moduleVersion": "12.0.15",
|
||||
"moduleVersion": "12.0.14",
|
||||
"moduleLicense": "Eclipse Public License - Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.eclipse.jetty.websocket:jetty-websocket-core-server",
|
||||
"moduleUrl": "https://jetty.org/",
|
||||
"moduleVersion": "12.0.15",
|
||||
"moduleVersion": "12.0.14",
|
||||
"moduleLicense": "Eclipse Public License - Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.eclipse.jetty.websocket:jetty-websocket-jetty-api",
|
||||
"moduleUrl": "https://jetty.org/",
|
||||
"moduleVersion": "12.0.15",
|
||||
"moduleVersion": "12.0.14",
|
||||
"moduleLicense": "Eclipse Public License - Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.eclipse.jetty.websocket:jetty-websocket-jetty-common",
|
||||
"moduleUrl": "https://jetty.org/",
|
||||
"moduleVersion": "12.0.15",
|
||||
"moduleVersion": "12.0.14",
|
||||
"moduleLicense": "Eclipse Public License - Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.eclipse.jetty:jetty-alpn-client",
|
||||
"moduleUrl": "https://jetty.org/",
|
||||
"moduleVersion": "12.0.15",
|
||||
"moduleVersion": "12.0.14",
|
||||
"moduleLicense": "Eclipse Public License - Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.eclipse.jetty:jetty-client",
|
||||
"moduleUrl": "https://jetty.org/",
|
||||
"moduleVersion": "12.0.15",
|
||||
"moduleVersion": "12.0.14",
|
||||
"moduleLicense": "Eclipse Public License - Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.eclipse.jetty:jetty-ee",
|
||||
"moduleUrl": "https://jetty.org/",
|
||||
"moduleVersion": "12.0.15",
|
||||
"moduleVersion": "12.0.14",
|
||||
"moduleLicense": "Eclipse Public License - Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.eclipse.jetty:jetty-http",
|
||||
"moduleUrl": "https://jetty.org/",
|
||||
"moduleVersion": "12.0.15",
|
||||
"moduleVersion": "12.0.14",
|
||||
"moduleLicense": "Eclipse Public License - Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.eclipse.jetty:jetty-io",
|
||||
"moduleUrl": "https://jetty.org/",
|
||||
"moduleVersion": "12.0.15",
|
||||
"moduleVersion": "12.0.14",
|
||||
"moduleLicense": "Eclipse Public License - Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.eclipse.jetty:jetty-plus",
|
||||
"moduleUrl": "https://jetty.org/",
|
||||
"moduleVersion": "12.0.15",
|
||||
"moduleVersion": "12.0.14",
|
||||
"moduleLicense": "Eclipse Public License - Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.eclipse.jetty:jetty-security",
|
||||
"moduleUrl": "https://jetty.org/",
|
||||
"moduleVersion": "12.0.15",
|
||||
"moduleVersion": "12.0.14",
|
||||
"moduleLicense": "Eclipse Public License - Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.eclipse.jetty:jetty-server",
|
||||
"moduleUrl": "https://jetty.org/",
|
||||
"moduleVersion": "12.0.15",
|
||||
"moduleVersion": "12.0.14",
|
||||
"moduleLicense": "Eclipse Public License - Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.eclipse.jetty:jetty-session",
|
||||
"moduleUrl": "https://jetty.org/",
|
||||
"moduleVersion": "12.0.15",
|
||||
"moduleVersion": "12.0.14",
|
||||
"moduleLicense": "Eclipse Public License - Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.eclipse.jetty:jetty-util",
|
||||
"moduleUrl": "https://jetty.org/",
|
||||
"moduleVersion": "12.0.15",
|
||||
"moduleVersion": "12.0.14",
|
||||
"moduleLicense": "Eclipse Public License - Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.eclipse.jetty:jetty-xml",
|
||||
"moduleUrl": "https://jetty.org/",
|
||||
"moduleVersion": "12.0.15",
|
||||
"moduleVersion": "12.0.14",
|
||||
"moduleLicense": "Eclipse Public License - Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/"
|
||||
},
|
||||
@@ -1048,23 +1000,23 @@
|
||||
{
|
||||
"moduleName": "org.hibernate.common:hibernate-commons-annotations",
|
||||
"moduleUrl": "http://hibernate.org",
|
||||
"moduleVersion": "7.0.3.Final",
|
||||
"moduleLicense": "Apache License Version 2.0",
|
||||
"moduleLicenseUrl": "https://opensource.org/licenses/Apache-2.0"
|
||||
"moduleVersion": "6.0.6.Final",
|
||||
"moduleLicense": "GNU Library General Public License v2.1 or later",
|
||||
"moduleLicenseUrl": "http://www.opensource.org/licenses/LGPL-2.1"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.hibernate.orm:hibernate-core",
|
||||
"moduleUrl": "https://www.hibernate.org/orm/6.6",
|
||||
"moduleVersion": "6.6.2.Final",
|
||||
"moduleUrl": "https://www.hibernate.org/orm/6.5",
|
||||
"moduleVersion": "6.5.3.Final",
|
||||
"moduleLicense": "GNU Library General Public License v2.1 or later",
|
||||
"moduleLicenseUrl": "https://www.opensource.org/licenses/LGPL-2.1"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.jboss.logging:jboss-logging",
|
||||
"moduleUrl": "http://www.jboss.org",
|
||||
"moduleVersion": "3.6.1.Final",
|
||||
"moduleLicense": "Apache License 2.0",
|
||||
"moduleLicenseUrl": "https://repository.jboss.org/licenses/apache-2.0.txt"
|
||||
"moduleVersion": "3.5.3.Final",
|
||||
"moduleLicense": "Public Domain",
|
||||
"moduleLicenseUrl": "http://repository.jboss.org/licenses/cc0-1.0.txt"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.latencyutils:LatencyUtils",
|
||||
@@ -1073,12 +1025,6 @@
|
||||
"moduleLicense": "Public Domain, per Creative Commons CC0",
|
||||
"moduleLicenseUrl": "http://creativecommons.org/publicdomain/zero/1.0/"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.locationtech.jts:jts-core",
|
||||
"moduleVersion": "1.18.1",
|
||||
"moduleLicense": "Eclipse Public License, Version 2.0",
|
||||
"moduleLicenseUrl": "https://github.com/locationtech/jts/blob/master/LICENSE_EPLv2.txt"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.opensaml:opensaml-core",
|
||||
"moduleVersion": "4.3.2",
|
||||
@@ -1154,21 +1100,21 @@
|
||||
{
|
||||
"moduleName": "org.ow2.asm:asm",
|
||||
"moduleUrl": "http://asm.ow2.org",
|
||||
"moduleVersion": "9.7.1",
|
||||
"moduleVersion": "9.7",
|
||||
"moduleLicense": "The Apache Software License, Version 2.0",
|
||||
"moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.ow2.asm:asm-commons",
|
||||
"moduleUrl": "http://asm.ow2.org",
|
||||
"moduleVersion": "9.7.1",
|
||||
"moduleVersion": "9.7",
|
||||
"moduleLicense": "The Apache Software License, Version 2.0",
|
||||
"moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.ow2.asm:asm-tree",
|
||||
"moduleUrl": "http://asm.ow2.org",
|
||||
"moduleVersion": "9.7.1",
|
||||
"moduleVersion": "9.7",
|
||||
"moduleLicense": "The Apache Software License, Version 2.0",
|
||||
"moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
|
||||
},
|
||||
@@ -1207,266 +1153,273 @@
|
||||
{
|
||||
"moduleName": "org.springframework.boot:spring-boot",
|
||||
"moduleUrl": "https://spring.io/projects/spring-boot",
|
||||
"moduleVersion": "3.4.0",
|
||||
"moduleVersion": "3.3.5",
|
||||
"moduleLicense": "Apache License, Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.springframework.boot:spring-boot-actuator",
|
||||
"moduleUrl": "https://spring.io/projects/spring-boot",
|
||||
"moduleVersion": "3.4.0",
|
||||
"moduleVersion": "3.3.5",
|
||||
"moduleLicense": "Apache License, Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.springframework.boot:spring-boot-actuator-autoconfigure",
|
||||
"moduleUrl": "https://spring.io/projects/spring-boot",
|
||||
"moduleVersion": "3.4.0",
|
||||
"moduleVersion": "3.3.5",
|
||||
"moduleLicense": "Apache License, Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.springframework.boot:spring-boot-autoconfigure",
|
||||
"moduleUrl": "https://spring.io/projects/spring-boot",
|
||||
"moduleVersion": "3.4.0",
|
||||
"moduleVersion": "3.3.5",
|
||||
"moduleLicense": "Apache License, Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.springframework.boot:spring-boot-devtools",
|
||||
"moduleUrl": "https://spring.io/projects/spring-boot",
|
||||
"moduleVersion": "3.4.0",
|
||||
"moduleVersion": "3.3.5",
|
||||
"moduleLicense": "Apache License, Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.springframework.boot:spring-boot-starter",
|
||||
"moduleUrl": "https://spring.io/projects/spring-boot",
|
||||
"moduleVersion": "3.4.0",
|
||||
"moduleVersion": "3.3.5",
|
||||
"moduleLicense": "Apache License, Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.springframework.boot:spring-boot-starter-actuator",
|
||||
"moduleUrl": "https://spring.io/projects/spring-boot",
|
||||
"moduleVersion": "3.4.0",
|
||||
"moduleVersion": "3.3.5",
|
||||
"moduleLicense": "Apache License, Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.springframework.boot:spring-boot-starter-aop",
|
||||
"moduleUrl": "https://spring.io/projects/spring-boot",
|
||||
"moduleVersion": "3.3.5",
|
||||
"moduleLicense": "Apache License, Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.springframework.boot:spring-boot-starter-data-jpa",
|
||||
"moduleUrl": "https://spring.io/projects/spring-boot",
|
||||
"moduleVersion": "3.4.0",
|
||||
"moduleVersion": "3.3.5",
|
||||
"moduleLicense": "Apache License, Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.springframework.boot:spring-boot-starter-jdbc",
|
||||
"moduleUrl": "https://spring.io/projects/spring-boot",
|
||||
"moduleVersion": "3.4.0",
|
||||
"moduleVersion": "3.3.5",
|
||||
"moduleLicense": "Apache License, Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.springframework.boot:spring-boot-starter-jetty",
|
||||
"moduleUrl": "https://spring.io/projects/spring-boot",
|
||||
"moduleVersion": "3.4.0",
|
||||
"moduleVersion": "3.3.5",
|
||||
"moduleLicense": "Apache License, Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.springframework.boot:spring-boot-starter-json",
|
||||
"moduleUrl": "https://spring.io/projects/spring-boot",
|
||||
"moduleVersion": "3.4.0",
|
||||
"moduleVersion": "3.3.5",
|
||||
"moduleLicense": "Apache License, Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.springframework.boot:spring-boot-starter-logging",
|
||||
"moduleUrl": "https://spring.io/projects/spring-boot",
|
||||
"moduleVersion": "3.4.0",
|
||||
"moduleVersion": "3.3.5",
|
||||
"moduleLicense": "Apache License, Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.springframework.boot:spring-boot-starter-oauth2-client",
|
||||
"moduleUrl": "https://spring.io/projects/spring-boot",
|
||||
"moduleVersion": "3.4.0",
|
||||
"moduleVersion": "3.3.5",
|
||||
"moduleLicense": "Apache License, Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.springframework.boot:spring-boot-starter-security",
|
||||
"moduleUrl": "https://spring.io/projects/spring-boot",
|
||||
"moduleVersion": "3.4.0",
|
||||
"moduleVersion": "3.3.5",
|
||||
"moduleLicense": "Apache License, Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.springframework.boot:spring-boot-starter-thymeleaf",
|
||||
"moduleUrl": "https://spring.io/projects/spring-boot",
|
||||
"moduleVersion": "3.4.0",
|
||||
"moduleVersion": "3.3.5",
|
||||
"moduleLicense": "Apache License, Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.springframework.boot:spring-boot-starter-web",
|
||||
"moduleUrl": "https://spring.io/projects/spring-boot",
|
||||
"moduleVersion": "3.4.0",
|
||||
"moduleVersion": "3.3.5",
|
||||
"moduleLicense": "Apache License, Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.springframework.data:spring-data-commons",
|
||||
"moduleUrl": "https://spring.io/projects/spring-data",
|
||||
"moduleVersion": "3.4.0",
|
||||
"moduleVersion": "3.3.5",
|
||||
"moduleLicense": "Apache License, Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.springframework.data:spring-data-jpa",
|
||||
"moduleUrl": "https://projects.spring.io/spring-data-jpa",
|
||||
"moduleVersion": "3.4.0",
|
||||
"moduleVersion": "3.3.5",
|
||||
"moduleLicense": "Apache License, Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.springframework.security:spring-security-config",
|
||||
"moduleUrl": "https://spring.io/projects/spring-security",
|
||||
"moduleVersion": "6.4.1",
|
||||
"moduleVersion": "6.3.4",
|
||||
"moduleLicense": "Apache License, Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.springframework.security:spring-security-core",
|
||||
"moduleUrl": "https://spring.io/projects/spring-security",
|
||||
"moduleVersion": "6.4.1",
|
||||
"moduleVersion": "6.3.4",
|
||||
"moduleLicense": "Apache License, Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.springframework.security:spring-security-crypto",
|
||||
"moduleUrl": "https://spring.io/projects/spring-security",
|
||||
"moduleVersion": "6.4.1",
|
||||
"moduleVersion": "6.3.4",
|
||||
"moduleLicense": "Apache License, Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.springframework.security:spring-security-oauth2-client",
|
||||
"moduleUrl": "https://spring.io/projects/spring-security",
|
||||
"moduleVersion": "6.4.1",
|
||||
"moduleVersion": "6.3.4",
|
||||
"moduleLicense": "Apache License, Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.springframework.security:spring-security-oauth2-core",
|
||||
"moduleUrl": "https://spring.io/projects/spring-security",
|
||||
"moduleVersion": "6.4.1",
|
||||
"moduleVersion": "6.3.4",
|
||||
"moduleLicense": "Apache License, Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.springframework.security:spring-security-oauth2-jose",
|
||||
"moduleUrl": "https://spring.io/projects/spring-security",
|
||||
"moduleVersion": "6.4.1",
|
||||
"moduleVersion": "6.3.4",
|
||||
"moduleLicense": "Apache License, Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.springframework.security:spring-security-saml2-service-provider",
|
||||
"moduleUrl": "https://spring.io/projects/spring-security",
|
||||
"moduleVersion": "6.4.1",
|
||||
"moduleVersion": "6.3.4",
|
||||
"moduleLicense": "Apache License, Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.springframework.security:spring-security-web",
|
||||
"moduleUrl": "https://spring.io/projects/spring-security",
|
||||
"moduleVersion": "6.4.1",
|
||||
"moduleVersion": "6.3.4",
|
||||
"moduleLicense": "Apache License, Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.springframework:spring-aop",
|
||||
"moduleUrl": "https://github.com/spring-projects/spring-framework",
|
||||
"moduleVersion": "6.2.0",
|
||||
"moduleVersion": "6.1.14",
|
||||
"moduleLicense": "Apache License, Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.springframework:spring-aspects",
|
||||
"moduleUrl": "https://github.com/spring-projects/spring-framework",
|
||||
"moduleVersion": "6.2.0",
|
||||
"moduleVersion": "6.1.14",
|
||||
"moduleLicense": "Apache License, Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.springframework:spring-beans",
|
||||
"moduleUrl": "https://github.com/spring-projects/spring-framework",
|
||||
"moduleVersion": "6.2.0",
|
||||
"moduleVersion": "6.1.14",
|
||||
"moduleLicense": "Apache License, Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.springframework:spring-context",
|
||||
"moduleUrl": "https://github.com/spring-projects/spring-framework",
|
||||
"moduleVersion": "6.2.0",
|
||||
"moduleVersion": "6.1.14",
|
||||
"moduleLicense": "Apache License, Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.springframework:spring-core",
|
||||
"moduleUrl": "https://github.com/spring-projects/spring-framework",
|
||||
"moduleVersion": "6.2.0",
|
||||
"moduleVersion": "6.1.14",
|
||||
"moduleLicense": "Apache License, Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.springframework:spring-expression",
|
||||
"moduleUrl": "https://github.com/spring-projects/spring-framework",
|
||||
"moduleVersion": "6.2.0",
|
||||
"moduleVersion": "6.1.14",
|
||||
"moduleLicense": "Apache License, Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.springframework:spring-jcl",
|
||||
"moduleUrl": "https://github.com/spring-projects/spring-framework",
|
||||
"moduleVersion": "6.2.0",
|
||||
"moduleVersion": "6.1.14",
|
||||
"moduleLicense": "Apache License, Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.springframework:spring-jdbc",
|
||||
"moduleUrl": "https://github.com/spring-projects/spring-framework",
|
||||
"moduleVersion": "6.2.0",
|
||||
"moduleVersion": "6.1.14",
|
||||
"moduleLicense": "Apache License, Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.springframework:spring-orm",
|
||||
"moduleUrl": "https://github.com/spring-projects/spring-framework",
|
||||
"moduleVersion": "6.2.0",
|
||||
"moduleVersion": "6.1.14",
|
||||
"moduleLicense": "Apache License, Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.springframework:spring-tx",
|
||||
"moduleUrl": "https://github.com/spring-projects/spring-framework",
|
||||
"moduleVersion": "6.2.0",
|
||||
"moduleVersion": "6.1.14",
|
||||
"moduleLicense": "Apache License, Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.springframework:spring-web",
|
||||
"moduleUrl": "https://github.com/spring-projects/spring-framework",
|
||||
"moduleVersion": "6.2.0",
|
||||
"moduleVersion": "6.1.14",
|
||||
"moduleLicense": "Apache License, Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.springframework:spring-webmvc",
|
||||
"moduleUrl": "https://github.com/spring-projects/spring-framework",
|
||||
"moduleVersion": "6.2.0",
|
||||
"moduleVersion": "6.1.14",
|
||||
"moduleLicense": "Apache License, Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
|
||||
},
|
||||
@@ -1511,17 +1464,10 @@
|
||||
{
|
||||
"moduleName": "org.yaml:snakeyaml",
|
||||
"moduleUrl": "https://bitbucket.org/snakeyaml/snakeyaml",
|
||||
"moduleVersion": "2.3",
|
||||
"moduleVersion": "2.2",
|
||||
"moduleLicense": "Apache License, Version 2.0",
|
||||
"moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
|
||||
},
|
||||
{
|
||||
"moduleName": "technology.tabula:tabula",
|
||||
"moduleUrl": "http://github.com/tabulapdf/tabula-java",
|
||||
"moduleVersion": "1.0.5",
|
||||
"moduleLicense": "MIT License",
|
||||
"moduleLicenseUrl": "http://www.opensource.org/licenses/mit-license.php"
|
||||
},
|
||||
{
|
||||
"moduleName": "xml-apis:xml-apis",
|
||||
"moduleUrl": "http://xml.apache.org/commons/components/external/",
|
||||
|
||||
@@ -19,15 +19,6 @@
|
||||
transform-origin: top left;
|
||||
}
|
||||
|
||||
#drag-container .multidrag {
|
||||
position: fixed;
|
||||
max-width: 200px;
|
||||
max-height: 200px;
|
||||
transform-origin: top left;
|
||||
margin-left: 1rem;
|
||||
background-color: rgba(0, 29, 41, 0.9);
|
||||
}
|
||||
|
||||
.drag-manager_dragging {
|
||||
width: 0px;
|
||||
visibility: hidden;
|
||||
|
||||
@@ -100,30 +100,3 @@ input:-webkit-autofill:focus {
|
||||
input[data-autocompleted] {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
.btn-tooltip {
|
||||
position: absolute;
|
||||
display: none;
|
||||
bottom: 3.2rem;
|
||||
white-space: nowrap;
|
||||
flex-wrap: nowrap;
|
||||
width: fit-content;
|
||||
padding: 7px;
|
||||
background-color: rgba(0, 29, 41, 0.9);
|
||||
border-radius: 3px;
|
||||
font-size: 12px;
|
||||
color: whitesmoke;
|
||||
animation: fadeup 0.15s linear;
|
||||
}
|
||||
@keyframes fadeup {
|
||||
0% {
|
||||
transform: translateY(10px);
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
.btn:hover .btn-tooltip {
|
||||
display: block;
|
||||
}
|
||||
|
||||
@@ -61,6 +61,10 @@ label {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#export-button {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.bg-card {
|
||||
background-color: var(--md-sys-color-surface-5);
|
||||
border-radius: 3rem;
|
||||
@@ -286,6 +290,3 @@ label {
|
||||
.checkbox-label {
|
||||
font-size: medium;
|
||||
}
|
||||
.btn {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
.pdf-actions_button-container {
|
||||
z-index: 4;
|
||||
z-index: 2;
|
||||
display: flex;
|
||||
opacity: 0;
|
||||
transition: opacity 0.1s linear;
|
||||
@@ -46,7 +46,7 @@
|
||||
width: 80px;
|
||||
height: 100%;
|
||||
|
||||
z-index: 3;
|
||||
z-index: 1;
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s;
|
||||
}
|
||||
@@ -116,7 +116,6 @@ html[dir="rtl"] .pdf-actions_container:last-child>.pdf-actions_insert-file-butto
|
||||
translate: 50% -50%;
|
||||
aspect-ratio: 1;
|
||||
border-radius: 100px;
|
||||
z-index: 4;
|
||||
}
|
||||
|
||||
.pdf-actions_split-file-button {
|
||||
@@ -126,9 +125,9 @@ html[dir="rtl"] .pdf-actions_container:last-child>.pdf-actions_insert-file-butto
|
||||
translate: 0 -50%;
|
||||
aspect-ratio: 1;
|
||||
border-radius: 100px;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
|
||||
.pdf-actions_checkbox {
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
@@ -138,7 +137,7 @@ html[dir="rtl"] .pdf-actions_container:last-child>.pdf-actions_insert-file-butto
|
||||
padding: 6px 8px;
|
||||
border-radius: 8px;
|
||||
font-size: 16px;
|
||||
z-index: 10;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
@@ -151,5 +150,4 @@ html[dir="rtl"] .pdf-actions_container:last-child>.pdf-actions_insert-file-butto
|
||||
translate: 0% -50%;
|
||||
aspect-ratio: 1;
|
||||
border-radius: 100px;
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
@@ -62,54 +62,53 @@ select#font-select option {
|
||||
background-color: rgba(52, 152, 219, 0.2);
|
||||
/* Darken background on hover */
|
||||
}
|
||||
|
||||
.signature-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||||
gap: 1rem;
|
||||
padding: 1rem;
|
||||
max-height: 400px;
|
||||
overflow-y: auto;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||||
gap: 1rem;
|
||||
padding: 1rem;
|
||||
max-height: 400px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.signature-list {
|
||||
max-height: 400px;
|
||||
overflow-y: auto;
|
||||
max-height: 400px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.signature-list-item {
|
||||
padding: 0.75rem;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 0.5rem;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s;
|
||||
padding: 0.75rem;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 0.5rem;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
|
||||
.signature-list-item:hover {
|
||||
background-color: #f8f9fa;
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
|
||||
.signature-list-info {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.signature-list-name {
|
||||
font-weight: 500;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.signature-list-details {
|
||||
color: #6c757d;
|
||||
font-size: 0.875rem;
|
||||
color: #6c757d;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.signature-list-details small:not(:last-child) {
|
||||
margin-right: 1rem;
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
.view-toggle {
|
||||
text-align: right;
|
||||
padding: 0.5rem 1rem;
|
||||
}
|
||||
text-align: right;
|
||||
padding: 0.5rem 1rem;
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-az" viewBox="0 0 640 480">
|
||||
<path fill="#3f9c35" d="M.1 0h640v480H.1z"/>
|
||||
<path fill="#ed2939" d="M.1 0h640v320H.1z"/>
|
||||
<path fill="#00b9e4" d="M.1 0h640v160H.1z"/>
|
||||
<circle cx="304" cy="240" r="72" fill="#fff"/>
|
||||
<circle cx="320" cy="240" r="60" fill="#ed2939"/>
|
||||
<path fill="#fff" d="m384 200 7.7 21.5 20.6-9.8-9.8 20.7L424 240l-21.5 7.7 9.8 20.6-20.6-9.8L384 280l-7.7-21.5-20.6 9.8 9.8-20.6L344 240l21.5-7.7-9.8-20.6 20.6 9.8z"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 501 B |
@@ -34,14 +34,6 @@
|
||||
const url = this.action;
|
||||
const files = $("#fileInput-input")[0].files;
|
||||
const formData = new FormData(this);
|
||||
const submitButton = document.getElementById("submitBtn");
|
||||
const showGameBtn = document.getElementById("show-game-btn");
|
||||
const originalButtonText = submitButton.textContent;
|
||||
var boredWaiting = localStorage.getItem("boredWaiting") || "disabled";
|
||||
|
||||
if (showGameBtn) {
|
||||
showGameBtn.style.display = "none";
|
||||
}
|
||||
|
||||
// Remove empty file entries
|
||||
for (let [key, value] of formData.entries()) {
|
||||
@@ -50,10 +42,14 @@
|
||||
}
|
||||
}
|
||||
const override = $("#override").val() || "";
|
||||
const originalButtonText = $("#submitBtn").text();
|
||||
$("#submitBtn").text("Processing...");
|
||||
console.log(override);
|
||||
|
||||
// Set a timeout to show the game button if operation takes more than 5 seconds
|
||||
const timeoutId = setTimeout(() => {
|
||||
var boredWaiting = localStorage.getItem("boredWaiting") || "disabled";
|
||||
const showGameBtn = document.getElementById("show-game-btn");
|
||||
if (boredWaiting === "enabled" && showGameBtn) {
|
||||
showGameBtn.style.display = "block";
|
||||
showGameBtn.parentNode.insertBefore(document.createElement('br'), showGameBtn.nextSibling);
|
||||
@@ -61,9 +57,6 @@
|
||||
}, 5000);
|
||||
|
||||
try {
|
||||
submitButton.textContent = "Processing...";
|
||||
submitButton.disabled = true;
|
||||
|
||||
if (remoteCall === true) {
|
||||
if (override === "multi" || (!multipleInputsForSingleRequest && files.length > 1 && override !== "single")) {
|
||||
await submitMultiPdfForm(url, files);
|
||||
@@ -72,17 +65,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
clearFileInput();
|
||||
clearTimeout(timeoutId);
|
||||
if (showGameBtn) {
|
||||
showGameBtn.style.display = "none";
|
||||
showGameBtn.style.marginTop = "";
|
||||
}
|
||||
submitButton.textContent = originalButtonText;
|
||||
submitButton.disabled = false;
|
||||
$("#submitBtn").text(originalButtonText);
|
||||
|
||||
// After process finishes, check for boredWaiting and gameDialog open status
|
||||
const boredWaiting = localStorage.getItem("boredWaiting") || "disabled";
|
||||
const gameDialog = document.getElementById('game-container-wrapper');
|
||||
if (boredWaiting === "enabled" && gameDialog && gameDialog.open) {
|
||||
// Display a green banner at the bottom of the screen saying "Download complete"
|
||||
@@ -99,11 +87,10 @@
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
clearFileInput();
|
||||
clearTimeout(timeoutId);
|
||||
showGameBtn.style.display = "none";
|
||||
submitButton.textContent = originalButtonText;
|
||||
submitButton.disabled = false;
|
||||
handleDownloadError(error);
|
||||
$("#submitBtn").text(originalButtonText);
|
||||
console.error(error);
|
||||
}
|
||||
});
|
||||
@@ -120,13 +107,13 @@
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async function handleSingleDownload(url, formData, isMulti = false, isZip = false) {
|
||||
const startTime = performance.now();
|
||||
const file = formData.get('fileInput');
|
||||
let success = false;
|
||||
let errorMessage = null;
|
||||
|
||||
|
||||
try {
|
||||
const response = await fetch(url, { method: "POST", body: formData });
|
||||
const contentType = response.headers.get("content-type");
|
||||
@@ -149,7 +136,7 @@
|
||||
|
||||
const blob = await response.blob();
|
||||
success = true;
|
||||
|
||||
|
||||
if (contentType.includes("application/pdf") || contentType.includes("image/")) {
|
||||
clearFileInput();
|
||||
return handleResponse(blob, filename, !isMulti, isZip);
|
||||
@@ -161,23 +148,23 @@
|
||||
} catch (error) {
|
||||
success = false;
|
||||
errorMessage = error.message;
|
||||
clearFileInput();
|
||||
console.error("Error in handleSingleDownload:", error);
|
||||
throw error;
|
||||
} finally {
|
||||
const processingTime = performance.now() - startTime;
|
||||
|
||||
|
||||
// Capture analytics
|
||||
const pageCount = file && file.type === 'application/pdf' ? await getPDFPageCount(file) : null;
|
||||
if(analyticsEnabled) {
|
||||
posthog.capture('file_processing', {
|
||||
success: success,
|
||||
file_type: file ? file.type || 'unknown' : 'unknown',
|
||||
file_size: file ? file.size : 0,
|
||||
processing_time: processingTime,
|
||||
error_message: errorMessage,
|
||||
pdf_pages: pageCount
|
||||
});
|
||||
}
|
||||
|
||||
posthog.capture('file_processing', {
|
||||
success: success,
|
||||
file_type: file ? file.type || 'unknown' : 'unknown',
|
||||
file_size: file ? file.size : 0,
|
||||
processing_time: processingTime,
|
||||
error_message: errorMessage,
|
||||
pdf_pages: pageCount
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -193,7 +180,7 @@
|
||||
|
||||
return filename;
|
||||
}
|
||||
|
||||
|
||||
async function handleJsonResponse(response) {
|
||||
const json = await response.json();
|
||||
const errorMessage = JSON.stringify(json, null, 2);
|
||||
|
||||
@@ -4,7 +4,6 @@ const DraggableUtils = {
|
||||
nextId: 0,
|
||||
pdfDoc: null,
|
||||
pageIndex: 0,
|
||||
elementAllPages: [],
|
||||
documentsMap: new Map(),
|
||||
lastInteracted: null,
|
||||
|
||||
@@ -198,68 +197,6 @@ const DraggableUtils = {
|
||||
deleteAllDraggableCanvases() {
|
||||
this.boxDragContainer.querySelectorAll(".draggable-canvas").forEach((el) => el.remove());
|
||||
},
|
||||
async addAllPagesDraggableCanvas(element) {
|
||||
if (element) {
|
||||
let currentPage = this.pageIndex
|
||||
if (!this.elementAllPages.includes(element)) {
|
||||
this.elementAllPages.push(element)
|
||||
element.style.filter = 'sepia(1) hue-rotate(90deg) brightness(1.2)';
|
||||
let newElement = {
|
||||
"element": element,
|
||||
"offsetWidth": element.width,
|
||||
"offsetHeight": element.height
|
||||
}
|
||||
|
||||
let pagesMap = this.documentsMap.get(this.pdfDoc);
|
||||
|
||||
if (!pagesMap) {
|
||||
pagesMap = {};
|
||||
this.documentsMap.set(this.pdfDoc, pagesMap);
|
||||
}
|
||||
let page = this.pageIndex
|
||||
|
||||
for (let pageIndex = 0; pageIndex < this.pdfDoc.numPages; pageIndex++) {
|
||||
|
||||
if (pagesMap[`${pageIndex}-offsetWidth`]) {
|
||||
if (!pagesMap[pageIndex].includes(newElement)) {
|
||||
pagesMap[pageIndex].push(newElement);
|
||||
}
|
||||
} else {
|
||||
pagesMap[pageIndex] = []
|
||||
pagesMap[pageIndex].push(newElement)
|
||||
pagesMap[`${pageIndex}-offsetWidth`] = pagesMap[`${page}-offsetWidth`];
|
||||
pagesMap[`${pageIndex}-offsetHeight`] = pagesMap[`${page}-offsetHeight`];
|
||||
}
|
||||
await this.goToPage(pageIndex)
|
||||
}
|
||||
} else {
|
||||
const index = this.elementAllPages.indexOf(element);
|
||||
if (index !== -1) {
|
||||
this.elementAllPages.splice(index, 1);
|
||||
}
|
||||
element.style.filter = '';
|
||||
let pagesMap = this.documentsMap.get(this.pdfDoc);
|
||||
|
||||
if (!pagesMap) {
|
||||
pagesMap = {};
|
||||
this.documentsMap.set(this.pdfDoc, pagesMap);
|
||||
}
|
||||
for (let pageIndex = 0; pageIndex < this.pdfDoc.numPages; pageIndex++) {
|
||||
if (pagesMap[`${pageIndex}-offsetWidth`] && pageIndex != currentPage) {
|
||||
const pageElements = pagesMap[pageIndex];
|
||||
pageElements.forEach(elementPage => {
|
||||
const elementIndex = pageElements.findIndex(elementPage => elementPage['element'].id === element.id);
|
||||
if (elementIndex !== -1) {
|
||||
pageElements.splice(elementIndex, 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
await this.goToPage(pageIndex)
|
||||
}
|
||||
}
|
||||
await this.goToPage(currentPage)
|
||||
}
|
||||
},
|
||||
deleteDraggableCanvas(element) {
|
||||
if (element) {
|
||||
//Check if deleted element is the last interacted
|
||||
@@ -304,7 +241,7 @@ const DraggableUtils = {
|
||||
}
|
||||
|
||||
const draggablesData = pagesMap[this.pageIndex];
|
||||
if (draggablesData && Array.isArray(draggablesData)) {
|
||||
if (draggablesData) {
|
||||
draggablesData.forEach((draggableData) => this.boxDragContainer.appendChild(draggableData.element));
|
||||
}
|
||||
|
||||
@@ -336,13 +273,6 @@ const DraggableUtils = {
|
||||
|
||||
//return pdfCanvas.toDataURL();
|
||||
},
|
||||
|
||||
async goToPage(pageIndex) {
|
||||
this.storePageContents();
|
||||
await this.renderPage(this.pdfDoc, pageIndex);
|
||||
this.loadPageContents();
|
||||
},
|
||||
|
||||
async incrementPage() {
|
||||
if (this.pageIndex < this.pdfDoc.numPages - 1) {
|
||||
this.storePageContents();
|
||||
@@ -367,7 +297,6 @@ const DraggableUtils = {
|
||||
this.storePageContents();
|
||||
|
||||
const pagesMap = this.documentsMap.get(this.pdfDoc);
|
||||
|
||||
for (let pageIdx in pagesMap) {
|
||||
if (pageIdx.includes("offset")) {
|
||||
continue;
|
||||
@@ -375,8 +304,7 @@ const DraggableUtils = {
|
||||
console.log(typeof pageIdx);
|
||||
|
||||
const page = pdfDocModified.getPage(parseInt(pageIdx));
|
||||
let draggablesData = pagesMap[pageIdx];
|
||||
|
||||
const draggablesData = pagesMap[pageIdx];
|
||||
const offsetWidth = pagesMap[pageIdx + "-offsetWidth"];
|
||||
const offsetHeight = pagesMap[pageIdx + "-offsetHeight"];
|
||||
|
||||
@@ -455,6 +383,7 @@ const DraggableUtils = {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
this.loadPageContents();
|
||||
return pdfDocModified;
|
||||
},
|
||||
|
||||
@@ -1,59 +1,27 @@
|
||||
function filterCards() {
|
||||
var input = document.getElementById("searchBar");
|
||||
var filter = input.value.toUpperCase();
|
||||
var cards = document.querySelectorAll(".feature-card");
|
||||
|
||||
let featureGroups = document.querySelectorAll(".feature-group");
|
||||
const collapsedGroups = getCollapsedGroups();
|
||||
for (var i = 0; i < cards.length; i++) {
|
||||
var card = cards[i];
|
||||
var title = card.querySelector("h5.card-title").innerText;
|
||||
var text = card.querySelector("p.card-text").innerText;
|
||||
|
||||
for (const featureGroup of featureGroups) {
|
||||
var cards = featureGroup.querySelectorAll(".feature-card");
|
||||
// Get the navbar tags associated with the card
|
||||
var navbarItem = document.querySelector(`a.dropdown-item[href="${card.id}"]`);
|
||||
var navbarTags = navbarItem ? navbarItem.getAttribute("data-bs-tags") : "";
|
||||
|
||||
let groupMatchesFilter = false;
|
||||
for (var i = 0; i < cards.length; i++) {
|
||||
var card = cards[i];
|
||||
var title = card.querySelector("h5.card-title").innerText;
|
||||
var text = card.querySelector("p.card-text").innerText;
|
||||
var content = title + " " + text + " " + navbarTags;
|
||||
|
||||
// Get the navbar tags associated with the card
|
||||
var navbarItem = document.querySelector(`a.dropdown-item[href="${card.id}"]`);
|
||||
var navbarTags = navbarItem ? navbarItem.getAttribute("data-bs-tags") : "";
|
||||
|
||||
var content = title + " " + text + " " + navbarTags;
|
||||
|
||||
if (content.toUpperCase().indexOf(filter) > -1) {
|
||||
card.style.display = "";
|
||||
groupMatchesFilter = true;
|
||||
} else {
|
||||
card.style.display = "none";
|
||||
}
|
||||
}
|
||||
|
||||
if (!groupMatchesFilter) {
|
||||
featureGroup.style.display = "none";
|
||||
if (content.toUpperCase().indexOf(filter) > -1) {
|
||||
card.style.display = "";
|
||||
} else {
|
||||
featureGroup.style.display = "";
|
||||
resetOrTemporarilyExpandGroup(featureGroup, filter, collapsedGroups);
|
||||
card.style.display = "none";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getCollapsedGroups() {
|
||||
return localStorage.getItem("collapsedGroups") ? JSON.parse(localStorage.getItem("collapsedGroups")) : [];
|
||||
}
|
||||
|
||||
function resetOrTemporarilyExpandGroup(featureGroup, filterKeywords = "", collapsedGroups = []) {
|
||||
const shouldResetCollapse = filterKeywords.trim() === "";
|
||||
if (shouldResetCollapse) {
|
||||
// Resetting the group's expand/collapse to its original state (as in collapsed groups)
|
||||
const isCollapsed = collapsedGroups.indexOf(featureGroup.id) != -1;
|
||||
expandCollapseToggle(featureGroup, !isCollapsed);
|
||||
} else {
|
||||
// Temporarily expands feature group without affecting the actual/stored collapsed groups
|
||||
featureGroup.classList.remove("collapsed");
|
||||
featureGroup.querySelector(".header-expand-button").classList.remove("collapsed");
|
||||
}
|
||||
}
|
||||
|
||||
function updateFavoritesSection() {
|
||||
const favoritesContainer = document.getElementById("groupFavorites").querySelector(".feature-group-container");
|
||||
favoritesContainer.style.maxHeight = "none";
|
||||
|
||||
@@ -1,21 +1,29 @@
|
||||
class DragDropManager {
|
||||
dragContainer;
|
||||
wrapper;
|
||||
pageDirection;
|
||||
movePageTo;
|
||||
pageDragging;
|
||||
draggelEl;
|
||||
draggedImageEl;
|
||||
hoveredEl;
|
||||
endInsertionElement;
|
||||
|
||||
constructor(id, wrapperId) {
|
||||
this.dragContainer = document.getElementById(id);
|
||||
this.pageDirection = document.documentElement.getAttribute("dir");
|
||||
this.wrapper = document.getElementById(wrapperId);
|
||||
this.pageDragging = false;
|
||||
this.hoveredEl = undefined;
|
||||
this.draggelEl = undefined;
|
||||
this.draggedImageEl = undefined;
|
||||
this.draggedEl = undefined;
|
||||
this.selectedPageElements = []; // Store selected pages for multi-page mode
|
||||
|
||||
// Add CSS dynamically
|
||||
const styleElement = document.createElement("link");
|
||||
var styleElement = document.createElement("link");
|
||||
styleElement.rel = "stylesheet";
|
||||
styleElement.href = "css/dragdrop.css";
|
||||
|
||||
document.head.appendChild(styleElement);
|
||||
|
||||
// Create the endpoint element
|
||||
const div = document.createElement("div");
|
||||
div.classList.add("drag-manager_endpoint");
|
||||
div.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-file-earmark-arrow-down" viewBox="0 0 16 16">
|
||||
@@ -24,7 +32,6 @@ class DragDropManager {
|
||||
</svg>`;
|
||||
this.endInsertionElement = div;
|
||||
|
||||
// Bind methods
|
||||
this.startDraggingPage = this.startDraggingPage.bind(this);
|
||||
this.onDragEl = this.onDragEl.bind(this);
|
||||
this.stopDraggingPage = this.stopDraggingPage.bind(this);
|
||||
@@ -33,41 +40,20 @@ class DragDropManager {
|
||||
}
|
||||
|
||||
startDraggingPage(div) {
|
||||
if (window.selectPage) {
|
||||
// Multi-page drag logic
|
||||
this.selectedPageElements = window.selectedPages.map((index) => {
|
||||
const pageEl = document.getElementById(`page-container-${index}`);
|
||||
if (pageEl) {
|
||||
pageEl.initialTransform = pageEl.style.transform || "translate(0px, 0px)";
|
||||
}
|
||||
return pageEl;
|
||||
}).filter(Boolean);
|
||||
this.pageDragging = true;
|
||||
this.draggedEl = div;
|
||||
const img = div.querySelector("img");
|
||||
div.classList.add("drag-manager_dragging");
|
||||
const imageSrc = img.src;
|
||||
|
||||
if (this.selectedPageElements.length === 0) return;
|
||||
const imgEl = document.createElement("img");
|
||||
imgEl.classList.add("dragged-img");
|
||||
imgEl.src = imageSrc;
|
||||
this.draggedImageEl = imgEl;
|
||||
imgEl.style.visibility = "hidden";
|
||||
imgEl.style.transform = `rotate(${img.style.rotate === "" ? "0deg" : img.style.rotate}) translate(-50%, -50%)`;
|
||||
this.dragContainer.appendChild(imgEl);
|
||||
|
||||
this.pageDragging = true;
|
||||
this.draggedImageEl = document.createElement("div");
|
||||
this.draggedImageEl.classList.add("multidrag");
|
||||
this.draggedImageEl.textContent = `${this.selectedPageElements.length} ${window.translations.dragDropMessage}`;
|
||||
this.draggedImageEl.style.visibility = "hidden";
|
||||
this.dragContainer.appendChild(this.draggedImageEl);
|
||||
} else {
|
||||
// Single-page drag logic
|
||||
this.pageDragging = true;
|
||||
this.draggedEl = div;
|
||||
const img = div.querySelector("img");
|
||||
div.classList.add("drag-manager_dragging");
|
||||
|
||||
const imgEl = document.createElement("img");
|
||||
imgEl.classList.add("dragged-img");
|
||||
imgEl.src = img.src;
|
||||
imgEl.style.visibility = "hidden";
|
||||
imgEl.style.transform = `rotate(${img.style.rotate === "" ? "0deg" : img.style.rotate}) translate(-50%, -50%)`;
|
||||
this.draggedImageEl = imgEl;
|
||||
this.dragContainer.appendChild(imgEl);
|
||||
}
|
||||
|
||||
// Common setup for both modes
|
||||
window.addEventListener("mouseup", this.stopDraggingPage);
|
||||
window.addEventListener("mousemove", this.onDragEl);
|
||||
this.wrapper.classList.add("drag-manager_dragging-container");
|
||||
@@ -88,43 +74,21 @@ class DragDropManager {
|
||||
this.wrapper.classList.remove("drag-manager_dragging-container");
|
||||
this.wrapper.removeChild(this.endInsertionElement);
|
||||
window.removeEventListener("mouseup", this.stopDraggingPage);
|
||||
|
||||
if (this.draggedImageEl) {
|
||||
this.dragContainer.removeChild(this.draggedImageEl);
|
||||
this.draggedImageEl = undefined;
|
||||
}
|
||||
|
||||
if (window.selectPage) {
|
||||
// Multi-page drop logic
|
||||
if (!this.hoveredEl) {
|
||||
this.selectedPageElements.forEach((pageEl) => {
|
||||
pageEl.style.transform = pageEl.initialTransform || "translate(0px, 0px)";
|
||||
pageEl.classList.remove("drag-manager_dragging");
|
||||
});
|
||||
} else {
|
||||
this.selectedPageElements.forEach((pageEl) => {
|
||||
pageEl.classList.remove("drag-manager_dragging");
|
||||
if (this.hoveredEl === this.endInsertionElement) {
|
||||
this.movePageTo(pageEl);
|
||||
} else {
|
||||
this.movePageTo(pageEl, this.hoveredEl);
|
||||
}
|
||||
});
|
||||
}
|
||||
this.selectedPageElements = [];
|
||||
window.resetPages()
|
||||
} else {
|
||||
// Single-page drop logic
|
||||
if (!this.hoveredEl) return;
|
||||
this.draggedEl.classList.remove("drag-manager_dragging");
|
||||
if (this.hoveredEl === this.endInsertionElement) {
|
||||
this.movePageTo(this.draggedEl);
|
||||
} else {
|
||||
this.movePageTo(this.draggedEl, this.hoveredEl);
|
||||
}
|
||||
}
|
||||
|
||||
this.draggedImageEl = undefined;
|
||||
this.pageDragging = false;
|
||||
this.draggedEl.classList.remove("drag-manager_dragging");
|
||||
this.hoveredEl?.classList.remove("drag-manager_draghover");
|
||||
this.dragContainer.childNodes.forEach((dragChild) => {
|
||||
this.dragContainer.removeChild(dragChild);
|
||||
});
|
||||
if (!this.hoveredEl) {
|
||||
return;
|
||||
}
|
||||
if (this.hoveredEl === this.endInsertionElement) {
|
||||
this.movePageTo(this.draggedEl);
|
||||
return;
|
||||
}
|
||||
this.movePageTo(this.draggedEl, this.hoveredEl);
|
||||
}
|
||||
|
||||
setActions({ movePageTo }) {
|
||||
|
||||
@@ -110,32 +110,31 @@ class PdfActionsManager {
|
||||
|
||||
const moveUp = document.createElement("button");
|
||||
moveUp.classList.add("pdf-actions_move-left-button", "btn", "btn-secondary");
|
||||
moveUp.innerHTML = `<span class="material-symbols-rounded">arrow_${leftDirection}_alt</span><span class="btn-tooltip">${window.translations.moveLeft}</span>`;
|
||||
moveUp.innerHTML = `<span class="material-symbols-rounded">arrow_${leftDirection}_alt</span>`;
|
||||
moveUp.onclick = this.moveUpButtonCallback;
|
||||
buttonContainer.appendChild(moveUp);
|
||||
|
||||
const moveDown = document.createElement("button");
|
||||
moveDown.classList.add("pdf-actions_move-right-button", "btn", "btn-secondary");
|
||||
moveDown.innerHTML = `<span class="material-symbols-rounded">arrow_${rightDirection}_alt</span><span class="btn-tooltip">${window.translations.moveRight}</span>`;
|
||||
moveDown.innerHTML = `<span class="material-symbols-rounded">arrow_${rightDirection}_alt</span>`;
|
||||
moveDown.onclick = this.moveDownButtonCallback;
|
||||
buttonContainer.appendChild(moveDown);
|
||||
|
||||
|
||||
const rotateCCW = document.createElement("button");
|
||||
rotateCCW.classList.add("btn", "btn-secondary");
|
||||
rotateCCW.innerHTML = `<span class="material-symbols-rounded">rotate_left</span><span class="btn-tooltip">${window.translations.rotateLeft}</span>`;
|
||||
rotateCCW.innerHTML = `<span class="material-symbols-rounded">rotate_left</span>`;
|
||||
rotateCCW.onclick = this.rotateCCWButtonCallback;
|
||||
buttonContainer.appendChild(rotateCCW);
|
||||
|
||||
const rotateCW = document.createElement("button");
|
||||
rotateCW.classList.add("btn", "btn-secondary");
|
||||
rotateCW.innerHTML = `<span class="material-symbols-rounded">rotate_right</span><span class="btn-tooltip">${window.translations.rotateRight}</span>`;
|
||||
rotateCW.innerHTML = `<span class="material-symbols-rounded">rotate_right</span>`;
|
||||
rotateCW.onclick = this.rotateCWButtonCallback;
|
||||
buttonContainer.appendChild(rotateCW);
|
||||
|
||||
const deletePage = document.createElement("button");
|
||||
deletePage.classList.add("btn", "btn-danger");
|
||||
deletePage.innerHTML = `<span class="material-symbols-rounded">delete</span><span class="btn-tooltip"></span><span class="btn-tooltip">${window.translations.delete}</span>`;
|
||||
deletePage.innerHTML = `<span class="material-symbols-rounded">delete</span>`;
|
||||
deletePage.onclick = this.deletePageButtonCallback;
|
||||
buttonContainer.appendChild(deletePage);
|
||||
|
||||
@@ -190,19 +189,19 @@ class PdfActionsManager {
|
||||
|
||||
const insertFileButton = document.createElement("button");
|
||||
insertFileButton.classList.add("btn", "btn-primary", "pdf-actions_insert-file-button");
|
||||
insertFileButton.innerHTML = `<span class="material-symbols-rounded">add</span></span><span class="btn-tooltip">${window.translations.addFile}</span>`;
|
||||
insertFileButton.innerHTML = `<span class="material-symbols-rounded">add</span>`;
|
||||
insertFileButton.onclick = this.insertFileButtonCallback;
|
||||
insertFileButtonContainer.appendChild(insertFileButton);
|
||||
|
||||
const splitFileButton = document.createElement("button");
|
||||
splitFileButton.classList.add("btn", "btn-primary", "pdf-actions_split-file-button");
|
||||
splitFileButton.innerHTML = `<span class="material-symbols-rounded">cut</span></span><span class="btn-tooltip">${window.translations.split}</span>`;
|
||||
splitFileButton.innerHTML = `<span class="material-symbols-rounded">cut</span>`;
|
||||
splitFileButton.onclick = this.splitFileButtonCallback;
|
||||
insertFileButtonContainer.appendChild(splitFileButton);
|
||||
|
||||
const insertFileBlankButton = document.createElement("button");
|
||||
insertFileBlankButton.classList.add("btn", "btn-primary", "pdf-actions_insert-file-blank-button");
|
||||
insertFileBlankButton.innerHTML = `<span class="material-symbols-rounded">insert_page_break</span></span><span class="btn-tooltip">${window.translations.insertPageBreak}</span>`;
|
||||
insertFileBlankButton.innerHTML = `<span class="material-symbols-rounded">insert_page_break</span>`;
|
||||
insertFileBlankButton.onclick = this.insertFileBlankButtonCallback;
|
||||
insertFileButtonContainer.appendChild(insertFileBlankButton);
|
||||
|
||||
|
||||
@@ -29,7 +29,6 @@ class PdfContainer {
|
||||
this.updatePagesFromCSV = this.updatePagesFromCSV.bind(this);
|
||||
this.addFilesBlankAll = this.addFilesBlankAll.bind(this)
|
||||
this.removeAllElements = this.removeAllElements.bind(this);
|
||||
this.resetPages = this.resetPages.bind(this);
|
||||
|
||||
this.pdfAdapters = pdfAdapters;
|
||||
|
||||
@@ -56,7 +55,6 @@ class PdfContainer {
|
||||
window.updatePageNumbersAndCheckboxes = this.updatePageNumbersAndCheckboxes;
|
||||
window.addFilesBlankAll = this.addFilesBlankAll
|
||||
window.removeAllElements = this.removeAllElements;
|
||||
window.resetPages = this.resetPages;
|
||||
|
||||
const filenameInput = document.getElementById("filename-input");
|
||||
const downloadBtn = document.getElementById("export-button");
|
||||
@@ -122,24 +120,12 @@ class PdfContainer {
|
||||
async addFilesFromFiles(files, nextSiblingElement) {
|
||||
this.fileName = files[0].name;
|
||||
for (var i = 0; i < files.length; i++) {
|
||||
const startTime = Date.now();
|
||||
let processingTime, errorMessage = null, pageCount = 0;
|
||||
try {
|
||||
const file = files[i];
|
||||
if (file.type === "application/pdf") {
|
||||
const { renderer, pdfDocument } = await this.loadFile(file);
|
||||
pageCount = renderer.pageCount || 0;
|
||||
await this.addPdfFile(renderer, pdfDocument, nextSiblingElement);
|
||||
await this.addPdfFile(file, nextSiblingElement);
|
||||
} else if (file.type.startsWith("image/")) {
|
||||
await this.addImageFile(file, nextSiblingElement);
|
||||
}
|
||||
processingTime = Date.now() - startTime;
|
||||
this.captureFileProcessingEvent(true, file, processingTime, null, pageCount);
|
||||
} catch (error) {
|
||||
processingTime = Date.now() - startTime;
|
||||
errorMessage = error.message || "Unknown error";
|
||||
this.captureFileProcessingEvent(false, files[i], processingTime, errorMessage, pageCount);
|
||||
}
|
||||
}
|
||||
|
||||
document.querySelectorAll(".enable-on-file").forEach((element) => {
|
||||
@@ -147,23 +133,6 @@ class PdfContainer {
|
||||
});
|
||||
}
|
||||
|
||||
captureFileProcessingEvent(success, file, processingTime, errorMessage, pageCount) {
|
||||
try{
|
||||
if(analyticsEnabled){
|
||||
posthog.capture('file_processing', {
|
||||
success,
|
||||
file_type: file?.type || 'unknown',
|
||||
file_size: file?.size || 0,
|
||||
processing_time: processingTime,
|
||||
error_message: errorMessage,
|
||||
pdf_pages: pageCount,
|
||||
});
|
||||
}
|
||||
}catch{
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async addFilesBlank(nextSiblingElement) {
|
||||
const pdfContent = `
|
||||
%PDF-1.4
|
||||
@@ -217,12 +186,14 @@ class PdfContainer {
|
||||
|
||||
}
|
||||
|
||||
async addPdfFile(renderer, pdfDocument, nextSiblingElement) {
|
||||
async addPdfFile(file, nextSiblingElement) {
|
||||
const { renderer, pdfDocument } = await this.loadFile(file);
|
||||
|
||||
for (var i = 0; i < renderer.pageCount; i++) {
|
||||
const div = document.createElement("div");
|
||||
|
||||
div.classList.add("page-container");
|
||||
div.id = "page-container-" + (i + 1);
|
||||
|
||||
var img = document.createElement("img");
|
||||
img.classList.add("page-image");
|
||||
const imageSrc = await renderer.renderPage(i);
|
||||
@@ -231,6 +202,7 @@ class PdfContainer {
|
||||
img.rend = renderer;
|
||||
img.doc = pdfDocument;
|
||||
div.appendChild(img);
|
||||
|
||||
this.pdfAdapters.forEach((adapter) => {
|
||||
adapter.adapt?.(div);
|
||||
});
|
||||
@@ -369,8 +341,8 @@ class PdfContainer {
|
||||
toggleSelectAll() {
|
||||
const checkboxes = document.querySelectorAll(".pdf-actions_checkbox");
|
||||
window.selectAll = !window.selectAll;
|
||||
const selectIcon = document.getElementById("select-All-Container");
|
||||
const deselectIcon = document.getElementById("deselect-All-Container");
|
||||
const selectIcon = document.getElementById("select-icon");
|
||||
const deselectIcon = document.getElementById("deselect-icon");
|
||||
|
||||
if (selectIcon.style.display === "none") {
|
||||
selectIcon.style.display = "inline";
|
||||
@@ -469,7 +441,7 @@ class PdfContainer {
|
||||
const selectedPagesList = document.getElementById("selected-pages-list");
|
||||
const selectedPagesInput = document.getElementById("csv-input");
|
||||
selectedPagesList.innerHTML = ""; // Clear the list
|
||||
window.selectedPages.sort((a, b) => a - b);
|
||||
|
||||
window.selectedPages.forEach((page) => {
|
||||
const pageItem = document.createElement("div");
|
||||
pageItem.className = "page-item";
|
||||
@@ -729,31 +701,6 @@ class PdfContainer {
|
||||
}
|
||||
}
|
||||
|
||||
resetPages() {
|
||||
const pageContainers = this.pagesContainer.querySelectorAll(".page-container");
|
||||
|
||||
pageContainers.forEach((container, index) => {
|
||||
container.id = "page-container-" + (index + 1);
|
||||
});
|
||||
|
||||
const checkboxes = document.querySelectorAll(".pdf-actions_checkbox");
|
||||
window.selectAll = false;
|
||||
const selectIcon = document.getElementById("select-All-Container");
|
||||
const deselectIcon = document.getElementById("deselect-All-Container");
|
||||
|
||||
selectIcon.style.display = "inline";
|
||||
deselectIcon.style.display = "none";
|
||||
|
||||
checkboxes.forEach((checkbox) => {
|
||||
const pageNumber = Array.from(checkbox.parentNode.parentNode.children).indexOf(checkbox.parentNode) + 1;
|
||||
|
||||
const index = window.selectedPages.indexOf(pageNumber);
|
||||
if (index !== -1) {
|
||||
window.selectedPages.splice(index, 1);
|
||||
}
|
||||
});
|
||||
window.toggleSelectPageVisibility();
|
||||
}
|
||||
|
||||
setDownloadAttribute() {
|
||||
this.downloadLink.setAttribute("download", this.fileName ? this.fileName : "managed.pdf");
|
||||
@@ -798,7 +745,7 @@ class PdfContainer {
|
||||
const selectedPages = document.getElementById("selected-pages-display");
|
||||
selectedPages.classList.toggle("hidden", !window.selectPage);
|
||||
const selectAll = document.getElementById("select-All-Container");
|
||||
selectAll.classList.toggle("hidden", !window.selectPage);
|
||||
selectedPages.classList.toggle("hidden", !window.selectPage);
|
||||
const exportSelected = document.getElementById("export-selected-button");
|
||||
exportSelected.classList.toggle("hidden", !window.selectPage);
|
||||
const selectPagesButton = document.getElementById("select-pages-button");
|
||||
|
||||
@@ -28,7 +28,7 @@ window.onload = function () {
|
||||
// Show search results as user types in search box
|
||||
document.querySelector("#navbarSearchInput").addEventListener("input", function (e) {
|
||||
var searchText = e.target.value.trim().toLowerCase(); // Trim whitespace and convert to lowercase
|
||||
var items = document.querySelectorAll('a.dropdown-item[data-bs-tags]');
|
||||
var items = document.querySelectorAll(".dropdown-item, .nav-link");
|
||||
var resultsBox = document.querySelector("#searchResults");
|
||||
|
||||
// Clear any previous results
|
||||
@@ -83,7 +83,7 @@ searchDropdown.addEventListener('shown.bs.dropdown', function () {
|
||||
searchDropdown.addEventListener('mouseenter', function () {
|
||||
const dropdownInstance = new bootstrap.Dropdown(searchDropdown);
|
||||
dropdownInstance.show();
|
||||
|
||||
|
||||
setTimeout(() => {
|
||||
searchInput.focus();
|
||||
}, 100);
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
<form id="PDFToCSVForm" th:action="@{'/api/v1/convert/pdf/csv'}" method="post" enctype="multipart/form-data">
|
||||
<input id="pageId" type="hidden" name="pageId">
|
||||
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
|
||||
<button type="submit" class="btn btn-primary" th:text="#{PDFToCSV.submit}" id="submitBtn"></button>
|
||||
<button type="submit" class="btn btn-primary" th:text="#{PDFToCSV.submit}"></button>
|
||||
</form>
|
||||
<p id="instruction-text" style="margin: 0; display: none" th:text="#{PDFToCSV.prompt}"></p>
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
<div class="col-md-9 bg-card">
|
||||
<div class="tool-header">
|
||||
<span class="material-symbols-rounded tool-header-icon organize">database</span>
|
||||
<span class="tool-header-text" th:text="#{database.title}">Database Im-/Export</span>
|
||||
<span class="tool-header-text" text="#{database.title}">Database Im-/Export</span>
|
||||
</div>
|
||||
<p th:if="${error}" th:text="#{'database.' + ${error}}" class="alert alert-danger text-center"></p>
|
||||
<p th:if="${infoMessage}" th:text="#{'database.' + ${infoMessage}}" class="alert alert-success text-center"></p>
|
||||
@@ -31,7 +31,7 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr th:each="backup : ${backupFiles}">
|
||||
<tr th:each="backup : ${systemUpdate}">
|
||||
<td th:text="${backup.fileName}"></td>
|
||||
<td th:text="${backup.formattedCreationDate}"></td>
|
||||
<td th:text="${backup.formattedFileSize}"></td>
|
||||
@@ -41,11 +41,10 @@
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h6 class="text-end"><span class="badge bg-dark" th:text="${databaseVersion}">DB-Version</span></h6>
|
||||
</div>
|
||||
<hr>
|
||||
<form th:action="@{'/api/v1/database/import-database'}" method="post" enctype="multipart/form-data" class="bg-card mt-3 mb-3">
|
||||
<div style="background: var(--md-sys-color-error-container);border-radius: 2rem;" class="mb-3 p-3">
|
||||
<div style="background: var(--md-sys-color-error-container);padding: .8rem;border-radius: 2rem;" class="mb-3">
|
||||
<p th:text="#{database.info_1}"></p>
|
||||
<p th:text="#{database.info_2}"></p>
|
||||
</div>
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="ca_CA"> <img th:src="@{'/images/flags/es-ct.svg'}" alt="icon" width="20" height="15"> Català</a>
|
||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="zh_CN"> <img th:src="@{'/images/flags/cn.svg'}" alt="icon" width="20" height="15"> 简体中文</a>
|
||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="zh_TW"> <img th:src="@{'/images/flags/tw.svg'}" alt="icon" width="20" height="15"> 繁體中文</a>
|
||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="az_AZ"> <img th:src="@{'/images/flags/az.svg'}" alt="icon" width="20" height="15"> Azərbaycan Dili</a>
|
||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="da_DK"> <img th:src="@{'/images/flags/dk.svg'}" alt="icon" width="20" height="15"> Dansk</a>
|
||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="de_DE"> <img th:src="@{'/images/flags/de.svg'}" alt="icon" width="20" height="15"> Deutsch</a>
|
||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="en_GB"> <img th:src="@{'/images/flags/gb.svg'}" alt="icon" width="20" height="15"> English (GB)</a>
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
<div>
|
||||
<span th:utext="#{multiTool-advert.message(|/multi-tool|)}"></span>
|
||||
<button id="closeMultiToolAdvert" style="position: absolute;
|
||||
inset-inline-end: 12px;
|
||||
inset-block-start: 10px;
|
||||
top: 10px;
|
||||
right: 12px;
|
||||
border: none;
|
||||
background: transparent;
|
||||
color: white;
|
||||
@@ -16,13 +16,13 @@
|
||||
<style>
|
||||
.multi-toolAdvert {
|
||||
margin-bottom: 10px;
|
||||
margin-inline-start: 50%;
|
||||
margin-left: 50%;
|
||||
transform: translateX(-50%);
|
||||
max-width: 52rem;
|
||||
z-index: 0;
|
||||
background-color: var(--md-sys-color-surface-5);
|
||||
border-radius: 2rem;
|
||||
padding-block: 10px;
|
||||
padding-inline: 20px 27px;
|
||||
padding: 10px 27px 10px 20px;
|
||||
font-size: 0.9rem;
|
||||
display: none;
|
||||
justify-content: center;
|
||||
@@ -43,7 +43,6 @@
|
||||
const closeBtn = document.getElementById('closeMultiToolAdvert');
|
||||
|
||||
const cacheKey = `closeMultiToolAdvert_${window.location.pathname}`;
|
||||
const isRTL = document.documentElement.dir === 'rtl';
|
||||
|
||||
if (localStorage.getItem(cacheKey) !== 'true') {
|
||||
advert.style.display = 'flex';
|
||||
@@ -53,13 +52,6 @@
|
||||
advert.style.display = 'none';
|
||||
localStorage.setItem(cacheKey, 'true');
|
||||
});
|
||||
|
||||
|
||||
if (isRTL) {
|
||||
advert.style.transform = 'translateX(50%)'; // Flip direction for RTL
|
||||
} else {
|
||||
advert.style.transform = 'translateX(-50%)';
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
@@ -47,7 +47,7 @@
|
||||
<div class="navbar-item col-lg-2 col-sm-6 py px-xl-1 px-2">
|
||||
<h6 class="menu-title" th:text="#{navbar.sections.organize}"></h6>
|
||||
<div
|
||||
th:replace="~{fragments/navbarEntry :: navbarEntry ('multi-tool', 'construction', 'home.multiTool.title', 'home.multiTool.desc', 'multiTool.tags', 'organize')}">
|
||||
th:replace="~{fragments/navbarEntry :: navbarEntry ('compress-pdf', 'zoom_in_map', 'home.compressPdfs.title', 'home.compressPdfs.desc', 'compressPdfs.tags', 'advance')}">
|
||||
</div>
|
||||
<div
|
||||
th:replace="~{fragments/navbarEntry :: navbarEntry ('merge-pdfs', 'add_to_photos', 'home.merge.title', 'home.merge.desc', 'merge.tags', 'organize')}">
|
||||
@@ -214,7 +214,7 @@
|
||||
<div class="navbar-item col-lg-2 col-sm-6 py px-xl-1 px-2">
|
||||
<h6 class="menu-title" th:text="#{navbar.sections.advance}"></h6>
|
||||
<div
|
||||
th:replace="~{fragments/navbarEntry :: navbarEntry ('compress-pdf', 'zoom_in_map', 'home.compressPdfs.title', 'home.compressPdfs.desc', 'compressPdfs.tags', 'advance')}">
|
||||
th:replace="~{fragments/navbarEntry :: navbarEntry ('multi-tool', 'construction', 'home.multiTool.title', 'home.multiTool.desc', 'multiTool.tags', 'advance')}">
|
||||
</div>
|
||||
<div
|
||||
th:replace="~{fragments/navbarEntry :: navbarEntry ('pipeline', 'family_history', 'home.pipeline.title', 'home.pipeline.desc', 'pipeline.tags', 'advance')}">
|
||||
|
||||
@@ -31,79 +31,64 @@
|
||||
<span class="material-symbols-rounded">
|
||||
add
|
||||
</span>
|
||||
<span class="btn-tooltip" th:text="#{multiTool.addFile}"></span>
|
||||
</button>
|
||||
<button class="btn btn-secondary enable-on-file" onclick="rotateAll(-90)" disabled>
|
||||
<span class="material-symbols-rounded">
|
||||
rotate_left
|
||||
</span>
|
||||
<span class="btn-tooltip" th:text="#{multiTool.rotateLeft}"></span>
|
||||
</button>
|
||||
<button class="btn btn-secondary enable-on-file" onclick="rotateAll(90)" disabled>
|
||||
<span class="material-symbols-rounded">
|
||||
rotate_right
|
||||
</span>
|
||||
<span class="btn-tooltip" th:text="#{multiTool.rotateRight}"></span>
|
||||
</button>
|
||||
<button class="btn btn-secondary enable-on-file" onclick="splitAll()" disabled>
|
||||
<span class="material-symbols-rounded">
|
||||
cut
|
||||
</span>
|
||||
<span class="btn-tooltip" th:text="#{multiTool.split}"></span>
|
||||
</button>
|
||||
<button id="select-pages-container" class="btn btn-secondary enable-on-file"
|
||||
th:title="#{multiTool.selectPages}" onclick="toggleSelectPageVisibility()" disabled>
|
||||
<span id="select-pages-button" class="material-symbols-rounded">
|
||||
event_list
|
||||
</span>
|
||||
</button>
|
||||
<button id="select-All-Container" class="btn btn-secondary enable-on-file hidden"
|
||||
onclick="toggleSelectAll()" disabled>
|
||||
<span th:title="#{multiTool.selectAll}" class="material-symbols-rounded"
|
||||
id="select-icon">select_all</span>
|
||||
<span th:title="#{multiTool.deselectAll}" class="material-symbols-rounded" style="display: none;"
|
||||
id="deselect-icon">deselect</span>
|
||||
</button>
|
||||
<div class=" button-container">
|
||||
<button th:title="#{multiTool.deleteSelected}" id="delete-button" class="btn btn-danger hidden"
|
||||
onclick="deleteSelected()">
|
||||
<span class="material-symbols-rounded">delete</span>
|
||||
</button>
|
||||
</div>
|
||||
<button id="export-selected-button" class="btn btn-primary enable-on-file hidden"
|
||||
onclick="exportPdf(true)" disabled>
|
||||
<span th:title="#{multiTool.downloadSelected}" class="material-symbols-rounded">
|
||||
file_save
|
||||
</span>
|
||||
</button>
|
||||
<button class="btn btn-secondary enable-on-file" onclick="addFilesBlankAll()" disabled>
|
||||
<span class="material-symbols-rounded">
|
||||
insert_page_break
|
||||
</span>
|
||||
<span class="btn-tooltip" th:text="#{multiTool.insertPageBreak}"></span>
|
||||
</button>
|
||||
<button id="select-pages-container" class="btn btn-secondary enable-on-file"
|
||||
onclick="toggleSelectPageVisibility()" disabled>
|
||||
<span id="select-pages-button" class="material-symbols-rounded">
|
||||
event_list
|
||||
<button id="export-button" class="btn btn-primary enable-on-file" onclick="exportPdf(false)" disabled>
|
||||
<span th:title="#{multiTool.downloadAll}" class="material-symbols-rounded">
|
||||
download
|
||||
</span>
|
||||
<span class="btn-tooltip" th:text="#{multiTool.selectPages}"></span>
|
||||
|
||||
</button>
|
||||
<button id="deselect-All-Container" class="btn btn-secondary enable-on-file hidden"
|
||||
onclick="toggleSelectAll()" disabled>
|
||||
<span class="material-symbols-rounded" id="deselect-icon">deselect</span>
|
||||
<span class="btn-tooltip" th:text="#{multiTool.deselectAll}"></span>
|
||||
</button>
|
||||
<button id="select-All-Container" class="btn btn-secondary enable-on-file hidden"
|
||||
onclick="toggleSelectAll()" disabled>
|
||||
<span class="material-symbols-rounded"
|
||||
id="select-icon">select_all</span>
|
||||
<span class="btn-tooltip" th:text="#{multiTool.selectAll}"></span>
|
||||
</button>
|
||||
<div class="button-container">
|
||||
<button id="delete-button" class="btn btn-danger delete hidden" onclick="deleteSelected()">
|
||||
<span class="material-symbols-rounded">delete</span>
|
||||
<span class="btn-tooltip" th:text="#{multiTool.deleteSelected}"></span>
|
||||
</button>
|
||||
</div>
|
||||
<div style="margin-left:auto">
|
||||
<button id="export-selected-button" class="btn btn-primary enable-on-file hidden"
|
||||
onclick="exportPdf(true)" disabled>
|
||||
<span class="btn-tooltip" th:text="#{multiTool.downloadSelected}"></span>
|
||||
<span class="material-symbols-rounded">
|
||||
file_save
|
||||
</span>
|
||||
</button>
|
||||
<button id="export-button" class="btn btn-primary enable-on-file" onclick="exportPdf(false)"
|
||||
disabled>
|
||||
<span class="material-symbols-rounded">
|
||||
download
|
||||
</span>
|
||||
<span class="btn-tooltip" th:text="#{multiTool.downloadAll}"></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="selected-pages-display" class="selected-pages-container hidden">
|
||||
<div style="display:flex; height:3rem; margin-right:1rem">
|
||||
<h5 th:text="#{multiTool.selectedPages}" style="white-space: nowrap; margin-right: 1rem;">Selected
|
||||
Pages</h5>
|
||||
<input type="text" id="csv-input" class="form-control" style="height:2.5rem" placeholder="1,3,5-10" value="">
|
||||
<input type="text" id="csv-input" class="form-control" style="height:2.5rem" placeholder="1,3,5-10"
|
||||
value="">
|
||||
</div>
|
||||
<ul id="selected-pages-list" class="pages-list"></ul>
|
||||
</div>
|
||||
@@ -112,6 +97,13 @@
|
||||
<div class="multi-tool-container">
|
||||
<div class="d-flex flex-wrap" id="pages-container-wrapper">
|
||||
<div id="pages-container">
|
||||
<div class="page-container" th:each="file, status: ${fileList}"
|
||||
th:id="'page-container-' + ${status.index}">
|
||||
<div class="page-number-container">
|
||||
<span th:text="${status.index + 1}"></span>
|
||||
</div>
|
||||
<img th:src="${file.imageUrl}" alt="File Page">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -129,21 +121,6 @@
|
||||
window.selectedPages = [];
|
||||
window.selectPage = false;
|
||||
window.selectAll = false;
|
||||
|
||||
window.translations = {
|
||||
rotateLeft: '[[#{multiTool.rotateLeft}]]',
|
||||
rotateRight: '[[#{multiTool.rotateRight}]]',
|
||||
moveLeft: '[[#{multiTool.moveLeft}]]',
|
||||
moveRight: '[[#{multiTool.moveRight}]]',
|
||||
delete: '[[#{multiTool.delete}]]',
|
||||
split: '[[#{multiTool.split}]]',
|
||||
addFile: '[[#{multiTool.addFile}]]',
|
||||
insertPageBreak:'[[#{multiTool.insertPageBreak}]]',
|
||||
dragDropMessage:'[[#{multiTool.dragDropMessage}]]'
|
||||
};
|
||||
|
||||
|
||||
|
||||
const csvInput = document.getElementById("csv-input");
|
||||
csvInput.addEventListener("keydown", function (event) {
|
||||
if (event.key === "Enter") {
|
||||
@@ -185,4 +162,4 @@
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
<!DOCTYPE html>
|
||||
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}"
|
||||
xmlns:th="https://www.thymeleaf.org">
|
||||
|
||||
<head>
|
||||
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}" xmlns:th="https://www.thymeleaf.org">
|
||||
<head>
|
||||
<th:block th:insert="~{fragments/common :: head(title=#{sign.title}, header=#{sign.header})}"></th:block>
|
||||
<link rel="stylesheet" th:href="@{'/css/sign.css'}">
|
||||
<link rel="stylesheet" th:href="@{'/css/sign.css'}">
|
||||
|
||||
<th:block th:each="font : ${fonts}">
|
||||
<th:block th:each="font : ${fonts}">
|
||||
<style th:inline="text">
|
||||
@font-face {
|
||||
font-family: "[[${font.name}]]";
|
||||
@@ -18,435 +16,372 @@
|
||||
cursive;
|
||||
}
|
||||
</style>
|
||||
</th:block>
|
||||
</th:block>
|
||||
|
||||
<script th:src="@{'/js/thirdParty/signature_pad.umd.min.js'}"></script>
|
||||
<script th:src="@{'/js/thirdParty/interact.min.js'}"></script>
|
||||
</head>
|
||||
<script th:src="@{'/js/thirdParty/signature_pad.umd.min.js'}"></script>
|
||||
<script th:src="@{'/js/thirdParty/interact.min.js'}"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="page-container">
|
||||
<div id="content-wrap">
|
||||
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
|
||||
<br><br>
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6 bg-card">
|
||||
<div class="tool-header">
|
||||
<span class="material-symbols-rounded tool-header-icon sign">signature</span>
|
||||
<span class="tool-header-text" th:text="#{sign.header}"></span>
|
||||
</div>
|
||||
|
||||
<!-- pdf selector -->
|
||||
<div
|
||||
th:replace="~{fragments/common :: fileSelector(name='pdf-upload', multipleInputsForSingleRequest=false, disableMultipleFiles=true, accept='application/pdf')}">
|
||||
</div>
|
||||
<script type="module" th:src="@{'/pdfjs-legacy/pdf.mjs'}"></script>
|
||||
<script>
|
||||
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;
|
||||
|
||||
// Extract filename from the data source path
|
||||
const filename = element.querySelector('.signature-list-name').textContent;
|
||||
|
||||
// Update preview modal
|
||||
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 file = event.target.files[0];
|
||||
if (file) {
|
||||
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";
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<div class="tab-group show-on-file-selected">
|
||||
<div class="tab-container" th:title="#{sign.upload}">
|
||||
<div
|
||||
th:replace="~{fragments/common :: fileSelector(name='image-upload', disableMultipleFiles=true, multipleInputsForSingleRequest=true, accept='image/*', inputText=#{imgPrompt})}">
|
||||
</div>
|
||||
<body>
|
||||
<div id="page-container">
|
||||
<div id="content-wrap">
|
||||
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
|
||||
<br><br>
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6 bg-card">
|
||||
<div class="tool-header">
|
||||
<span class="material-symbols-rounded tool-header-icon sign">signature</span>
|
||||
<span class="tool-header-text" th:text="#{sign.header}"></span>
|
||||
</div>
|
||||
|
||||
<div class="tab-container drawing-pad-container" th:title="#{sign.draw}">
|
||||
<canvas id="drawing-pad-canvas"></canvas>
|
||||
<br>
|
||||
<button id="clear-signature" class="btn btn-outline-danger mt-2" onclick="signaturePad.clear()"
|
||||
th:text="#{sign.clear}"></button>
|
||||
<button id="save-signature" class="btn btn-outline-success mt-2" onclick="addDraggableFromPad()"
|
||||
th:text="#{sign.add}"></button>
|
||||
</div>
|
||||
<!-- pdf selector -->
|
||||
<div th:replace="~{fragments/common :: fileSelector(name='pdf-upload', multipleInputsForSingleRequest=false, disableMultipleFiles=true, accept='application/pdf')}"></div>
|
||||
<script type="module" th:src="@{'/pdfjs-legacy/pdf.mjs'}"></script>
|
||||
<script>
|
||||
let currentPreviewSrc = null;
|
||||
|
||||
<div class="tab-container" th:title="#{sign.saved}">
|
||||
<div class="saved-signatures-section" th:if="${not #lists.isEmpty(signatures)}">
|
||||
<!-- View Toggle Button -->
|
||||
<div class="view-toggle mb-3">
|
||||
<button class="btn btn-outline-secondary btn-sm" onclick="toggleSignatureView()">
|
||||
<span class="material-symbols-rounded grid-view-text">view_list</span>
|
||||
<span class="material-symbols-rounded list-view-text" style="display: none;">grid_view</span>
|
||||
</button>
|
||||
</div>
|
||||
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');
|
||||
|
||||
<!-- Preview Modal -->
|
||||
<div class="modal fade" id="signaturePreview" tabindex="-1">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title"><span id="previewFileName"></span></h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body text-center">
|
||||
<img id="previewImage" src="" alt="Signature Preview" style="max-width: 100%;">
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal"
|
||||
th:text="#{close}"></button>
|
||||
<button type="button" class="btn btn-primary" onclick="addSignatureFromPreview()"
|
||||
th:text="#{addToDoc}">Add to Document</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Grid View -->
|
||||
<div id="gridView">
|
||||
<!-- Personal Signatures -->
|
||||
<div class="signature-category" th:if="${not #lists.isEmpty(signatures.?[category == 'Personal'])}">
|
||||
<h5 th:text="#{sign.personalSigs}"></h5>
|
||||
<div class="signature-grid">
|
||||
<div th:each="sig : ${signatures}" th:if="${sig.category == 'Personal'}" class="signature-item">
|
||||
<img th:src="@{'/api/v1/general/sign/' + ${sig.fileName}}" th:alt="${sig.fileName}"
|
||||
th:data-filename="${sig.fileName}" style="max-width: 200px; cursor: pointer;"
|
||||
onclick="DraggableUtils.createDraggableCanvasFromUrl(this.src)" />
|
||||
<div class="signature-name" th:text="${sig.fileName}"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Shared Signatures -->
|
||||
<div class="signature-category" th:if="${not #lists.isEmpty(signatures.?[category == 'Shared'])}">
|
||||
<h5 th:text="#{sign.sharedSigs}"></h5>
|
||||
<div class="signature-grid">
|
||||
<div th:each="sig : ${signatures}" th:if="${sig.category == 'Shared'}" class="signature-item">
|
||||
<img th:src="@{'/api/v1/general/sign/' + ${sig.fileName}}" th:alt="${sig.fileName}"
|
||||
th:data-filename="${sig.fileName}" style="max-width: 200px; cursor: pointer;"
|
||||
onclick="DraggableUtils.createDraggableCanvasFromUrl(this.src)" />
|
||||
<div class="signature-name" th:text="${sig.fileName}"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- List View (Initially Hidden) -->
|
||||
<div id="listView" style="display: none;">
|
||||
<!-- Personal Signatures -->
|
||||
<div class="signature-category" th:if="${not #lists.isEmpty(signatures.?[category == 'Personal'])}">
|
||||
<h5 th:text="#{sign.personalSigs}"></h5>
|
||||
<div class="signature-list">
|
||||
<div th:each="sig : ${signatures}" th:if="${sig.category == 'Personal'}"
|
||||
class="signature-list-item" th:data-src="@{'/api/v1/general/sign/' + ${sig.fileName}}"
|
||||
onclick="previewSignature(this)">
|
||||
<div class="signature-list-info">
|
||||
<span th:text="${sig.fileName}" class="signature-list-name"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Shared Signatures -->
|
||||
<div class="signature-category" th:if="${not #lists.isEmpty(signatures.?[category == 'Shared'])}">
|
||||
<h5 th:text="#{sign.sharedSigs}"></h5>
|
||||
<div class="signature-list">
|
||||
<div th:each="sig : ${signatures}" th:if="${sig.category == 'Shared'}"
|
||||
class="signature-list-item" th:data-src="@{'/api/v1/general/sign/' + ${sig.fileName}}"
|
||||
onclick="previewSignature(this)">
|
||||
<div class="signature-list-info">
|
||||
<span th:text="${sig.fileName}" class="signature-list-name"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div th:if="${#lists.isEmpty(signatures)}" class="text-center p-3">
|
||||
<p th:text="#{sign.noSavedSigs}">No saved signatures found</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab-container" th:title="#{sign.text}">
|
||||
<label class="form-check-label" for="sigText" th:text="#{text}"></label>
|
||||
<textarea class="form-control" id="sigText" name="sigText" rows="3"></textarea>
|
||||
<label th:text="#{font}"></label>
|
||||
<select class="form-control" name="font" id="font-select">
|
||||
<option th:each="font : ${fonts}" th:value="${font.name}" th:text="${font.name}"
|
||||
th:class="${font.name.toLowerCase()+'-font'}"></option>
|
||||
</select>
|
||||
<div class="margin-auto-parent">
|
||||
<button id="save-text-signature" class="btn btn-outline-success mt-2 margin-center"
|
||||
onclick="addDraggableFromText()" th:text="#{sign.add}"></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
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);
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<script>
|
||||
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;
|
||||
}
|
||||
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';
|
||||
}
|
||||
}
|
||||
|
||||
let croppedWidth = maxX - minX;
|
||||
let croppedHeight = maxY - minY;
|
||||
if (croppedWidth < 0 || croppedHeight < 0) return null;
|
||||
let cuttedImageData = originalCtx.getImageData(minX, minY, croppedWidth, croppedHeight);
|
||||
function previewSignature(element) {
|
||||
const src = element.dataset.src;
|
||||
currentPreviewSrc = src;
|
||||
|
||||
let croppedCanvas = document.createElement('canvas'),
|
||||
croppedCtx = croppedCanvas.getContext('2d');
|
||||
// Extract filename from the data source path
|
||||
const filename = element.querySelector('.signature-list-name').textContent;
|
||||
|
||||
croppedCanvas.width = croppedWidth;
|
||||
croppedCanvas.height = croppedHeight;
|
||||
croppedCtx.putImageData(cuttedImageData, 0, 0);
|
||||
// Update preview modal
|
||||
const previewImage = document.getElementById('previewImage');
|
||||
const previewFileName = document.getElementById('previewFileName');
|
||||
|
||||
return croppedCanvas.toDataURL();
|
||||
}
|
||||
previewImage.src = src;
|
||||
previewFileName.textContent = filename;
|
||||
|
||||
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();
|
||||
const modal = new bootstrap.Modal(document.getElementById('signaturePreview'));
|
||||
modal.show();
|
||||
}
|
||||
}).observe(signaturePadCanvas);
|
||||
|
||||
new ResizeObserver(resizeCanvas).observe(signaturePadCanvas);
|
||||
</script>
|
||||
<script>
|
||||
function addDraggableFromText() {
|
||||
const sigText = document.getElementById('sigText').value;
|
||||
const font = document.querySelector('select[name=font]').value;
|
||||
const fontSize = 100;
|
||||
function addSignatureFromPreview() {
|
||||
if (currentPreviewSrc) {
|
||||
DraggableUtils.createDraggableCanvasFromUrl(currentPreviewSrc);
|
||||
bootstrap.Modal.getInstance(document.getElementById('signaturePreview')).hide();
|
||||
}
|
||||
}
|
||||
|
||||
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/);
|
||||
let originalFileName = '';
|
||||
document.querySelector('input[name=pdf-upload]').addEventListener('change', async (event) => {
|
||||
const file = event.target.files[0];
|
||||
if (file) {
|
||||
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);
|
||||
|
||||
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;
|
||||
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";
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
const dataURL = canvas.toDataURL();
|
||||
DraggableUtils.createDraggableCanvasFromUrl(dataURL);
|
||||
}
|
||||
</script>
|
||||
<div class="tab-group show-on-file-selected">
|
||||
<div class="tab-container" th:title="#{sign.upload}">
|
||||
<div th:replace="~{fragments/common :: fileSelector(name='image-upload', disableMultipleFiles=true, multipleInputsForSingleRequest=true, accept='image/*', inputText=#{imgPrompt})}"></div>
|
||||
<script>
|
||||
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);
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
|
||||
<!-- draggables box -->
|
||||
<div id="box-drag-container" class="show-on-file-selected">
|
||||
<canvas id="pdf-canvas"></canvas>
|
||||
<script th:src="@{'/js/thirdParty/pdf-lib.min.js'}"></script>
|
||||
<script th:src="@{'/js/draggable-utils.js'}"></script>
|
||||
<div class="draggable-buttons-box ignore-rtl">
|
||||
<button class="btn btn-outline-secondary"
|
||||
onclick="DraggableUtils.deleteDraggableCanvas(DraggableUtils.getLastInteracted())">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-trash"
|
||||
viewBox="0 0 16 16">
|
||||
<path
|
||||
d="M5.5 5.5A.5.5 0 0 1 6 6v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5Zm2.5 0a.5.5 0 0 1 .5.5v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5Zm3 .5a.5.5 0 0 0-1 0v6a.5.5 0 0 0 1 0V6Z" />
|
||||
<path
|
||||
d="M14.5 3a1 1 0 0 1-1 1H13v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V4h-.5a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1H6a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1h3.5a1 1 0 0 1 1 1v1ZM4.118 4 4 4.059V13a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V4.059L11.882 4H4.118ZM2.5 3h11V2h-11v1Z" />
|
||||
</svg>
|
||||
<span class="btn-tooltip" th:text="#{sign.delete}"></span>
|
||||
</button>
|
||||
<button class="btn btn-outline-secondary"
|
||||
onclick="DraggableUtils.addAllPagesDraggableCanvas(DraggableUtils.getLastInteracted())">
|
||||
<span class="material-symbols-rounded">
|
||||
content_copy
|
||||
</span>
|
||||
<span class="btn-tooltip" th:text="#{sign.addToAll}"></span>
|
||||
</button>
|
||||
<button class="btn btn-outline-secondary" onclick="goToFirstOrLastPage(false)" style="margin-left:auto">
|
||||
<span class="material-symbols-rounded">
|
||||
keyboard_double_arrow_left
|
||||
</span>
|
||||
<span class="btn-tooltip" th:text="#{sign.first}"></span>
|
||||
</button>
|
||||
<button class="btn btn-outline-secondary" id="incrementPage"
|
||||
onclick="document.documentElement.getAttribute('dir')==='rtl' ? DraggableUtils.incrementPage() : DraggableUtils.decrementPage()">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor"
|
||||
class="bi bi-chevron-left" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd"
|
||||
d="M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z" />
|
||||
</svg>
|
||||
<span class="btn-tooltip" th:text="#{sign.previous}"></span>
|
||||
</button>
|
||||
<button class="btn btn-outline-secondary" id="decrementPage"
|
||||
onclick="document.documentElement.getAttribute('dir')==='rtl' ? DraggableUtils.decrementPage() : DraggableUtils.incrementPage()">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor"
|
||||
class="bi bi-chevron-right" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd"
|
||||
d="M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z" />
|
||||
</svg>
|
||||
<span class="btn-tooltip" th:text="#{sign.next}"></span>
|
||||
</button>
|
||||
<button class="btn btn-outline-secondary" onclick="goToFirstOrLastPage(true)">
|
||||
<span class="material-symbols-rounded">
|
||||
keyboard_double_arrow_right
|
||||
</span>
|
||||
<span class="btn-tooltip" th:text="#{sign.last}"></span>
|
||||
</button>
|
||||
<div class="tab-container drawing-pad-container" th:title="#{sign.draw}">
|
||||
<canvas id="drawing-pad-canvas"></canvas>
|
||||
<br>
|
||||
<button id="clear-signature" class="btn btn-outline-danger mt-2" onclick="signaturePad.clear()" th:text="#{sign.clear}"></button>
|
||||
<button id="save-signature" class="btn btn-outline-success mt-2" onclick="addDraggableFromPad()" th:text="#{sign.add}"></button>
|
||||
<script>
|
||||
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);
|
||||
</script>
|
||||
</div>
|
||||
|
||||
<div class="tab-container" th:title="#{sign.saved}">
|
||||
<div class="saved-signatures-section" th:if="${not #lists.isEmpty(signatures)}">
|
||||
<!-- View Toggle Button -->
|
||||
<div class="view-toggle mb-3">
|
||||
<button class="btn btn-outline-secondary btn-sm" onclick="toggleSignatureView()">
|
||||
<span class="material-symbols-rounded grid-view-text">view_list</span>
|
||||
<span class="material-symbols-rounded list-view-text" style="display: none;">grid_view</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Preview Modal -->
|
||||
<div class="modal fade" id="signaturePreview" tabindex="-1">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title"><span id="previewFileName"></span></h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body text-center">
|
||||
<img id="previewImage" src="" alt="Signature Preview" style="max-width: 100%;">
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal" th:text="#{close}"></button>
|
||||
<button type="button" class="btn btn-primary" onclick="addSignatureFromPreview()" th:text="#{addToDoc}">Add to Document</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Grid View -->
|
||||
<div id="gridView">
|
||||
<!-- Personal Signatures -->
|
||||
<div class="signature-category" th:if="${not #lists.isEmpty(signatures.?[category == 'Personal'])}">
|
||||
<h5 th:text="#{sign.personalSigs}"></h5>
|
||||
<div class="signature-grid">
|
||||
<div th:each="sig : ${signatures}" th:if="${sig.category == 'Personal'}" class="signature-item">
|
||||
<img th:src="@{'/api/v1/general/sign/' + ${sig.fileName}}" th:alt="${sig.fileName}" th:data-filename="${sig.fileName}" style="max-width: 200px; cursor: pointer;" onclick="DraggableUtils.createDraggableCanvasFromUrl(this.src)"/>
|
||||
<div class="signature-name" th:text="${sig.fileName}"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Shared Signatures -->
|
||||
<div class="signature-category" th:if="${not #lists.isEmpty(signatures.?[category == 'Shared'])}">
|
||||
<h5 th:text="#{sign.sharedSigs}"></h5>
|
||||
<div class="signature-grid">
|
||||
<div th:each="sig : ${signatures}" th:if="${sig.category == 'Shared'}" class="signature-item">
|
||||
<img th:src="@{'/api/v1/general/sign/' + ${sig.fileName}}" th:alt="${sig.fileName}" th:data-filename="${sig.fileName}" style="max-width: 200px; cursor: pointer;" onclick="DraggableUtils.createDraggableCanvasFromUrl(this.src)"/>
|
||||
<div class="signature-name" th:text="${sig.fileName}"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- List View (Initially Hidden) -->
|
||||
<div id="listView" style="display: none;">
|
||||
<!-- Personal Signatures -->
|
||||
<div class="signature-category" th:if="${not #lists.isEmpty(signatures.?[category == 'Personal'])}">
|
||||
<h5 th:text="#{sign.personalSigs}"></h5>
|
||||
<div class="signature-list">
|
||||
<div th:each="sig : ${signatures}" th:if="${sig.category == 'Personal'}" class="signature-list-item" th:data-src="@{'/api/v1/general/sign/' + ${sig.fileName}}" onclick="previewSignature(this)">
|
||||
<div class="signature-list-info">
|
||||
<span th:text="${sig.fileName}" class="signature-list-name"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Shared Signatures -->
|
||||
<div class="signature-category" th:if="${not #lists.isEmpty(signatures.?[category == 'Shared'])}">
|
||||
<h5 th:text="#{sign.sharedSigs}"></h5>
|
||||
<div class="signature-list">
|
||||
<div th:each="sig : ${signatures}" th:if="${sig.category == 'Shared'}" class="signature-list-item" th:data-src="@{'/api/v1/general/sign/' + ${sig.fileName}}" onclick="previewSignature(this)">
|
||||
<div class="signature-list-info">
|
||||
<span th:text="${sig.fileName}" class="signature-list-name"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div th:if="${#lists.isEmpty(signatures)}" class="text-center p-3">
|
||||
<p th:text="#{sign.noSavedSigs}">No saved signatures found</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab-container" th:title="#{sign.text}">
|
||||
<label class="form-check-label" for="sigText" th:text="#{text}"></label>
|
||||
<textarea class="form-control" id="sigText" name="sigText" rows="3"></textarea>
|
||||
<label th:text="#{font}"></label>
|
||||
<select class="form-control" name="font" id="font-select">
|
||||
<option th:each="font : ${fonts}" th:value="${font.name}" th:text="${font.name}" th:class="${font.name.toLowerCase()+'-font'}"></option>
|
||||
</select>
|
||||
<div class="margin-auto-parent">
|
||||
<button id="save-text-signature" class="btn btn-outline-success mt-2 margin-center" onclick="addDraggableFromText()" th:text="#{sign.add}"></button>
|
||||
</div>
|
||||
<script>
|
||||
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);
|
||||
}
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- draggables box -->
|
||||
<div id="box-drag-container" class="show-on-file-selected">
|
||||
<canvas id="pdf-canvas"></canvas>
|
||||
<script th:src="@{'/js/thirdParty/pdf-lib.min.js'}"></script>
|
||||
<script th:src="@{'/js/draggable-utils.js'}"></script>
|
||||
<div class="draggable-buttons-box ignore-rtl">
|
||||
<button class="btn btn-outline-secondary" onclick="DraggableUtils.deleteDraggableCanvas(DraggableUtils.getLastInteracted())">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-trash" viewBox="0 0 16 16">
|
||||
<path d="M5.5 5.5A.5.5 0 0 1 6 6v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5Zm2.5 0a.5.5 0 0 1 .5.5v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5Zm3 .5a.5.5 0 0 0-1 0v6a.5.5 0 0 0 1 0V6Z" />
|
||||
<path d="M14.5 3a1 1 0 0 1-1 1H13v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V4h-.5a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1H6a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1h3.5a1 1 0 0 1 1 1v1ZM4.118 4 4 4.059V13a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V4.059L11.882 4H4.118ZM2.5 3h11V2h-11v1Z" />
|
||||
</svg>
|
||||
</button>
|
||||
<button class="btn btn-outline-secondary" onclick="document.documentElement.getAttribute('dir')==='rtl' ? DraggableUtils.incrementPage() : DraggableUtils.decrementPage()" style="margin-left:auto">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-chevron-left" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd" d="M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z" />
|
||||
</svg>
|
||||
</button>
|
||||
<button class="btn btn-outline-secondary" onclick="document.documentElement.getAttribute('dir')==='rtl' ? DraggableUtils.decrementPage() : DraggableUtils.incrementPage()">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-chevron-right" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd" d="M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- download button -->
|
||||
<div class="margin-auto-parent">
|
||||
<button id="download-pdf" class="btn btn-primary mb-2 show-on-file-selected margin-center" th:text="#{downloadPdf}"></button>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
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();
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
|
||||
<!-- download button -->
|
||||
<div class="margin-auto-parent">
|
||||
<button id="download-pdf" class="btn btn-primary mb-2 show-on-file-selected margin-center"
|
||||
th:text="#{downloadPdf}"></button>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
async function goToFirstOrLastPage(page) {
|
||||
if (page) {
|
||||
const lastPage = DraggableUtils.pdfDoc.numPages
|
||||
await DraggableUtils.goToPage(lastPage - 1)
|
||||
} else {
|
||||
await DraggableUtils.goToPage(0)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<script>
|
||||
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();
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
|
||||
</div>
|
||||
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
|
||||
</div>
|
||||
<!-- Link the draggable.js file -->
|
||||
<script th:src="@{'/js/draggable.js'}"></script>
|
||||
</body>
|
||||
|
||||
<!-- Link the draggable.js file -->
|
||||
<script th:src="@{'/js/draggable.js'}"></script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user