Compare commits

..

1 Commits

Author SHA1 Message Date
github-actions[bot]
f60db126c5 Update translation files
Signed-off-by: GitHub Action <action@github.com>
2025-01-18 12:14:49 +00:00
75 changed files with 1859 additions and 6703 deletions

View File

@@ -1,34 +1,15 @@
# Description of Changes
# Description
Please provide a summary of the changes, including:
- What was changed
- Why the change was made
- Any challenges encountered
Please provide a summary of the changes, including relevant motivation and context.
Closes #(issue_number)
---
## Checklist
### General
- [ ] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md)
- [ ] I have read the [Stirling-PDF Developer Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md) (if applicable)
- [ ] I have read the [How to add new languages to Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md) (if applicable)
- [ ] I have performed a self-review of my own code
- [ ] I have attached images of the change if it is UI based
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] If my code has heavily changed functionality I have updated relevant docs on [Stirling-PDFs doc repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/)
- [ ] My changes generate no new warnings
### Documentation
- [ ] I have updated relevant docs on [Stirling-PDF's doc repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/) (if functionality has heavily changed)
- [ ] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only)
### UI Changes (if applicable)
- [ ] Screenshots or videos demonstrating the UI changes are attached (e.g., as comments or direct attachments in the PR)
### Testing (if applicable)
- [ ] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#6-testing) for more details.

51
.github/scripts/check_duplicates.py vendored Normal file
View File

@@ -0,0 +1,51 @@
import sys
def find_duplicate_keys(file_path):
"""
Finds duplicate keys in a properties file and returns their occurrences.
This function reads a properties file, identifies any keys that occur more than
once, and returns a dictionary with these keys and the line numbers of their occurrences.
Parameters:
file_path (str): The path to the properties file to be checked.
Returns:
dict: A dictionary where each key is a duplicated key in the file, and the value is a list
of line numbers where the key occurs.
"""
with open(file_path, "r", encoding="utf-8") as file:
lines = file.readlines()
keys = {}
duplicates = {}
for line_number, line in enumerate(lines, start=1):
line = line.strip()
if line and not line.startswith("#") and "=" in line:
key = line.split("=", 1)[0].strip()
if key in keys:
# If the key already exists, add the current line number
duplicates.setdefault(key, []).append(line_number)
# Also add the first instance of the key if not already done
if keys[key] not in duplicates[key]:
duplicates[key].insert(0, keys[key])
else:
# Store the line number of the first instance of the key
keys[key] = line_number
return duplicates
if __name__ == "__main__":
failed = False
for ar in sys.argv[1:]:
duplicates = find_duplicate_keys(ar)
if duplicates:
for key, lines in duplicates.items():
lines_str = ", ".join(map(str, lines))
print(f"{key} duplicated in {ar} on lines {lines_str}")
failed = True
if failed:
sys.exit(1)

View File

