Compare commits
33 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e382d254ee | ||
|
|
507d21772d | ||
|
|
5bf050d77f | ||
|
|
976caeb79d | ||
|
|
2d3611fd00 | ||
|
|
118de1789a | ||
|
|
b56d54a35a | ||
|
|
b9bfcd59cd | ||
|
|
f8adc0f101 | ||
|
|
69d4b52b06 | ||
|
|
5e3612a9b0 | ||
|
|
b3a4597ad1 | ||
|
|
dc46172deb | ||
|
|
04696dc2aa | ||
|
|
b37457b41d | ||
|
|
46c53a9c88 | ||
|
|
5f610da671 | ||
|
|
bacc502f07 | ||
|
|
883281e4df | ||
|
|
9e8c16f313 | ||
|
|
4a7df3fd3f | ||
|
|
f725f92e1a | ||
|
|
9d2c23f612 | ||
|
|
637446fab8 | ||
|
|
196c0f4a39 | ||
|
|
f9b92eec71 | ||
|
|
3be6b0659e | ||
|
|
00e1f74f48 | ||
|
|
6ae2fddd48 | ||
|
|
c5cffdcacb | ||
|
|
d9eda14521 | ||
|
|
b865f4379f | ||
|
|
4294dc54b1 |
2
.github/CODEOWNERS
vendored
@@ -1,2 +1,2 @@
|
||||
# All PRs to V1 must be approved by Frooodle
|
||||
* @Frooodle @reecebrowne @Ludy87 @DarioGii
|
||||
* @Frooodle @reecebrowne @Ludy87 @DarioGii @ConnorYoh
|
||||
|
||||
5
.github/workflows/PR-Demo-Comment.yml
vendored
@@ -27,7 +27,8 @@ jobs:
|
||||
github.event.comment.user.login == 'LaserKaspar' ||
|
||||
github.event.comment.user.login == 'sbplat' ||
|
||||
github.event.comment.user.login == 'reecebrowne' ||
|
||||
github.event.comment.user.login == 'DarioGii'
|
||||
github.event.comment.user.login == 'DarioGii' ||
|
||||
github.event.comment.user.login == 'ConnorYoh'
|
||||
)
|
||||
outputs:
|
||||
pr_number: ${{ steps.get-pr.outputs.pr_number }}
|
||||
@@ -93,7 +94,7 @@ jobs:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@7a6d8a8234af8eb26422e24e3006232cccaa061b # v4.6.0
|
||||
uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 # v4.7.0
|
||||
with:
|
||||
java-version: "17"
|
||||
distribution: "temurin"
|
||||
|
||||
43
.github/workflows/build.yml
vendored
@@ -32,17 +32,11 @@ jobs:
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
|
||||
- name: Set up JDK ${{ matrix.jdk-version }}
|
||||
uses: actions/setup-java@7a6d8a8234af8eb26422e24e3006232cccaa061b # v4.6.0
|
||||
uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 # v4.7.0
|
||||
with:
|
||||
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:
|
||||
@@ -64,6 +58,35 @@ jobs:
|
||||
build/reports/problems/
|
||||
retention-days: 3
|
||||
|
||||
check-licence:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 # v4.7.0
|
||||
with:
|
||||
java-version: "17"
|
||||
distribution: "adopt"
|
||||
|
||||
- name: check the licenses for compatibility
|
||||
run: ./gradlew clean checkLicense
|
||||
|
||||
- name: FAILED - check the licenses for compatibility
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
|
||||
with:
|
||||
name: dependencies-without-allowed-license.json
|
||||
path: |
|
||||
build/reports/dependency-license/dependencies-without-allowed-license.json
|
||||
retention-days: 3
|
||||
|
||||
docker-compose-tests:
|
||||
# if: github.event_name == 'push' && github.ref == 'refs/heads/main' ||
|
||||
# (github.event_name == 'pull_request' &&
|
||||
@@ -91,7 +114,7 @@ jobs:
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
|
||||
- name: Set up Java 17
|
||||
uses: actions/setup-java@7a6d8a8234af8eb26422e24e3006232cccaa061b # v4.6.0
|
||||
uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 # v4.7.0
|
||||
with:
|
||||
java-version: "17"
|
||||
distribution: "adopt"
|
||||
@@ -105,7 +128,7 @@ jobs:
|
||||
sudo chmod +x /usr/local/bin/docker-compose
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0
|
||||
uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0
|
||||
with:
|
||||
python-version: "3.12"
|
||||
cache: 'pip' # caching pip dependencies
|
||||
@@ -118,4 +141,4 @@ jobs:
|
||||
run: |
|
||||
chmod +x ./testing/test_webpages.sh
|
||||
chmod +x ./testing/test.sh
|
||||
./testing/test.sh "${{ github.event.pull_request.user.login == 'dependabot[bot]' }}"
|
||||
./testing/test.sh
|
||||
|
||||
2
.github/workflows/check_properties.yml
vendored
@@ -26,7 +26,7 @@ jobs:
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0
|
||||
uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0
|
||||
with:
|
||||
python-version: "3.12"
|
||||
|
||||
|
||||
21
.github/workflows/licenses-update.yml
vendored
@@ -24,24 +24,33 @@ jobs:
|
||||
|
||||
- name: Generate GitHub App Token
|
||||
id: generate-token
|
||||
uses: actions/create-github-app-token@c1a285145b9d317df6ced56c09f525b5c2b6f755 # v1.11.1
|
||||
uses: actions/create-github-app-token@136412a57a7081aa63c935a2cc2918f76c34f514 # v1.11.2
|
||||
with:
|
||||
app-id: ${{ secrets.GH_APP_ID }}
|
||||
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
|
||||
|
||||
|
||||
- name: Check out code
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@7a6d8a8234af8eb26422e24e3006232cccaa061b # v4.6.0
|
||||
uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 # v4.7.0
|
||||
with:
|
||||
java-version: "17"
|
||||
distribution: "adopt"
|
||||
|
||||
- uses: gradle/actions/setup-gradle@0bdd871935719febd78681f197cd39af5b6e16a6 # v4.2.2
|
||||
- uses: gradle/actions/setup-gradle@94baf225fe0a508e581a564467443d0e2379123b # v4.3.0
|
||||
|
||||
- name: Run Gradle Command
|
||||
run: ./gradlew clean generateLicenseReport
|
||||
- name: check the licenses for compatibility
|
||||
run: ./gradlew clean checkLicense
|
||||
|
||||
- name: FAILED - check the licenses for compatibility
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
|
||||
with:
|
||||
name: dependencies-without-allowed-license.json
|
||||
path: |
|
||||
build/reports/dependency-license/dependencies-without-allowed-license.json
|
||||
retention-days: 3
|
||||
|
||||
- name: Move and Rename License File
|
||||
run: |
|
||||
|
||||
2
.github/workflows/manage-label.yml
vendored
@@ -23,7 +23,7 @@ jobs:
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
|
||||
- name: Run Labeler
|
||||
uses: crazy-max/ghaction-github-labeler@b54af0c25861143e7c8813d7cbbf46d2c341680c # v5.1.0
|
||||
uses: crazy-max/ghaction-github-labeler@31674a3852a9074f2086abcf1c53839d466a47e7 # v5.2.0
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
yaml-file: .github/labels.yml
|
||||
|
||||
8
.github/workflows/multiOSReleases.yml
vendored
@@ -58,12 +58,12 @@ jobs:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
|
||||
- name: Set up JDK 21
|
||||
uses: actions/setup-java@7a6d8a8234af8eb26422e24e3006232cccaa061b # v4.6.0
|
||||
uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 # v4.7.0
|
||||
with:
|
||||
java-version: "21"
|
||||
distribution: "temurin"
|
||||
|
||||
- uses: gradle/actions/setup-gradle@0bdd871935719febd78681f197cd39af5b6e16a6 # v4.2.2
|
||||
- uses: gradle/actions/setup-gradle@94baf225fe0a508e581a564467443d0e2379123b # v4.3.0
|
||||
with:
|
||||
gradle-version: 8.12
|
||||
|
||||
@@ -146,12 +146,12 @@ jobs:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
|
||||
- name: Set up JDK 21
|
||||
uses: actions/setup-java@7a6d8a8234af8eb26422e24e3006232cccaa061b # v4.6.0
|
||||
uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 # v4.7.0
|
||||
with:
|
||||
java-version: "21"
|
||||
distribution: "temurin"
|
||||
|
||||
- uses: gradle/actions/setup-gradle@0bdd871935719febd78681f197cd39af5b6e16a6 # v4.2.2
|
||||
- uses: gradle/actions/setup-gradle@94baf225fe0a508e581a564467443d0e2379123b # v4.3.0
|
||||
with:
|
||||
gradle-version: 8.12
|
||||
|
||||
|
||||
4
.github/workflows/pre_commit.yml
vendored
@@ -22,7 +22,7 @@ jobs:
|
||||
|
||||
- name: Generate GitHub App Token
|
||||
id: generate-token
|
||||
uses: actions/create-github-app-token@c1a285145b9d317df6ced56c09f525b5c2b6f755 # v1.11.1
|
||||
uses: actions/create-github-app-token@136412a57a7081aa63c935a2cc2918f76c34f514 # v1.11.2
|
||||
with:
|
||||
app-id: ${{ secrets.GH_APP_ID }}
|
||||
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
|
||||
@@ -42,7 +42,7 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0
|
||||
uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0
|
||||
with:
|
||||
python-version: 3.12
|
||||
cache: 'pip' # caching pip dependencies
|
||||
|
||||
4
.github/workflows/push-docker.yml
vendored
@@ -25,12 +25,12 @@ jobs:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@7a6d8a8234af8eb26422e24e3006232cccaa061b # v4.6.0
|
||||
uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 # v4.7.0
|
||||
with:
|
||||
java-version: "17"
|
||||
distribution: "temurin"
|
||||
|
||||
- uses: gradle/actions/setup-gradle@0bdd871935719febd78681f197cd39af5b6e16a6 # v4.2.2
|
||||
- uses: gradle/actions/setup-gradle@94baf225fe0a508e581a564467443d0e2379123b # v4.3.0
|
||||
with:
|
||||
gradle-version: 8.12
|
||||
|
||||
|
||||
4
.github/workflows/releaseArtifacts.yml
vendored
@@ -30,12 +30,12 @@ jobs:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@7a6d8a8234af8eb26422e24e3006232cccaa061b # v4.6.0
|
||||
uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 # v4.7.0
|
||||
with:
|
||||
java-version: "17"
|
||||
distribution: "temurin"
|
||||
|
||||
- uses: gradle/actions/setup-gradle@0bdd871935719febd78681f197cd39af5b6e16a6 # v4.2.2
|
||||
- uses: gradle/actions/setup-gradle@94baf225fe0a508e581a564467443d0e2379123b # v4.3.0
|
||||
with:
|
||||
gradle-version: 8.12
|
||||
|
||||
|
||||
2
.github/workflows/scorecards.yml
vendored
@@ -74,6 +74,6 @@ jobs:
|
||||
|
||||
# Upload the results to GitHub's code scanning dashboard.
|
||||
- name: "Upload to code-scanning"
|
||||
uses: github/codeql-action/upload-sarif@17a820bf2e43b47be2c72b39cc905417bc1ab6d0 # v3.28.6
|
||||
uses: github/codeql-action/upload-sarif@dd746615b3b9d728a6a37ca2045b68ca76d4841a # v3.28.8
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
|
||||
64
.github/workflows/sonarqube.yml
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
branches: [ "main" ]
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
pull-requests: read
|
||||
actions: read
|
||||
name: Run Sonarqube
|
||||
jobs:
|
||||
sonarqube:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
||||
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 # v4.2.1
|
||||
with:
|
||||
java-version: '17'
|
||||
distribution: 'temurin'
|
||||
|
||||
- name: Build and analyze with Gradle
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
||||
DOCKER_ENABLE_SECURITY: true
|
||||
STIRLING_PDF_DESKTOP_UI: true
|
||||
run: |
|
||||
./gradlew clean build sonar \
|
||||
-Dsonar.projectKey=Stirling-Tools_Stirling-PDF \
|
||||
-Dsonar.organization=stirling-tools \
|
||||
-Dsonar.host.url=https://sonarcloud.io \
|
||||
-Dsonar.log.level=DEBUG \
|
||||
--info
|
||||
|
||||
- name: Upload Problems Report on Failure
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
with:
|
||||
name: gradle-problems-report
|
||||
path: build/reports/problems/problems-report.html
|
||||
retention-days: 7
|
||||
|
||||
- name: Upload Sonar Logs on Failure
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
with:
|
||||
name: sonar-logs
|
||||
path: |
|
||||
.scannerwork/report-task.txt
|
||||
build/sonar/
|
||||
retention-days: 7
|
||||
4
.github/workflows/swagger.yml
vendored
@@ -21,12 +21,12 @@ jobs:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@7a6d8a8234af8eb26422e24e3006232cccaa061b # v4.6.0
|
||||
uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 # v4.7.0
|
||||
with:
|
||||
java-version: "17"
|
||||
distribution: "temurin"
|
||||
|
||||
- uses: gradle/actions/setup-gradle@0bdd871935719febd78681f197cd39af5b6e16a6 # v4.2.2
|
||||
- uses: gradle/actions/setup-gradle@94baf225fe0a508e581a564467443d0e2379123b # v4.3.0
|
||||
|
||||
- name: Generate Swagger documentation
|
||||
run: ./gradlew generateOpenApiDocs
|
||||
|
||||
35
.github/workflows/sync_files.yml
vendored
@@ -8,8 +8,6 @@ on:
|
||||
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"
|
||||
@@ -32,7 +30,7 @@ jobs:
|
||||
|
||||
- name: Generate GitHub App Token
|
||||
id: generate-token
|
||||
uses: actions/create-github-app-token@c1a285145b9d317df6ced56c09f525b5c2b6f755 # v1.11.1
|
||||
uses: actions/create-github-app-token@136412a57a7081aa63c935a2cc2918f76c34f514 # v1.11.2
|
||||
with:
|
||||
app-id: ${{ secrets.GH_APP_ID }}
|
||||
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
|
||||
@@ -65,7 +63,7 @@ jobs:
|
||||
|
||||
- name: Generate GitHub App Token
|
||||
id: generate-token
|
||||
uses: actions/create-github-app-token@c1a285145b9d317df6ced56c09f525b5c2b6f755 # v1.11.1
|
||||
uses: actions/create-github-app-token@136412a57a7081aa63c935a2cc2918f76c34f514 # v1.11.2
|
||||
with:
|
||||
app-id: ${{ vars.GH_APP_ID }}
|
||||
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
|
||||
@@ -73,7 +71,7 @@ jobs:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0
|
||||
uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0
|
||||
with:
|
||||
python-version: "3.12"
|
||||
cache: 'pip' # caching pip dependencies
|
||||
@@ -104,22 +102,6 @@ jobs:
|
||||
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"
|
||||
|
||||
- name: Create Pull Request
|
||||
uses: peter-evans/create-pull-request@67ccf781d68cd99b580ae25a5c18a1cc84ffff1f # v7.0.6
|
||||
with:
|
||||
@@ -129,11 +111,11 @@ jobs:
|
||||
author: ${{ needs.read_bot_entries.outputs.committer }}
|
||||
signoff: true
|
||||
branch: sync_readme
|
||||
title: ":globe_with_meridians: Sync Translations + Update README Progress Table + Update Verification Metadata"
|
||||
title: ":globe_with_meridians: Sync Translations + Update README 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:
|
||||
This Pull Request was automatically generated to synchronize updates to translation files 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`.
|
||||
@@ -145,14 +127,9 @@ jobs:
|
||||
- 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.
|
||||
|
||||
---
|
||||
|
||||
@@ -166,5 +143,3 @@ jobs:
|
||||
add-paths: |
|
||||
README.md
|
||||
src/main/resources/messages_*.properties
|
||||
gradle/verification-keyring.keys
|
||||
gradle/verification-metadata.xml
|
||||
|
||||
2
.github/workflows/testdriver.yml
vendored
@@ -20,7 +20,7 @@ jobs:
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@7a6d8a8234af8eb26422e24e3006232cccaa061b # v4.6.0
|
||||
uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 # v4.7.0
|
||||
with:
|
||||
java-version: '17'
|
||||
distribution: 'temurin'
|
||||
|
||||
1
.gitignore
vendored
@@ -140,6 +140,7 @@ venv.bak/
|
||||
# VS Code
|
||||
/.vscode/**/*
|
||||
!/.vscode/settings.json
|
||||
!/.vscode/extensions.json
|
||||
|
||||
# IntelliJ IDEA
|
||||
.idea/
|
||||
|
||||
24
.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"elagil.pre-commit-helper", // Support for pre-commit hooks to enforce code quality
|
||||
"josevseb.google-java-format-for-vs-code", // Google Java code formatter to follow the Google Java Style Guide
|
||||
"ms-python.black-formatter", // Python code formatter using Black
|
||||
"ms-python.flake8", // Flake8 linter for Python to enforce code quality
|
||||
"ms-python.python", // Official Microsoft Python extension with IntelliSense, debugging, and Jupyter support
|
||||
// "ms-vscode-remote.remote-containers", // Support for remote development with containers (Docker, Dev Containers)
|
||||
// "ms-vscode-remote.vscode-remote-extensionpack", // Remote Development Pack for SSH, WSL, and Containers
|
||||
"Oracle.oracle-java", // Oracle Java extension with additional features for Java development
|
||||
"redhat.java", // Java support by Red Hat with IntelliSense, debugging, and code navigation
|
||||
"shengchen.vscode-checkstyle", // Checkstyle integration for Java code quality checks
|
||||
"streetsidesoftware.code-spell-checker", // Spell checker for code to avoid typos
|
||||
"vmware.vscode-boot-dev-pack", // Developer tools for Spring Boot by VMware
|
||||
"vmware.vscode-spring-boot", // Spring Boot tools by VMware for enhanced Spring development
|
||||
"vscjava.vscode-gradle", // Gradle extension for build and automation support
|
||||
"vscjava.vscode-java-debug", // Debugging support for Java projects
|
||||
"vscjava.vscode-java-dependency", // Java dependency management within VS Code
|
||||
"vscjava.vscode-java-pack", // Java Extension Pack with essential Java tools for VS Code
|
||||
"vscjava.vscode-java-test", // Java test framework for running and debugging tests in VS Code
|
||||
"vscjava.vscode-spring-boot-dashboard", // Spring Boot dashboard for managing and visualizing Spring Boot applications
|
||||
"vscjava.vscode-spring-initializr" // Support for Spring Initializr to create new Spring projects
|
||||
]
|
||||
}
|
||||
@@ -39,6 +39,16 @@ Stirling-PDF is built using:
|
||||
2. Install Docker and JDK17 if not already installed.
|
||||
|
||||
3. Install a recommended Java IDE such as Eclipse, IntelliJ, or VSCode
|
||||
1. Only VSCode
|
||||
1. Open VS Code.
|
||||
2. When prompted, install the recommended extensions.
|
||||
3. Alternatively, open the command palette (`Ctrl + Shift + P` or `Cmd + Shift + P` on macOS) and run:
|
||||
|
||||
```sh
|
||||
Extensions: Show Recommended Extensions
|
||||
```
|
||||
|
||||
4. Install the required extensions from the list.
|
||||
|
||||
4. Lombok Setup
|
||||
Stirling-PDF uses Lombok to reduce boilerplate code. Some IDEs, like Eclipse, don't support Lombok out of the box. To set up Lombok in your development environment:
|
||||
@@ -575,42 +585,3 @@ In your Thymeleaf templates, use the `#{key}` syntax to reference the new transl
|
||||
```
|
||||
|
||||
Remember, never hard-code text in your templates or Java code. Always use translation keys to ensure proper localization.
|
||||
|
||||
|
||||
## Managing Dependencies
|
||||
|
||||
When adding new dependencies or updating existing ones in Stirling-PDF, follow these steps to ensure proper verification and security:
|
||||
|
||||
1. Update the dependency in `build.gradle`:
|
||||
```groovy
|
||||
dependencies {
|
||||
// Add or update your dependency
|
||||
implementation "com.example:new-library:1.2.3"
|
||||
}
|
||||
```
|
||||
|
||||
2. Generate new verification metadata and keys:
|
||||
```bash
|
||||
# Generate verification metadata with signatures and checksums
|
||||
./gradlew clean dependencies buildEnvironment spotlessApply --write-verification-metadata sha256,pgp
|
||||
|
||||
# Export the .keys file
|
||||
./gradlew --export-keys
|
||||
```
|
||||
|
||||
3. Files to commit:
|
||||
- `build.gradle` - Your dependency changes
|
||||
- `gradle/verification-metadata.xml` - Contains verification rules and checksums
|
||||
- `gradle/verification-keyring.keys` - Contains PGP keys in text format
|
||||
|
||||
4. Verify the build works with the new verification:
|
||||
```bash
|
||||
./gradlew build
|
||||
```
|
||||
|
||||
5. Before committing, check:
|
||||
- Verify any new BOM files are properly handled in verification metadata
|
||||
- Review the changes in `verification-metadata.xml` to ensure they match your dependency updates
|
||||
|
||||
This ensures dependencies are properly verified and secure while maintaining transparency in the repository.
|
||||
|
||||
|
||||
@@ -18,9 +18,7 @@ Any SVG flags are fine; most of the current ones were sourced from [here](https:
|
||||
For example, to add Polish, you would add:
|
||||
|
||||
```html
|
||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="pl_PL">
|
||||
<img src="images/flags/pl.svg" alt="icon" width="20" height="15"> Polski
|
||||
</a>
|
||||
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'pl_PL')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="pl_PL"> <img th:src="@{'/images/flags/pl.svg'}" alt="icon" width="20" height="15"> Polski</a>
|
||||
```
|
||||
|
||||
The `data-bs-language-code` is the code used to reference the file in the next step.
|
||||
|
||||
@@ -117,14 +117,14 @@ Stirling-PDF currently supports 39 languages!
|
||||
|
||||
| Language | Progress |
|
||||
| -------------------------------------------- | -------------------------------------- |
|
||||
| Arabic (العربية) (ar_AR) |  |
|
||||
| Arabic (العربية) (ar_AR) |  |
|
||||
| Azerbaijani (Azərbaycan Dili) (az_AZ) |  |
|
||||
| Basque (Euskara) (eu_ES) |  |
|
||||
| Bulgarian (Български) (bg_BG) |  |
|
||||
| Catalan (Català) (ca_CA) |  |
|
||||
| Croatian (Hrvatski) (hr_HR) |  |
|
||||
| Czech (Česky) (cs_CZ) |  |
|
||||
| Danish (Dansk) (da_DK) |  |
|
||||
| Danish (Dansk) (da_DK) |  |
|
||||
| Dutch (Nederlands) (nl_NL) |  |
|
||||
| English (English) (en_GB) |  |
|
||||
| English (US) (en_US) |  |
|
||||
@@ -133,7 +133,7 @@ Stirling-PDF currently supports 39 languages!
|
||||
| Greek (Ελληνικά) (el_GR) |  |
|
||||
| Hindi (हिंदी) (hi_IN) |  |
|
||||
| Hungarian (Magyar) (hu_HU) |  |
|
||||
| Indonesian (Bahasa Indonesia) (id_ID) |  |
|
||||
| Indonesian (Bahasa Indonesia) (id_ID) |  |
|
||||
| Irish (Gaeilge) (ga_IE) |  |
|
||||
| Italian (Italiano) (it_IT) |  |
|
||||
| Japanese (日本語) (ja_JP) |  |
|
||||
@@ -155,7 +155,7 @@ Stirling-PDF currently supports 39 languages!
|
||||
| Tibetan (བོད་ཡིག་) (zh_BO) |  |
|
||||
| Traditional Chinese (繁體中文) (zh_TW) |  |
|
||||
| Turkish (Türkçe) (tr_TR) |  |
|
||||
| Ukrainian (Українська) (uk_UA) |  |
|
||||
| Ukrainian (Українська) (uk_UA) |  |
|
||||
| Vietnamese (Tiếng Việt) (vi_VN) |  |
|
||||
|
||||
|
||||
|
||||
164
allowed-licenses.json
Normal file
@@ -0,0 +1,164 @@
|
||||
{
|
||||
"allowedLicenses": [
|
||||
{
|
||||
"moduleName": ".*",
|
||||
"moduleLicense": "BSD License"
|
||||
},
|
||||
{
|
||||
"moduleName": ".*",
|
||||
"moduleLicense": "The BSD License"
|
||||
},
|
||||
{
|
||||
"moduleName": ".*",
|
||||
"moduleLicense": "BSD-2-Clause"
|
||||
},
|
||||
{
|
||||
"moduleName": ".*",
|
||||
"moduleLicense": "BSD 2-Clause License"
|
||||
},
|
||||
{
|
||||
"moduleName": ".*",
|
||||
"moduleLicense": "The 2-Clause BSD License"
|
||||
},
|
||||
{
|
||||
"moduleName": ".*",
|
||||
"moduleLicense": "BSD-3-Clause"
|
||||
},
|
||||
{
|
||||
"moduleName": ".*",
|
||||
"moduleLicense": "The BSD 3-Clause License (BSD3)"
|
||||
},
|
||||
{
|
||||
"moduleName": ".*",
|
||||
"moduleLicense": "BSD-4 License"
|
||||
},
|
||||
{
|
||||
"moduleName": ".*",
|
||||
"moduleLicense": "MIT"
|
||||
},
|
||||
{
|
||||
"moduleName": ".*",
|
||||
"moduleLicense": "MIT License"
|
||||
},
|
||||
{
|
||||
"moduleName": ".*",
|
||||
"moduleLicense": "The MIT License"
|
||||
},
|
||||
{
|
||||
"moduleName": "com.github.jai-imageio:jai-imageio-core",
|
||||
"moduleLicense": "LICENSE.txt"
|
||||
},
|
||||
{
|
||||
"moduleName": "com.github.jai-imageio:jai-imageio-jpeg2000",
|
||||
"moduleLicense": "LICENSE-JJ2000.txt, LICENSE-Sun.txt"
|
||||
},
|
||||
{
|
||||
"moduleName": ".*",
|
||||
"moduleLicense": "Apache 2"
|
||||
},
|
||||
{
|
||||
"moduleName": ".*",
|
||||
"moduleLicense": "Apache 2.0"
|
||||
},
|
||||
{
|
||||
"moduleName": ".*",
|
||||
"moduleLicense": "Apache-2.0"
|
||||
},
|
||||
{
|
||||
"moduleName": ".*",
|
||||
"moduleLicense": "Apache-2.0 License"
|
||||
},
|
||||
{
|
||||
"moduleName": ".*",
|
||||
"moduleLicense": "Apache License 2.0"
|
||||
},
|
||||
{
|
||||
"moduleName": ".*",
|
||||
"moduleLicense": "Apache License Version 2.0"
|
||||
},
|
||||
{
|
||||
"moduleName": ".*",
|
||||
"moduleLicense": "Apache License, Version 2.0"
|
||||
},
|
||||
{
|
||||
"moduleName": ".*",
|
||||
"moduleLicense": "The Apache License, Version 2.0"
|
||||
},
|
||||
{
|
||||
"moduleName": ".*",
|
||||
"moduleLicense": "The Apache Software License, Version 2.0"
|
||||
},
|
||||
{
|
||||
"moduleName": "com.nimbusds:oauth2-oidc-sdk",
|
||||
"moduleLicense": "\"Apache License, version 2.0\";link=\"https://www.apache.org/licenses/LICENSE-2.0.html\""
|
||||
},
|
||||
{
|
||||
"moduleName": ".*",
|
||||
"moduleLicense": "MPL 2.0"
|
||||
},
|
||||
{
|
||||
"moduleName": ".*",
|
||||
"moduleLicense": "UnboundID SCIM2 SDK Free Use License"
|
||||
},
|
||||
{
|
||||
"moduleName": ".*",
|
||||
"moduleLicense": "GPL2 w/ CPE"
|
||||
},
|
||||
{
|
||||
"moduleName": ".*",
|
||||
"moduleLicense": "GPLv2+CE"
|
||||
},
|
||||
{
|
||||
"moduleName": ".*",
|
||||
"moduleLicense": "GNU GENERAL PUBLIC LICENSE, Version 2 + Classpath Exception"
|
||||
},
|
||||
{
|
||||
"moduleName": "com.martiansoftware:jsap",
|
||||
"moduleLicense": "LGPL"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.hibernate.orm:hibernate-core",
|
||||
"moduleLicense": "GNU Library General Public License v2.1 or later"
|
||||
},
|
||||
{
|
||||
"moduleName": ".*",
|
||||
"moduleLicense": "COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0"
|
||||
},
|
||||
{
|
||||
"moduleName": ".*",
|
||||
"moduleLicense": "Eclipse Public License - v 1.0"
|
||||
},
|
||||
{
|
||||
"moduleName": ".*",
|
||||
"moduleLicense": "Eclipse Public License v. 2.0"
|
||||
},
|
||||
{
|
||||
"moduleName": ".*",
|
||||
"moduleLicense": "Eclipse Public License - v 2.0"
|
||||
},
|
||||
{
|
||||
"moduleName": ".*",
|
||||
"moduleLicense": "Eclipse Public License - Version 2.0"
|
||||
},
|
||||
{
|
||||
"moduleName": ".*",
|
||||
"moduleLicense": "Eclipse Public License, Version 2.0"
|
||||
},
|
||||
{
|
||||
"moduleName": ".*",
|
||||
"moduleLicense": "Ubuntu Font Licence 1.0"
|
||||
},
|
||||
{
|
||||
"moduleName": ".*",
|
||||
"moduleLicense": "Bouncy Castle Licence"
|
||||
},
|
||||
{
|
||||
"moduleName": ".*",
|
||||
"moduleLicense": "Public Domain, per Creative Commons CC0"
|
||||
},
|
||||
{
|
||||
"moduleName": ".*",
|
||||
"moduleLicense": "The W3C License"
|
||||
}
|
||||
]
|
||||
}
|
||||
20
build.gradle
@@ -8,7 +8,8 @@ plugins {
|
||||
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("org.panteleyev.jpackageplugin") version "1.6.1"
|
||||
id "org.sonarqube" version "6.0.1.5171"
|
||||
}
|
||||
|
||||
import com.github.jk1.license.render.*
|
||||
@@ -25,7 +26,7 @@ ext {
|
||||
}
|
||||
|
||||
group = "stirling.software"
|
||||
version = "0.40.0"
|
||||
version = "0.40.2"
|
||||
|
||||
java {
|
||||
// 17 is lowest but we support and recommend 21
|
||||
@@ -34,13 +35,13 @@ 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" }
|
||||
}
|
||||
|
||||
licenseReport {
|
||||
renderers = [new JsonReportRenderer()]
|
||||
allowedLicensesFile = new File("$projectDir/allowed-licenses.json")
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
@@ -268,6 +269,17 @@ spotless {
|
||||
}
|
||||
}
|
||||
|
||||
sonar {
|
||||
properties {
|
||||
property "sonar.projectKey", "Stirling-Tools_Stirling-PDF"
|
||||
property "sonar.organization", "stirling-tools"
|
||||
|
||||
property "sonar.exclusions", "**/build-wrapper-dump.json, src/main/java/org/apache/**, src/main/resources/static/pdfjs/**, src/main/resources/static/pdfjs-legacy/**, src/main/resources/static/js/thirdParty/**"
|
||||
property "sonar.coverage.exclusions", "src/main/java/org/apache/**, src/main/resources/static/pdfjs/**, src/main/resources/static/pdfjs-legacy/**, src/main/resources/static/js/thirdParty/**"
|
||||
property "sonar.cpd.exclusions", "src/main/java/org/apache/**, src/main/resources/static/pdfjs/**, src/main/resources/static/pdfjs-legacy/**, src/main/resources/static/js/thirdParty/**"
|
||||
}
|
||||
}
|
||||
|
||||
//gradleLint {
|
||||
// rules=['unused-dependency']
|
||||
// }
|
||||
@@ -366,7 +378,7 @@ dependencies {
|
||||
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"
|
||||
|
||||
|
Before Width: | Height: | Size: 242 KiB After Width: | Height: | Size: 169 KiB |
|
Before Width: | Height: | Size: 145 KiB After Width: | Height: | Size: 118 KiB |
@@ -4,6 +4,7 @@ import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
@@ -74,6 +75,11 @@ public class AppConfig {
|
||||
: "null";
|
||||
}
|
||||
|
||||
@Bean(name = "languages")
|
||||
public List<String> languages() {
|
||||
return applicationProperties.getUi().getLanguages();
|
||||
}
|
||||
|
||||
@Bean(name = "navBarText")
|
||||
public String navBarText() {
|
||||
String defaultNavBar =
|
||||
|
||||
@@ -2,7 +2,9 @@ package stirling.software.SPDF.controller.api;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
@@ -26,11 +28,14 @@ public class AdditionalLanguageJsController {
|
||||
@Hidden
|
||||
@GetMapping(value = "/additionalLanguageCode.js", produces = "application/javascript")
|
||||
public void generateAdditionalLanguageJs(HttpServletResponse response) throws IOException {
|
||||
List<String> supportedLanguages = languageService.getSupportedLanguages();
|
||||
Set<String> supportedLanguages = languageService.getSupportedLanguages();
|
||||
response.setContentType("application/javascript");
|
||||
PrintWriter writer = response.getWriter();
|
||||
// Erstelle das JavaScript dynamisch
|
||||
writer.println("const supportedLanguages = " + toJsonArray(supportedLanguages) + ";");
|
||||
writer.println(
|
||||
"const supportedLanguages = "
|
||||
+ toJsonArray(new ArrayList<>(supportedLanguages))
|
||||
+ ";");
|
||||
// Generiere die `getDetailedLanguageCode`-Funktion
|
||||
writer.println(
|
||||
"""
|
||||
|
||||
@@ -13,6 +13,7 @@ import io.github.pixee.security.Filenames;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
||||
import stirling.software.SPDF.model.ApplicationProperties;
|
||||
import stirling.software.SPDF.model.api.converters.HTMLToPdfRequest;
|
||||
import stirling.software.SPDF.service.CustomPDDocumentFactory;
|
||||
import stirling.software.SPDF.utils.FileToPdf;
|
||||
@@ -27,19 +28,23 @@ public class ConvertHtmlToPDF {
|
||||
|
||||
private final CustomPDDocumentFactory pdfDocumentFactory;
|
||||
|
||||
private final ApplicationProperties applicationProperties;
|
||||
|
||||
@Autowired
|
||||
public ConvertHtmlToPDF(
|
||||
CustomPDDocumentFactory pdfDocumentFactory,
|
||||
@Qualifier("bookAndHtmlFormatsInstalled") boolean bookAndHtmlFormatsInstalled) {
|
||||
@Qualifier("bookAndHtmlFormatsInstalled") boolean bookAndHtmlFormatsInstalled,
|
||||
ApplicationProperties applicationProperties) {
|
||||
this.pdfDocumentFactory = pdfDocumentFactory;
|
||||
this.bookAndHtmlFormatsInstalled = bookAndHtmlFormatsInstalled;
|
||||
this.applicationProperties = applicationProperties;
|
||||
}
|
||||
|
||||
@PostMapping(consumes = "multipart/form-data", value = "/html/pdf")
|
||||
@Operation(
|
||||
summary = "Convert an HTML or ZIP (containing HTML and CSS) to PDF",
|
||||
description =
|
||||
"This endpoint takes an HTML or ZIP file input and converts it to a PDF format.")
|
||||
"This endpoint takes an HTML or ZIP file input and converts it to a PDF format. Input:HTML Output:PDF Type:SISO")
|
||||
public ResponseEntity<byte[]> HtmlToPdf(@ModelAttribute HTMLToPdfRequest request)
|
||||
throws Exception {
|
||||
MultipartFile fileInput = request.getFileInput();
|
||||
@@ -54,12 +59,17 @@ public class ConvertHtmlToPDF {
|
||||
|| (!originalFilename.endsWith(".html") && !originalFilename.endsWith(".zip"))) {
|
||||
throw new IllegalArgumentException("File must be either .html or .zip format.");
|
||||
}
|
||||
|
||||
boolean disableSanitize =
|
||||
Boolean.TRUE.equals(applicationProperties.getSystem().getDisableSanitize());
|
||||
|
||||
byte[] pdfBytes =
|
||||
FileToPdf.convertHtmlToPdf(
|
||||
request,
|
||||
fileInput.getBytes(),
|
||||
originalFilename,
|
||||
bookAndHtmlFormatsInstalled);
|
||||
bookAndHtmlFormatsInstalled,
|
||||
disableSanitize);
|
||||
|
||||
pdfBytes = pdfDocumentFactory.createNewBytesBasedOnOldDocument(pdfBytes);
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ import io.github.pixee.security.Filenames;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
||||
import stirling.software.SPDF.model.ApplicationProperties;
|
||||
import stirling.software.SPDF.model.api.GeneralFile;
|
||||
import stirling.software.SPDF.service.CustomPDDocumentFactory;
|
||||
import stirling.software.SPDF.utils.FileToPdf;
|
||||
@@ -37,12 +38,16 @@ public class ConvertMarkdownToPdf {
|
||||
|
||||
private final CustomPDDocumentFactory pdfDocumentFactory;
|
||||
|
||||
private final ApplicationProperties applicationProperties;
|
||||
|
||||
@Autowired
|
||||
public ConvertMarkdownToPdf(
|
||||
CustomPDDocumentFactory pdfDocumentFactory,
|
||||
@Qualifier("bookAndHtmlFormatsInstalled") boolean bookAndHtmlFormatsInstalled) {
|
||||
@Qualifier("bookAndHtmlFormatsInstalled") boolean bookAndHtmlFormatsInstalled,
|
||||
ApplicationProperties applicationProperties) {
|
||||
this.pdfDocumentFactory = pdfDocumentFactory;
|
||||
this.bookAndHtmlFormatsInstalled = bookAndHtmlFormatsInstalled;
|
||||
this.applicationProperties = applicationProperties;
|
||||
}
|
||||
|
||||
@PostMapping(consumes = "multipart/form-data", value = "/markdown/pdf")
|
||||
@@ -76,12 +81,16 @@ public class ConvertMarkdownToPdf {
|
||||
|
||||
String htmlContent = renderer.render(document);
|
||||
|
||||
boolean disableSanitize =
|
||||
Boolean.TRUE.equals(applicationProperties.getSystem().getDisableSanitize());
|
||||
|
||||
byte[] pdfBytes =
|
||||
FileToPdf.convertHtmlToPdf(
|
||||
null,
|
||||
htmlContent.getBytes(),
|
||||
"converted.html",
|
||||
bookAndHtmlFormatsInstalled);
|
||||
bookAndHtmlFormatsInstalled,
|
||||
disableSanitize);
|
||||
pdfBytes = pdfDocumentFactory.createNewBytesBasedOnOldDocument(pdfBytes);
|
||||
String outputFilename =
|
||||
originalFilename.replaceFirst("[.][^.]+$", "")
|
||||
|
||||
@@ -8,7 +8,7 @@ import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
import org.apache.pdfbox.multipdf.PDFMergerUtility;
|
||||
@@ -65,6 +65,9 @@ public class OCRController {
|
||||
}
|
||||
|
||||
@PostMapping(consumes = "multipart/form-data", value = "/ocr-pdf")
|
||||
@Operation(
|
||||
summary = "Process PDF files with OCR using Tesseract",
|
||||
description = "Takes a PDF file as input, performs OCR using specified languages and OCR type (skip-text/force-ocr), and returns the processed PDF. Input:PDF Output:PDF Type:SISO")
|
||||
public ResponseEntity<byte[]> processPdfWithOCR(
|
||||
@ModelAttribute ProcessPdfWithOcrRequest request)
|
||||
throws IOException, InterruptedException {
|
||||
|
||||
@@ -36,8 +36,9 @@ public class DatabaseWebController {
|
||||
}
|
||||
List<FileInfo> backupList = databaseService.getBackupList();
|
||||
model.addAttribute("backupFiles", backupList);
|
||||
model.addAttribute("databaseVersion", databaseService.getH2Version());
|
||||
if ("Unknown".equalsIgnoreCase(databaseService.getH2Version())) {
|
||||
String dbVersion = databaseService.getH2Version();
|
||||
model.addAttribute("databaseVersion", dbVersion);
|
||||
if ("Unknown".equalsIgnoreCase(dbVersion)) {
|
||||
model.addAttribute("infoMessage", "notSupported");
|
||||
}
|
||||
return "database";
|
||||
|
||||
@@ -265,7 +265,8 @@ public class ApplicationProperties {
|
||||
return getKeycloak();
|
||||
default:
|
||||
throw new UnsupportedProviderException(
|
||||
"Logout from the provider is not supported? Report it at https://github.com/Stirling-Tools/Stirling-PDF/issues");
|
||||
"Logout from the provider is not supported? Report it at"
|
||||
+ " https://github.com/Stirling-Tools/Stirling-PDF/issues");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -283,6 +284,7 @@ public class ApplicationProperties {
|
||||
private Boolean enableAlphaFunctionality;
|
||||
private String enableAnalytics;
|
||||
private Datasource datasource;
|
||||
private Boolean disableSanitize;
|
||||
}
|
||||
|
||||
@Data
|
||||
@@ -312,10 +314,10 @@ public class ApplicationProperties {
|
||||
@Override
|
||||
public String toString() {
|
||||
return """
|
||||
Driver {
|
||||
driverName='%s'
|
||||
}
|
||||
"""
|
||||
Driver {
|
||||
driverName='%s'
|
||||
}
|
||||
"""
|
||||
.formatted(driverName);
|
||||
}
|
||||
}
|
||||
@@ -325,6 +327,7 @@ public class ApplicationProperties {
|
||||
private String appName;
|
||||
private String homeDescription;
|
||||
private String appNameNavbar;
|
||||
private List<String> languages;
|
||||
|
||||
public String getAppName() {
|
||||
return appName != null && appName.trim().length() > 0 ? appName : null;
|
||||
|
||||
@@ -1,41 +1,56 @@
|
||||
package stirling.software.SPDF.service;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import stirling.software.SPDF.model.ApplicationProperties;
|
||||
|
||||
@Service
|
||||
@Slf4j
|
||||
public class LanguageService {
|
||||
|
||||
private final ApplicationProperties applicationProperties;
|
||||
private final PathMatchingResourcePatternResolver resourcePatternResolver =
|
||||
new PathMatchingResourcePatternResolver();
|
||||
|
||||
public List<String> getSupportedLanguages() {
|
||||
List<String> supportedLanguages = new ArrayList<>();
|
||||
public LanguageService(ApplicationProperties applicationProperties) {
|
||||
this.applicationProperties = applicationProperties;
|
||||
}
|
||||
|
||||
public Set<String> getSupportedLanguages() {
|
||||
try {
|
||||
Resource[] resources =
|
||||
resourcePatternResolver.getResources("classpath*:messages_*.properties");
|
||||
for (Resource resource : resources) {
|
||||
if (resource.exists() && resource.isReadable()) {
|
||||
String filename = resource.getFilename();
|
||||
if (filename != null
|
||||
&& filename.startsWith("messages_")
|
||||
&& filename.endsWith(".properties")) {
|
||||
String languageCode =
|
||||
filename.replace("messages_", "").replace(".properties", "");
|
||||
supportedLanguages.add(languageCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return supportedLanguages;
|
||||
return Arrays.stream(resources)
|
||||
.map(Resource::getFilename)
|
||||
.filter(
|
||||
filename ->
|
||||
filename != null
|
||||
&& filename.startsWith("messages_")
|
||||
&& filename.endsWith(".properties"))
|
||||
.map(filename -> filename.replace("messages_", "").replace(".properties", ""))
|
||||
.filter(
|
||||
languageCode -> {
|
||||
Set<String> allowedLanguages =
|
||||
new HashSet<>(applicationProperties.getUi().getLanguages());
|
||||
return allowedLanguages.isEmpty()
|
||||
|| allowedLanguages.contains(languageCode)
|
||||
|| "en_GB".equals(languageCode);
|
||||
})
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
} catch (IOException e) {
|
||||
log.error("Error retrieving supported languages", e);
|
||||
return new HashSet<>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,8 @@ public class FileToPdf {
|
||||
HTMLToPdfRequest request,
|
||||
byte[] fileBytes,
|
||||
String fileName,
|
||||
boolean htmlFormatsInstalled)
|
||||
boolean htmlFormatsInstalled,
|
||||
boolean disableSanitize)
|
||||
throws IOException, InterruptedException {
|
||||
|
||||
Path tempOutputFile = Files.createTempFile("output_", ".pdf");
|
||||
@@ -36,12 +37,13 @@ public class FileToPdf {
|
||||
if (fileName.endsWith(".html")) {
|
||||
tempInputFile = Files.createTempFile("input_", ".html");
|
||||
String sanitizedHtml =
|
||||
sanitizeHtmlContent(new String(fileBytes, StandardCharsets.UTF_8));
|
||||
sanitizeHtmlContent(
|
||||
new String(fileBytes, StandardCharsets.UTF_8), disableSanitize);
|
||||
Files.write(tempInputFile, sanitizedHtml.getBytes(StandardCharsets.UTF_8));
|
||||
} else if (fileName.endsWith(".zip")) {
|
||||
tempInputFile = Files.createTempFile("input_", ".zip");
|
||||
Files.write(tempInputFile, fileBytes);
|
||||
sanitizeHtmlFilesInZip(tempInputFile);
|
||||
sanitizeHtmlFilesInZip(tempInputFile, disableSanitize);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unsupported file format: " + fileName);
|
||||
}
|
||||
@@ -89,11 +91,12 @@ public class FileToPdf {
|
||||
return pdfBytes;
|
||||
}
|
||||
|
||||
private static String sanitizeHtmlContent(String htmlContent) {
|
||||
return CustomHtmlSanitizer.sanitize(htmlContent);
|
||||
private static String sanitizeHtmlContent(String htmlContent, boolean disableSanitize) {
|
||||
return (!disableSanitize) ? CustomHtmlSanitizer.sanitize(htmlContent) : htmlContent;
|
||||
}
|
||||
|
||||
private static void sanitizeHtmlFilesInZip(Path zipFilePath) throws IOException {
|
||||
private static void sanitizeHtmlFilesInZip(Path zipFilePath, boolean disableSanitize)
|
||||
throws IOException {
|
||||
Path tempUnzippedDir = Files.createTempDirectory("unzipped_");
|
||||
try (ZipInputStream zipIn =
|
||||
ZipSecurity.createHardenedInputStream(
|
||||
@@ -106,7 +109,7 @@ public class FileToPdf {
|
||||
if (entry.getName().toLowerCase().endsWith(".html")
|
||||
|| entry.getName().toLowerCase().endsWith(".htm")) {
|
||||
String content = new String(zipIn.readAllBytes(), StandardCharsets.UTF_8);
|
||||
String sanitizedContent = sanitizeHtmlContent(content);
|
||||
String sanitizedContent = sanitizeHtmlContent(content, disableSanitize);
|
||||
Files.write(filePath, sanitizedContent.getBytes(StandardCharsets.UTF_8));
|
||||
} else {
|
||||
Files.copy(zipIn, filePath);
|
||||
|
||||
@@ -587,9 +587,7 @@ public class GeneralUtils {
|
||||
for (byte b : hash) {
|
||||
fingerprint.append(String.format("%02x", b));
|
||||
}
|
||||
|
||||
return fingerprint.toString();
|
||||
|
||||
} catch (Exception e) {
|
||||
return "GenericID";
|
||||
}
|
||||
|
||||
@@ -218,6 +218,9 @@ public class ProcessExecutor {
|
||||
errorReaderThread.join();
|
||||
outputReaderThread.join();
|
||||
|
||||
boolean isQpdf =
|
||||
command != null && !command.isEmpty() && command.get(0).contains("qpdf");
|
||||
|
||||
if (outputLines.size() > 0) {
|
||||
String outputMessage = String.join("\n", outputLines);
|
||||
messages += outputMessage;
|
||||
@@ -233,20 +236,28 @@ public class ProcessExecutor {
|
||||
log.warn("Command error output:\n" + errorMessage);
|
||||
}
|
||||
if (exitCode != 0) {
|
||||
throw new IOException(
|
||||
"Command process failed with exit code "
|
||||
+ exitCode
|
||||
+ ". Error message: "
|
||||
+ errorMessage);
|
||||
if (isQpdf && exitCode == 3) {
|
||||
log.warn("qpdf succeeded with warnings: {}", messages);
|
||||
} else {
|
||||
throw new IOException(
|
||||
"Command process failed with exit code "
|
||||
+ exitCode
|
||||
+ ". Error message: "
|
||||
+ errorMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (exitCode != 0) {
|
||||
throw new IOException(
|
||||
"Command process failed with exit code "
|
||||
+ exitCode
|
||||
+ "\nLogs: "
|
||||
+ messages);
|
||||
if (isQpdf && exitCode == 3) {
|
||||
log.warn("qpdf succeeded with warnings: {}", messages);
|
||||
} else {
|
||||
throw new IOException(
|
||||
"Command process failed with exit code "
|
||||
+ exitCode
|
||||
+ "\nLogs: "
|
||||
+ messages);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
semaphore.release();
|
||||
|
||||
@@ -273,6 +273,7 @@ home.legacyHomepage=Old homepage
|
||||
home.newHomePage=Try our new homepage!
|
||||
home.alphabetical=Alphabetical
|
||||
home.globalPopularity=Global Popularity
|
||||
home.sortBy=Sort by:
|
||||
|
||||
home.multiTool.title=أداة متعددة PDF
|
||||
home.multiTool.desc=دمج الصفحات وتدويرها وإعادة ترتيبها وإزالتها
|
||||
|
||||
@@ -273,6 +273,7 @@ home.legacyHomepage=Old homepage
|
||||
home.newHomePage=Try our new homepage!
|
||||
home.alphabetical=Alphabetical
|
||||
home.globalPopularity=Global Popularity
|
||||
home.sortBy=Sort by:
|
||||
|
||||
home.multiTool.title=PDF Multi-alət
|
||||
home.multiTool.desc=Səhifələri Birləşdir, Çevir, Yenidən Sırala, Böl və Sil
|
||||
|
||||
@@ -273,6 +273,7 @@ home.legacyHomepage=Old homepage
|
||||
home.newHomePage=Try our new homepage!
|
||||
home.alphabetical=Alphabetical
|
||||
home.globalPopularity=Global Popularity
|
||||
home.sortBy=Sort by:
|
||||
|
||||
home.multiTool.title=PDF Мулти инструмент
|
||||
home.multiTool.desc=Обединяване, завъртане, пренареждане и премахване на страници
|
||||
|
||||
@@ -273,6 +273,7 @@ home.legacyHomepage=Old homepage
|
||||
home.newHomePage=Try our new homepage!
|
||||
home.alphabetical=Alphabetical
|
||||
home.globalPopularity=Global Popularity
|
||||
home.sortBy=Sort by:
|
||||
|
||||
home.multiTool.title=Eina Multifunció de PDF
|
||||
home.multiTool.desc=Fusiona, Rota, Reorganitza i Esborra pàgines
|
||||
|
||||
@@ -273,6 +273,7 @@ home.legacyHomepage=Old homepage
|
||||
home.newHomePage=Try our new homepage!
|
||||
home.alphabetical=Alphabetical
|
||||
home.globalPopularity=Global Popularity
|
||||
home.sortBy=Sort by:
|
||||
|
||||
home.multiTool.title=PDF Multi nástroj
|
||||
home.multiTool.desc=Sloučit, otočit, přeuspořádat, rozdělit a odstranit stránky
|
||||
|
||||
@@ -273,6 +273,7 @@ home.legacyHomepage=Old homepage
|
||||
home.newHomePage=Try our new homepage!
|
||||
home.alphabetical=Alphabetical
|
||||
home.globalPopularity=Global Popularity
|
||||
home.sortBy=Sort by:
|
||||
|
||||
home.multiTool.title=PDF Multi Værktøj
|
||||
home.multiTool.desc=Flet, Rotér, Omarrangér og Fjern sider
|
||||
|
||||
@@ -138,7 +138,7 @@ analytics.settings=Sie können die Einstellungen für die Analytics in der confi
|
||||
# NAVBAR #
|
||||
#############
|
||||
navbar.favorite=Favoriten
|
||||
navbar.recent=New and recently updated
|
||||
navbar.recent=Neu und kürzlich aktualisiert
|
||||
navbar.darkmode=Dunkler Modus
|
||||
navbar.language=Sprachen
|
||||
navbar.settings=Einstellungen
|
||||
@@ -266,13 +266,14 @@ home.viewPdf.title=PDF anzeigen
|
||||
home.viewPdf.desc=Anzeigen, Kommentieren, Text oder Bilder hinzufügen
|
||||
viewPdf.tags=anzeigen,lesen,kommentieren,text,bild
|
||||
|
||||
home.setFavorites=Set Favourites
|
||||
home.hideFavorites=Hide Favourites
|
||||
home.showFavorites=Show Favourites
|
||||
home.legacyHomepage=Old homepage
|
||||
home.newHomePage=Try our new homepage!
|
||||
home.alphabetical=Alphabetical
|
||||
home.globalPopularity=Global Popularity
|
||||
home.setFavorites=Favoriten festlegen
|
||||
home.hideFavorites=Favoriten ausblenden
|
||||
home.showFavorites=Favoriten anzeigen
|
||||
home.legacyHomepage=Alte Homepage
|
||||
home.newHomePage=Probieren Sie unsere neue Homepage aus!
|
||||
home.alphabetical=Alphabetisch
|
||||
home.globalPopularity=Beliebtheit
|
||||
home.sortBy=Sort by:
|
||||
|
||||
home.multiTool.title=PDF-Multitool
|
||||
home.multiTool.desc=Seiten zusammenführen, drehen, neu anordnen und entfernen
|
||||
|
||||
@@ -273,6 +273,7 @@ home.legacyHomepage=Old homepage
|
||||
home.newHomePage=Try our new homepage!
|
||||
home.alphabetical=Alphabetical
|
||||
home.globalPopularity=Global Popularity
|
||||
home.sortBy=Sort by:
|
||||
|
||||
home.multiTool.title=Πολυεργαλείο PDF
|
||||
home.multiTool.desc=Συγχώνευση, Περιστροφή, Αναδιάταξη, Διαχωρισμός και Αφαίρεση σελίδων
|
||||
|
||||
@@ -273,6 +273,7 @@ home.legacyHomepage=Old homepage
|
||||
home.newHomePage=Try our new homepage!
|
||||
home.alphabetical=Alphabetical
|
||||
home.globalPopularity=Global Popularity
|
||||
home.sortBy=Sort by:
|
||||
|
||||
home.multiTool.title=PDF Multi Tool
|
||||
home.multiTool.desc=Merge, Rotate, Rearrange, Split, and Remove pages
|
||||
|
||||
@@ -273,6 +273,7 @@ home.legacyHomepage=Old homepage
|
||||
home.newHomePage=Try our new homepage!
|
||||
home.alphabetical=Alphabetical
|
||||
home.globalPopularity=Global Popularity
|
||||
home.sortBy=Sort by:
|
||||
|
||||
home.multiTool.title=PDF Multi Tool
|
||||
home.multiTool.desc=Merge, Rotate, Rearrange, Split, and Remove pages
|
||||
|
||||
@@ -273,6 +273,7 @@ home.legacyHomepage=Old homepage
|
||||
home.newHomePage=Try our new homepage!
|
||||
home.alphabetical=Alphabetical
|
||||
home.globalPopularity=Global Popularity
|
||||
home.sortBy=Sort by:
|
||||
|
||||
home.multiTool.title=Multi-herramienta PDF
|
||||
home.multiTool.desc=Combinar, rotar, reorganizar y eliminar páginas
|
||||
|
||||
@@ -273,6 +273,7 @@ home.legacyHomepage=Old homepage
|
||||
home.newHomePage=Try our new homepage!
|
||||
home.alphabetical=Alphabetical
|
||||
home.globalPopularity=Global Popularity
|
||||
home.sortBy=Sort by:
|
||||
|
||||
home.multiTool.title=Erabilera anitzeko tresna PDF
|
||||
home.multiTool.desc=Orriak konbinatu, biratu, berrantolatu eta ezabatu
|
||||
|
||||
@@ -273,6 +273,7 @@ home.legacyHomepage=Old homepage
|
||||
home.newHomePage=Try our new homepage!
|
||||
home.alphabetical=Alphabetical
|
||||
home.globalPopularity=Global Popularity
|
||||
home.sortBy=Sort by:
|
||||
|
||||
home.multiTool.title=ابزار چندگانه PDF
|
||||
home.multiTool.desc=ترکیب، چرخش، بازآرایی، تقسیم و حذف صفحات
|
||||
|
||||
@@ -273,6 +273,7 @@ home.legacyHomepage=Old homepage
|
||||
home.newHomePage=Try our new homepage!
|
||||
home.alphabetical=Alphabetical
|
||||
home.globalPopularity=Global Popularity
|
||||
home.sortBy=Sort by:
|
||||
|
||||
home.multiTool.title=Outil multifonction PDF
|
||||
home.multiTool.desc=Fusionnez, faites pivoter, réorganisez et supprimez des pages.
|
||||
|
||||
@@ -273,6 +273,7 @@ home.legacyHomepage=Old homepage
|
||||
home.newHomePage=Try our new homepage!
|
||||
home.alphabetical=Alphabetical
|
||||
home.globalPopularity=Global Popularity
|
||||
home.sortBy=Sort by:
|
||||
|
||||
home.multiTool.title=Il-uirlis PDF
|
||||
home.multiTool.desc=Cumaisc, Rothlaigh, Atheagraigh, agus Bain leathanaigh
|
||||
|
||||
@@ -273,6 +273,7 @@ home.legacyHomepage=Old homepage
|
||||
home.newHomePage=Try our new homepage!
|
||||
home.alphabetical=Alphabetical
|
||||
home.globalPopularity=Global Popularity
|
||||
home.sortBy=Sort by:
|
||||
|
||||
home.multiTool.title=PDF मल्टी टूल
|
||||
home.multiTool.desc=मर्ज करें, घुमाएं, पुनर्व्यवस्थित करें और पृष्ठ हटाएं
|
||||
|
||||
@@ -273,6 +273,7 @@ home.legacyHomepage=Old homepage
|
||||
home.newHomePage=Try our new homepage!
|
||||
home.alphabetical=Alphabetical
|
||||
home.globalPopularity=Global Popularity
|
||||
home.sortBy=Sort by:
|
||||
|
||||
home.multiTool.title=PDF Višestruki alat
|
||||
home.multiTool.desc=Spajanje, rotiranje, preuređivanje i uklanjanje stranica
|
||||
|
||||
@@ -273,6 +273,7 @@ home.legacyHomepage=Old homepage
|
||||
home.newHomePage=Try our new homepage!
|
||||
home.alphabetical=Alphabetical
|
||||
home.globalPopularity=Global Popularity
|
||||
home.sortBy=Sort by:
|
||||
|
||||
home.multiTool.title=PDF többfunkciós eszköz
|
||||
home.multiTool.desc=Egyesítés, forgatás, átrendezés és oldalak eltávolítása
|
||||
|
||||
@@ -273,6 +273,7 @@ home.legacyHomepage=Old homepage
|
||||
home.newHomePage=Try our new homepage!
|
||||
home.alphabetical=Alphabetical
|
||||
home.globalPopularity=Global Popularity
|
||||
home.sortBy=Sort by:
|
||||
|
||||
home.multiTool.title=Alat Multi PDF
|
||||
home.multiTool.desc=Menggabungkan, Memutar, Mengatur Ulang, dan Menghapus halaman
|
||||
|
||||
@@ -27,7 +27,7 @@ bored=Stanco di aspettare?
|
||||
alphabet=Alfabeto
|
||||
downloadPdf=Scarica PDF
|
||||
text=Testo
|
||||
font=Fonte
|
||||
font=Font
|
||||
selectFillter=-- Seleziona --
|
||||
pageNum=Numero pagina
|
||||
sizes.small=Piccolo
|
||||
@@ -273,6 +273,7 @@ home.legacyHomepage=Vecchia homepage
|
||||
home.newHomePage=Prova la nostra nuova homepage!
|
||||
home.alphabetical=Alfabetico
|
||||
home.globalPopularity=Popolarità globale
|
||||
home.sortBy=Ordina per:
|
||||
|
||||
home.multiTool.title=Multifunzione PDF
|
||||
home.multiTool.desc=Unisci, Ruota, Riordina, e Rimuovi pagine
|
||||
@@ -601,7 +602,7 @@ autoRedact.submitButton=Invia
|
||||
redact.title=Redazione manuale
|
||||
redact.header=Redazione manuale
|
||||
redact.submit=Redazione
|
||||
redact.textBasedRedaction=TRedazione basata sul testo
|
||||
redact.textBasedRedaction=Redazione basata sul testo
|
||||
redact.pageBasedRedaction=Redazione basata sulla pagina
|
||||
redact.convertPDFToImageLabel=Converti PDF in immagine PDF (utilizzato per rimuovere il testo dietro la casella)
|
||||
redact.pageRedactionNumbers.title=Pagine
|
||||
@@ -951,7 +952,7 @@ compress.title=Comprimi
|
||||
compress.header=Comprimi PDF
|
||||
compress.credit=Questo servizio utilizza qpdf per la compressione/ottimizzazione dei PDF.
|
||||
compress.selectText.1=Modalità manuale - Da 1 a 5
|
||||
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.1.1=Nei livelli di ottimizzazione da 6 a 9, oltre alla compressione PDF generale, la risoluzione dell'immagine viene ridotta per ridurre ulteriormente le dimensioni del file. Livelli più alti comportano una compressione dell'immagine più forte (fino al 50% delle dimensioni originali), ottenendo una maggiore riduzione delle dimensioni ma con una potenziale perdita di qualità nelle immagini.
|
||||
compress.selectText.2=Livello di ottimizzazione:
|
||||
compress.selectText.3=4 (Terribile per le immagini di testo)
|
||||
compress.selectText.4=Modalità automatica - Regola automaticamente la qualità per ottenere le dimensioni esatte del PDF
|
||||
|
||||
@@ -273,6 +273,7 @@ home.legacyHomepage=Old homepage
|
||||
home.newHomePage=Try our new homepage!
|
||||
home.alphabetical=Alphabetical
|
||||
home.globalPopularity=Global Popularity
|
||||
home.sortBy=Sort by:
|
||||
|
||||
home.multiTool.title=PDFマルチツール
|
||||
home.multiTool.desc=ページの結合、回転、並べ替え、削除します。
|
||||
|
||||
@@ -273,6 +273,7 @@ home.legacyHomepage=Old homepage
|
||||
home.newHomePage=Try our new homepage!
|
||||
home.alphabetical=Alphabetical
|
||||
home.globalPopularity=Global Popularity
|
||||
home.sortBy=Sort by:
|
||||
|
||||
home.multiTool.title=PDF 멀티 도구
|
||||
home.multiTool.desc=병합, 회전, 재배치, 분할 및 페이지 제거
|
||||
|
||||
@@ -273,6 +273,7 @@ home.legacyHomepage=Old homepage
|
||||
home.newHomePage=Try our new homepage!
|
||||
home.alphabetical=Alphabetical
|
||||
home.globalPopularity=Global Popularity
|
||||
home.sortBy=Sort by:
|
||||
|
||||
home.multiTool.title=PDF multitool
|
||||
home.multiTool.desc=Pagina's samenvoegen, draaien, herschikken en verwijderen
|
||||
|
||||
@@ -273,6 +273,7 @@ home.legacyHomepage=Old homepage
|
||||
home.newHomePage=Try our new homepage!
|
||||
home.alphabetical=Alphabetical
|
||||
home.globalPopularity=Global Popularity
|
||||
home.sortBy=Sort by:
|
||||
|
||||
home.multiTool.title=PDF Multi Verktøy
|
||||
home.multiTool.desc=Slå sammen, roter, omorganiser og fjern sider
|
||||
|
||||
@@ -273,6 +273,7 @@ home.legacyHomepage=Old homepage
|
||||
home.newHomePage=Try our new homepage!
|
||||
home.alphabetical=Alphabetical
|
||||
home.globalPopularity=Global Popularity
|
||||
home.sortBy=Sort by:
|
||||
|
||||
home.multiTool.title=Wielofunkcyjne Narzędzie PDF
|
||||
home.multiTool.desc=Łącz, dziel, obracaj, zmieniaj kolejność i usuwaj strony
|
||||
|
||||
@@ -273,6 +273,7 @@ home.legacyHomepage=Old homepage
|
||||
home.newHomePage=Try our new homepage!
|
||||
home.alphabetical=Alphabetical
|
||||
home.globalPopularity=Global Popularity
|
||||
home.sortBy=Sort by:
|
||||
|
||||
home.multiTool.title=Multiferramentas de PDF
|
||||
home.multiTool.desc=Mesclar, girar, reorganizar, dividir, inserir e remover páginas.
|
||||
|
||||
@@ -273,6 +273,7 @@ home.legacyHomepage=Old homepage
|
||||
home.newHomePage=Try our new homepage!
|
||||
home.alphabetical=Alphabetical
|
||||
home.globalPopularity=Global Popularity
|
||||
home.sortBy=Sort by:
|
||||
|
||||
home.multiTool.title=Multi Ferramenta PDF
|
||||
home.multiTool.desc=Juntar, Rodar, Reorganizar, Dividir e Remover páginas
|
||||
|
||||
@@ -273,6 +273,7 @@ home.legacyHomepage=Old homepage
|
||||
home.newHomePage=Try our new homepage!
|
||||
home.alphabetical=Alphabetical
|
||||
home.globalPopularity=Global Popularity
|
||||
home.sortBy=Sort by:
|
||||
|
||||
home.multiTool.title=Instrument multiplu PDF
|
||||
home.multiTool.desc=Unifică, rotește, rearanjează și elimină pagini
|
||||
|
||||
@@ -273,6 +273,7 @@ home.legacyHomepage=Old homepage
|
||||
home.newHomePage=Try our new homepage!
|
||||
home.alphabetical=Alphabetical
|
||||
home.globalPopularity=Global Popularity
|
||||
home.sortBy=Sort by:
|
||||
|
||||
home.multiTool.title=Мультиинструмент PDF
|
||||
home.multiTool.desc=Объединение, поворот, переупорядочивание и удаление страниц
|
||||
|
||||
@@ -273,6 +273,7 @@ home.legacyHomepage=Old homepage
|
||||
home.newHomePage=Try our new homepage!
|
||||
home.alphabetical=Alphabetical
|
||||
home.globalPopularity=Global Popularity
|
||||
home.sortBy=Sort by:
|
||||
|
||||
home.multiTool.title=PDF Multi Tool
|
||||
home.multiTool.desc=Zlúčiť, otočiť, preusporiadať a odstrániť stránky
|
||||
|
||||
@@ -273,6 +273,7 @@ home.legacyHomepage=Old homepage
|
||||
home.newHomePage=Try our new homepage!
|
||||
home.alphabetical=Alphabetical
|
||||
home.globalPopularity=Global Popularity
|
||||
home.sortBy=Sort by:
|
||||
|
||||
home.multiTool.title=PDF Multi Tool
|
||||
home.multiTool.desc=Spoji, zavrti, prerazporedi, razdeli in odstrani strani
|
||||
|
||||
@@ -273,6 +273,7 @@ home.legacyHomepage=Old homepage
|
||||
home.newHomePage=Try our new homepage!
|
||||
home.alphabetical=Alphabetical
|
||||
home.globalPopularity=Global Popularity
|
||||
home.sortBy=Sort by:
|
||||
|
||||
home.multiTool.title=PDF Multi Alat
|
||||
home.multiTool.desc=Spajanje, rotacija, premeštanje i uklanjanje stranica
|
||||
|
||||
@@ -273,6 +273,7 @@ home.legacyHomepage=Old homepage
|
||||
home.newHomePage=Try our new homepage!
|
||||
home.alphabetical=Alphabetical
|
||||
home.globalPopularity=Global Popularity
|
||||
home.sortBy=Sort by:
|
||||
|
||||
home.multiTool.title=PDF Multi-verktyg
|
||||
home.multiTool.desc=Sammanfoga, rotera, ordna om och ta bort sidor
|
||||
|
||||
@@ -273,6 +273,7 @@ home.legacyHomepage=Old homepage
|
||||
home.newHomePage=Try our new homepage!
|
||||
home.alphabetical=Alphabetical
|
||||
home.globalPopularity=Global Popularity
|
||||
home.sortBy=Sort by:
|
||||
|
||||
home.multiTool.title=เครื่องมือ PDF หลายตัว
|
||||
home.multiTool.desc=รวม หมุน จัดเรียง และลบหน้าต่างๆ
|
||||
|
||||
@@ -273,6 +273,7 @@ home.legacyHomepage=Old homepage
|
||||
home.newHomePage=Try our new homepage!
|
||||
home.alphabetical=Alphabetical
|
||||
home.globalPopularity=Global Popularity
|
||||
home.sortBy=Sort by:
|
||||
|
||||
home.multiTool.title=PDF Çoklu Araç
|
||||
home.multiTool.desc=Birleştir, Döndür, Yeniden Düzenle ve Sayfaları Kaldır
|
||||
|
||||
@@ -273,6 +273,7 @@ home.legacyHomepage=Old homepage
|
||||
home.newHomePage=Try our new homepage!
|
||||
home.alphabetical=Alphabetical
|
||||
home.globalPopularity=Global Popularity
|
||||
home.sortBy=Sort by:
|
||||
|
||||
home.multiTool.title=Мультіінструмент PDF
|
||||
home.multiTool.desc=Об'єднання, поворот, зміна порядку та видалення сторінок
|
||||
|
||||
@@ -273,6 +273,7 @@ home.legacyHomepage=Old homepage
|
||||
home.newHomePage=Try our new homepage!
|
||||
home.alphabetical=Alphabetical
|
||||
home.globalPopularity=Global Popularity
|
||||
home.sortBy=Sort by:
|
||||
|
||||
home.multiTool.title=Đa công cụ PDF
|
||||
home.multiTool.desc=Ghép nối, Xoay, Sắp xếp lại và Xóa trang
|
||||
|
||||
@@ -273,6 +273,7 @@ home.legacyHomepage=Old homepage
|
||||
home.newHomePage=Try our new homepage!
|
||||
home.alphabetical=Alphabetical
|
||||
home.globalPopularity=Global Popularity
|
||||
home.sortBy=Sort by:
|
||||
|
||||
home.multiTool.title=PDF ལག་ཆ་མང་པོ།
|
||||
home.multiTool.desc=སྡེབ་སྦྱོར། འཁོར་སྐྱོད། བསྐྱར་སྒྲིག ཁ་གྱེས། དང་ཤོག་ངོས་སུབ་པ།
|
||||
|
||||
@@ -273,6 +273,7 @@ home.legacyHomepage=Old homepage
|
||||
home.newHomePage=Try our new homepage!
|
||||
home.alphabetical=Alphabetical
|
||||
home.globalPopularity=Global Popularity
|
||||
home.sortBy=Sort by:
|
||||
|
||||
home.multiTool.title=PDF 多功能工具
|
||||
home.multiTool.desc=合并、旋转、重新排列和删除 PDF 页面
|
||||
|
||||
@@ -273,6 +273,7 @@ home.legacyHomepage=Old homepage
|
||||
home.newHomePage=Try our new homepage!
|
||||
home.alphabetical=Alphabetical
|
||||
home.globalPopularity=Global Popularity
|
||||
home.sortBy=Sort by:
|
||||
|
||||
home.multiTool.title=PDF 複合工具
|
||||
home.multiTool.desc=合併、旋轉、重新排列和移除頁面
|
||||
|
||||
@@ -16,7 +16,7 @@ security:
|
||||
csrfDisabled: false # set to 'true' to disable CSRF protection (not recommended for production)
|
||||
loginAttemptCount: 5 # lock user account after 5 tries; when using e.g. Fail2Ban you can deactivate the function with -1
|
||||
loginResetTimeMinutes: 120 # lock account for 2 hours after x attempts
|
||||
loginMethod: all # Accepts values like 'all' and 'normal'(only Login with Username/Password), 'oauth2'(only Login with OAuth2) or 'saml2'(only Login with SAML2)
|
||||
loginMethod: all # Accepts values like 'all' and 'normal'(only Login with Username/Password), 'oauth2'(only Login with OAuth2) or 'saml2'(only Login with SAML2)
|
||||
initialLogin:
|
||||
username: '' # initial username for the first login
|
||||
password: '' # initial password for the first login
|
||||
@@ -86,6 +86,7 @@ system:
|
||||
customHTMLFiles: false # enable to have files placed in /customFiles/templates override the existing template HTML files
|
||||
tessdataDir: /usr/share/tessdata # path to the directory containing the Tessdata files. This setting is relevant for Windows systems. For Windows users, this path should be adjusted to point to the appropriate directory where the Tessdata files are stored.
|
||||
enableAnalytics: 'true' # set to 'true' to enable analytics, set to 'false' to disable analytics; for enterprise users, this is set to true
|
||||
disableSanitize: false # set to true to disable Sanitize HTML; (can lead to injections in HTML)
|
||||
datasource:
|
||||
enableCustomDatabase: false # Enterprise users ONLY, set this property to 'true' if you would like to use your own custom database configuration
|
||||
customDatabaseUrl: '' # eg jdbc:postgresql://localhost:5432/postgres, set the url for your own custom database connection. If provided, the type, hostName, port and name are not necessary and will not be used
|
||||
@@ -100,6 +101,7 @@ ui:
|
||||
appName: '' # application's visible name
|
||||
homeDescription: '' # short description or tagline shown on the homepage
|
||||
appNameNavbar: '' # name displayed on the navigation bar
|
||||
languages: [] # If empty, all languages are enabled. To display only German and Polish ["de_DE", "pl_PL"]. British English is always enabled.
|
||||
|
||||
endpoints:
|
||||
toRemove: [] # list endpoints to disable (e.g. ['img-to-pdf', 'remove-pages'])
|
||||
@@ -113,7 +115,7 @@ AutomaticallyGenerated:
|
||||
key: example
|
||||
UUID: example
|
||||
appVersion: 0.35.0
|
||||
|
||||
|
||||
processExecutor:
|
||||
sessionLimit: # Process executor instances limits
|
||||
libreOfficeSessionLimit: 1
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24">
|
||||
<symbol id="icon-redact-auto" viewBox="0 0 24 24"> <g id="Layer_2" data-name="Layer 2">
|
||||
<g id="Layer_1-2" data-name="Layer 1">
|
||||
<rect width="24" height="24" style="fill: none"/>
|
||||
@@ -14,4 +14,5 @@
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</symbol>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.5 KiB |
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24">
|
||||
<symbol id="icon-redact-manual" viewBox="-2 0 24 24">
|
||||
<g id="Layer_2" data-name="Layer 2">
|
||||
<g id="Layer_1-2" data-name="Layer 1">
|
||||
|
||||
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.5 KiB |
@@ -1,6 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<symbol id="icon-split-auto" viewBox="0 0 24 24"> <g id="Layer_2" data-name="Layer 2"> <g id="Layer_2" data-name="Layer 2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24">
|
||||
<symbol id="icon-split-auto" viewBox="0 0 24 24">
|
||||
<g id="Layer_2" data-name="Layer 2">
|
||||
<g id="Layer_1-2" data-name="Layer 1">
|
||||
<g>
|
||||
<path d="M18.42466,20.16555,12,13.74089,9.84315,15.89774a2.45776,2.45776,0,0,1,.2524.73425,4.481,4.481,0,0,1,.06883.78013A3.53515,3.53515,0,0,1,9.086,20.00493a3.53516,3.53516,0,0,1-2.59281,1.07843,3.53516,3.53516,0,0,1-2.59281-1.07843,3.6561,3.6561,0,0,1,0-5.18561,3.53516,3.53516,0,0,1,2.59281-1.07843,4.48117,4.48117,0,0,1,.78014.06884,2.45778,2.45778,0,0,1,.73424.25239l2.15685-2.15685L8.00753,9.74842a2.45752,2.45752,0,0,1-.73424.2524,4.48117,4.48117,0,0,1-.78014.06884A3.53516,3.53516,0,0,1,3.90034,8.99123,3.53515,3.53515,0,0,1,2.82192,6.39842,3.53515,3.53515,0,0,1,3.90034,3.80561,3.53515,3.53515,0,0,1,6.49315,2.72719,3.53515,3.53515,0,0,1,9.086,3.80561a3.53515,3.53515,0,0,1,1.07842,2.59281,4.48107,4.48107,0,0,1-.06883.78014,2.45786,2.45786,0,0,1-.2524.73425L21.17808,19.24774v.91781Zm-3.67123-9.17809L12.91781,9.15185,18.42466,3.645h2.75342v.91781ZM6.49315,8.234A1.841,1.841,0,0,0,8.32877,6.39842,1.841,1.841,0,0,0,6.49315,4.56281,1.841,1.841,0,0,0,4.65753,6.39842,1.841,1.841,0,0,0,6.49315,8.234ZM12,12.36418a.47051.47051,0,1,0-.32123-.13767A.44026.44026,0,0,0,12,12.36418ZM6.49315,19.24774a1.83562,1.83562,0,1,0-1.29641-3.132,1.83562,1.83562,0,0,0,1.29641,3.132Z" style="fill: currentColor"/>
|
||||
@@ -9,4 +10,5 @@
|
||||
<rect width="24" height="24" style="fill: none"/>
|
||||
</g>
|
||||
</g>
|
||||
</symbol>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
@@ -1,6 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<symbol id="icon-split-chapters" viewBox="0 0 24 24"> <g id="Layer_2" data-name="Layer 2"> <g id="Layer_2" data-name="Layer 2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24">
|
||||
<symbol id="icon-split-chapters" viewBox="0 0 24 24">
|
||||
<g id="Layer_2" data-name="Layer 2">
|
||||
<g id="Layer_1-2" data-name="Layer 1">
|
||||
<g>
|
||||
<path d="M17.632,9.18527v5.44l1.94-1.16,1.94,1.16v-5.44Z" style="fill: currentColor"/>
|
||||
@@ -9,4 +10,5 @@
|
||||
<rect width="24" height="24" style="fill: none"/>
|
||||
</g>
|
||||
</g>
|
||||
</symbol>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
@@ -1,6 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" style="padding-left: 20px; margin-right: -20px;">
|
||||
<symbol id="icon-split-size" viewBox="0 0 24 24"> <g id="Layer_2" data-name="Layer 2"> <g id="Layer_2" data-name="Layer 2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24">
|
||||
<symbol id="icon-split-size" viewBox="0 0 24 24">
|
||||
<g id="Layer_2" data-name="Layer 2">
|
||||
<g id="Layer_1-2" data-name="Layer 1">
|
||||
<g>
|
||||
<path d="M17.90313,20.16555l-6.42466-6.42466L9.32162,15.89774a2.45776,2.45776,0,0,1,.2524.73425,4.481,4.481,0,0,1,.06883.78013,3.53515,3.53515,0,0,1-1.07842,2.59281,3.53516,3.53516,0,0,1-2.59281,1.07843,3.53516,3.53516,0,0,1-2.59281-1.07843,3.6561,3.6561,0,0,1,0-5.18561,3.53516,3.53516,0,0,1,2.59281-1.07843,4.48117,4.48117,0,0,1,.78014.06884,2.45778,2.45778,0,0,1,.73424.25239l2.15685-2.15685L7.486,9.74842a2.45752,2.45752,0,0,1-.73424.2524,4.48117,4.48117,0,0,1-.78014.06884A3.53516,3.53516,0,0,1,3.37881,8.99123,3.53515,3.53515,0,0,1,2.30039,6.39842,3.53515,3.53515,0,0,1,3.37881,3.80561,3.53515,3.53515,0,0,1,5.97162,2.72719,3.53515,3.53515,0,0,1,8.56443,3.80561,3.53515,3.53515,0,0,1,9.64285,6.39842a4.48107,4.48107,0,0,1-.06883.78014,2.45786,2.45786,0,0,1-.2524.73425L20.65655,19.24774v.91781Zm-3.67124-9.17809L12.39628,9.15185,17.90313,3.645h2.75342v.91781ZM5.97162,8.234A1.841,1.841,0,0,0,7.80724,6.39842,1.841,1.841,0,0,0,5.97162,4.56281,1.841,1.841,0,0,0,4.136,6.39842,1.841,1.841,0,0,0,5.97162,8.234Zm5.50685,4.13014a.47051.47051,0,1,0-.32123-.13767A.44026.44026,0,0,0,11.47847,12.36418ZM5.97162,19.24774a1.83562,1.83562,0,1,0-1.29641-3.132,1.83562,1.83562,0,0,0,1.29641,3.132Z" style="fill: currentColor"/>
|
||||
@@ -13,4 +14,5 @@
|
||||
<rect width="24" height="24" style="fill: none"/>
|
||||
</g>
|
||||
</g>
|
||||
</symbol>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
@@ -95,24 +95,23 @@ function reorderCards(container) {
|
||||
|
||||
function initializeCards() {
|
||||
updateFavoritesSection();
|
||||
updateFavoritesView();
|
||||
updateFavoritesDropdown();
|
||||
filterCards();
|
||||
}
|
||||
|
||||
function updateFavoritesView() {
|
||||
const isFavoritesView = JSON.parse(localStorage.getItem('favoritesView') || 'false');
|
||||
const textElement = document.getElementById('toggle-favourites-text');
|
||||
const iconElement = document.getElementById('toggle-favourites-icon');
|
||||
const favoritesGroup = document.querySelector('#groupFavorites');
|
||||
const favoritesList = JSON.parse(localStorage.getItem('favoritesList')) || [];
|
||||
document.getElementById('favouritesVisibility').style.display = 'flex';
|
||||
|
||||
if (isFavoritesView && favoritesList.length > 0) {
|
||||
iconElement.textContent = 'visibility_off';
|
||||
document.getElementById('favouritesVisibility').style.display = 'flex';
|
||||
favoritesGroup.style.display = 'flex';
|
||||
} else {
|
||||
if (favoritesList.length > 0) {
|
||||
iconElement.textContent = 'visibility';
|
||||
document.getElementById('favouritesVisibility').style.display = 'flex';
|
||||
favoritesGroup.style.display = 'none';
|
||||
} else {
|
||||
document.getElementById('favouritesVisibility').style.display = 'none';
|
||||
|
||||
@@ -43,37 +43,36 @@ function toolsManager() {
|
||||
}
|
||||
|
||||
window.tooltipSetup = () => {
|
||||
const tooltipElements = document.querySelectorAll("[title]");
|
||||
const tooltipElements = document.querySelectorAll('[title]');
|
||||
|
||||
tooltipElements.forEach((element) => {
|
||||
const tooltipText = element.getAttribute("title");
|
||||
element.removeAttribute("title");
|
||||
|
||||
const customTooltip = document.createElement("div");
|
||||
customTooltip.className = "btn-tooltip";
|
||||
const tooltipText = element.getAttribute('title');
|
||||
element.removeAttribute('title');
|
||||
element.setAttribute('data-title', 'tooltipText');
|
||||
const customTooltip = document.createElement('div');
|
||||
customTooltip.className = 'btn-tooltip';
|
||||
customTooltip.textContent = tooltipText;
|
||||
|
||||
document.body.appendChild(customTooltip);
|
||||
|
||||
element.addEventListener("mouseenter", (event) => {
|
||||
customTooltip.style.display = "block";
|
||||
element.addEventListener('mouseenter', (event) => {
|
||||
customTooltip.style.display = 'block';
|
||||
customTooltip.style.left = `${event.pageX + 10}px`; // Position tooltip slightly away from the cursor
|
||||
customTooltip.style.top = `${event.pageY + 10}px`;
|
||||
});
|
||||
|
||||
// Update the position of the tooltip as the user moves the mouse
|
||||
element.addEventListener("mousemove", (event) => {
|
||||
element.addEventListener('mousemove', (event) => {
|
||||
customTooltip.style.left = `${event.pageX + 10}px`;
|
||||
customTooltip.style.top = `${event.pageY + 10}px`;
|
||||
});
|
||||
|
||||
|
||||
// Hide the tooltip when the mouse leaves
|
||||
element.addEventListener("mouseleave", () => {
|
||||
customTooltip.style.display = "none";
|
||||
element.addEventListener('mouseleave', () => {
|
||||
customTooltip.style.display = 'none';
|
||||
});
|
||||
});
|
||||
}
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
};
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
tooltipSetup();
|
||||
});
|
||||
|
||||
@@ -127,14 +127,9 @@ function adjustVisibleElements() {
|
||||
}
|
||||
|
||||
function adjustContainerAlignment() {
|
||||
console.log('Adjusting container alignment');
|
||||
|
||||
document.querySelectorAll('.features-container').forEach((parent) => {
|
||||
parent.querySelectorAll('.feature-rows').forEach((container) => {
|
||||
const childElements = Array.from(container.children);
|
||||
|
||||
const containerWidth = parent.offsetWidth;
|
||||
console.log(containerWidth < 32 * parseFloat(getComputedStyle(document.documentElement).fontSize));
|
||||
if (containerWidth < 32 * parseFloat(getComputedStyle(document.documentElement).fontSize)) {
|
||||
container.classList.add('single-column');
|
||||
} else {
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
TabContainer = {
|
||||
initTabGroups() {
|
||||
const groups = document.querySelectorAll(".tab-group");
|
||||
const groups = document.querySelectorAll('.tab-group');
|
||||
const unloadedGroups = [...groups].filter((g) => !g.initialised);
|
||||
unloadedGroups.forEach((group) => {
|
||||
const containers = group.querySelectorAll(".tab-container");
|
||||
const tabTitles = [...containers].map((c) => c.getAttribute("title"));
|
||||
|
||||
const tabList = document.createElement("div");
|
||||
tabList.classList.add("tab-buttons");
|
||||
const containers = group.querySelectorAll('.tab-container');
|
||||
const tabTitles = [...containers].map((c) => c.getAttribute('data-title'));
|
||||
const tabList = document.createElement('div');
|
||||
tabList.classList.add('tab-buttons');
|
||||
tabTitles.forEach((title) => {
|
||||
const tabButton = document.createElement("button");
|
||||
const tabButton = document.createElement('button');
|
||||
tabButton.innerHTML = title;
|
||||
tabButton.onclick = (e) => {
|
||||
this.setActiveTab(e.target);
|
||||
@@ -24,15 +23,15 @@ TabContainer = {
|
||||
});
|
||||
},
|
||||
setActiveTab(tabButton) {
|
||||
const group = tabButton.closest(".tab-group");
|
||||
const group = tabButton.closest('.tab-group');
|
||||
|
||||
group.querySelectorAll(".active").forEach((el) => el.classList.remove("active"));
|
||||
group.querySelectorAll('.active').forEach((el) => el.classList.remove('active'));
|
||||
|
||||
tabButton.classList.add("active");
|
||||
group.querySelector(`[title="${tabButton.innerHTML}"]`).classList.add("active");
|
||||
tabButton.classList.add('active');
|
||||
group.querySelector(`[data-title="${tabButton.innerHTML}"]`).classList.add('active');
|
||||
},
|
||||
};
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
TabContainer.initTabGroups();
|
||||
});
|
||||
|
||||
@@ -17,7 +17,9 @@
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6 bg-card">
|
||||
<div class="tool-header">
|
||||
<span class="material-symbols-rounded tool-header-icon advance">cut</span>
|
||||
<svg class="material-symbols-rounded tool-header-icon advance">
|
||||
<use xlink:href="/images/split-auto.svg#icon-split-auto"></use>
|
||||
</svg>
|
||||
<span class="tool-header-text" th:text="#{autoSplitPDF.header}"></span>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,42 +1,42 @@
|
||||
<th:block th:fragment="langs">
|
||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="bg_BG"> <img th:src="@{'/images/flags/bg.svg'}" alt="icon" width="20" height="15"> Български</a>
|
||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="ar_AR"> <img th:src="@{'/images/flags/sa.svg'}" alt="icon" width="20" height="15"> العربية</a>
|
||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="ca_CA"> <img th:src="@{'/images/flags/es-ct.svg'}" alt="icon" width="20" height="15"> Català</a>
|
||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="zh_CN"> <img th:src="@{'/images/flags/cn.svg'}" alt="icon" width="20" height="15"> 简体中文</a>
|
||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="zh_TW"> <img th:src="@{'/images/flags/tw.svg'}" alt="icon" width="20" height="15"> 繁體中文</a>
|
||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="zh_BO"> <img th:src="@{'/images/flags/cn.svg'}" alt="icon" width="20" height="15"> བོད་ཡིག</a>
|
||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="az_AZ"> <img th:src="@{'/images/flags/az.svg'}" alt="icon" width="20" height="15"> Azərbaycan Dili</a>
|
||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="da_DK"> <img th:src="@{'/images/flags/dk.svg'}" alt="icon" width="20" height="15"> Dansk</a>
|
||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="de_DE"> <img th:src="@{'/images/flags/de.svg'}" alt="icon" width="20" height="15"> Deutsch</a>
|
||||
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'bg_BG')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="bg_BG"> <img th:src="@{'/images/flags/bg.svg'}" alt="icon" width="20" height="15"> Български</a>
|
||||
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'ar_AR')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="ar_AR"> <img th:src="@{'/images/flags/sa.svg'}" alt="icon" width="20" height="15"> العربية</a>
|
||||
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'ca_CA')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="ca_CA"> <img th:src="@{'/images/flags/es-ct.svg'}" alt="icon" width="20" height="15"> Català</a>
|
||||
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'zh_CN')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="zh_CN"> <img th:src="@{'/images/flags/cn.svg'}" alt="icon" width="20" height="15"> 简体中文</a>
|
||||
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'zh_TW')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="zh_TW"> <img th:src="@{'/images/flags/tw.svg'}" alt="icon" width="20" height="15"> 繁體中文</a>
|
||||
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'zh_BO')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="zh_BO"> <img th:src="@{'/images/flags/cn.svg'}" alt="icon" width="20" height="15"> བོད་ཡིག</a>
|
||||
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'az_AZ')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="az_AZ"> <img th:src="@{'/images/flags/az.svg'}" alt="icon" width="20" height="15"> Azərbaycan Dili</a>
|
||||
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'da_DK')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="da_DK"> <img th:src="@{'/images/flags/dk.svg'}" alt="icon" width="20" height="15"> Dansk</a>
|
||||
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'de_DE')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="de_DE"> <img th:src="@{'/images/flags/de.svg'}" alt="icon" width="20" height="15"> Deutsch</a>
|
||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="en_GB"> <img th:src="@{'/images/flags/gb.svg'}" alt="icon" width="20" height="15"> English (GB)</a>
|
||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="en_US"> <img th:src="@{'/images/flags/us.svg'}" alt="icon" width="20" height="15"> English (US)</a>
|
||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="eu_ES"> <img th:src="@{'/images/flags/eu.svg'}" alt="icon" width="20" height="15"> Euskara</a>
|
||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="es_ES"> <img th:src="@{'/images/flags/es.svg'}" alt="icon" width="20" height="15"> Español</a>
|
||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="fr_FR"> <img th:src="@{'/images/flags/fr.svg'}" alt="icon" width="20" height="15"> Français</a>
|
||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="id_ID"> <img th:src="@{'/images/flags/id.svg'}" alt="icon" width="20" height="15"> Indonesia</a>
|
||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="ga_IE"> <img th:src="@{'/images/flags/ie.svg'}" alt="icon" width="20" height="15"> Irish</a>
|
||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="it_IT"> <img th:src="@{'/images/flags/it.svg'}" alt="icon" width="20" height="15"> Italiano</a>
|
||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="nl_NL"> <img th:src="@{'/images/flags/nl.svg'}" alt="icon" width="20" height="15"> Nederlands</a>
|
||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="fa_IR"> <img th:src="@{'/images/flags/ir.svg'}" alt="icon" width="20" height="15"> پارسی</a>
|
||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="pl_PL"> <img th:src="@{'/images/flags/pl.svg'}" alt="icon" width="20" height="15"> Polski</a>
|
||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="pt_BR"> <img th:src="@{'/images/flags/pt_br.svg'}" alt="icon" width="20" height="15"> Português (BR)</a>
|
||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="pt_PT"> <img th:src="@{'/images/flags/pt_pt.svg'}" alt="icon" width="20" height="15"> Português (PT)</a>
|
||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="ro_RO"> <img th:src="@{'/images/flags/ro.svg'}" alt="icon" width="20" height="15"> Romanian</a>
|
||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="sk_SK"> <img th:src="@{'/images/flags/sk.svg'}" alt="icon" width="20" height="15"> Slovensky</a>
|
||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="sl_SI"> <img th:src="@{'/images/flags/si.svg'}" alt="icon" width="20" height="15"> Slovenian</a>
|
||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="sv_SE"> <img th:src="@{'/images/flags/se.svg'}" alt="icon" width="20" height="15"> Svenska</a>
|
||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="tr_TR"> <img th:src="@{'/images/flags/tr.svg'}" alt="icon" width="20" height="15"> Türkçe</a>
|
||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="ru_RU"> <img th:src="@{'/images/flags/ru.svg'}" alt="icon" width="20" height="15"> Русский</a>
|
||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="ko_KR"> <img th:src="@{'/images/flags/kr.svg'}" alt="icon" width="20" height="15"> 한국어</a>
|
||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="ja_JP"> <img th:src="@{'/images/flags/jp.svg'}" alt="icon" width="20" height="15"> 日本語</a>
|
||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="el_GR"> <img th:src="@{'/images/flags/gr.svg'}" alt="icon" width="20" height="15"> Ελληνικά</a>
|
||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="hu_HU"> <img th:src="@{'/images/flags/hu.svg'}" alt="icon" width="20" height="15"> Hungarian</a>
|
||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="hi_IN"> <img th:src="@{'/images/flags/in.svg'}" alt="icon" width="20" height="15"> हिन्दी</a>
|
||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="sr_LATN_RS"> <img th:src="@{'/images/flags/rs.svg'}" alt="icon" width="20" height="15"> Srpski</a>
|
||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="uk_UA"> <img th:src="@{'/images/flags/ua.svg'}" alt="icon" width="20" height="15"> Українська</a>
|
||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="cs_CZ"> <img th:src="@{'/images/flags/cz.svg'}" alt="icon" width="20" height="15"> Česky</a>
|
||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="hr_HR"> <img th:src="@{'/images/flags/hr.svg'}" alt="icon" width="20" height="15"> Hrvatski</a>
|
||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="no_NB"> <img th:src="@{'/images/flags/no.svg'}" alt="icon" width="20" height="15"> Norsk</a>
|
||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="th_TH"> <img th:src="@{'/images/flags/th.svg'}" alt="icon" width="20" height="15"> ไทย</a>
|
||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="vi_VN"> <img th:src="@{'/images/flags/vn.svg'}" alt="icon" width="20" height="15"> Tiếng Việt</a>
|
||||
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'en_US')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="en_US"> <img th:src="@{'/images/flags/us.svg'}" alt="icon" width="20" height="15"> English (US)</a>
|
||||
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'eu_ES')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="eu_ES"> <img th:src="@{'/images/flags/eu.svg'}" alt="icon" width="20" height="15"> Euskara</a>
|
||||
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'es_ES')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="es_ES"> <img th:src="@{'/images/flags/es.svg'}" alt="icon" width="20" height="15"> Español</a>
|
||||
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'fr_FR')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="fr_FR"> <img th:src="@{'/images/flags/fr.svg'}" alt="icon" width="20" height="15"> Français</a>
|
||||
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'id_ID')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="id_ID"> <img th:src="@{'/images/flags/id.svg'}" alt="icon" width="20" height="15"> Indonesia</a>
|
||||
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'ga_IE')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="ga_IE"> <img th:src="@{'/images/flags/ie.svg'}" alt="icon" width="20" height="15"> Irish</a>
|
||||
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'it_IT')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="it_IT"> <img th:src="@{'/images/flags/it.svg'}" alt="icon" width="20" height="15"> Italiano</a>
|
||||
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'nl_NL')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="nl_NL"> <img th:src="@{'/images/flags/nl.svg'}" alt="icon" width="20" height="15"> Nederlands</a>
|
||||
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'fa_IR')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="fa_IR"> <img th:src="@{'/images/flags/ir.svg'}" alt="icon" width="20" height="15"> پارسی</a>
|
||||
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'pl_PL')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="pl_PL"> <img th:src="@{'/images/flags/pl.svg'}" alt="icon" width="20" height="15"> Polski</a>
|
||||
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'pt_BR')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="pt_BR"> <img th:src="@{'/images/flags/pt_br.svg'}" alt="icon" width="20" height="15"> Português (BR)</a>
|
||||
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'pt_PT')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="pt_PT"> <img th:src="@{'/images/flags/pt_pt.svg'}" alt="icon" width="20" height="15"> Português (PT)</a>
|
||||
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'ro_RO')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="ro_RO"> <img th:src="@{'/images/flags/ro.svg'}" alt="icon" width="20" height="15"> Romanian</a>
|
||||
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'sk_SK')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="sk_SK"> <img th:src="@{'/images/flags/sk.svg'}" alt="icon" width="20" height="15"> Slovensky</a>
|
||||
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'sl_SI')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="sl_SI"> <img th:src="@{'/images/flags/si.svg'}" alt="icon" width="20" height="15"> Slovenian</a>
|
||||
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'sv_SE')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="sv_SE"> <img th:src="@{'/images/flags/se.svg'}" alt="icon" width="20" height="15"> Svenska</a>
|
||||
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'tr_TR')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="tr_TR"> <img th:src="@{'/images/flags/tr.svg'}" alt="icon" width="20" height="15"> Türkçe</a>
|
||||
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'ru_RU')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="ru_RU"> <img th:src="@{'/images/flags/ru.svg'}" alt="icon" width="20" height="15"> Русский</a>
|
||||
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'ko_KR')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="ko_KR"> <img th:src="@{'/images/flags/kr.svg'}" alt="icon" width="20" height="15"> 한국어</a>
|
||||
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'ja_JP')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="ja_JP"> <img th:src="@{'/images/flags/jp.svg'}" alt="icon" width="20" height="15"> 日本語</a>
|
||||
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'el_GR')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="el_GR"> <img th:src="@{'/images/flags/gr.svg'}" alt="icon" width="20" height="15"> Ελληνικά</a>
|
||||
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'hu_HU')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="hu_HU"> <img th:src="@{'/images/flags/hu.svg'}" alt="icon" width="20" height="15"> Hungarian</a>
|
||||
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'hi_IN')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="hi_IN"> <img th:src="@{'/images/flags/in.svg'}" alt="icon" width="20" height="15"> हिन्दी</a>
|
||||
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'sr_LATN_RS')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="sr_LATN_RS"> <img th:src="@{'/images/flags/rs.svg'}" alt="icon" width="20" height="15"> Srpski</a>
|
||||
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'uk_UA')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="uk_UA"> <img th:src="@{'/images/flags/ua.svg'}" alt="icon" width="20" height="15"> Українська</a>
|
||||
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'cs_CZ')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="cs_CZ"> <img th:src="@{'/images/flags/cz.svg'}" alt="icon" width="20" height="15"> Česky</a>
|
||||
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'hr_HR')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="hr_HR"> <img th:src="@{'/images/flags/hr.svg'}" alt="icon" width="20" height="15"> Hrvatski</a>
|
||||
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'no_NB')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="no_NB"> <img th:src="@{'/images/flags/no.svg'}" alt="icon" width="20" height="15"> Norsk</a>
|
||||
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'th_TH')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="th_TH"> <img th:src="@{'/images/flags/th.svg'}" alt="icon" width="20" height="15"> ไทย</a>
|
||||
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'vi_VN')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="vi_VN"> <img th:src="@{'/images/flags/vn.svg'}" alt="icon" width="20" height="15"> Tiếng Việt</a>
|
||||
</th:block>
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
<th:block th:fragment="navbarEntry(endpoint, toolIcon, titleKey, descKey, tagKey, toolGroup)"
|
||||
th:if="${@endpointConfiguration.isEndpointEnabled(endpoint)}">
|
||||
th:if="${@endpointConfiguration.isEndpointEnabled(endpoint)}">
|
||||
<a th:id="@{${endpoint}}" class="dropdown-item" style="position:relative" th:href="@{${endpoint}}"
|
||||
th:data-bs-link="@{${endpoint}}"
|
||||
th:classappend="${endpoint.equals(currentPage)} ? ${toolGroup} + ' active' : '' + ${toolGroup}"
|
||||
th:data-bs-tags="#{${tagKey}}" th:data-bs-title='#{${titleKey}}'>
|
||||
<div style="height:2.5rem;align-items: center;display: flex" th:title="#{${descKey}}" class="icon" alt="icon"
|
||||
th:class="@{${toolGroup}}">
|
||||
<svg class="nav-icon" style="height: 2.7rem; width:2.7rem; align-items:center; display: flex; justify-content: center;">
|
||||
<use th:href="@{${toolIcon}}"></use>
|
||||
<svg class="nav-icon"
|
||||
style="height: 2.7rem; width:2.7rem; align-items:center; display: flex; justify-content: center;">
|
||||
<use th:xlink:href="@{${toolIcon}}"></use>
|
||||
</svg>
|
||||
<span class="icon-text" th:text="#{${titleKey}}"></span>
|
||||
</div>
|
||||
|
||||
@@ -148,7 +148,7 @@
|
||||
</div>
|
||||
<div class="feature-group-container">
|
||||
<div
|
||||
th:replace="~{fragments/card :: card(id='img-to-pdf', cardTitle=#{home.imageToPdf.title}, cardText=#{home.imageToPdf.desc}, cardLink='picture_as_pdf', toolIcon='picture_as_pdf', tags=#{imageToPdf.tags}, toolGroup='image')}">
|
||||
th:replace="~{fragments/card :: card(id='img-to-pdf', cardTitle=#{home.imageToPdf.title}, cardText=#{home.imageToPdf.desc}, cardLink='img-to-pdf', toolIcon='picture_as_pdf', tags=#{imageToPdf.tags}, toolGroup='image')}">
|
||||
</div>
|
||||
<div
|
||||
th:replace="~{fragments/card :: card(id='file-to-pdf', cardTitle=#{home.fileToPDF.title}, cardText=#{home.fileToPDF.desc}, cardLink='file-to-pdf', toolIcon='draft', tags=#{fileToPDF.tags}, toolGroup='convert')}">
|
||||
|
||||
@@ -55,10 +55,10 @@
|
||||
</span>
|
||||
<input type="text" id="searchBar" onkeyup="filterCards()" th:placeholder="#{home.searchBar}" autofocus>
|
||||
|
||||
<div style="display: flex;">
|
||||
<div style="display: flex; column-gap: 3rem; flex-wrap: wrap; margin-left:1rem">
|
||||
<div
|
||||
style="height:2.5rem; display: flex; align-items: center; cursor: pointer; justify-content: center;">
|
||||
<label for="sort-options">Sort by:</label>
|
||||
<label for="sort-options" th:text="#{home.sortBy}">Sort by:</label>
|
||||
<select id="sort-options" style="border:none;">
|
||||
<option value="alphabetical" th:text="#{home.alphabetical}"> </option>
|
||||
<!-- <option value="personal">Your most used</option> -->
|
||||
@@ -66,9 +66,8 @@
|
||||
<!-- <option value="server">Popularity in organisation</option> -->
|
||||
</select>
|
||||
</div>
|
||||
<div
|
||||
style="display: flex; margin-left:2rem;align-items: center; flex-wrap: wrap; align-content: flex-start; width: fit-content; max-width: 100%; gap:3rem; justify-content: center;">
|
||||
<div th:title="#{home.setFavorites}" style="display: flex; align-items: center; cursor: pointer;" onclick="toggleFavoritesMode()">
|
||||
<div style="display: flex; align-items: center; flex-wrap: wrap; align-content: flex-start; width: fit-content; max-width: 100%; gap:2rem; justify-content: center;">
|
||||
<div th:title="#{home.setFavorites}" style="display: flex; align-items: center; cursor: pointer;" onclick="toggleFavoritesMode()">
|
||||
<span class="material-symbols-rounded toggle-favourites"
|
||||
style="font-size: 2rem; margin-left: 0.2rem;">
|
||||
star
|
||||
|
||||
@@ -1,86 +1,96 @@
|
||||
<!DOCTYPE html>
|
||||
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}" xmlns:th="https://www.thymeleaf.org">
|
||||
<head>
|
||||
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}"
|
||||
xmlns:th="https://www.thymeleaf.org">
|
||||
|
||||
<head>
|
||||
<th:block th:insert="~{fragments/common :: head(title=#{autoRedact.title}, header=#{autoRedact.header})}"></th:block>
|
||||
</head>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="page-container">
|
||||
<div id="content-wrap">
|
||||
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
|
||||
<br><br>
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6 bg-card">
|
||||
<div class="tool-header">
|
||||
<span class="material-symbols-rounded tool-header-icon security">playlist_remove</span>
|
||||
<span class="tool-header-text" th:text="#{autoRedact.header}"></span>
|
||||
</div>
|
||||
<form th:action="@{'api/v1/security/auto-redact'}" method="post" enctype="multipart/form-data">
|
||||
<div class="mb-3">
|
||||
<input type="file" class="form-control" id="fileInput" name="fileInput" required accept="application/pdf">
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="listOfText" class="form-label" th:text="#{autoRedact.textsToRedactLabel}"></label>
|
||||
<textarea class="form-control" id="listOfText" name="listOfText" rows="4" required th:placeholder="#{autoRedact.textsToRedactPlaceholder}"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="defaultColor" class="form-label" th:text="#{autoRedact.colorLabel}">Color</label>
|
||||
<select class="form-control" id="defaultColor" name="defaultColor" onchange="handleColorChange(this.value)">
|
||||
<option value="#000000" th:text="#{black}">Black</option>
|
||||
<option value="#FFFFFF" th:text="#{white}">White</option>
|
||||
<option value="#FF0000" th:text="#{red}">Red</option>
|
||||
<option value="#00FF00" th:text="#{green}">Green</option>
|
||||
<option value="#0000FF" th:text="#{blue}">Blue</option>
|
||||
<option value="custom" th:text="#{custom}">Custom...</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Custom Color Input -->
|
||||
<div class="mb-3" id="customColorContainer" style="display: none;">
|
||||
<label for="customColor" class="form-label" th:text="#{autoRedact.colorLabel}">Custom Color</label>
|
||||
<input type="text" class="form-control" id="customColor" name="redactColor" placeholder="#FF00FF">
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function handleColorChange(selectedValue) {
|
||||
if (selectedValue === "custom") {
|
||||
document.getElementById('customColorContainer').style.display = 'block';
|
||||
} else {
|
||||
document.getElementById('customColorContainer').style.display = 'none';
|
||||
document.getElementById('customColor').value = selectedValue;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<div class="mb-3 form-check">
|
||||
<input type="checkbox" id="useRegex" name="useRegex">
|
||||
<label for="useRegex" th:text="#{autoRedact.useRegexLabel}"></label>
|
||||
</div>
|
||||
|
||||
<div class="mb-3 form-check">
|
||||
<input type="checkbox" id="wholeWordSearch" name="wholeWordSearch">
|
||||
<label for="wholeWordSearch" th:text="#{autoRedact.wholeWordSearchLabel}"></label>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="customPadding" class="form-label" th:text="#{autoRedact.customPaddingLabel}"></label>
|
||||
<input type="number" step="0.1" class="form-control" id="customPadding" name="customPadding" value="0.1">
|
||||
</div>
|
||||
|
||||
<div class="mb-3 form-check">
|
||||
<input type="checkbox" id="convertPDFToImage" name="convertPDFToImage" checked>
|
||||
<label for="convertPDFToImage" th:text="#{autoRedact.convertPDFToImageLabel}"></label>
|
||||
</div>
|
||||
|
||||
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{autoRedact.submitButton}"></button>
|
||||
</form>
|
||||
<body>
|
||||
<div id="page-container">
|
||||
<div id="content-wrap">
|
||||
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
|
||||
<br><br>
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6 bg-card">
|
||||
<div class="tool-header">
|
||||
<svg class="material-symbols-rounded tool-header-icon security">
|
||||
<use xlink:href="/images/redact-auto.svg#icon-redact-auto"></use>
|
||||
</svg>
|
||||
<span class="tool-header-text" th:text="#{autoRedact.header}"></span>
|
||||
</div>
|
||||
<form th:action="@{'api/v1/security/auto-redact'}" method="post" enctype="multipart/form-data">
|
||||
<div class="mb-3">
|
||||
<input type="file" class="form-control" id="fileInput" name="fileInput" required
|
||||
accept="application/pdf">
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="listOfText" class="form-label" th:text="#{autoRedact.textsToRedactLabel}"></label>
|
||||
<textarea class="form-control" id="listOfText" name="listOfText" rows="4" required
|
||||
th:placeholder="#{autoRedact.textsToRedactPlaceholder}"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="defaultColor" class="form-label" th:text="#{autoRedact.colorLabel}">Color</label>
|
||||
<select class="form-control" id="defaultColor" name="defaultColor"
|
||||
onchange="handleColorChange(this.value)">
|
||||
<option value="#000000" th:text="#{black}">Black</option>
|
||||
<option value="#FFFFFF" th:text="#{white}">White</option>
|
||||
<option value="#FF0000" th:text="#{red}">Red</option>
|
||||
<option value="#00FF00" th:text="#{green}">Green</option>
|
||||
<option value="#0000FF" th:text="#{blue}">Blue</option>
|
||||
<option value="custom" th:text="#{custom}">Custom...</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Custom Color Input -->
|
||||
<div class="mb-3" id="customColorContainer" style="display: none;">
|
||||
<label for="customColor" class="form-label" th:text="#{autoRedact.colorLabel}">Custom Color</label>
|
||||
<input type="text" class="form-control" id="customColor" name="redactColor" placeholder="#FF00FF">
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function handleColorChange(selectedValue) {
|
||||
if (selectedValue === "custom") {
|
||||
document.getElementById('customColorContainer').style.display = 'block';
|
||||
} else {
|
||||
document.getElementById('customColorContainer').style.display = 'none';
|
||||
document.getElementById('customColor').value = selectedValue;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<div class="mb-3 form-check">
|
||||
<input type="checkbox" id="useRegex" name="useRegex">
|
||||
<label for="useRegex" th:text="#{autoRedact.useRegexLabel}"></label>
|
||||
</div>
|
||||
|
||||
<div class="mb-3 form-check">
|
||||
<input type="checkbox" id="wholeWordSearch" name="wholeWordSearch">
|
||||
<label for="wholeWordSearch" th:text="#{autoRedact.wholeWordSearchLabel}"></label>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="customPadding" class="form-label" th:text="#{autoRedact.customPaddingLabel}"></label>
|
||||
<input type="number" step="0.1" class="form-control" id="customPadding" name="customPadding"
|
||||
value="0.1">
|
||||
</div>
|
||||
|
||||
<div class="mb-3 form-check">
|
||||
<input type="checkbox" id="convertPDFToImage" name="convertPDFToImage" checked>
|
||||
<label for="convertPDFToImage" th:text="#{autoRedact.convertPDFToImageLabel}"></label>
|
||||
</div>
|
||||
|
||||
<button type="submit" id="submitBtn" class="btn btn-primary"
|
||||
th:text="#{autoRedact.submitButton}"></button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
|
||||
</div>
|
||||
</body>
|
||||
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -31,7 +31,9 @@
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6 bg-card">
|
||||
<div class="tool-header">
|
||||
<span class="material-symbols-rounded tool-header-icon security">playlist_remove</span>
|
||||
<svg class="material-symbols-rounded tool-header-icon security">
|
||||
<use xlink:href="/images/redact-manual.svg#icon-redact-manual"></use>
|
||||
</svg>
|
||||
<span class="tool-header-text" th:text="#{redact.header}"></span>
|
||||
</div>
|
||||
<form th:action="@{'api/v1/security/redact'}" method="post" enctype="multipart/form-data">
|
||||
|
||||
@@ -43,13 +43,13 @@
|
||||
</div>
|
||||
<script type="module" th:src="@{'/pdfjs-legacy/pdf.mjs'}"></script>
|
||||
<div class="tab-group show-on-file-selected">
|
||||
<div class="tab-container" th:title="#{sign.upload}">
|
||||
<div class="tab-container" th:title="#{sign.upload}" th:data-title="#{sign.upload}">
|
||||
<div
|
||||
th:replace="~{fragments/common :: fileSelector(name='image-upload', disableMultipleFiles=false, multipleInputsForSingleRequest=true, accept='image/*', inputText=#{imgPrompt})}">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab-container drawing-pad-container" th:title="#{sign.draw}">
|
||||
<div class="tab-container drawing-pad-container" th:title="#{sign.draw}" th:data-title="#{sign.draw}">
|
||||
<canvas id="drawing-pad-canvas"></canvas>
|
||||
<br>
|
||||
<button id="clear-signature" class="btn btn-outline-danger mt-2" onclick="signaturePad.clear()"
|
||||
@@ -58,7 +58,7 @@
|
||||
th:text="#{sign.add}"></button>
|
||||
</div>
|
||||
|
||||
<div class="tab-container" th:title="#{sign.saved}">
|
||||
<div class="tab-container" th:title="#{sign.saved}" th:data-title="#{sign.saved}">
|
||||
<div class="saved-signatures-section" th:if="${not #lists.isEmpty(signatures)}">
|
||||
|
||||
<!-- Preview Modal -->
|
||||
@@ -130,7 +130,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab-container" th:title="#{sign.text}">
|
||||
<div class="tab-container" th:title="#{sign.text}" th:data-title="#{sign.text}">
|
||||
<label class="form-check-label" for="sigText" th:text="#{text}"></label>
|
||||
<textarea class="form-control" id="sigText" name="sigText" rows="3"></textarea>
|
||||
<label th:text="#{font}"></label>
|
||||
|
||||
@@ -1,41 +1,52 @@
|
||||
<!DOCTYPE html>
|
||||
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}" xmlns:th="https://www.thymeleaf.org">
|
||||
<head>
|
||||
<th:block th:insert="~{fragments/common :: head(title=#{split-by-size-or-count.title}, header=#{split-by-size-or-count.header})}"></th:block>
|
||||
</head>
|
||||
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}"
|
||||
xmlns:th="https://www.thymeleaf.org">
|
||||
|
||||
<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>
|
||||
<br><br>
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6 bg-card">
|
||||
<div class="tool-header">
|
||||
<span class="material-symbols-rounded tool-header-icon advance">vertical_split</span>
|
||||
<span class="tool-header-text" th:text="#{split-by-size-or-count.header}"></span>
|
||||
</div>
|
||||
<form method="post" enctype="multipart/form-data" th:action="@{'/api/v1/general/split-by-size-or-count'}">
|
||||
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
|
||||
<label for="splitType" th:text="#{split-by-size-or-count.type.label}">Split Type</label>
|
||||
<select id="splitType" name="splitType" class="form-control">
|
||||
<option value="0" th:text="#{split-by-size-or-count.type.size}">Size</option>
|
||||
<option value="1" th:text="#{split-by-size-or-count.type.pageCount}">Page Count</option>
|
||||
<option value="2" th:text="#{split-by-size-or-count.type.docCount}">Document Count</option>
|
||||
</select>
|
||||
<br>
|
||||
<label for="splitValue" th:text="#{split-by-size-or-count.value.label}">Split Value</label>
|
||||
<input type="text" id="splitValue" name="splitValue" class="form-control" required th:placeholder="#{split-by-size-or-count.value.placeholder}">
|
||||
<br>
|
||||
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{split-by-size-or-count.submit}">Submit</button>
|
||||
</form>
|
||||
<head>
|
||||
<th:block
|
||||
th:insert="~{fragments/common :: head(title=#{split-by-size-or-count.title}, header=#{split-by-size-or-count.header})}">
|
||||
</th:block>
|
||||
</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>
|
||||
<br><br>
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6 bg-card">
|
||||
<div class="tool-header">
|
||||
<svg class="material-symbols-rounded tool-header-icon advance">
|
||||
<use xlink:href="/images/split-size.svg#icon-split-size"></use>
|
||||
</svg>
|
||||
<span class="tool-header-text" th:text="#{split-by-size-or-count.header}"></span>
|
||||
</div>
|
||||
<form method="post" enctype="multipart/form-data" th:action="@{'/api/v1/general/split-by-size-or-count'}">
|
||||
<div
|
||||
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}">
|
||||
</div>
|
||||
<label for="splitType" th:text="#{split-by-size-or-count.type.label}">Split Type</label>
|
||||
<select id="splitType" name="splitType" class="form-control">
|
||||
<option value="0" th:text="#{split-by-size-or-count.type.size}">Size</option>
|
||||
<option value="1" th:text="#{split-by-size-or-count.type.pageCount}">Page Count</option>
|
||||
<option value="2" th:text="#{split-by-size-or-count.type.docCount}">Document Count</option>
|
||||
</select>
|
||||
<br>
|
||||
<label for="splitValue" th:text="#{split-by-size-or-count.value.label}">Split Value</label>
|
||||
<input type="text" id="splitValue" name="splitValue" class="form-control" required
|
||||
th:placeholder="#{split-by-size-or-count.value.placeholder}">
|
||||
<br>
|
||||
<button type="submit" id="submitBtn" class="btn btn-primary"
|
||||
th:text="#{split-by-size-or-count.submit}">Submit</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
|
||||
</div>
|
||||
</body>
|
||||
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -1,66 +1,72 @@
|
||||
<!DOCTYPE html>
|
||||
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}"
|
||||
xmlns:th="https://www.thymeleaf.org">
|
||||
xmlns:th="https://www.thymeleaf.org">
|
||||
|
||||
<head>
|
||||
<th:block th:insert="~{fragments/common :: head(title=#{splitByChapters.title}, header=#{splitByChapters.header})}"></th:block>
|
||||
<th:block th:insert="~{fragments/common :: head(title=#{splitByChapters.title}, header=#{splitByChapters.header})}">
|
||||
</th:block>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="page-container">
|
||||
<div id="content-wrap">
|
||||
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
|
||||
<br><br>
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6 bg-card">
|
||||
<div class="tool-header">
|
||||
<span class="material-symbols-rounded tool-header-icon organize">book</span>
|
||||
<span class="tool-header-text" th:text="#{splitByChapters.header}"></span>
|
||||
<div id="page-container">
|
||||
<div id="content-wrap">
|
||||
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
|
||||
<br><br>
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6 bg-card">
|
||||
<div class="tool-header">
|
||||
<svg class="material-symbols-rounded tool-header-icon advance">
|
||||
<use xlink:href="/images/split-chapters.svg#icon-split-chapters"></use>
|
||||
</svg>
|
||||
<span class="tool-header-text" th:text="#{splitByChapters.header}"></span>
|
||||
</div>
|
||||
<form th:action="@{'/api/v1/general/split-pdf-by-chapters'}" method="post" enctype="multipart/form-data">
|
||||
<div
|
||||
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}">
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="bookmarkLevel" th:text="#{splitByChapters.bookmarkLevel}"></label>
|
||||
<input type="number" class="form-control" id="bookmarkLevel" name="bookmarkLevel" min="0" value="0"
|
||||
required>
|
||||
</div>
|
||||
|
||||
<div class="mb-3 form-check">
|
||||
<input type="checkbox" class="form-check-input" id="includeMetadata" name="includeMetadata">
|
||||
<label class="form-check-label" for="includeMetadata"
|
||||
th:text="#{splitByChapters.includeMetadata}"></label>
|
||||
<input type="hidden" name="includeMetadata" value="false" />
|
||||
</div>
|
||||
|
||||
<div class="mb-3 form-check">
|
||||
<input type="checkbox" class="form-check-input" id="allowDuplicates" name="allowDuplicates">
|
||||
<label class="form-check-label" for="allowDuplicates"
|
||||
th:text="#{splitByChapters.allowDuplicates}"></label>
|
||||
<input type="hidden" name="allowDuplicates" value="false" />
|
||||
</div>
|
||||
|
||||
<p>
|
||||
<a class="btn btn-outline-primary" data-bs-toggle="collapse" href="#info" role="button"
|
||||
aria-expanded="false" aria-controls="info" th:text="#{info}"></a>
|
||||
</p>
|
||||
<div class="collapse" id="info">
|
||||
<p th:text="#{splitByChapters.desc.1}"></p>
|
||||
<p th:text="#{splitByChapters.desc.2}"></p>
|
||||
<p th:text="#{splitByChapters.desc.3}"></p>
|
||||
<p th:text="#{splitByChapters.desc.4}"></p>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{splitByChapters.submit}"></button>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
<form th:action="@{'/api/v1/general/split-pdf-by-chapters'}" method="post" enctype="multipart/form-data">
|
||||
<div
|
||||
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}">
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="bookmarkLevel" th:text="#{splitByChapters.bookmarkLevel}"></label>
|
||||
<input type="number" class="form-control" id="bookmarkLevel" name="bookmarkLevel" min="0" value="0" required>
|
||||
</div>
|
||||
|
||||
<div class="mb-3 form-check">
|
||||
<input type="checkbox" class="form-check-input" id="includeMetadata" name="includeMetadata">
|
||||
<label class="form-check-label" for="includeMetadata" th:text="#{splitByChapters.includeMetadata}"></label>
|
||||
<input type="hidden" name="includeMetadata" value="false" />
|
||||
</div>
|
||||
|
||||
<div class="mb-3 form-check">
|
||||
<input type="checkbox" class="form-check-input" id="allowDuplicates" name="allowDuplicates">
|
||||
<label class="form-check-label" for="allowDuplicates" th:text="#{splitByChapters.allowDuplicates}"></label>
|
||||
<input type="hidden" name="allowDuplicates" value="false" />
|
||||
</div>
|
||||
|
||||
<p>
|
||||
<a class="btn btn-outline-primary" data-bs-toggle="collapse" href="#info" role="button"
|
||||
aria-expanded="false" aria-controls="info" th:text="#{info}"></a>
|
||||
</p>
|
||||
<div class="collapse" id="info">
|
||||
<p th:text="#{splitByChapters.desc.1}"></p>
|
||||
<p th:text="#{splitByChapters.desc.2}"></p>
|
||||
<p th:text="#{splitByChapters.desc.3}"></p>
|
||||
<p th:text="#{splitByChapters.desc.4}"></p>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{splitByChapters.submit}"></button>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
|
||||
</div>
|
||||
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -15,10 +15,11 @@ public class FileToPdfTest {
|
||||
byte[] fileBytes = new byte[0]; // Sample file bytes
|
||||
String fileName = "test.html"; // Sample file name
|
||||
boolean htmlFormatsInstalled = true; // Sample boolean value
|
||||
boolean disableSanitize = false; // Sample boolean value
|
||||
|
||||
// Check if the method throws IOException
|
||||
assertThrows(IOException.class, () -> {
|
||||
FileToPdf.convertHtmlToPdf(request, fileBytes, fileName, htmlFormatsInstalled);
|
||||
FileToPdf.convertHtmlToPdf(request, fileBytes, fileName, htmlFormatsInstalled, disableSanitize);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
156
testing/test.sh
@@ -1,8 +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"
|
||||
@@ -26,18 +23,18 @@ check_health() {
|
||||
local end=$((SECONDS+60))
|
||||
|
||||
echo -n "Waiting for $service_name to become healthy..."
|
||||
until [ "$(docker inspect --format='{{json .State.Health.Status}}' "$service_name")" == '"healthy"' ] || [ $SECONDS -ge $end ]; do
|
||||
sleep 3
|
||||
echo -n "."
|
||||
if [ $SECONDS -ge $end ]; then
|
||||
echo -e "\n$service_name health check timed out after 80 seconds."
|
||||
echo "Printing logs for $service_name:"
|
||||
until [ "$(docker inspect --format='{{json .State.Health.Status}}' "$service_name")" == '"healthy"' ] || [ $SECONDS -ge $end ]; do
|
||||
sleep 3
|
||||
echo -n "."
|
||||
if [ $SECONDS -ge $end ]; then
|
||||
echo -e "\n$service_name health check timed out after 80 seconds."
|
||||
echo "Printing logs for $service_name:"
|
||||
docker logs "$service_name"
|
||||
return 1
|
||||
fi
|
||||
done
|
||||
echo -e "\n$service_name is healthy!"
|
||||
echo "Printing logs for $service_name:"
|
||||
return 1
|
||||
fi
|
||||
done
|
||||
echo -e "\n$service_name is healthy!"
|
||||
echo "Printing logs for $service_name:"
|
||||
docker logs "$service_name"
|
||||
return 0
|
||||
}
|
||||
@@ -81,16 +78,10 @@ run_tests() {
|
||||
|
||||
# Main testing routine
|
||||
main() {
|
||||
SECONDS=0
|
||||
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
|
||||
@@ -98,28 +89,26 @@ main() {
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
# Building Docker images
|
||||
# docker build --no-cache --pull --build-arg VERSION_TAG=alpha -t stirlingtools/stirling-pdf:latest -f ./Dockerfile .
|
||||
docker build --no-cache --pull --build-arg VERSION_TAG=alpha -t stirlingtools/stirling-pdf:latest-ultra-lite -f ./Dockerfile.ultra-lite .
|
||||
|
||||
|
||||
# Test each configuration
|
||||
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")
|
||||
else
|
||||
failed_tests+=("Webpage-Accessibility-lite")
|
||||
echo "Webpage accessibility lite tests failed"
|
||||
fi
|
||||
cd "$PROJECT_ROOT"
|
||||
docker-compose -f "./exampleYmlFiles/docker-compose-latest-ultra-lite.yml" down
|
||||
|
||||
|
||||
#run_tests "Stirling-PDF" "./exampleYmlFiles/docker-compose-latest.yml"
|
||||
#docker-compose -f "./exampleYmlFiles/docker-compose-latest.yml" down
|
||||
echo "Testing webpage accessibility..."
|
||||
cd "testing"
|
||||
if ./test_webpages.sh -f webpage_urls.txt -b http://localhost:8080; then
|
||||
passed_tests+=("Webpage-Accessibility-lite")
|
||||
else
|
||||
failed_tests+=("Webpage-Accessibility-lite")
|
||||
echo "Webpage accessibility lite tests failed"
|
||||
fi
|
||||
cd "$PROJECT_ROOT"
|
||||
docker-compose -f "./exampleYmlFiles/docker-compose-latest-ultra-lite.yml" down
|
||||
|
||||
# run_tests "Stirling-PDF" "./exampleYmlFiles/docker-compose-latest.yml"
|
||||
# docker-compose -f "./exampleYmlFiles/docker-compose-latest.yml" down
|
||||
|
||||
export DOCKER_ENABLE_SECURITY=true
|
||||
# Run the gradlew build command and check if it fails
|
||||
@@ -128,72 +117,68 @@ main() {
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
# Building Docker images with security enabled
|
||||
# docker build --no-cache --pull --build-arg VERSION_TAG=alpha -t stirlingtools/stirling-pdf:latest -f ./Dockerfile .
|
||||
# docker build --no-cache --pull --build-arg VERSION_TAG=alpha -t stirlingtools/stirling-pdf:latest-ultra-lite -f ./Dockerfile.ultra-lite .
|
||||
# docker build --no-cache --pull --build-arg VERSION_TAG=alpha -t stirlingtools/stirling-pdf:latest -f ./Dockerfile .
|
||||
# docker build --no-cache --pull --build-arg VERSION_TAG=alpha -t stirlingtools/stirling-pdf:latest-ultra-lite -f ./Dockerfile.ultra-lite .
|
||||
docker build --no-cache --pull --build-arg VERSION_TAG=alpha -t stirlingtools/stirling-pdf:latest-fat -f ./Dockerfile.fat .
|
||||
|
||||
|
||||
|
||||
|
||||
# Test each configuration with security
|
||||
# run_tests "Stirling-PDF-Ultra-Lite-Security" "./exampleYmlFiles/docker-compose-latest-ultra-lite-security.yml"
|
||||
#docker-compose -f "./exampleYmlFiles/docker-compose-latest-ultra-lite-security.yml" down
|
||||
# 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-Ultra-Lite-Security" "./exampleYmlFiles/docker-compose-latest-ultra-lite-security.yml"
|
||||
# docker-compose -f "./exampleYmlFiles/docker-compose-latest-ultra-lite-security.yml" down
|
||||
# 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" "./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"
|
||||
|
||||
run_tests "Stirling-PDF-Security-Fat-with-login" "./exampleYmlFiles/test_cicd.yml"
|
||||
docker-compose -f "./exampleYmlFiles/docker-compose-latest-fat-security.yml" down
|
||||
|
||||
run_tests "Stirling-PDF-Security-Fat-with-login" "./exampleYmlFiles/test_cicd.yml"
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
cd "testing/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"
|
||||
echo "Printed docker logs of failed regression"
|
||||
fi
|
||||
cd "$PROJECT_ROOT"
|
||||
fi
|
||||
|
||||
docker-compose -f "./exampleYmlFiles/test_cicd.yml" down
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
cd "testing/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"
|
||||
echo "Printed docker logs of failed regression"
|
||||
fi
|
||||
cd "$PROJECT_ROOT"
|
||||
fi
|
||||
|
||||
docker-compose -f "./exampleYmlFiles/test_cicd.yml" down
|
||||
|
||||
# Report results
|
||||
echo "All tests completed in $SECONDS seconds."
|
||||
|
||||
|
||||
if [ ${#passed_tests[@]} -ne 0 ]; then
|
||||
echo "Passed tests:"
|
||||
fi
|
||||
if [ ${#passed_tests[@]} -ne 0 ]; then
|
||||
echo "Passed tests:"
|
||||
fi
|
||||
for test in "${passed_tests[@]}"; do
|
||||
echo -e "\e[32m$test\e[0m" # Green color for passed tests
|
||||
done
|
||||
|
||||
if [ ${#failed_tests[@]} -ne 0 ]; then
|
||||
echo "Failed tests:"
|
||||
fi
|
||||
if [ ${#failed_tests[@]} -ne 0 ]; then
|
||||
echo "Failed tests:"
|
||||
fi
|
||||
for test in "${failed_tests[@]}"; do
|
||||
echo -e "\e[31m$test\e[0m" # Red color for failed tests
|
||||
done
|
||||
|
||||
|
||||
|
||||
# Check if there are any failed tests and exit with an error code if so
|
||||
if [ ${#failed_tests[@]} -ne 0 ]; then
|
||||
echo "Some tests failed."
|
||||
@@ -202,7 +187,6 @@ main() {
|
||||
echo "All tests passed successfully."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
}
|
||||
|
||||
main
|
||||
main
|
||||
|
||||