@@ -21,60 +21,25 @@ import argparse
import re
def find_duplicate_keys(file_path):
"""
Identifies duplicate keys in a .properties file.
:param file_path: Path to the .properties file.
:return: List of tuples (key, first_occurrence_line, duplicate_line).
"""
keys = {}
duplicates = []
with open(file_path, "r", encoding="utf-8") as file:
for line_number, line in enumerate(file, start=1):
stripped_line = line.strip()
# Skip empty lines and comments
if not stripped_line or stripped_line.startswith("#"):
continue
# Split the line into key and value
if "=" in stripped_line:
key, _ = stripped_line.split("=", 1)
key = key.strip()
# Check if the key already exists
if key in keys:
duplicates.append((key, keys[key], line_number))
else:
keys[key] = line_number
return duplicates
# 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 structured list of its contents.
:param file_path: Path to the .properties file.
:return: List of dictionaries representing each line in the file.
"""
"""Parses a .properties file and returns a list of objects (including comments, empty lines, and line numbers)."""
properties_list = []
with open(file_path, "r", encoding="utf-8") as file:
for line_number, line in enumerate(file, start=1):
stripped_line = line.strip()
# Handle empty lines
# Empty lines
if not stripped_line:
properties_list.append(
{"line_number": line_number, "type": "empty", "content": ""}
)
continue
# Handle comments
# Comments
if stripped_line.startswith("#"):
properties_list.append(
{
@@ -85,7 +50,7 @@ def parse_properties_file(file_path):
)
continue
# Handle key-value pairs
# Key-value pairs
match = re.match(r"^([^=]+)=(.*)$", line)
if match:
key, value = match.groups()
@@ -102,14 +67,9 @@ def parse_properties_file(file_path):
def write_json_file(file_path, updated_properties):
"""
Writes updated properties back to the file in their original format.
:param file_path: Path to the .properties file.
:param updated_properties: List of updated properties to write.
"""
updated_lines = {entry["line_number"]: entry for entry in updated_properties}
# Sort lines by their numbers and retain comments and empty lines
# Sort by line numbers and retain comments and empty lines
all_lines = sorted(set(updated_lines.keys()))
original_format = []
@@ -128,8 +88,8 @@ def write_json_file(file_path, updated_properties):
# Replace entries with those from the current JSON
original_format.append(entry)
# Write the updated content back to the file
with open(file_path, "w", encoding="utf-8", newline="\n") as file:
# Write back in the original format
with open(file_path, "w", encoding="utf-8") as file:
for entry in original_format:
if entry["type"] == "comment":
file.write(f"{entry['content']}\n")
@@ -140,12 +100,6 @@ def write_json_file(file_path, updated_properties):
def update_missing_keys(reference_file, file_list, branch=""):
"""
Updates missing keys in the translation files based on the reference file.
:param reference_file: Path to the reference .properties file.
:param file_list: List of translation files to update.
:param branch: Branch where the files are located.
"""
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))
@@ -291,24 +245,6 @@ def check_for_differences(reference_file, file_list, branch, actor):
)
else:
report.append("2. **Test Status:** ✅ **_Passed_**")
if find_duplicate_keys(os.path.join(branch, file_path)):
has_differences = True
output = "\n".join(
[
f" - `{key}`: first at line {first}, duplicate at `line {duplicate}`"
for key, first, duplicate in find_duplicate_keys(
os.path.join(branch, file_path)
)
]
)
report.append("3. **Test Status:** ❌ **_Failed_**")
report.append(" - **Issue:**")
report.append(" - duplicate entries were found:")
report.append(output)
else:
report.append("3. **Test Status:** ✅ **_Passed_**")
report.append("")
report.append("---")
report.append("")

85
.github/scripts/check_tabulator.py vendored Normal file
View File

@@ -0,0 +1,85 @@
"""check_tabulator.py"""
import argparse
import sys
def check_tabs(file_path):
"""
Checks for tabs in the specified file.
Args:
file_path (str): The path to the file to be checked.
Returns:
bool: True if tabs are found, False otherwise.
"""
with open(file_path, "r", encoding="utf-8") as file:
content = file.read()
if "\t" in content:
print(f"Tab found in {file_path}")
return True
return False
def replace_tabs_with_spaces(file_path, replace_with=" "):
"""
Replaces tabs with a specified number of spaces in the file.
Args:
file_path (str): The path to the file where tabs will be replaced.
replace_with (str): The character(s) to replace tabs with. Defaults to two spaces.
"""
with open(file_path, "r", encoding="utf-8") as file:
content = file.read()
updated_content = content.replace("\t", replace_with)
with open(file_path, "w", encoding="utf-8") as file:
file.write(updated_content)
def main():
"""
Main function to replace tabs with spaces in the provided files.
The replacement character and files to check are taken from command line arguments.
"""
# Create ArgumentParser instance
parser = argparse.ArgumentParser(
description="Replace tabs in files with specified characters."
)
# Define optional argument `--replace_with`
parser.add_argument(
"--replace_with",
default=" ",
help="Character(s) to replace tabs with. Default is two spaces.",
)
# Define argument for file paths
parser.add_argument("files", metavar="FILE", nargs="+", help="Files to process.")
# Parse arguments
args = parser.parse_args()
# Extract replacement characters and files from the parsed arguments
replace_with = args.replace_with
files_checked = args.files
error = False
for file_path in files_checked:
if check_tabs(file_path):
replace_tabs_with_spaces(file_path, replace_with)
error = True
if error:
print("Error: Originally found tabs in HTML files, now replaced.")
sys.exit(1)
sys.exit(0)
if __name__ == "__main__":
main()

View File

@@ -36,7 +36,7 @@ jobs:
steps:
- name: Harden Runner
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
with:
egress-policy: audit
@@ -81,7 +81,7 @@ jobs:
steps:
- name: Harden Runner
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
with:
egress-policy: audit
@@ -119,7 +119,7 @@ jobs:
password: ${{ secrets.DOCKER_HUB_API }}
- name: Build and push PR-specific image
uses: docker/build-push-action@67a2d409c0a876cbe6b11854e3e25193efe4e62d # v6.12.0
uses: docker/build-push-action@b32b51a8eda65d6793cd0494a773d4f6bcef32dc # v6.11.0
with:
context: .
file: ./Dockerfile

View File

@@ -21,7 +21,7 @@ jobs:
steps:
- name: Harden Runner
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
with:
egress-policy: audit

View File

@@ -13,7 +13,7 @@ jobs:
pull-requests: write
steps:
- name: Harden Runner
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
with:
egress-policy: audit

View File

@@ -24,7 +24,7 @@ jobs:
steps:
- name: Harden Runner
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
with:
egress-policy: audit
@@ -37,12 +37,6 @@ jobs:
java-version: ${{ matrix.jdk-version }}
distribution: "temurin"
- name: PR | Generate verification metadata with signatures and checksums for dependabot[bot]
if: github.event.pull_request.user.login == 'dependabot[bot]'
run: |
./gradlew clean dependencies buildEnvironment spotlessApply --write-verification-metadata sha256 --refresh-dependencies help
./gradlew clean dependencies buildEnvironment spotlessApply --write-verification-metadata sha256,pgp --refresh-keys --export-keys --refresh-dependencies help
- name: Build with Gradle and no spring security
run: ./gradlew clean build
env:
@@ -83,7 +77,7 @@ jobs:
steps:
- name: Harden Runner
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
with:
egress-policy: audit
@@ -101,7 +95,7 @@ jobs:
- name: Install Docker Compose
run: |
sudo curl -SL "https://github.com/docker/compose/releases/download/v2.32.4/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo curl -SL "https://github.com/docker/compose/releases/download/v2.32.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
- name: Set up Python
@@ -112,10 +106,10 @@ jobs:
- name: Pip requirements
run: |
pip install --require-hashes -r ./testing/cucumber/requirements.txt
pip install --require-hashes -r ./cucumber/requirements.txt
- name: Run Docker Compose Tests
run: |
chmod +x ./testing/test_webpages.sh
chmod +x ./testing/test.sh
./testing/test.sh "${{ github.event.pull_request.user.login == 'dependabot[bot]' }}"
chmod +x ./cucumber/test_webpages.sh
chmod +x ./test.sh
./test.sh

View File

@@ -18,7 +18,7 @@ jobs:
pull-requests: write
steps:
- name: Harden Runner
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
with:
egress-policy: audit
@@ -58,7 +58,7 @@ jobs:
run: |
echo "Fetching PR changed files..."
echo "Getting list of changed files from PR..."
gh pr view ${{ steps.get-pr-data.outputs.pr_number }} --json files -q ".files[].path" | grep -E '^src/main/resources/messages_[a-zA-Z_]{2}_[a-zA-Z_]{2,7}\.properties$' > changed_files.txt # Filter only matching property files
gh pr view ${{ steps.get-pr-data.outputs.pr_number }} --json files -q ".files[].path" | grep -E '^src/main/resources/messages_[a-zA-Z_]+\.properties$' > changed_files.txt # Filter only matching property files
- name: Determine reference file test
id: determine-file
@@ -99,7 +99,7 @@ jobs:
// Filter for relevant files based on the PR changes
const changedFiles = files
.map(file => file.filename)
.filter(file => /^src\/main\/resources\/messages_[a-zA-Z_]{2}_[a-zA-Z_]{2,7}\.properties$/.test(file));
.filter(file => /^src\/main\/resources\/messages_[a-zA-Z_]+\.properties$/.test(file));
console.log("Changed files:", changedFiles);

View File

@@ -17,7 +17,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Harden Runner
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
with:
egress-policy: audit

View File

@@ -18,7 +18,7 @@ jobs:
pull-requests: write
steps:
- name: Harden Runner
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
with:
egress-policy: audit

View File

@@ -15,7 +15,7 @@ jobs:
issues: write
steps:
- name: Harden Runner
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
with:
egress-policy: audit

View File

@@ -16,7 +16,7 @@ jobs:
versionMac: ${{ steps.versionNumberMac.outputs.versionNumberMac }}
steps:
- name: Harden Runner
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
with:
egress-policy: audit
@@ -51,7 +51,7 @@ jobs:
file_suffix: ""
steps:
- name: Harden Runner
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
with:
egress-policy: audit
@@ -101,7 +101,7 @@ jobs:
file_suffix: ""
steps:
- name: Harden Runner
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
with:
egress-policy: audit
@@ -139,7 +139,7 @@ jobs:
contents: write
steps:
- name: Harden Runner
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
with:
egress-policy: audit
@@ -210,7 +210,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Harden Runner
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
with:
egress-policy: audit
@@ -271,7 +271,7 @@ jobs:
contents: write
steps:
- name: Harden Runner
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
with:
egress-policy: audit

View File

@@ -2,41 +2,23 @@ name: Pre-commit
on:
workflow_dispatch:
schedule:
- cron: "0 0 * * 1"
permissions:
contents: read
jobs:
pre-commit:
if: ${{ github.event.pull_request.user.login != 'dependabot[bot]' }}
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- name: Harden Runner
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
with:
egress-policy: audit
- name: Generate GitHub App Token
id: generate-token
uses: actions/create-github-app-token@c1a285145b9d317df6ced56c09f525b5c2b6f755 # v1.11.1
with:
app-id: ${{ secrets.GH_APP_ID }}
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
- name: Get GitHub App User ID
id: get-user-id
run: echo "user-id=$(gh api "/users/${{ steps.generate-token.outputs.app-slug }}[bot]" --jq .id)" >> $GITHUB_OUTPUT
env:
GH_TOKEN: ${{ steps.generate-token.outputs.token }}
- id: committer
run: |
echo "string=${{ steps.generate-token.outputs.app-slug }}[bot] <${{ steps.get-user-id.outputs.user-id }}+${{ steps.generate-token.outputs.app-slug }}[bot]@users.noreply.github.com>" >> "$GITHUB_OUTPUT"
- name: Checkout repository
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
@@ -53,25 +35,25 @@ jobs:
continue-on-error: true
- name: Set up git config
run: |
git config --global user.name ${{ steps.generate-token.outputs.app-slug }}[bot]
git config --global user.email "${{ steps.get-user-id.outputs.user-id }}+${{ steps.generate-token.outputs.app-slug }}[bot]@users.noreply.github.com"
git config --global user.name "github-actions[bot]"
git config --global user.email "github-actions[bot]@users.noreply.github.com"
- name: git add
run: |
git add .
git diff --staged --quiet || echo "CHANGES_DETECTED=true" >> $GITHUB_ENV
git diff --staged --quiet || git commit -m ":file_folder: pre-commit
> Made via .github/workflows/pre_commit.yml" || echo "pre-commit: no changes"
- name: Create Pull Request
if: env.CHANGES_DETECTED == 'true'
uses: peter-evans/create-pull-request@67ccf781d68cd99b580ae25a5c18a1cc84ffff1f # v7.0.6
with:
token: ${{ steps.generate-token.outputs.token }}
commit-message: ":file_folder: pre-commit"
committer: ${{ steps.committer.outputs.string }}
author: ${{ steps.committer.outputs.string }}
token: ${{ secrets.GITHUB_TOKEN }}
commit-message: "ci: 🤖 format everything with pre-commit"
committer: GitHub Action <action@github.com>
author: GitHub Action <action@github.com>
signoff: true
branch: pre-commit
title: "🤖 format everything with pre-commit by <${{ steps.generate-token.outputs.app-slug }}>"
title: "🤖 format everything with pre-commit by <github-actions[bot]>"
body: |
Auto-generated by [create-pull-request][1] with **${{ steps.generate-token.outputs.app-slug }}**
Auto-generated by [create-pull-request][1]
[1]: https://github.com/peter-evans/create-pull-request
draft: false

View File

@@ -18,7 +18,7 @@ jobs:
id-token: write
steps:
- name: Harden Runner
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
with:
egress-policy: audit
@@ -89,7 +89,7 @@ jobs:
- name: Build and push main Dockerfile
id: build-push-regular
uses: docker/build-push-action@67a2d409c0a876cbe6b11854e3e25193efe4e62d # v6.12.0
uses: docker/build-push-action@b32b51a8eda65d6793cd0494a773d4f6bcef32dc # v6.11.0
with:
builder: ${{ steps.buildx.outputs.name }}
context: .
@@ -134,7 +134,7 @@ jobs:
- name: Build and push Dockerfile-ultra-lite
id: build-push-lite
uses: docker/build-push-action@67a2d409c0a876cbe6b11854e3e25193efe4e62d # v6.12.0
uses: docker/build-push-action@b32b51a8eda65d6793cd0494a773d4f6bcef32dc # v6.11.0
if: github.ref != 'refs/heads/main'
with:
context: .
@@ -165,7 +165,7 @@ jobs:
- name: Build and push main Dockerfile fat
id: build-push-fat
uses: docker/build-push-action@67a2d409c0a876cbe6b11854e3e25193efe4e62d # v6.12.0
uses: docker/build-push-action@b32b51a8eda65d6793cd0494a773d4f6bcef32dc # v6.11.0
if: github.ref != 'refs/heads/main'
with:
builder: ${{ steps.buildx.outputs.name }}

View File

@@ -23,7 +23,7 @@ jobs:
version: ${{ steps.versionNumber.outputs.versionNumber }}
steps:
- name: Harden Runner
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
with:
egress-policy: audit
@@ -83,7 +83,7 @@ jobs:
file_suffix: ""
steps:
- name: Harden Runner
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
with:
egress-policy: audit
@@ -161,7 +161,7 @@ jobs:
file_suffix: ""
steps:
- name: Harden Runner
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
with:
egress-policy: audit

View File

@@ -34,7 +34,7 @@ jobs:
steps:
- name: Harden Runner
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
with:
egress-policy: audit

View File

@@ -16,7 +16,7 @@ jobs:
pull-requests: write
steps:
- name: Harden Runner
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
with:
egress-policy: audit

View File

@@ -14,7 +14,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Harden Runner
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
with:
egress-policy: audit

View File

@@ -1,170 +1,63 @@
name: Sync Files
on:
workflow_dispatch:
push:
branches:
- main
paths:
- "build.gradle"
- "README.md"
- "gradle/verification-keyring.keys"
- "gradle/verification-metadata.xml"
- "src/main/resources/messages_*.properties"
- "src/main/resources/static/3rdPartyLicenses.json"
- "scripts/ignore_translation.toml"
permissions:
contents: read
jobs:
read_bot_entries:
sync-readme:
runs-on: ubuntu-latest
outputs:
userName: ${{ steps.get-user-id.outputs.user_name }}
userEmail: ${{ steps.get-user-id.outputs.user_email }}
committer: ${{ steps.committer.outputs.committer }}
permissions:
contents: write
pull-requests: write
steps:
- name: Harden Runner
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
with:
egress-policy: audit
- name: Generate GitHub App Token
id: generate-token
uses: actions/create-github-app-token@c1a285145b9d317df6ced56c09f525b5c2b6f755 # v1.11.1
with:
app-id: ${{ secrets.GH_APP_ID }}
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
- name: Get GitHub App User ID
id: get-user-id
run: |
USER_NAME="${{ steps.generate-token.outputs.app-slug }}[bot]"
USER_ID=$(gh api "/users/$USER_NAME" --jq .id)
USER_EMAIL="$USER_ID+$USER_NAME@users.noreply.github.com"
echo "user_name=$USER_NAME" >> "$GITHUB_OUTPUT"
echo "user_email=$USER_EMAIL" >> "$GITHUB_OUTPUT"
echo "user-id=$USER_ID" >> "$GITHUB_OUTPUT"
env:
GH_TOKEN: ${{ steps.generate-token.outputs.token }}
- id: committer
run: |
COMMITTER="${{ steps.get-user-id.outputs.user_name }} <${{ steps.get-user-id.outputs.user_email }}>"
echo "committer=$COMMITTER" >> "$GITHUB_OUTPUT"
sync-files:
needs: ["read_bot_entries"]
runs-on: ubuntu-latest
steps:
- name: Harden Runner
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
with:
egress-policy: audit
- name: Generate GitHub App Token
id: generate-token
uses: actions/create-github-app-token@c1a285145b9d317df6ced56c09f525b5c2b6f755 # v1.11.1
with:
app-id: ${{ vars.GH_APP_ID }}
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Set up Python
uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0
with:
python-version: "3.12"
cache: 'pip' # caching pip dependencies
- name: Sync translation property files
run: |
python .github/scripts/check_language_properties.py --reference-file "src/main/resources/messages_en_GB.properties" --branch main
- name: Set up git config
run: |
git config --global user.name ${{ needs.read_bot_entries.outputs.userName }}
git config --global user.email ${{ needs.read_bot_entries.outputs.userEmail }}
- name: Run git add
run: |
git add src/main/resources/messages_*.properties
git diff --staged --quiet || git commit -m ":memo: Sync translation files" || echo "no changes"
- name: Install dependencies
run: pip install --require-hashes -r ./.github/scripts/requirements_sync_readme.txt
- name: Sync README.md
- name: Sync README
run: python scripts/counter_translation.py
- name: Set up git config
run: |
python scripts/counter_translation.py
git config --global user.name "github-actions[bot]"
git config --global user.email "github-actions[bot]@users.noreply.github.com"
- name: Run git add
run: |
git add README.md
git diff --staged --quiet || git commit -m ":memo: Sync README.md" || echo "no changes"
- name: Generate verification metadata with signatures and checksums
run: |
set -e
if [ -f ./gradle/verification-metadata.xml ]; then
rm ./gradle/verification-metadata.xml
fi
./gradlew clean dependencies buildEnvironment spotlessApply --write-verification-metadata sha256 help
./gradlew clean dependencies buildEnvironment spotlessApply --write-verification-metadata sha256,pgp --refresh-keys --export-keys --refresh-dependencies help
./gradlew clean build
- name: Run git add
run: |
git add gradle/verification-keyring.keys
git add gradle/verification-metadata.xml
git diff --staged --quiet || git commit -m ":memo: Generate verification metadata with signatures and checksums" || echo "no changes"
git add .
git diff --staged --quiet || git commit -m ":memo: Sync README
> Made via sync_files.yml" || echo "no changes"
- name: Create Pull Request
uses: peter-evans/create-pull-request@67ccf781d68cd99b580ae25a5c18a1cc84ffff1f # v7.0.6
with:
token: ${{ steps.generate-token.outputs.token }}
token: ${{ secrets.GITHUB_TOKEN }}
commit-message: Update files
committer: ${{ needs.read_bot_entries.outputs.committer }}
author: ${{ needs.read_bot_entries.outputs.committer }}
committer: GitHub Action <action@github.com>
author: GitHub Action <action@github.com>
signoff: true
branch: sync_readme
title: ":globe_with_meridians: Sync Translations + Update README Progress Table + Update Verification Metadata"
title: ":memo: Update README: Translation Progress Table"
body: |
### Description of Changes
This Pull Request was automatically generated to synchronize updates to translation files, verification metadata, and documentation. Below are the details of the changes made:
#### **1. Synchronization of Translation Files**
- Updated translation files (`messages_*.properties`) to reflect changes in the reference file `messages_en_GB.properties`.
- Ensured consistency and synchronization across all supported language files.
- Highlighted any missing or incomplete translations.
#### **2. Update README.md**
- Generated the translation progress table in `README.md`.
- Added a summary of the current translation status for all supported languages.
- Included up-to-date statistics on translation coverage.
#### **3. Verification Metadata Updates**
- Generated or refreshed the `verification-keyring.keys` and `verification-metadata.xml` files.
- Included the latest dependency signatures and checksums to enhance the build's integrity.
#### **Why these changes are necessary**
- Keeps translation files aligned with the latest reference updates.
- Ensures the documentation reflects the current translation progress.
- Strengthens dependency verification for a more secure build process.
---
Auto-generated by [create-pull-request][1].
Auto-generated by [create-pull-request][1]
[1]: https://github.com/peter-evans/create-pull-request
draft: false
delete-branch: true
labels: github-actions
labels: Documentation,Translation,github-actions
sign-commits: true
add-paths: |
README.md
src/main/resources/messages_*.properties
gradle/verification-keyring.keys
gradle/verification-metadata.xml

View File

@@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Harden Runner
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
with:
egress-policy: audit
@@ -46,7 +46,7 @@ jobs:
password: ${{ secrets.DOCKER_HUB_API }}
- name: Build and push test image
uses: docker/build-push-action@67a2d409c0a876cbe6b11854e3e25193efe4e62d # v6.12.0
uses: docker/build-push-action@b32b51a8eda65d6793cd0494a773d4f6bcef32dc # v6.11.0
with:
context: .
file: ./Dockerfile
@@ -105,7 +105,7 @@ jobs:
steps:
- name: Harden Runner
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
with:
egress-policy: audit
@@ -122,7 +122,7 @@ jobs:
Start-Process "C:/Program Files/Google/Chrome/Application/chrome.exe" -ArgumentList "--start-maximized", "--load-extension=$(pwd)/node_modules/dashcam-chrome/build", "http://${{ secrets.VPS_HOST }}:1337"
Start-Sleep -Seconds 20
prompt: |
1. /run testing/testdriver/test.yml
1. /run testdriver/test.yml
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
FORCE_COLOR: "3"
@@ -134,7 +134,7 @@ jobs:
steps:
- name: Harden Runner
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
with:
egress-policy: audit

View File

@@ -0,0 +1,72 @@
name: Update Translations
on:
workflow_dispatch:
push:
branches: ["main"]
paths:
- "src/main/resources/messages_en_GB.properties"
permissions:
contents: read
jobs:
update-translations-main:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- name: Harden Runner
uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
with:
egress-policy: audit
- name: Checkout repository
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Set up Python
uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0
with:
python-version: "3.12"
- name: Run Python script to check files
id: run-check
run: |
echo "Running Python script to check files..."
python .github/scripts/check_language_properties.py \
--reference-file src/main/resources/messages_en_GB.properties \
--branch main
- 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: |
git add src/main/resources/messages_*.properties
git diff --staged --quiet || echo "CHANGES_DETECTED=true" >> $GITHUB_ENV
- name: Create Pull Request
id: cpr
if: env.CHANGES_DETECTED == 'true'
uses: peter-evans/create-pull-request@67ccf781d68cd99b580ae25a5c18a1cc84ffff1f # v7.0.6
with:
token: ${{ secrets.GITHUB_TOKEN }}
commit-message: "Update translation files"
committer: GitHub Action <action@github.com>
author: GitHub Action <action@github.com>
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]
[1]: https://github.com/peter-evans/create-pull-request
draft: false
delete-branch: true
labels: Translation,github-actions
sign-commits: true

1
.gitignore vendored
View File

@@ -25,7 +25,6 @@ clientWebUI/
!cucumber/
!cucumber/exampleFiles/
!cucumber/exampleFiles/example_html.zip
exampleYmlFiles/stirling/
# Gradle
.gradle

View File

@@ -6,10 +6,10 @@ repos:
args:
- --fix
- --line-length=127
files: ^((\.github/scripts|scripts)/.+)?[^/]+\.py$
files: ^((.github/scripts|scripts)/.+)?[^/]+\.py$
exclude: (split_photos.py)
- id: ruff-format
files: ^((\.github/scripts|scripts)/.+)?[^/]+\.py$
files: ^((.github/scripts|scripts)/.+)?[^/]+\.py$
exclude: (split_photos.py)
- repo: https://github.com/codespell-project/codespell
rev: v2.3.0
@@ -19,18 +19,39 @@ repos:
- --ignore-words-list=
- --skip="./.*,*.csv,*.json,*.ambr"
- --quiet-level=2
files: \.(html|css|js|py|md)$
files: \.(properties|html|css|js|py|md)$
exclude: (.vscode|.devcontainer|src/main/resources|Dockerfile|.*/pdfjs.*|.*/thirdParty.*|bootstrap.*|.*\.min\..*|.*diff\.js)
- repo: https://github.com/gitleaks/gitleaks
rev: v8.22.0
hooks:
- id: gitleaks
- repo: https://github.com/jumanjihouse/pre-commit-hooks
rev: 3.0.0
hooks:
- id: shellcheck
files: ^.*(\.bash|\.sh|\.ksh|\.zsh)$
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
hooks:
- id: end-of-file-fixer
files: ^.*(\.js|\.java|\.py|\.yml)$
exclude: ^(.*/pdfjs.*|.*/thirdParty.*|bootstrap.*|.*\.min\..*|.*diff\.js|\.github/workflows/.*$)
exclude: ^(.*/pdfjs.*|.*/thirdParty.*|bootstrap.*|.*\.min\..*|.*diff\.js$)
- id: trailing-whitespace
files: ^.*(\.js|\.java|\.py|\.yml)$
exclude: ^(.*/pdfjs.*|.*/thirdParty.*|bootstrap.*|.*\.min\..*|.*diff\.js|\.github/workflows/.*$)
exclude: ^(.*/pdfjs.*|.*/thirdParty.*|bootstrap.*|.*\.min\..*|.*diff\.js$)
- repo: local
hooks:
- id: check-duplicate-properties-keys
name: Check Duplicate Properties Keys
entry: python .github/scripts/check_duplicates.py
language: python
files: ^(src)/.+\.properties$
- id: check-html-tabs
name: Check HTML for tabs
description: Ensures HTML/CSS/JS files do not contain tab characters
# args: ["--replace_with= "]
entry: python .github/scripts/check_tabulator.py
language: python
exclude: ^(.*/pdfjs.*|.*/thirdParty.*|bootstrap.*|.*\.min\..*|.*diff\.js$)
files: ^.*(\.html|\.css|\.js)$

View File

@@ -25,13 +25,7 @@ LABEL org.opencontainers.image.keywords="PDF, manipulation, merge, split, conver
# Set Environment Variables
ENV DOCKER_ENABLE_SECURITY=false \
VERSION_TAG=$VERSION_TAG \
JAVA_TOOL_OPTIONS="-XX:+UnlockExperimentalVMOptions \
-XX:MaxRAMPercentage=75 \
-XX:InitiatingHeapOccupancyPercent=20 \
-XX:+G1PeriodicGCInvokesConcurrent \
-XX:G1PeriodicGCInterval=10000 \
-XX:+UseStringDeduplication \
-XX:G1PeriodicGCSystemLoadThreshold=70" \
JAVA_TOOL_OPTIONS="$JAVA_TOOL_OPTIONS -XX:MaxRAMPercentage=75" \
HOME=/home/stirlingpdfuser \
PUID=1000 \
PGID=1000 \

View File

@@ -25,13 +25,7 @@ ARG VERSION_TAG
# Set Environment Variables
ENV DOCKER_ENABLE_SECURITY=false \
VERSION_TAG=$VERSION_TAG \
JAVA_TOOL_OPTIONS="-XX:+UnlockExperimentalVMOptions \
-XX:MaxRAMPercentage=75 \
-XX:InitiatingHeapOccupancyPercent=20 \
-XX:+G1PeriodicGCInvokesConcurrent \
-XX:G1PeriodicGCInterval=10000 \
-XX:+UseStringDeduplication \
-XX:G1PeriodicGCSystemLoadThreshold=70" \
JAVA_TOOL_OPTIONS="$JAVA_TOOL_OPTIONS -XX:MaxRAMPercentage=75" \
HOME=/home/stirlingpdfuser \
PUID=1000 \
PGID=1000 \

View File

@@ -7,13 +7,7 @@ ARG VERSION_TAG
ENV DOCKER_ENABLE_SECURITY=false \
HOME=/home/stirlingpdfuser \
VERSION_TAG=$VERSION_TAG \
JAVA_TOOL_OPTIONS="-XX:+UnlockExperimentalVMOptions \
-XX:MaxRAMPercentage=75 \
-XX:InitiatingHeapOccupancyPercent=20 \
-XX:+G1PeriodicGCInvokesConcurrent \
-XX:G1PeriodicGCInterval=10000 \
-XX:+UseStringDeduplication \
-XX:G1PeriodicGCSystemLoadThreshold=70" \
JAVA_TOOL_OPTIONS="$JAVA_TOOL_OPTIONS -XX:MaxRAMPercentage=75" \
PUID=1000 \
PGID=1000 \
UMASK=022

View File

@@ -117,42 +117,42 @@ Stirling-PDF currently supports 39 languages!
| Language | Progress |
| -------------------------------------------- | -------------------------------------- |
| Arabic (العربية) (ar_AR) | ![90%](https://geps.dev/progress/90) |
| Arabic (العربية) (ar_AR) | ![91%](https://geps.dev/progress/91) |
| Azerbaijani (Azərbaycan Dili) (az_AZ) | ![89%](https://geps.dev/progress/89) |
| Basque (Euskara) (eu_ES) | ![51%](https://geps.dev/progress/51) |
| Bulgarian (Български) (bg_BG) | ![86%](https://geps.dev/progress/86) |
| Catalan (Català) (ca_CA) | ![81%](https://geps.dev/progress/81) |
| Croatian (Hrvatski) (hr_HR) | ![87%](https://geps.dev/progress/87) |
| Czech (Česky) (cs_CZ) | ![99%](https://geps.dev/progress/99) |
| Danish (Dansk) (da_DK) | ![86%](https://geps.dev/progress/86) |
| Dutch (Nederlands) (nl_NL) | ![85%](https://geps.dev/progress/85) |
| Croatian (Hrvatski) (hr_HR) | ![88%](https://geps.dev/progress/88) |
| Czech (Česky) (cs_CZ) | ![87%](https://geps.dev/progress/87) |
| Danish (Dansk) (da_DK) | ![87%](https://geps.dev/progress/87) |
| Dutch (Nederlands) (nl_NL) | ![86%](https://geps.dev/progress/86) |
| English (English) (en_GB) | ![100%](https://geps.dev/progress/100) |
| English (US) (en_US) | ![100%](https://geps.dev/progress/100) |
| French (Français) (fr_FR) | ![96%](https://geps.dev/progress/96) |
| French (Français) (fr_FR) | ![93%](https://geps.dev/progress/93) |
| German (Deutsch) (de_DE) | ![100%](https://geps.dev/progress/100) |
| Greek (Ελληνικά) (el_GR) | ![98%](https://geps.dev/progress/98) |
| Greek (Ελληνικά) (el_GR) | ![99%](https://geps.dev/progress/99) |
| Hindi (हिंदी) (hi_IN) | ![99%](https://geps.dev/progress/99) |
| Hungarian (Magyar) (hu_HU) | ![96%](https://geps.dev/progress/96) |
| Hungarian (Magyar) (hu_HU) | ![97%](https://geps.dev/progress/97) |
| Indonesian (Bahasa Indonesia) (id_ID) | ![87%](https://geps.dev/progress/87) |
| Irish (Gaeilge) (ga_IE) | ![79%](https://geps.dev/progress/79) |
| Irish (Gaeilge) (ga_IE) | ![80%](https://geps.dev/progress/80) |
| Italian (Italiano) (it_IT) | ![99%](https://geps.dev/progress/99) |
| Japanese (日本語) (ja_JP) | ![90%](https://geps.dev/progress/90) |
| Korean (한국어) (ko_KR) | ![99%](https://geps.dev/progress/99) |
| Norwegian (Norsk) (no_NB) | ![79%](https://geps.dev/progress/79) |
| Korean (한국어) (ko_KR) | ![86%](https://geps.dev/progress/86) |
| Norwegian (Norsk) (no_NB) | ![80%](https://geps.dev/progress/80) |
| Persian (فارسی) (fa_IR) | ![95%](https://geps.dev/progress/95) |
| Polish (Polski) (pl_PL) | ![86%](https://geps.dev/progress/86) |
| Polish (Polski) (pl_PL) | ![87%](https://geps.dev/progress/87) |
| Portuguese (Português) (pt_PT) | ![98%](https://geps.dev/progress/98) |
| Portuguese Brazilian (Português) (pt_BR) | ![97%](https://geps.dev/progress/97) |
| Romanian (Română) (ro_RO) | ![81%](https://geps.dev/progress/81) |
| Portuguese Brazilian (Português) (pt_BR) | ![98%](https://geps.dev/progress/98) |
| Romanian (Română) (ro_RO) | ![82%](https://geps.dev/progress/82) |
| Russian (Русский) (ru_RU) | ![99%](https://geps.dev/progress/99) |
| Serbian Latin alphabet (Srpski) (sr_LATN_RS) | ![64%](https://geps.dev/progress/64) |
| Simplified Chinese (简体中文) (zh_CN) | ![90%](https://geps.dev/progress/90) |
| Slovakian (Slovensky) (sk_SK) | ![75%](https://geps.dev/progress/75) |
| Slovenian (Slovenščina) (sl_SI) | ![97%](https://geps.dev/progress/97) |
| Spanish (Español) (es_ES) | ![87%](https://geps.dev/progress/87) |
| Swedish (Svenska) (sv_SE) | ![87%](https://geps.dev/progress/87) |
| Thai (ไทย) (th_TH) | ![86%](https://geps.dev/progress/86) |
| Tibetan (བོད་ཡིག་) (zh_BO) | ![95%](https://geps.dev/progress/95) |
| Slovenian (Slovenščina) (sl_SI) | ![94%](https://geps.dev/progress/75) |
| Spanish (Español) (es_ES) | ![88%](https://geps.dev/progress/88) |
| Swedish (Svenska) (sv_SE) | ![88%](https://geps.dev/progress/88) |
| Thai (ไทย) (th_TH) | ![87%](https://geps.dev/progress/87) |
| Tibetan (བོད་ཡིག་) (zh_BO) | ![96%](https://geps.dev/progress/96) |
| Traditional Chinese (繁體中文) (zh_TW) | ![99%](https://geps.dev/progress/99) |
| Turkish (Türkçe) (tr_TR) | ![83%](https://geps.dev/progress/83) |
| Ukrainian (Українська) (uk_UA) | ![73%](https://geps.dev/progress/73) |

View File

@@ -7,8 +7,8 @@ plugins {
id "edu.sc.seis.launch4j" version "3.0.6"
id "com.diffplug.spotless" version "7.0.2"
id "com.github.jk1.dependency-license-report" version "2.9"
//id "nebula.lint" version "19.0.3"
id("org.panteleyev.jpackageplugin") version "1.6.0"
//id "nebula.lint" version "19.0.3"
id("org.panteleyev.jpackageplugin") version "1.6.0"
}
import com.github.jk1.license.render.*
@@ -19,13 +19,13 @@ ext {
logbackVersion = "1.5.7"
imageioVersion = "3.12.0"
lombokVersion = "1.18.36"
bouncycastleVersion = "1.80"
bouncycastleVersion = "1.79"
springSecuritySamlVersion = "6.4.2"
openSamlVersion = "4.3.2"
}
group = "stirling.software"
version = "0.39.0"
version = "0.37.1"
java {
@@ -35,9 +35,9 @@ java {
repositories {
mavenCentral()
maven { url = "https://jitpack.io" }
maven { url = "https://build.shibboleth.net/maven/releases" }
maven { url = "https://maven.pkg.github.com/jcefmaven/jcefmaven" }
maven { url "https://jitpack.io" }
maven { url "https://build.shibboleth.net/maven/releases" }
maven { url "https://maven.pkg.github.com/jcefmaven/jcefmaven" }
}
@@ -66,7 +66,7 @@ sourceSets {
}
if (System.getenv("STIRLING_PDF_DESKTOP_UI") == "false") {
exclude "stirling/software/SPDF/UI/impl/**"
exclude "stirling/software/SPDF/UI/impl/**"
}
}
@@ -138,10 +138,10 @@ jpackage {
// Windows-specific configuration
windows {
launcherAsService = false
appVersion = project.version
launcherAsService = false
appVersion = project.version
winConsole = false
winConsole = false
winMenu = true // Creates start menu entry
winShortcut = true // Creates desktop shortcut
winShortcutPrompt = true // Lets user choose whether to create shortcuts
@@ -157,7 +157,7 @@ jpackage {
// macOS-specific configuration
mac {
appVersion = getMacVersion(project.version.toString())
appVersion = getMacVersion(project.version.toString())
icon = "src/main/resources/static/favicon.icns"
type = "dmg"
macPackageIdentifier = "com.stirling.software.pdf"
@@ -181,7 +181,7 @@ jpackage {
// Linux-specific configuration
linux {
appVersion = project.version
appVersion = project.version
icon = "src/main/resources/static/favicon.png"
type = "deb" // Can also use "rpm" for Red Hat-based systems
@@ -229,9 +229,9 @@ launch4j {
outfile="Stirling-PDF.exe"
if(System.getenv("STIRLING_PDF_DESKTOP_UI") == 'true') {
headerType = "gui"
headerType = "gui"
} else {
headerType = "console"
headerType = "console"
}
jarTask = tasks.bootJar
@@ -239,11 +239,13 @@ launch4j {
downloadUrl="https://download.oracle.com/java/21/latest/jdk-21_windows-x64_bin.exe"
if(System.getenv("STIRLING_PDF_DESKTOP_UI") == 'true') {
variables=["BROWSER_OPEN=true", "STIRLING_PDF_DESKTOP_UI=true"]
variables=["BROWSER_OPEN=true", "STIRLING_PDF_DESKTOP_UI=true"]
} else {
variables=["BROWSER_OPEN=true"]
variables=["BROWSER_OPEN=true"]
}
jreMinVersion="17"
mutexName="Stirling-PDF"
@@ -284,14 +286,14 @@ configurations.all {
}
dependencies {
if (System.getenv("STIRLING_PDF_DESKTOP_UI") != "false") {
implementation "me.friwi:jcefmaven:127.3.1"
implementation "org.openjfx:javafx-controls:21"
implementation "org.openjfx:javafx-swing:21"
if (System.getenv("STIRLING_PDF_DESKTOP_UI") != "false") {
implementation "me.friwi:jcefmaven:127.3.1"
implementation "org.openjfx:javafx-controls:21"
implementation "org.openjfx:javafx-swing:21"
}
//security updates
implementation "org.springframework:spring-webmvc:6.2.2"
implementation "org.springframework:spring-webmvc:6.2.1"
implementation("io.github.pixee:java-security-toolkit:1.2.1")
@@ -313,10 +315,10 @@ dependencies {
implementation "org.springframework.boot:spring-boot-starter-data-jpa:$springBootVersion"
implementation "org.springframework.boot:spring-boot-starter-oauth2-client:$springBootVersion"
implementation "org.springframework.session:spring-session-core:$springBootVersion"
implementation "org.springframework:spring-jdbc:6.2.2"
implementation "org.springframework.session:spring-session-core:$springBootVersion"
implementation "org.springframework:spring-jdbc:6.2.1"
implementation 'com.unboundid.product.scim2:scim2-sdk-client:2.3.5'
implementation 'com.unboundid.product.scim2:scim2-sdk-client:2.3.5'
// Don't upgrade h2database
runtimeOnly "com.h2database:h2:2.3.232"
runtimeOnly "org.postgresql:postgresql:42.7.4"
@@ -326,7 +328,7 @@ dependencies {
implementation "org.opensaml:opensaml-saml-impl:$openSamlVersion"
}
implementation "org.springframework.security:spring-security-saml2-service-provider:$springSecuritySamlVersion"
// implementation 'org.springframework.security:spring-security-core:$springSecuritySamlVersion'
// implementation 'org.springframework.security:spring-security-core:$springSecuritySamlVersion'
implementation 'com.coveo:saml-client:5.0.0'
@@ -370,8 +372,6 @@ dependencies {
implementation ("org.apache.pdfbox:pdfbox:$pdfboxVersion") {
exclude group: "commons-logging", module: "commons-logging"
}
implementation "org.apache.pdfbox:preflight:$pdfboxVersion"
implementation ("org.apache.pdfbox:xmpbox:$pdfboxVersion") {
exclude group: "commons-logging", module: "commons-logging"
@@ -397,9 +397,9 @@ dependencies {
// https://mvnrepository.com/artifact/com.bucket4j/bucket4j_jdk17
implementation "com.bucket4j:bucket4j_jdk17-core:8.14.0"
implementation "com.fathzer:javaluator:3.0.5"
implementation 'com.vladsch.flexmark:flexmark-html2md-converter:0.64.8'
implementation 'com.vladsch.flexmark:flexmark-html2md-converter:0.64.8'
developmentOnly("org.springframework.boot:spring-boot-devtools:$springBootVersion")
compileOnly "org.projectlombok:lombok:$lombokVersion"
annotationProcessor "org.projectlombok:lombok:$lombokVersion"
@@ -423,13 +423,13 @@ task writeVersion {
}
swaggerhubUpload {
// dependsOn = generateOpenApiDocs // Depends on your task generating Swagger docs
api = "Stirling-PDF" // The name of your API on SwaggerHub
owner = "Frooodle" // Your SwaggerHub username (or organization name)
version = project.version // The version of your API
inputFile = "./SwaggerDoc.json" // The path to your Swagger docs
token = "${System.getenv("SWAGGERHUB_API_KEY")}" // Your SwaggerHub API key, passed as an environment variable
oas = "3.0.0" // The version of the OpenAPI Specification you"re using
//dependsOn generateOpenApiDocs // Depends on your task generating Swagger docs
api "Stirling-PDF" // The name of your API on SwaggerHub
owner "Frooodle" // Your SwaggerHub username (or organization name)
version project.version // The version of your API
inputFile "./SwaggerDoc.json" // The path to your Swagger docs
token "${System.getenv("SWAGGERHUB_API_KEY")}" // Your SwaggerHub API key, passed as an environment variable
oas "3.0.0" // The version of the OpenAPI Specification you"re using
}
jar {
@@ -454,4 +454,4 @@ task printMacVersion {
doLast {
println getMacVersion(project.version.toString())
}
}
}

View File

@@ -2,16 +2,17 @@
# Function to check a single webpage
check_webpage() {
local url=$(echo "$1" | tr -d '\r') # Remove carriage returns
local base_url=$(echo "$2" | tr -d '\r')
local url=$1
local base_url=${2:-"http://localhost:8080"}
local full_url="${base_url}${url}"
local timeout=10
echo -n "Testing $full_url ... "
# Use curl to fetch the page with timeout
response=$(curl -s -w "\n%{http_code}" --max-time $timeout "$full_url")
if [ $? -ne 0 ]; then
echo "FAILED - Connection error or timeout $full_url "
echo "FAILED - Connection error or timeout"
return 1
fi
@@ -26,7 +27,7 @@ check_webpage() {
fi
# Check if response contains HTML
if ! printf '%s' "$BODY" | grep -q "<!DOCTYPE html>\|<html"; then
if ! echo "$BODY" | grep -q "<!DOCTYPE html>\|<html"; then
echo "FAILED - Response is not HTML"
return 1
fi
@@ -45,12 +46,11 @@ test_all_urls() {
echo "Starting webpage tests..."
echo "Base URL: $base_url"
echo "Number of lines: $(wc -l < "$url_file")"
echo "----------------------------------------"
while IFS= read -r url || [ -n "$url" ]; do
# Skip empty lines and comments
[[ -z "$url" || "$url" =~ ^#.*$ ]] && continue
# Skip empty lines
[ -z "$url" ] && continue
((total_count++))
if ! check_webpage "$url" "$base_url"; then
@@ -60,7 +60,7 @@ test_all_urls() {
local end_time=$(date +%s)
local duration=$((end_time - start_time))
echo "----------------------------------------"
echo "Test Summary:"
echo "Total tests: $total_count"
@@ -71,44 +71,18 @@ test_all_urls() {
return $failed_count
}
# Print usage information
usage() {
echo "Usage: $0 [-f url_file] [-b base_url]"
echo "Options:"
echo " -f url_file Path to file containing URLs to test (required)"
echo " -b base_url Base URL to prepend to test URLs (default: http://localhost:8080)"
exit 1
}
# Main execution
main() {
local url_file=""
local base_url="http://localhost:8080"
local script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
local url_file="${script_dir}/webpage_urls.txt"
# Parse command line options
while getopts ":f:b:h" opt; do
case $opt in
f) url_file="$OPTARG" ;;
b) base_url="$OPTARG" ;;
h) usage ;;
\?) echo "Invalid option -$OPTARG" >&2; usage ;;
esac
done
# Check if URL file is provided
if [ -z "$url_file" ]; then
echo "Error: URL file is required"
usage
fi
# Check if URL file exists
if [ ! -f "$url_file" ]; then
echo "Error: URL list file not found: $url_file"
exit 1
fi
# Run tests using the URL list
if test_all_urls "$url_file" "$base_url"; then
if test_all_urls "$url_file"; then
echo "All webpage tests passed!"
exit 0
else

View File

@@ -1,6 +1,6 @@
services:
stirling-pdf:
container_name: Stirling-PDF-Security-Fat-with-login
container_name: Stirling-PDF-Security-Fat
image: stirlingtools/stirling-pdf:latest-fat
deploy:
resources:

View File

@@ -5198,21 +5198,6 @@ BQJJZbt1AhsMBQkCAikAACEJEEEQY6Og/9EZFiEE3d7odhLp+5X1yNkeQRBjo6D/
=KAHr
-----END PGP PUBLIC KEY BLOCK-----
pub 436902AF59EDF60E
uid Sebastian Sampaoli <ssampaoli@equo.dev>
sub D94994D14B55169B
-----BEGIN PGP PUBLIC KEY BLOCK-----
mDMEY4fp+xYJKwYBBAHaRw8BAQdArb04PVwQKvEhtUEmEu7/aASZivOWgEkZBqX0
Tovwvq+0J1NlYmFzdGlhbiBTYW1wYW9saSA8c3NhbXBhb2xpQGVxdW8uZGV2Prg4
BGOH6fsSCisGAQQBl1UBBQEBB0CSPWzZfBjKWyPW+D6RDRLFz5xlO9/30yGD/VhA
EPXybAMBCAeIfQQYFgoAJhYhBB0sfvitoPeUtYx8Y0NpAq9Z7fYOBQJjh+n7AhsM
BQkDwmcAAAoJEENpAq9Z7fYOTMMBAKfZb2ahnfGNBt8Hrbu1j99580a2IaFQddAk
xXZy2unHAPYyfxDLPkbTR7Mm4k8Cva8PCcXotDow4bDLm9rhwVkJ
=Hgs4
-----END PGP PUBLIC KEY BLOCK-----
pub 4989E0E939C2999B
uid Scott Conway <scott@conwayfamily.name>

File diff suppressed because it is too large Load Diff

View File

@@ -75,7 +75,7 @@ def write_readme(progress_list: list[tuple[str, int]]) -> None:
f"![{value}%](https://geps.dev/progress/{value})",
)
with open("README.md", "w", encoding="utf-8", newline="\n") as file:
with open("README.md", "w", encoding="utf-8") as file:
file.writelines(content)
@@ -196,7 +196,7 @@ def compare_files(
)
)
ignore_translation = convert_to_multiline(sort_ignore_translation)
with open(ignore_translation_file, "w", encoding="utf-8", newline="\n") as file:
with open(ignore_translation_file, "w", encoding="utf-8") as file:
file.write(tomlkit.dumps(ignore_translation))
unique_data = list(set(result_list))

View File

@@ -24,6 +24,7 @@ ignore = [
[cs_CZ]
ignore = [
'language.direction',
'pipeline.header',
'text',
]

View File

@@ -1,180 +0,0 @@
package stirling.software.SPDF.controller.api;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageTree;
import org.apache.pdfbox.pdmodel.PDDocumentInformation;
import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotation;
import org.apache.pdfbox.pdmodel.encryption.PDEncryption;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import stirling.software.SPDF.model.api.PDFFile;
import java.io.IOException;
import java.util.*;
@RestController
@RequestMapping("/api/v1/analysis")
@Tag(name = "Analysis", description = "Analysis APIs")
public class AnalysisController {
@PostMapping(value = "/page-count", consumes = "multipart/form-data")
@Operation(summary = "Get PDF page count",
description = "Returns total number of pages in PDF. Input:PDF Output:JSON Type:SISO")
public Map<String, Integer> getPageCount(@ModelAttribute PDFFile file) throws IOException {
try (PDDocument document = Loader.loadPDF(file.getFileInput().getBytes())) {
return Map.of("pageCount", document.getNumberOfPages());
}
}
@PostMapping(value ="/basic-info", consumes = "multipart/form-data")
@Operation(summary = "Get basic PDF information",
description = "Returns page count, version, file size. Input:PDF Output:JSON Type:SISO")
public Map<String, Object> getBasicInfo(@ModelAttribute PDFFile file) throws IOException {
try (PDDocument document = Loader.loadPDF(file.getFileInput().getBytes())) {
Map<String, Object> info = new HashMap<>();
info.put("pageCount", document.getNumberOfPages());
info.put("pdfVersion", document.getVersion());
info.put("fileSize", file.getFileInput().getSize());
return info;
}
}
@PostMapping(value ="/document-properties", consumes = "multipart/form-data")
@Operation(summary = "Get PDF document properties",
description = "Returns title, author, subject, etc. Input:PDF Output:JSON Type:SISO")
public Map<String, String> getDocumentProperties(@ModelAttribute PDFFile file) throws IOException {
try (PDDocument document = Loader.loadPDF(file.getFileInput().getBytes())) {
PDDocumentInformation info = document.getDocumentInformation();
Map<String, String> properties = new HashMap<>();
properties.put("title", info.getTitle());
properties.put("author", info.getAuthor());
properties.put("subject", info.getSubject());
properties.put("keywords", info.getKeywords());
properties.put("creator", info.getCreator());
properties.put("producer", info.getProducer());
properties.put("creationDate", info.getCreationDate().toString());
properties.put("modificationDate", info.getModificationDate().toString());
return properties;
}
}
@PostMapping(value ="/page-dimensions", consumes = "multipart/form-data")
@Operation(summary = "Get page dimensions for all pages",
description = "Returns width and height of each page. Input:PDF Output:JSON Type:SISO")
public List<Map<String, Float>> getPageDimensions(@ModelAttribute PDFFile file) throws IOException {
try (PDDocument document = Loader.loadPDF(file.getFileInput().getBytes())) {
List<Map<String, Float>> dimensions = new ArrayList<>();
PDPageTree pages = document.getPages();
for (PDPage page : pages) {
Map<String, Float> pageDim = new HashMap<>();
pageDim.put("width", page.getBBox().getWidth());
pageDim.put("height", page.getBBox().getHeight());
dimensions.add(pageDim);
}
return dimensions;
}
}
@PostMapping(value ="/form-fields", consumes = "multipart/form-data")
@Operation(summary = "Get form field information",
description = "Returns count and details of form fields. Input:PDF Output:JSON Type:SISO")
public Map<String, Object> getFormFields(@ModelAttribute PDFFile file) throws IOException {
try (PDDocument document = Loader.loadPDF(file.getFileInput().getBytes())) {
Map<String, Object> formInfo = new HashMap<>();
PDAcroForm form = document.getDocumentCatalog().getAcroForm();
if (form != null) {
formInfo.put("fieldCount", form.getFields().size());
formInfo.put("hasXFA", form.hasXFA());
formInfo.put("isSignaturesExist", form.isSignaturesExist());
} else {
formInfo.put("fieldCount", 0);
formInfo.put("hasXFA", false);
formInfo.put("isSignaturesExist", false);
}
return formInfo;
}
}
@PostMapping(value ="/annotation-info", consumes = "multipart/form-data")
@Operation(summary = "Get annotation information",
description = "Returns count and types of annotations. Input:PDF Output:JSON Type:SISO")
public Map<String, Object> getAnnotationInfo(@ModelAttribute PDFFile file) throws IOException {
try (PDDocument document = Loader.loadPDF(file.getFileInput().getBytes())) {
Map<String, Object> annotInfo = new HashMap<>();
int totalAnnotations = 0;
Map<String, Integer> annotationTypes = new HashMap<>();
for (PDPage page : document.getPages()) {
for (PDAnnotation annot : page.getAnnotations()) {
totalAnnotations++;
String subType = annot.getSubtype();
annotationTypes.merge(subType, 1, Integer::sum);
}
}
annotInfo.put("totalCount", totalAnnotations);
annotInfo.put("typeBreakdown", annotationTypes);
return annotInfo;
}
}
@PostMapping(value ="/font-info", consumes = "multipart/form-data")
@Operation(summary = "Get font information",
description = "Returns list of fonts used in the document. Input:PDF Output:JSON Type:SISO")
public Map<String, Object> getFontInfo(@ModelAttribute PDFFile file) throws IOException {
try (PDDocument document = Loader.loadPDF(file.getFileInput().getBytes())) {
Map<String, Object> fontInfo = new HashMap<>();
Set<String> fontNames = new HashSet<>();
for (PDPage page : document.getPages()) {
for (COSName font : page.getResources().getFontNames()) {
fontNames.add(font.getName());
}
}
fontInfo.put("fontCount", fontNames.size());
fontInfo.put("fonts", fontNames);
return fontInfo;
}
}
@PostMapping(value ="/security-info", consumes = "multipart/form-data")
@Operation(summary = "Get security information",
description = "Returns encryption and permission details. Input:PDF Output:JSON Type:SISO")
public Map<String, Object> getSecurityInfo(@ModelAttribute PDFFile file) throws IOException {
try (PDDocument document = Loader.loadPDF(file.getFileInput().getBytes())) {
Map<String, Object> securityInfo = new HashMap<>();
PDEncryption encryption = document.getEncryption();
if (encryption != null) {
securityInfo.put("isEncrypted", true);
securityInfo.put("keyLength", encryption.getLength());
// Get permissions
Map<String, Boolean> permissions = new HashMap<>();
permissions.put("canPrint", document.getCurrentAccessPermission().canPrint());
permissions.put("canModify", document.getCurrentAccessPermission().canModify());
permissions.put("canExtractContent", document.getCurrentAccessPermission().canExtractContent());
permissions.put("canModifyAnnotations", document.getCurrentAccessPermission().canModifyAnnotations());
securityInfo.put("permissions", permissions);
} else {
securityInfo.put("isEncrypted", false);
}
return securityInfo;
}
}
}

View File

@@ -39,7 +39,7 @@ public class MetricsAggregatorService {
if (method == null || uri == null) {
return;
}
if (!"GET".equals(method) && !"POST".equals(method)) {
if (!method.equals("GET") && !method.equals("POST")) {
return;
}
// Skip URIs that are 2 characters or shorter

File diff suppressed because it is too large Load Diff

View File

@@ -452,9 +452,9 @@ home.MarkdownToPDF.title=Markdown zu PDF
home.MarkdownToPDF.desc=Konvertiert jede Markdown-Datei zu PDF
MarkdownToPDF.tags=markup,web-content,transformation,konvertieren
home.PDFToMarkdown.title=PDF zu Markdown
home.PDFToMarkdown.desc=Konvertiert jedes PDF in Markdown
PDFToMarkdown.tags=markup,web inhalt,transformation,konvertieren,md
home.PDFToMarkdown.title=PDF to Markdown
home.PDFToMarkdown.desc=Converts any PDF to Markdown
PDFToMarkdown.tags=markup,web-content,transformation,convert,md
home.getPdfInfo.title=Alle Informationen anzeigen
home.getPdfInfo.desc=Erfasst alle möglichen Informationen in einer PDF
@@ -650,9 +650,9 @@ MarkdownToPDF.credit=Verwendet WeasyPrint
#pdf-to-markdown
PDFToMarkdown.title=PDF zu Markdown
PDFToMarkdown.header=PDF zu Markdown
PDFToMarkdown.submit=Konvertieren
PDFToMarkdown.title=PDF To Markdown
PDFToMarkdown.header=PDF To Markdown
PDFToMarkdown.submit=Convert
#url-to-pdf

View File

@@ -82,7 +82,7 @@ pages=Pages
loading=Chargement...
addToDoc=Ajouter au Document
reset=Réinitialiser
apply=Appliquer
apply=Apply
legal.privacy=Politique de Confidentialité
legal.terms=Conditions Générales
@@ -239,20 +239,20 @@ database.creationDate=Date de Création
database.fileSize=Taille du Fichier
database.deleteBackupFile=Supprimer le fichier de sauvegarde
database.importBackupFile=Importer le fichier de sauvegarde
database.createBackupFile=Créer un fichier de sauvegarde
database.createBackupFile=Create Backup File
database.downloadBackupFile=Télécharger le fichier de sauvegarde
database.info_1=Lors de l'importation des données, il est crucial de garantir la structure correcte. Si vous n'êtes pas sûr de ce que vous faites, sollicitez un avis et un soutien d'un professionnel. Une erreur dans la structure peut entraîner des dysfonctionnements de l'application, allant jusqu'à l'incapacité totale d'exécuter l'application.
database.info_2=Le nom du fichier ne fait pas de différence lors de l'upload. Il sera renommé ultérieurement selon le format backup_user_yyyyMMddHHmm.sql, assurant ainsi une convention de nommage cohérente.
database.submit=Importer la sauvegarde
database.importIntoDatabaseSuccessed=Importation dans la base de données réussie
database.backupCreated=Sauvegarde de la base de donnée réussie
database.fileNotFound=Fichier introuvable
database.backupCreated=Database backup successful
database.fileNotFound=File not Found
database.fileNullOrEmpty=Fichier ne peut pas être null ou vide
database.failedImportFile=Échec de l'imporation du fichier
database.notSupported=Cette fonctionnalité n'est pas supportée avec votre base de donnée
database.failedImportFile=Failed Import File
database.notSupported=This function is not available for your database connection.
session.expired=Votre session a expiré. Veuillez recharger la page et réessayer.
session.refreshPage=Rafraichir la page
session.refreshPage=Refresh Page
#############
# HOME-PAGE #
@@ -479,8 +479,8 @@ home.autoRedact.title=Caviarder automatiquement
home.autoRedact.desc=Caviardez automatiquement les informations sensibles d'un PDF.
autoRedact.tags=caviarder,redact,auto
home.redact.title=Rédaction manuelle
home.redact.desc=Rédiger un PDF en fonction de texte sélectionné, formes dessinées et/ou des pages sélectionnées.
home.redact.title=Manual Redaction
home.redact.desc=Redacts a PDF based on selected text, drawn shapes and/or selected page(s)
redact.tags=Redact,Hide,black out,black,marker,hidden,manual
home.tableExtraxt.title=PDF en CSV
@@ -589,30 +589,30 @@ autoRedact.convertPDFToImageLabel=Convertir un PDF en PDF-Image (utilisé pour s
autoRedact.submitButton=Caviarder
#redact
redact.title=Rédaction manuelle
redact.header=Rédaction manuelle
redact.submit=Rédiger
redact.textBasedRedaction=Rédaction en fonction de texte
redact.pageBasedRedaction=Rédaction en fonction de pages
redact.convertPDFToImageLabel=Convertir en PDF-Image (pour supprimer le texte derrière le rectangle)
redact.title=Manual Redaction
redact.header=Manual Redaction
redact.submit=Redact
redact.textBasedRedaction=Text based Redaction
redact.pageBasedRedaction=Page-based Redaction
redact.convertPDFToImageLabel=Convert PDF to PDF-Image (Used to remove text behind the box)
redact.pageRedactionNumbers.title=Pages
redact.pageRedactionNumbers.placeholder=(ex: 1,2,8 ou 4,7,12-16 ou 2n-1)
redact.redactionColor.title=Couleur
redact.export=Exporter
redact.upload=Téléverser
redact.boxRedaction=Dessiner le rectangle à rédiger
redact.pageRedactionNumbers.placeholder=(e.g. 1,2,8 or 4,7,12-16 or 2n-1)
redact.redactionColor.title=Redaction Color
redact.export=Export
redact.upload=Upload
redact.boxRedaction=Box draw redaction
redact.zoom=Zoom
redact.zoomIn=Zoom avant
redact.zoomOut=Zoom arrière
redact.nextPage=Page suivante
redact.previousPage=Page précédente
redact.zoomIn=Zoom in
redact.zoomOut=Zoom out
redact.nextPage=Next Page
redact.previousPage=Previous Page
redact.toggleSidebar=Toggle Sidebar
redact.showThumbnails=Afficher les miniatures
redact.showDocumentOutline=Montrer les contours du document (double-click pour agrandir/réduire tous les éléments)
redact.showAttatchments=Montrer les éléments attachés
redact.showLayers=Montrer les calques (double-click pour réinitialiser tous les calques à l'état par défaut)
redact.colourPicker=Sélection de couleur
redact.findCurrentOutlineItem=Trouver l'élément de contour courrant
redact.showThumbnails=Show Thumbnails
redact.showDocumentOutline=Show Document Outline (double-click to expand/collapse all items)
redact.showAttatchments=Show Attachments
redact.showLayers=Show Layers (double-click to reset all layers to the default state)
redact.colourPicker=Colour Picker
redact.findCurrentOutlineItem=Find current outline item
#showJS
showJS.title=Afficher le JavaScript
@@ -864,13 +864,13 @@ sign.save=Enregistrer le sceau
sign.personalSigs=Sceaux personnels
sign.sharedSigs=Sceaux partagés
sign.noSavedSigs=Aucun sceau enregistré trouvé
sign.addToAll=Ajouter à toutes les pages
sign.delete=Supprimer
sign.first=Première page
sign.last=Dernière page
sign.next=Page suivante
sign.previous=Page précédente
sign.maintainRatio=Conserver les proportions
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
sign.maintainRatio=Toggle maintain aspect ratio
#repair
@@ -1011,14 +1011,14 @@ multiTool.undo=Undo
multiTool.redo=Redo
#decrypt
decrypt.passwordPrompt=Ce fichier est protégé par un mot de passe. Veuillez saisir le mot de passe :
decrypt.cancelled=Operation annulée pour le PDF: {0}
decrypt.noPassword=Pas de mot de passe fourni pour le PDF chiffré : {0}
decrypt.invalidPassword=Veuillez réessayer avec le bon mot de passe
decrypt.invalidPasswordHeader=Mauvais mot de passe ou chiffrement non supporté pour le PDF : {0}
decrypt.unexpectedError=Une erreur est survenue lors de traitement du fichier. Veuillez essayer de nouveau.
decrypt.serverError=Erreur du serveur lors du déchiffrement : {0}
decrypt.success=Fichier déchiffré avec succès.
decrypt.passwordPrompt=This file is password-protected. Please enter the password:
decrypt.cancelled=Operation cancelled for PDF: {0}
decrypt.noPassword=No password provided for encrypted PDF: {0}
decrypt.invalidPassword=Please try again with the correct password.
decrypt.invalidPasswordHeader=Incorrect password or unsupported encryption for PDF: {0}
decrypt.unexpectedError=There was an error processing the file. Please try again.
decrypt.serverError=Server error while decrypting: {0}
decrypt.success=File decrypted successfully.
#multiTool-advert
multiTool-advert.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 !

File diff suppressed because it is too large Load Diff

View File

@@ -331,7 +331,7 @@ changeMetadata.tags=標題,作者,日期,建立,時間,出版商,製作人,統
home.fileToPDF.title=檔案轉 PDF
home.fileToPDF.desc=將幾乎所有格式轉換為 PDFDOCX、PNG、XLS、PPT、TXT 等等)
fileToPDF.tags=轉換,格式,文件,圖片,投影片,文字,轉換,office,docs,Word,Excel,PowerPoint
fileToPDF.tags=轉換,格式,文件,圖片,幻燈片,文字,轉換,辦公室,文件,Word,Excel,PowerPoint
home.ocr.title=OCR / 清理掃描
home.ocr.desc=清理掃描並從 PDF 中的影像中偵測文字並重新新增為文字。
@@ -344,15 +344,15 @@ extractImages.tags=圖片,照片,儲存,存檔,壓縮檔,捕獲,抓取
home.pdfToPDFA.title=PDF 轉 PDF/A
home.pdfToPDFA.desc=將 PDF 轉換為長期儲存的 PDF/A
pdfToPDFA.tags=存檔,長期,標準,轉換,儲存,
pdfToPDFA.tags=存檔,長期,標準,轉換,儲存,
home.PDFToWord.title=PDF 轉 Word
home.PDFToWord.desc=將 PDF 轉換為 Word 格式DOC、DOCX 和 ODT
PDFToWord.tags=doc,docx,odt,word,轉換,格式,轉檔,office,微軟,docfile
PDFToWord.tags=doc,docx,odt,word,轉換,格式,轉換,辦公室,微軟,docfile
home.PDFToPresentation.title=PDF 轉簡報
home.PDFToPresentation.desc=將 PDF 轉換為簡報格式PPT、PPTX 和 ODP
PDFToPresentation.tags=投影片,展示,office,微軟
PDFToPresentation.tags=幻燈片,展示,辦公室,微軟
home.PDFToText.title=PDF 轉 RTF文字
home.PDFToText.desc=將 PDF 轉換為文字或 RTF 格式
@@ -365,7 +365,7 @@ PDFToHTML.tags=網頁內容,瀏覽器友善
home.PDFToXML.title=PDF 轉 XML
home.PDFToXML.desc=將 PDF 轉換為 XML 格式
PDFToXML.tags=資料提取,結構化內容,互操作,轉換,轉
PDFToXML.tags=資料提取,結構化內容,互操作,轉換,轉
home.ScannerImageSplit.title=偵測/分割掃描照片
home.ScannerImageSplit.desc=從照片/PDF 中分割多張照片
@@ -381,7 +381,7 @@ flatten.tags=靜態,停用,非互動,簡化
home.repair.title=修復
home.repair.desc=嘗試修復損壞/破損的 PDF
repair.tags=修復,恢復,修正,復
repair.tags=修復,恢復,修正,
home.removeBlanks.title=移除空白頁面
home.removeBlanks.desc=偵測並從文件中移除空白頁面
@@ -437,7 +437,7 @@ autoSplitPDF.tags=基於 QR Code,分離,掃描區段,組織
home.sanitizePdf.title=清理
home.sanitizePdf.desc=從 PDF 檔案中移除指令碼和其他元素
sanitizePdf.tags=清理,安全,無害,移除威脅
sanitizePdf.tags=清理,安全,安全,移除威脅
home.URLToPDF.title=網址/網站轉 PDF
home.URLToPDF.desc=將任何 http(s) 網址轉換為 PDF
@@ -445,16 +445,16 @@ URLToPDF.tags=網頁捕獲,儲存頁面,網頁轉文件,存檔
home.HTMLToPDF.title=HTML 轉 PDF
home.HTMLToPDF.desc=將任何 HTML 檔案或壓縮檔轉換為 PDF
HTMLToPDF.tags=標記,網頁內容,轉換,轉
HTMLToPDF.tags=標記,網頁內容,轉換,轉
home.MarkdownToPDF.title=Markdown 轉 PDF
home.MarkdownToPDF.desc=將任何 Markdown 檔案轉換為 PDF
MarkdownToPDF.tags=標記,網頁內容,轉換,轉檔,md
MarkdownToPDF.tags=標記,網頁內容,轉換,轉
home.PDFToMarkdown.title=PDF Markdown
home.PDFToMarkdown.desc=將任何 PDF 轉換為 Markdown 檔案
PDFToMarkdown.tags=標記語言,網頁內容,轉換,轉檔,md
home.PDFToMarkdown.title=PDF to Markdown
home.PDFToMarkdown.desc=Converts any PDF to Markdown
PDFToMarkdown.tags=markup,web-content,transformation,convert,md
home.getPdfInfo.title=取得 PDF 的所有資訊
home.getPdfInfo.desc=取得 PDF 的所有可能資訊
@@ -477,11 +477,11 @@ showJS.tags=JS
home.autoRedact.title=自動塗黑
home.autoRedact.desc=根據輸入的文字自動塗黑 PDF 中的文字
autoRedact.tags=,隱藏,塗黑,黑色,標記,遮蔽
autoRedact.tags=,隱藏,塗黑,黑色,標記,隱藏
home.redact.title=手動塗黑
home.redact.desc=依據選取的文字、繪製的形狀和選取的頁面塗黑 PDF
redact.tags=,隱藏,黑,黑色,標記,遮蔽,手動
redact.tags=,隱藏,黑色標記,黑色,標記,隱藏,手動
home.tableExtraxt.title=PDF 轉 CSV
home.tableExtraxt.desc=從 PDF 中提取表格並將其轉換為 CSV
@@ -942,7 +942,7 @@ compress.title=壓縮
compress.header=壓縮 PDF
compress.credit=此服務使用 qpdf 進行 PDF 壓縮/最佳化。
compress.selectText.1=手動模式 - 從 1 到 5
compress.selectText.1.1=在最佳化等級 6 到 9 時,除了一般 PDF 壓縮外,圖片解析度也會降低以進一步減少檔案大小。較高的壓縮等級會進行更高強度的圖片壓縮(最多可壓縮到原始大小的 50%),以達到更高的壓縮率,但可能會影響圖片品質。
compress.selectText.1.1=In optimization levels 6 to 9, in addition to general PDF compression, image resolution is scaled down to further reduce file size. Higher levels result in stronger image compression (up to 50% of the original size), achieving greater size reduction but with potential quality loss in images.
compress.selectText.2=最佳化等級:
compress.selectText.3=4對於含有文字的影像來說結果很糟
compress.selectText.4=自動模式 - 自動調整品質使 PDF 達到指定的檔案大小

View File

@@ -376,132 +376,6 @@
"moduleLicense": "UnboundID SCIM2 SDK Free Use License",
"moduleLicenseUrl": "https://github.com/pingidentity/scim2"
},
{
"moduleName": "com.vladsch.flexmark:flexmark",
"moduleVersion": "0.64.8",
"moduleLicense": "BSD 2-Clause License",
"moduleLicenseUrl": "http://opensource.org/licenses/BSD-2-Clause"
},
{
"moduleName": "com.vladsch.flexmark:flexmark-ext-emoji",
"moduleVersion": "0.64.8",
"moduleLicense": "BSD 2-Clause License",
"moduleLicenseUrl": "http://opensource.org/licenses/BSD-2-Clause"
},
{
"moduleName": "com.vladsch.flexmark:flexmark-ext-gfm-strikethrough",
"moduleVersion": "0.64.8",
"moduleLicense": "BSD 2-Clause License",
"moduleLicenseUrl": "http://opensource.org/licenses/BSD-2-Clause"
},
{
"moduleName": "com.vladsch.flexmark:flexmark-ext-ins",
"moduleVersion": "0.64.8",
"moduleLicense": "BSD 2-Clause License",
"moduleLicenseUrl": "http://opensource.org/licenses/BSD-2-Clause"
},
{
"moduleName": "com.vladsch.flexmark:flexmark-ext-superscript",
"moduleVersion": "0.64.8",
"moduleLicense": "BSD 2-Clause License",
"moduleLicenseUrl": "http://opensource.org/licenses/BSD-2-Clause"
},
{
"moduleName": "com.vladsch.flexmark:flexmark-ext-tables",
"moduleVersion": "0.64.8",
"moduleLicense": "BSD 2-Clause License",
"moduleLicenseUrl": "http://opensource.org/licenses/BSD-2-Clause"
},
{
"moduleName": "com.vladsch.flexmark:flexmark-ext-wikilink",
"moduleVersion": "0.64.8",
"moduleLicense": "BSD 2-Clause License",
"moduleLicenseUrl": "http://opensource.org/licenses/BSD-2-Clause"
},
{
"moduleName": "com.vladsch.flexmark:flexmark-html2md-converter",
"moduleVersion": "0.64.8",
"moduleLicense": "BSD 2-Clause License",
"moduleLicenseUrl": "http://opensource.org/licenses/BSD-2-Clause"
},
{
"moduleName": "com.vladsch.flexmark:flexmark-jira-converter",
"moduleVersion": "0.64.8",
"moduleLicense": "BSD 2-Clause License",
"moduleLicenseUrl": "http://opensource.org/licenses/BSD-2-Clause"
},
{
"moduleName": "com.vladsch.flexmark:flexmark-util",
"moduleVersion": "0.64.8",
"moduleLicense": "BSD 2-Clause License",
"moduleLicenseUrl": "http://opensource.org/licenses/BSD-2-Clause"
},
{
"moduleName": "com.vladsch.flexmark:flexmark-util-ast",
"moduleVersion": "0.64.8",
"moduleLicense": "BSD 2-Clause License",
"moduleLicenseUrl": "http://opensource.org/licenses/BSD-2-Clause"
},
{
"moduleName": "com.vladsch.flexmark:flexmark-util-builder",
"moduleVersion": "0.64.8",
"moduleLicense": "BSD 2-Clause License",
"moduleLicenseUrl": "http://opensource.org/licenses/BSD-2-Clause"
},
{
"moduleName": "com.vladsch.flexmark:flexmark-util-collection",
"moduleVersion": "0.64.8",
"moduleLicense": "BSD 2-Clause License",
"moduleLicenseUrl": "http://opensource.org/licenses/BSD-2-Clause"
},
{
"moduleName": "com.vladsch.flexmark:flexmark-util-data",
"moduleVersion": "0.64.8",
"moduleLicense": "BSD 2-Clause License",
"moduleLicenseUrl": "http://opensource.org/licenses/BSD-2-Clause"
},
{
"moduleName": "com.vladsch.flexmark:flexmark-util-dependency",
"moduleVersion": "0.64.8",
"moduleLicense": "BSD 2-Clause License",
"moduleLicenseUrl": "http://opensource.org/licenses/BSD-2-Clause"
},
{
"moduleName": "com.vladsch.flexmark:flexmark-util-format",
"moduleVersion": "0.64.8",
"moduleLicense": "BSD 2-Clause License",
"moduleLicenseUrl": "http://opensource.org/licenses/BSD-2-Clause"
},
{
"moduleName": "com.vladsch.flexmark:flexmark-util-html",
"moduleVersion": "0.64.8",
"moduleLicense": "BSD 2-Clause License",
"moduleLicenseUrl": "http://opensource.org/licenses/BSD-2-Clause"
},
{
"moduleName": "com.vladsch.flexmark:flexmark-util-misc",
"moduleVersion": "0.64.8",
"moduleLicense": "BSD 2-Clause License",
"moduleLicenseUrl": "http://opensource.org/licenses/BSD-2-Clause"
},
{
"moduleName": "com.vladsch.flexmark:flexmark-util-options",
"moduleVersion": "0.64.8",
"moduleLicense": "BSD 2-Clause License",
"moduleLicenseUrl": "http://opensource.org/licenses/BSD-2-Clause"
},
{
"moduleName": "com.vladsch.flexmark:flexmark-util-sequence",
"moduleVersion": "0.64.8",
"moduleLicense": "BSD 2-Clause License",
"moduleLicenseUrl": "http://opensource.org/licenses/BSD-2-Clause"
},
{
"moduleName": "com.vladsch.flexmark:flexmark-util-visitor",
"moduleVersion": "0.64.8",
"moduleLicense": "BSD 2-Clause License",
"moduleLicenseUrl": "http://opensource.org/licenses/BSD-2-Clause"
},
{
"moduleName": "com.zaxxer:HikariCP",
"moduleUrl": "https://github.com/brettwooldridge/HikariCP",
@@ -873,13 +747,6 @@
"moduleLicense": "Apache-2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
},
{
"moduleName": "org.apache.pdfbox:preflight",
"moduleUrl": "https://pdfbox.apache.org",
"moduleVersion": "3.0.3",
"moduleLicense": "Apache-2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
},
{
"moduleName": "org.apache.pdfbox:xmpbox",
"moduleUrl": "https://pdfbox.apache.org",
@@ -951,15 +818,15 @@
},
{
"moduleName": "org.bouncycastle:bcpkix-jdk18on",
"moduleUrl": "https://www.bouncycastle.org/download/bouncy-castle-java/",
"moduleVersion": "1.80",
"moduleUrl": "https://www.bouncycastle.org/java.html",
"moduleVersion": "1.79",
"moduleLicense": "Bouncy Castle Licence",
"moduleLicenseUrl": "https://www.bouncycastle.org/licence.html"
},
{
"moduleName": "org.bouncycastle:bcprov-jdk18on",
"moduleUrl": "https://www.bouncycastle.org/download/bouncy-castle-java/",
"moduleVersion": "1.80",
"moduleUrl": "https://www.bouncycastle.org/java.html",
"moduleVersion": "1.79",
"moduleLicense": "Bouncy Castle Licence",
"moduleLicenseUrl": "https://www.bouncycastle.org/licence.html"
},
@@ -972,8 +839,8 @@
},
{
"moduleName": "org.bouncycastle:bcutil-jdk18on",
"moduleUrl": "https://www.bouncycastle.org/download/bouncy-castle-java/",
"moduleVersion": "1.80",
"moduleUrl": "https://www.bouncycastle.org/java.html",
"moduleVersion": "1.79",
"moduleLicense": "Bouncy Castle Licence",
"moduleLicenseUrl": "https://www.bouncycastle.org/licence.html"
},
@@ -1241,20 +1108,6 @@
"moduleLicense": "Apache License 2.0",
"moduleLicenseUrl": "https://repository.jboss.org/licenses/apache-2.0.txt"
},
{
"moduleName": "org.jetbrains:annotations",
"moduleUrl": "https://github.com/JetBrains/java-annotations",
"moduleVersion": "24.0.1",
"moduleLicense": "The Apache Software License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
},
{
"moduleName": "org.jsoup:jsoup",
"moduleUrl": "https://jsoup.org/",
"moduleVersion": "1.15.4",
"moduleLicense": "The MIT License",
"moduleLicenseUrl": "https://jsoup.org/license"
},
{
"moduleName": "org.latencyutils:LatencyUtils",
"moduleUrl": "http://latencyutils.github.io/LatencyUtils/",
@@ -1665,7 +1518,7 @@
{
"moduleName": "org.springframework:spring-jdbc",
"moduleUrl": "https://github.com/spring-projects/spring-framework",
"moduleVersion": "6.2.2",
"moduleVersion": "6.2.1",
"moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
},
@@ -1693,7 +1546,7 @@
{
"moduleName": "org.springframework:spring-webmvc",
"moduleUrl": "https://github.com/spring-projects/spring-framework",
"moduleVersion": "6.2.2",
"moduleVersion": "6.2.1",
"moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
},

View File

@@ -126,10 +126,6 @@ html {
border-color: rgba(6, 114, 197, 0.82) !important;
}
#pdfToImageBtn:hover .btn-tooltip {
display: block;
}
#redactionsPaletteContainer {
height: var(--toolButton-height);
width: var(--toolButton-width);

View File

@@ -32,7 +32,7 @@ select#font-select option {
margin-left: -2.2rem;
}
.draggable-buttons-box > button {
.draggable-buttons-box>button {
z-index: 4;
background-color: rgba(13, 110, 253, 0.1);
flex: 1 1 auto;
@@ -40,6 +40,7 @@ select#font-select option {
max-width: 4rem;
}
.rotation-handle {
width: 20px;
height: 20px;
@@ -162,76 +163,3 @@ select#font-select option {
.small-file-container-saved:hover .drag-icon {
display: flex;
}
/* The container must be positioned relative: */
.custom-select {
position: relative;
font-family: inherit;
}
.custom-select select {
display: none; /*hide original SELECT element: */
}
.select-selected {
background-color: inherit;
line-height: 30px;
font-size: 30px;
border-radius: 3rem !important;
}
/* Style the arrow inside the select element: */
.select-selected:after {
position: absolute;
content: "";
top: 50%;
right: 10px;
translate: 0 -50%;
width: 0;
height: 0;
border: 6px solid transparent;
border-color: #fff transparent transparent transparent;
}
/* Point the arrow upwards when the select box is open (active): */
.select-selected.select-arrow-active:after {
border-color: transparent transparent #fff transparent;
translate: 0 -75%;
}
/* style the items (options), including the selected item: */
.select-items div,
.select-selected {
color: inherit;
padding: 8px 16px;
cursor: pointer;
}
.select-items div {
border: 1px solid transparent;
border-color: transparent transparent transparent transparent;
line-height: 30px;
font-size: 30px;
}
/* Style items (options): */
.select-items {
position: absolute;
background-color: inherit;
top: 100%;
left: 0;
right: 0;
z-index: 101;
border: inherit;
}
/* Hide the items when the select box is closed: */
.select-hide {
display: none;
}
.select-items div:hover,
.same-as-selected {
background-color: rgba(54, 54, 54, 0.1);
}

View File

@@ -8,21 +8,21 @@ window.goToFirstOrLastPage = goToFirstOrLastPage;
let currentPreviewSrc = null;
function toggleSignatureView() {
const gridView = document.getElementById("gridView");
const listView = document.getElementById("listView");
const gridText = document.querySelector(".grid-view-text");
const listText = document.querySelector(".list-view-text");
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";
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";
gridView.style.display = 'block';
listView.style.display = 'none';
gridText.style.display = 'inline';
listText.style.display = 'none';
}
}
@@ -30,204 +30,63 @@ function previewSignature(element) {
const src = element.dataset.src;
currentPreviewSrc = src;
const filename = element.querySelector(".signature-list-name").textContent;
const filename = element.querySelector('.signature-list-name').textContent;
const previewImage = document.getElementById("previewImage");
const previewFileName = document.getElementById("previewFileName");
const previewImage = document.getElementById('previewImage');
const previewFileName = document.getElementById('previewFileName');
previewImage.src = src;
previewFileName.textContent = filename;
const modal = new bootstrap.Modal(
document.getElementById("signaturePreview")
);
const modal = new bootstrap.Modal(document.getElementById('signaturePreview'));
modal.show();
}
function addSignatureFromPreview() {
if (currentPreviewSrc) {
DraggableUtils.createDraggableCanvasFromUrl(currentPreviewSrc);
bootstrap.Modal.getInstance(
document.getElementById("signaturePreview")
).hide();
bootstrap.Modal.getInstance(document.getElementById('signaturePreview')).hide();
}
}
let originalFileName = "";
document
.querySelector("input[name=pdf-upload]")
.addEventListener("change", async (event) => {
const fileInput = event.target;
fileInput.addEventListener("file-input-change", async (e) => {
const { allFiles } = e.detail;
if (allFiles && allFiles.length > 0) {
const file = allFiles[0];
originalFileName = file.name.replace(/\.[^/.]+$/, "");
const pdfData = await file.arrayBuffer();
pdfjsLib.GlobalWorkerOptions.workerSrc =
"./pdfjs-legacy/pdf.worker.mjs";
const pdfDoc = await pdfjsLib.getDocument({ data: pdfData }).promise;
await DraggableUtils.renderPage(pdfDoc, 0);
let originalFileName = '';
document.querySelector('input[name=pdf-upload]').addEventListener('change', async (event) => {
const fileInput = event.target;
fileInput.addEventListener('file-input-change', async (e) => {
const {allFiles} = e.detail;
if (allFiles && allFiles.length > 0) {
const file = allFiles[0];
originalFileName = file.name.replace(/\.[^/.]+$/, '');
const pdfData = await file.arrayBuffer();
pdfjsLib.GlobalWorkerOptions.workerSrc = './pdfjs-legacy/pdf.worker.mjs';
const pdfDoc = await pdfjsLib.getDocument({data: pdfData}).promise;
await DraggableUtils.renderPage(pdfDoc, 0);
document.querySelectorAll(".show-on-file-selected").forEach((el) => {
el.style.cssText = "";
});
}
});
});
document.addEventListener("DOMContentLoaded", () => {
document.querySelectorAll(".show-on-file-selected").forEach((el) => {
el.style.cssText = "display:none !important";
});
document
.querySelectorAll(".small-file-container-saved img ")
.forEach((img) => {
img.addEventListener("dragstart", (e) => {
e.dataTransfer.setData("fileUrl", img.src);
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';
});
document.querySelectorAll('.small-file-container-saved img ').forEach((img) => {
img.addEventListener('dragstart', (e) => {
e.dataTransfer.setData('fileUrl', img.src);
});
document.addEventListener("keydown", (e) => {
if (e.key === "Delete") {
});
document.addEventListener('keydown', (e) => {
if (e.key === 'Delete') {
DraggableUtils.deleteDraggableCanvas(DraggableUtils.getLastInteracted());
}
});
addCustomSelect();
});
function addCustomSelect() {
let customSelectElementContainer =
document.getElementById("signFontSelection");
let originalSelectElement =
customSelectElementContainer.querySelector("select");
let optionsCount = originalSelectElement.length;
let selectedItem = createAndStyleSelectedItem();
customSelectElementContainer.appendChild(selectedItem);
let customSelectionsOptionsContainer = createCustomOptionsContainer();
createAndAddCustomOptions();
customSelectElementContainer.appendChild(customSelectionsOptionsContainer);
selectedItem.addEventListener("click", function (e) {
/* When the select box is clicked, close any other select boxes,
and open/close the current select box: */
e.stopPropagation();
closeAllSelect(this);
this.nextSibling.classList.toggle("select-hide");
this.classList.toggle("select-arrow-active");
});
function createAndAddCustomOptions() {
for (let j = 0; j < optionsCount; j++) {
/* For each option in the original select element,
create a new DIV that will act as an option item: */
let customOptionItem = createAndStyleCustomOption(j);
customOptionItem.addEventListener("click", onCustomOptionClick);
customSelectionsOptionsContainer.appendChild(customOptionItem);
}
}
function createCustomOptionsContainer() {
let customSelectionsOptionsContainer = document.createElement("DIV");
customSelectionsOptionsContainer.setAttribute(
"class",
"select-items select-hide"
);
return customSelectionsOptionsContainer;
}
function createAndStyleSelectedItem() {
let selectedItem = document.createElement("DIV");
selectedItem.setAttribute("class", "select-selected");
selectedItem.innerHTML =
originalSelectElement.options[
originalSelectElement.selectedIndex
].innerHTML;
selectedItem.style.fontFamily = window.getComputedStyle(
originalSelectElement.options[originalSelectElement.selectedIndex]
).fontFamily;
return selectedItem;
}
function onCustomOptionClick(e) {
/* When an item is clicked, update the original select box,
and the selected item: */
let selectElement =
this.parentNode.parentNode.getElementsByTagName("select")[0];
let optionsCount = selectElement.length;
let currentlySelectedCustomOption = this.parentNode.previousSibling;
for (let i = 0; i < optionsCount; i++) {
if (selectElement.options[i].innerHTML == this.innerHTML) {
selectElement.selectedIndex = i;
currentlySelectedCustomOption.innerHTML = this.innerHTML;
currentlySelectedCustomOption.style.fontFamily = this.style.fontFamily;
let previouslySelectedOption =
this.parentNode.getElementsByClassName("same-as-selected");
if (previouslySelectedOption && previouslySelectedOption.length > 0)
previouslySelectedOption[0].classList.remove("same-as-selected");
this.classList.add("same-as-selected");
break;
}
}
currentlySelectedCustomOption.click();
}
function createAndStyleCustomOption(j) {
let customOptionItem = document.createElement("DIV");
customOptionItem.innerHTML = originalSelectElement.options[j].innerHTML;
customOptionItem.classList.add(originalSelectElement.options[j].className);
customOptionItem.style.fontFamily = window.getComputedStyle(
originalSelectElement.options[j]
).fontFamily;
if (j == originalSelectElement.selectedIndex)
customOptionItem.classList.add("same-as-selected");
return customOptionItem;
}
function closeAllSelect(element) {
/* A function that will close all select boxes in the document,
except the current select box: */
let allSelectedOptions = document.getElementsByClassName("select-selected");
let allSelectedOptionsCount = allSelectedOptions.length;
let indicesOfContainersToHide = [];
for (let i = 0; i < allSelectedOptionsCount; i++) {
if (element == allSelectedOptions[i]) {
indicesOfContainersToHide.push(i);
} else {
allSelectedOptions[i].classList.remove("select-arrow-active");
}
}
hideOptionsContainers(indicesOfContainersToHide);
}
/* If the user clicks anywhere outside the select box,
then close all select boxes: */
document.addEventListener("click", closeAllSelect);
function hideOptionsContainers(containersIndices) {
let allOptionsContainers = document.getElementsByClassName("select-items");
let allSelectionListsContainerCount = allOptionsContainers.length;
for (let i = 0; i < allSelectionListsContainerCount; i++) {
if (containersIndices.indexOf(i)) {
allOptionsContainers[i].classList.add("select-hide");
}
}
}
}
const imageUpload = document.querySelector("input[name=image-upload]");
imageUpload.addEventListener("change", (e) => {
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();
@@ -238,11 +97,11 @@ imageUpload.addEventListener("change", (e) => {
}
});
const signaturePadCanvas = document.getElementById("drawing-pad-canvas");
const signaturePadCanvas = document.getElementById('drawing-pad-canvas');
const signaturePad = new SignaturePad(signaturePadCanvas, {
minWidth: 1,
maxWidth: 2,
penColor: "black",
penColor: 'black',
});
function addDraggableFromPad() {
@@ -254,7 +113,7 @@ function addDraggableFromPad() {
}
function getCroppedCanvasDataUrl(canvas) {
let originalCtx = canvas.getContext("2d");
let originalCtx = canvas.getContext('2d');
let originalWidth = canvas.width;
let originalHeight = canvas.height;
let imageData = originalCtx.getImageData(0, 0, originalWidth, originalHeight);
@@ -270,8 +129,7 @@ function getCroppedCanvasDataUrl(canvas) {
for (y = 0; y < originalHeight; y++) {
for (x = 0; x < originalWidth; x++) {
currentPixelColorValueIndex = (y * originalWidth + x) * 4;
let currentPixelAlphaValue =
imageData.data[currentPixelColorValueIndex + 3];
let currentPixelAlphaValue = imageData.data[currentPixelColorValueIndex + 3];
if (currentPixelAlphaValue > 0) {
if (minX > x) minX = x;
if (maxX < x) maxX = x;
@@ -284,15 +142,10 @@ function getCroppedCanvasDataUrl(canvas) {
let croppedWidth = maxX - minX;
let croppedHeight = maxY - minY;
if (croppedWidth < 0 || croppedHeight < 0) return null;
let cuttedImageData = originalCtx.getImageData(
minX,
minY,
croppedWidth,
croppedHeight
);
let cuttedImageData = originalCtx.getImageData(minX, minY, croppedWidth, croppedHeight);
let croppedCanvas = document.createElement("canvas"),
croppedCtx = croppedCanvas.getContext("2d");
let croppedCanvas = document.createElement('canvas'),
croppedCtx = croppedCanvas.getContext('2d');
croppedCanvas.width = croppedWidth;
croppedCanvas.height = croppedHeight;
@@ -305,13 +158,9 @@ 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);
signaturePadCanvas.width = signaturePadCanvas.offsetWidth * ratio * additionalFactor;
signaturePadCanvas.height = signaturePadCanvas.offsetHeight * ratio * additionalFactor;
signaturePadCanvas.getContext('2d').scale(ratio * additionalFactor, ratio * additionalFactor);
signaturePad.clear();
}
@@ -325,12 +174,12 @@ new IntersectionObserver((entries, observer) => {
new ResizeObserver(resizeCanvas).observe(signaturePadCanvas);
function addDraggableFromText() {
const sigText = document.getElementById("sigText").value;
const font = document.querySelector("select[name=font]").value;
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");
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
ctx.font = `${fontSize}px ${font}`;
const textWidth = ctx.measureText(sigText).width;
const textHeight = fontSize;
@@ -341,7 +190,7 @@ function addDraggableFromText() {
canvas.height = paragraphs.length * textHeight * 1.35; // for tails
ctx.font = `${fontSize}px ${font}`;
ctx.textBaseline = "top";
ctx.textBaseline = 'top';
let y = 0;
@@ -363,8 +212,8 @@ async function goToFirstOrLastPage(page) {
}
}
document.getElementById("download-pdf").addEventListener("click", async () => {
const downloadButton = document.getElementById("download-pdf");
document.getElementById('download-pdf').addEventListener('click', async () => {
const downloadButton = document.getElementById('download-pdf');
const originalContent = downloadButton.innerHTML;
downloadButton.disabled = true;
@@ -375,13 +224,13 @@ document.getElementById("download-pdf").addEventListener("click", async () => {
try {
const modifiedPdf = await DraggableUtils.getOverlayedPdfDocument();
const modifiedPdfBytes = await modifiedPdf.save();
const blob = new Blob([modifiedPdfBytes], { type: "application/pdf" });
const link = document.createElement("a");
const blob = new Blob([modifiedPdfBytes], {type: 'application/pdf'});
const link = document.createElement('a');
link.href = URL.createObjectURL(blob);
link.download = originalFileName + "_signed.pdf";
link.download = originalFileName + '_signed.pdf';
link.click();
} catch (error) {
console.error("Error downloading PDF:", error);
console.error('Error downloading PDF:', error);
} finally {
downloadButton.disabled = false;
downloadButton.innerHTML = originalContent;

View File

@@ -4919,26 +4919,6 @@ dialog :link {
left: 3px;
top: var(--toolbar-height);
}
#toolbarViewerRight > div.splitToolbarButton .btn-tooltip {
bottom: unset !important;
left: 100%;
}
#man-shape-redact {
position: relative;
}
#man-shape-redact .btn-tooltip {
bottom: 100%;
left: -5px;
white-space: normal !important;
}
#redactionsPaletteContainer .btn-tooltip {
white-space: normal !important;
hyphens: auto;
}
}
@media (max-width: 885px) {

View File

@@ -5,7 +5,6 @@
</head>
<body>
<th:block th:insert="~{fragments/common :: game}"></th:block>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>

View File

@@ -15,8 +15,7 @@
#font-select option[value="[[${font.name}]]"] {
font-family: "[[${font.name}]]",
cursive
!important;
cursive;
}
</style>
</th:block>
@@ -134,12 +133,10 @@
<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>
<div id="signFontSelection" class="custom-select form-control">
<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>
<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>

View File

@@ -1,24 +1,5 @@
#!/bin/bash
# Default value for the Boolean parameter
VERIFICATION=${1:-false} # Default is "false" if no parameter is passed
# Find project root by locating build.gradle
find_root() {
local dir="$PWD"
while [[ "$dir" != "/" ]]; do
if [[ -f "$dir/build.gradle" ]]; then
echo "$dir"
return 0
fi
dir="$(dirname "$dir")"
done
echo "Error: build.gradle not found" >&2
exit 1
}
PROJECT_ROOT=$(find_root)
# Function to check the health of the service with a timeout of 80 seconds
check_health() {
local service_name=$1
@@ -83,14 +64,6 @@ run_tests() {
main() {
SECONDS=0
cd "$PROJECT_ROOT"
# Run the gradlew build command and check if it fails
if [[ "$VERIFICATION" == "true" ]]; then
./gradlew clean dependencies buildEnvironment spotlessApply --write-verification-metadata sha256 --refresh-dependencies help
./gradlew clean dependencies buildEnvironment spotlessApply --write-verification-metadata sha256,pgp --refresh-keys --export-keys --refresh-dependencies help
fi
export DOCKER_ENABLE_SECURITY=false
# Run the gradlew build command and check if it fails
if ! ./gradlew clean build; then
@@ -107,14 +80,13 @@ main() {
run_tests "Stirling-PDF-Ultra-Lite" "./exampleYmlFiles/docker-compose-latest-ultra-lite.yml"
echo "Testing webpage accessibility..."
cd "testing"
if ./test_webpages.sh -f webpage_urls.txt -b http://localhost:8080; then
passed_tests+=("Webpage-Accessibility-lite")
if ./cucumber/test_webpages.sh; then
passed_tests+=("Webpage-Accessibility")
else
failed_tests+=("Webpage-Accessibility-lite")
echo "Webpage accessibility lite tests failed"
failed_tests+=("Webpage-Accessibility")
echo "Webpage accessibility tests failed"
fi
cd "$PROJECT_ROOT"
docker-compose -f "./exampleYmlFiles/docker-compose-latest-ultra-lite.yml" down
@@ -141,38 +113,20 @@ main() {
# run_tests "Stirling-PDF-Security" "./exampleYmlFiles/docker-compose-latest-security.yml"
# docker-compose -f "./exampleYmlFiles/docker-compose-latest-security.yml" down
run_tests "Stirling-PDF-Security-Fat" "./exampleYmlFiles/docker-compose-latest-fat-security.yml"
echo "Testing webpage accessibility..."
cd "testing"
if ./test_webpages.sh -f webpage_urls_full.txt -b http://localhost:8080; then
passed_tests+=("Webpage-Accessibility-full")
else
failed_tests+=("Webpage-Accessibility-full")
echo "Webpage accessibility full tests failed"
fi
cd "$PROJECT_ROOT"
docker-compose -f "./exampleYmlFiles/docker-compose-latest-fat-security.yml" down
run_tests "Stirling-PDF-Security-Fat-with-login" "./exampleYmlFiles/test_cicd.yml"
run_tests "Stirling-PDF-Security-Fat" "./exampleYmlFiles/test_cicd.yml"
if [ $? -eq 0 ]; then
cd "testing/cucumber"
cd cucumber
if python -m behave; then
passed_tests+=("Stirling-PDF-Regression")
else
failed_tests+=("Stirling-PDF-Regression")
echo "Printing docker logs of failed regression"
docker logs "Stirling-PDF-Security-Fat-with-login"
docker logs "Stirling-PDF-Security-Fat"
echo "Printed docker logs of failed regression"
fi
cd "$PROJECT_ROOT"
cd ..
fi
docker-compose -f "./exampleYmlFiles/test_cicd.yml" down
docker-compose -f "./exampleYmlFiles/docker-compose-latest-fat-security.yml" down
# Report results
echo "All tests completed in $SECONDS seconds."

View File

@@ -1,66 +0,0 @@
/
/add-image
/add-page-numbers
/add-password
/add-watermark
/adjust-contrast
/auto-redact
/auto-rename
/auto-split-pdf
/cert-sign
/change-metadata
/change-permissions
/compare
/compress-pdf
/crop
/extract-image-scans
/extract-images
/extract-page
/file-to-pdf
/flatten
/get-info-on-pdf
/html-to-pdf
/img-to-pdf
/licenses
/markdown-to-pdf
/merge-pdfs
/multi-page-layout
/multi-tool
/ocr-pdf
/overlay-pdf
/pdf-organizer
/pdf-to-csv
/pdf-to-html
/pdf-to-img
/pdf-to-markdown
/pdf-to-presentation
/pdf-to-single-page
/pdf-to-text
/pdf-to-word
/pdf-to-xml
/pipeline
/redact
/releases
/remove-annotations
/remove-blanks
/remove-cert-sign
/remove-image-pdf
/remove-pages
/remove-password
/repair
/replace-and-invert-color-pdf
/rotate-pdf
/sanitize-pdf
/scale-pages
/show-javascript
/sign
/split-by-size-or-count
/split-pdf-by-chapters
/split-pdf-by-sections
/split-pdfs
/stamp
/url-to-pdf
/validate-signature
/view-pdf
/swagger-ui/index.html