Compare commits
155 Commits
formatting
...
filePath
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f7add727a6 | ||
|
|
cd947c2554 | ||
|
|
9422a30ebf | ||
|
|
e754e6012a | ||
|
|
3227da55e0 | ||
|
|
709a79c3d9 | ||
|
|
77bb15bc8b | ||
|
|
beaa86cbf9 | ||
|
|
b8303e3860 | ||
|
|
ed273a6e92 | ||
|
|
0136c25e1d | ||
|
|
cf03bdc17b | ||
|
|
093b882141 | ||
|
|
46507f10b6 | ||
|
|
54fbf666fd | ||
|
|
faecaf1ee4 | ||
|
|
a062b36ee5 | ||
|
|
00b6d60309 | ||
|
|
1e0121b4d6 | ||
|
|
5ba98e4411 | ||
|
|
d7a3708a13 | ||
|
|
73ac17942f | ||
|
|
116dfcc065 | ||
|
|
54e599a18b | ||
|
|
2d76927b3c | ||
|
|
41c269f208 | ||
|
|
38633d4db1 | ||
|
|
0ff45c656a | ||
|
|
bc282c6aa5 | ||
|
|
50575bc80b | ||
|
|
6f04f01c2b | ||
|
|
e80eaaa6d1 | ||
|
|
01288dafe8 | ||
|
|
e3c7b6f955 | ||
|
|
875f5a85ef | ||
|
|
ef174a1e8a | ||
|
|
958c214605 | ||
|
|
56ab9c3eee | ||
|
|
7b768db969 | ||
|
|
36c4d8552d | ||
|
|
4544fb7211 | ||
|
|
807639308a | ||
|
|
49fb634690 | ||
|
|
715efca25d | ||
|
|
a4c988c7b2 | ||
|
|
f45de05c99 | ||
|
|
d5faddbc85 | ||
|
|
5e173b92d4 | ||
|
|
574c474804 | ||
|
|
2e23149090 | ||
|
|
7be8db7832 | ||
|
|
702433d4c5 | ||
|
|
0e824005c3 | ||
|
|
8c2a468915 | ||
|
|
7d81ca799f | ||
|
|
0f24fa104e | ||
|
|
db1fa28230 | ||
|
|
9884c65b10 | ||
|
|
2abfe599ed | ||
|
|
cbdec6ec25 | ||
|
|
d4f25db647 | ||
|
|
0f1b94303f | ||
|
|
4dfdf2d8ec | ||
|
|
d6937b1784 | ||
|
|
3ae732352e | ||
|
|
f9b90692fb | ||
|
|
04f72f151d | ||
|
|
73ee9651a3 | ||
|
|
a0f3d7b7d4 | ||
|
|
7223b379e7 | ||
|
|
e833d688e7 | ||
|
|
74cab98a1b | ||
|
|
5c1abdd49e | ||
|
|
341a234b7c | ||
|
|
34ddc298cf | ||
|
|
f4ae4ac5c4 | ||
|
|
91aece2284 | ||
|
|
2a1dad80f0 | ||
|
|
f1bf42544d | ||
|
|
2fd8d8809b | ||
|
|
b2628d4c3f | ||
|
|
50fb665c29 | ||
|
|
b0881cdb4c | ||
|
|
988a877199 | ||
|
|
80f53e972a | ||
|
|
6697cccfa9 | ||
|
|
9820a3276d | ||
|
|
fb6cefa301 | ||
|
|
974b947fc3 | ||
|
|
4e45bae856 | ||
|
|
74c6ea7c60 | ||
|
|
15abef1b82 | ||
|
|
fb61fea3e4 | ||
|
|
f29d4db31e | ||
|
|
b86eac20ea | ||
|
|
7ccb9db9f9 | ||
|
|
e920eb0555 | ||
|
|
822ccbabdf | ||
|
|
0d2b5f605f | ||
|
|
76924ccd2f | ||
|
|
12e8aceb4c | ||
|
|
46261c55b5 | ||
|
|
d2e3c2a12c | ||
|
|
58bcb9fdb1 | ||
|
|
ae44c4c723 | ||
|
|
a7f900b959 | ||
|
|
c0873050a3 | ||
|
|
f09e70218d | ||
|
|
e0d37d2e10 | ||
|
|
70cc03fc63 | ||
|
|
4ee756f7dd | ||
|
|
67a08a7c05 | ||
|
|
d136f9bd31 | ||
|
|
a909592533 | ||
|
|
6a609712ba | ||
|
|
238db1aaff | ||
|
|
c08329ec48 | ||
|
|
1defa441d6 | ||
|
|
51577a0a69 | ||
|
|
0366bc6bf9 | ||
|
|
b6c66c47cd | ||
|
|
de6bfa2af2 | ||
|
|
2dd580cf57 | ||
|
|
5df445eabf | ||
|
|
d3e0c0bce1 | ||
|
|
5c0a606d66 | ||
|
|
226cbe1a8e | ||
|
|
f13945b05f | ||
|
|
889b612cf3 | ||
|
|
9aec9d3451 | ||
|
|
b65ac4032e | ||
|
|
e7f2576857 | ||
|
|
c877f122c4 | ||
|
|
ff99c46434 | ||
|
|
8f5c5211d1 | ||
|
|
125bebf9ff | ||
|
|
dd03875ece | ||
|
|
6456a28436 | ||
|
|
1bfa534dcd | ||
|
|
7b78c5cbf1 | ||
|
|
adb715f2f2 | ||
|
|
b3230f1e52 | ||
|
|
e5d62042c4 | ||
|
|
801a2a792b | ||
|
|
b8466c2b97 | ||
|
|
1006aa110e | ||
|
|
daae6bfd3e | ||
|
|
1795d5764a | ||
|
|
9dcb3009f0 | ||
|
|
ca9abf76be | ||
|
|
3870f73949 | ||
|
|
dc5b214932 | ||
|
|
bba3d65368 | ||
|
|
e3d2bd3b1b | ||
|
|
26340626df |
2
.github/CODEOWNERS
vendored
2
.github/CODEOWNERS
vendored
@@ -1,2 +1,2 @@
|
|||||||
# All PRs to V1 must be approved by Frooodle
|
# All PRs to V1 must be approved by Frooodle
|
||||||
* @Frooodle
|
* @Frooodle @reecebrowne @Ludy87 @DarioGii
|
||||||
|
|||||||
6
.github/dependabot.yml
vendored
6
.github/dependabot.yml
vendored
@@ -11,7 +11,13 @@ updates:
|
|||||||
interval: "weekly"
|
interval: "weekly"
|
||||||
open-pull-requests-limit: 10
|
open-pull-requests-limit: 10
|
||||||
rebase-strategy: "auto"
|
rebase-strategy: "auto"
|
||||||
|
|
||||||
- package-ecosystem: "docker"
|
- package-ecosystem: "docker"
|
||||||
directory: "/" # Location of Dockerfile
|
directory: "/" # Location of Dockerfile
|
||||||
schedule:
|
schedule:
|
||||||
interval: "weekly"
|
interval: "weekly"
|
||||||
|
|
||||||
|
- package-ecosystem: github-actions
|
||||||
|
directory: /
|
||||||
|
schedule:
|
||||||
|
interval: weekly
|
||||||
|
|||||||
13
.github/labeler-config.yml
vendored
13
.github/labeler-config.yml
vendored
@@ -30,6 +30,8 @@ Security:
|
|||||||
- any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/model/provider/**/*'
|
- any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/model/provider/**/*'
|
||||||
- any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/model/AuthenticationType.java'
|
- any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/model/AuthenticationType.java'
|
||||||
- any-glob-to-any-file: 'scripts/download-security-jar.sh'
|
- any-glob-to-any-file: 'scripts/download-security-jar.sh'
|
||||||
|
- any-glob-to-any-file: '.github/workflows/dependency-review.yml'
|
||||||
|
- any-glob-to-any-file: '.github/workflows/scorecards.yml'
|
||||||
|
|
||||||
API:
|
API:
|
||||||
- changed-files:
|
- changed-files:
|
||||||
@@ -37,6 +39,7 @@ API:
|
|||||||
- any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/controller/api/**/*'
|
- any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/controller/api/**/*'
|
||||||
- any-glob-to-any-file: 'scripts/png_to_webp.py'
|
- any-glob-to-any-file: 'scripts/png_to_webp.py'
|
||||||
- any-glob-to-any-file: 'split_photos.py'
|
- any-glob-to-any-file: 'split_photos.py'
|
||||||
|
- any-glob-to-any-file: '.github/workflows/swagger.yml'
|
||||||
|
|
||||||
Documentation:
|
Documentation:
|
||||||
- changed-files:
|
- changed-files:
|
||||||
@@ -46,17 +49,25 @@ Documentation:
|
|||||||
|
|
||||||
Docker:
|
Docker:
|
||||||
- changed-files:
|
- changed-files:
|
||||||
|
- any-glob-to-any-file: '.github/workflows/build.yml'
|
||||||
|
- any-glob-to-any-file: '.github/workflows/push-docker.yml'
|
||||||
- any-glob-to-any-file: 'Dockerfile'
|
- any-glob-to-any-file: 'Dockerfile'
|
||||||
- any-glob-to-any-file: 'Dockerfile-*'
|
- any-glob-to-any-file: 'Dockerfile.*'
|
||||||
- any-glob-to-any-file: 'exampleYmlFiles/*.yml'
|
- any-glob-to-any-file: 'exampleYmlFiles/*.yml'
|
||||||
|
- any-glob-to-any-file: 'scripts/download-security-jar.sh'
|
||||||
- any-glob-to-any-file: 'scripts/init.sh'
|
- any-glob-to-any-file: 'scripts/init.sh'
|
||||||
- any-glob-to-any-file: 'scripts/init-without-ocr.sh'
|
- any-glob-to-any-file: 'scripts/init-without-ocr.sh'
|
||||||
- any-glob-to-any-file: 'scripts/installFonts.sh'
|
- any-glob-to-any-file: 'scripts/installFonts.sh'
|
||||||
|
- any-glob-to-any-file: 'test.sh'
|
||||||
|
- any-glob-to-any-file: 'test2.sh'
|
||||||
|
|
||||||
Test:
|
Test:
|
||||||
- changed-files:
|
- changed-files:
|
||||||
- any-glob-to-any-file: 'cucumber/**/*'
|
- any-glob-to-any-file: 'cucumber/**/*'
|
||||||
- any-glob-to-any-file: 'src/test**/*'
|
- any-glob-to-any-file: 'src/test**/*'
|
||||||
|
- any-glob-to-any-file: '.pre-commit-config'
|
||||||
|
- any-glob-to-any-file: '.github/workflows/pre_commit.yml'
|
||||||
|
- any-glob-to-any-file: '.github/workflows/scorecards.yml'
|
||||||
|
|
||||||
Github:
|
Github:
|
||||||
- changed-files:
|
- changed-files:
|
||||||
|
|||||||
4
.github/release.yml
vendored
4
.github/release.yml
vendored
@@ -9,7 +9,7 @@ changelog:
|
|||||||
- title: Bug Fixes
|
- title: Bug Fixes
|
||||||
labels:
|
labels:
|
||||||
- Bug
|
- Bug
|
||||||
|
|
||||||
- title: Enhancements
|
- title: Enhancements
|
||||||
labels:
|
labels:
|
||||||
- enhancement
|
- enhancement
|
||||||
@@ -26,7 +26,7 @@ changelog:
|
|||||||
- title: Translation Changes
|
- title: Translation Changes
|
||||||
labels:
|
labels:
|
||||||
- Translation
|
- Translation
|
||||||
|
|
||||||
- title: Other Changes
|
- title: Other Changes
|
||||||
labels:
|
labels:
|
||||||
- "*"
|
- "*"
|
||||||
|
|||||||
61
.github/workflows/PR-Demo-Comment.yml
vendored
61
.github/workflows/PR-Demo-Comment.yml
vendored
@@ -4,18 +4,24 @@ on:
|
|||||||
issue_comment:
|
issue_comment:
|
||||||
types: [created]
|
types: [created]
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
check-comment:
|
check-comment:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
pull-requests: read
|
||||||
|
issues: read
|
||||||
if: |
|
if: |
|
||||||
github.event.issue.pull_request &&
|
github.event.issue.pull_request &&
|
||||||
(
|
(
|
||||||
contains(github.event.comment.body, 'prdeploy') ||
|
contains(github.event.comment.body, 'prdeploy') ||
|
||||||
contains(github.event.comment.body, 'deploypr')
|
contains(github.event.comment.body, 'deploypr')
|
||||||
)
|
)
|
||||||
&&
|
&&
|
||||||
(
|
(
|
||||||
github.event.comment.user.login == 'frooodle' ||
|
github.event.comment.user.login == 'frooodle' ||
|
||||||
github.event.comment.user.login == 'sf298' ||
|
github.event.comment.user.login == 'sf298' ||
|
||||||
github.event.comment.user.login == 'Ludy87' ||
|
github.event.comment.user.login == 'Ludy87' ||
|
||||||
github.event.comment.user.login == 'LaserKaspar' ||
|
github.event.comment.user.login == 'LaserKaspar' ||
|
||||||
@@ -28,9 +34,14 @@ jobs:
|
|||||||
pr_ref: ${{ steps.get-pr-info.outputs.ref }}
|
pr_ref: ${{ steps.get-pr-info.outputs.ref }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
- name: Harden Runner
|
||||||
|
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
||||||
|
with:
|
||||||
|
egress-policy: audit
|
||||||
|
|
||||||
- name: Get PR data
|
- name: Get PR data
|
||||||
id: get-pr
|
id: get-pr
|
||||||
uses: actions/github-script@v7
|
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
const prNumber = context.payload.issue.number;
|
const prNumber = context.payload.issue.number;
|
||||||
@@ -39,41 +50,49 @@ jobs:
|
|||||||
|
|
||||||
- name: Get PR repository and ref
|
- name: Get PR repository and ref
|
||||||
id: get-pr-info
|
id: get-pr-info
|
||||||
uses: actions/github-script@v7
|
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
const { owner, repo } = context.repo;
|
const { owner, repo } = context.repo;
|
||||||
const prNumber = context.payload.issue.number;
|
const prNumber = context.payload.issue.number;
|
||||||
|
|
||||||
const { data: pr } = await github.rest.pulls.get({
|
const { data: pr } = await github.rest.pulls.get({
|
||||||
owner,
|
owner,
|
||||||
repo,
|
repo,
|
||||||
pull_number: prNumber,
|
pull_number: prNumber,
|
||||||
});
|
});
|
||||||
|
|
||||||
// For forks, use the full repository name, for internal PRs use the current repo
|
// For forks, use the full repository name, for internal PRs use the current repo
|
||||||
const repository = pr.head.repo.fork ? pr.head.repo.full_name : `${owner}/${repo}`;
|
const repository = pr.head.repo.fork ? pr.head.repo.full_name : `${owner}/${repo}`;
|
||||||
|
|
||||||
console.log(`PR Repository: ${repository}`);
|
console.log(`PR Repository: ${repository}`);
|
||||||
console.log(`PR Branch: ${pr.head.ref}`);
|
console.log(`PR Branch: ${pr.head.ref}`);
|
||||||
|
|
||||||
core.setOutput('repository', repository);
|
core.setOutput('repository', repository);
|
||||||
core.setOutput('ref', pr.head.ref);
|
core.setOutput('ref', pr.head.ref);
|
||||||
|
|
||||||
deploy-pr:
|
deploy-pr:
|
||||||
needs: check-comment
|
needs: check-comment
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
pull-requests: write
|
||||||
|
issues: write
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
- name: Harden Runner
|
||||||
|
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
||||||
|
with:
|
||||||
|
egress-policy: audit
|
||||||
|
|
||||||
- name: Checkout PR
|
- name: Checkout PR
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
repository: ${{ needs.check-comment.outputs.pr_repository }}
|
repository: ${{ needs.check-comment.outputs.pr_repository }}
|
||||||
ref: ${{ needs.check-comment.outputs.pr_ref }}
|
ref: ${{ needs.check-comment.outputs.pr_ref }}
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Set up JDK
|
- name: Set up JDK
|
||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@7a6d8a8234af8eb26422e24e3006232cccaa061b # v4.6.0
|
||||||
with:
|
with:
|
||||||
java-version: '17'
|
java-version: '17'
|
||||||
distribution: 'temurin'
|
distribution: 'temurin'
|
||||||
@@ -84,20 +103,22 @@ jobs:
|
|||||||
DOCKER_ENABLE_SECURITY: false
|
DOCKER_ENABLE_SECURITY: false
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3
|
uses: docker/setup-buildx-action@6524bf65af31da8d45b59e8c27de4bd072b392f5 # v3.8.0
|
||||||
|
|
||||||
- name: Get version number
|
- name: Get version number
|
||||||
id: versionNumber
|
id: versionNumber
|
||||||
run: echo "versionNumber=$(./gradlew printVersion --quiet | tail -1)" >> $GITHUB_OUTPUT
|
run: |
|
||||||
|
VERSION=$(grep "^version =" build.gradle | awk -F'"' '{print $2}')
|
||||||
|
echo "versionNumber=$VERSION" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Login to Docker Hub
|
- name: Login to Docker Hub
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||||
password: ${{ secrets.DOCKER_HUB_API }}
|
password: ${{ secrets.DOCKER_HUB_API }}
|
||||||
|
|
||||||
- name: Build and push PR-specific image
|
- name: Build and push PR-specific image
|
||||||
uses: docker/build-push-action@v6
|
uses: docker/build-push-action@48aba3b46d1b1fec4febb7c5d0c644b249a11355 # v6.10.0
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
file: ./Dockerfile
|
file: ./Dockerfile
|
||||||
@@ -146,10 +167,10 @@ jobs:
|
|||||||
ssh -i ../private.key -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -T ${{ secrets.VPS_USERNAME }}@${{ secrets.VPS_HOST }} << 'ENDSSH'
|
ssh -i ../private.key -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -T ${{ secrets.VPS_USERNAME }}@${{ secrets.VPS_HOST }} << 'ENDSSH'
|
||||||
# Create PR-specific directories
|
# Create PR-specific directories
|
||||||
mkdir -p /stirling/PR-${{ needs.check-comment.outputs.pr_number }}/{data,config,logs}
|
mkdir -p /stirling/PR-${{ needs.check-comment.outputs.pr_number }}/{data,config,logs}
|
||||||
|
|
||||||
# Move docker-compose file to correct location
|
# Move docker-compose file to correct location
|
||||||
mv /tmp/docker-compose.yml /stirling/PR-${{ needs.check-comment.outputs.pr_number }}/docker-compose.yml
|
mv /tmp/docker-compose.yml /stirling/PR-${{ needs.check-comment.outputs.pr_number }}/docker-compose.yml
|
||||||
|
|
||||||
# Start or restart the container
|
# Start or restart the container
|
||||||
cd /stirling/PR-${{ needs.check-comment.outputs.pr_number }}
|
cd /stirling/PR-${{ needs.check-comment.outputs.pr_number }}
|
||||||
docker-compose pull
|
docker-compose pull
|
||||||
@@ -158,7 +179,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Post deployment URL to PR
|
- name: Post deployment URL to PR
|
||||||
if: success()
|
if: success()
|
||||||
uses: actions/github-script@v7
|
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
const { GITHUB_REPOSITORY } = process.env;
|
const { GITHUB_REPOSITORY } = process.env;
|
||||||
|
|||||||
27
.github/workflows/PR-Demo-cleanup.yml
vendored
27
.github/workflows/PR-Demo-cleanup.yml
vendored
@@ -5,8 +5,7 @@ on:
|
|||||||
types: [opened, synchronize, reopened, closed]
|
types: [opened, synchronize, reopened, closed]
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: read
|
||||||
pull-requests: write
|
|
||||||
|
|
||||||
env:
|
env:
|
||||||
SERVER_IP: ${{ secrets.VPS_IP }} # Add this to your GitHub secrets
|
SERVER_IP: ${{ secrets.VPS_IP }} # Add this to your GitHub secrets
|
||||||
@@ -15,9 +14,17 @@ env:
|
|||||||
jobs:
|
jobs:
|
||||||
cleanup:
|
cleanup:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
pull-requests: write
|
||||||
if: github.event.action == 'closed'
|
if: github.event.action == 'closed'
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
- name: Harden Runner
|
||||||
|
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
||||||
|
with:
|
||||||
|
egress-policy: audit
|
||||||
|
|
||||||
- name: Set up SSH
|
- name: Set up SSH
|
||||||
run: |
|
run: |
|
||||||
mkdir -p ~/.ssh/
|
mkdir -p ~/.ssh/
|
||||||
@@ -30,20 +37,20 @@ jobs:
|
|||||||
CLEANUP_STATUS=$(ssh -i ../private.key -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -T ${{ secrets.VPS_USERNAME }}@${{ secrets.VPS_HOST }} << 'ENDSSH'
|
CLEANUP_STATUS=$(ssh -i ../private.key -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -T ${{ secrets.VPS_USERNAME }}@${{ secrets.VPS_HOST }} << 'ENDSSH'
|
||||||
if [ -d "/stirling/PR-${{ github.event.pull_request.number }}" ]; then
|
if [ -d "/stirling/PR-${{ github.event.pull_request.number }}" ]; then
|
||||||
echo "Found PR directory, proceeding with cleanup..."
|
echo "Found PR directory, proceeding with cleanup..."
|
||||||
|
|
||||||
# Stop and remove containers
|
# Stop and remove containers
|
||||||
cd /stirling/PR-${{ github.event.pull_request.number }}
|
cd /stirling/PR-${{ github.event.pull_request.number }}
|
||||||
docker-compose down || true
|
docker-compose down || true
|
||||||
|
|
||||||
# Go back to root before removal
|
# Go back to root before removal
|
||||||
cd /
|
cd /
|
||||||
|
|
||||||
# Remove PR-specific directories
|
# Remove PR-specific directories
|
||||||
rm -rf /stirling/PR-${{ github.event.pull_request.number }}
|
rm -rf /stirling/PR-${{ github.event.pull_request.number }}
|
||||||
|
|
||||||
# Remove the Docker image
|
# Remove the Docker image
|
||||||
docker rmi --no-prune ${{ secrets.DOCKER_HUB_USERNAME }}/test:pr-${{ github.event.pull_request.number }} || true
|
docker rmi --no-prune ${{ secrets.DOCKER_HUB_USERNAME }}/test:pr-${{ github.event.pull_request.number }} || true
|
||||||
|
|
||||||
echo "PERFORMED_CLEANUP"
|
echo "PERFORMED_CLEANUP"
|
||||||
else
|
else
|
||||||
echo "PR directory not found, nothing to clean up"
|
echo "PR directory not found, nothing to clean up"
|
||||||
@@ -51,7 +58,7 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
ENDSSH
|
ENDSSH
|
||||||
)
|
)
|
||||||
|
|
||||||
if [[ $CLEANUP_STATUS == *"PERFORMED_CLEANUP"* ]]; then
|
if [[ $CLEANUP_STATUS == *"PERFORMED_CLEANUP"* ]]; then
|
||||||
echo "cleanup_performed=true" >> $GITHUB_OUTPUT
|
echo "cleanup_performed=true" >> $GITHUB_OUTPUT
|
||||||
else
|
else
|
||||||
@@ -60,7 +67,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Post cleanup notice to PR
|
- name: Post cleanup notice to PR
|
||||||
if: steps.cleanup.outputs.cleanup_performed == 'true'
|
if: steps.cleanup.outputs.cleanup_performed == 'true'
|
||||||
uses: actions/github-script@v7
|
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
const { GITHUB_REPOSITORY } = process.env;
|
const { GITHUB_REPOSITORY } = process.env;
|
||||||
|
|||||||
19
.github/workflows/auto-labeler.yml
vendored
19
.github/workflows/auto-labeler.yml
vendored
@@ -3,17 +3,24 @@ on:
|
|||||||
pull_request_target:
|
pull_request_target:
|
||||||
types: [opened, synchronize]
|
types: [opened, synchronize]
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
labeler:
|
labeler:
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
pull-requests: write
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
pull-requests: write
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- name: Harden Runner
|
||||||
|
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
||||||
|
with:
|
||||||
|
egress-policy: audit
|
||||||
|
|
||||||
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
|
||||||
- name: Apply Labels
|
- name: Apply Labels
|
||||||
uses: actions/labeler@v5
|
uses: actions/labeler@8558fd74291d67161a8a78ce36a881fa63b766a9 # v5.0.0
|
||||||
with:
|
with:
|
||||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
configuration-path: .github/labeler-config.yml
|
configuration-path: .github/labeler-config.yml
|
||||||
|
|||||||
55
.github/workflows/build.yml
vendored
55
.github/workflows/build.yml
vendored
@@ -6,13 +6,15 @@ on:
|
|||||||
pull_request:
|
pull_request:
|
||||||
branches: ["main"]
|
branches: ["main"]
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
actions: read
|
actions: read
|
||||||
contents: read
|
|
||||||
security-events: write
|
security-events: write
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
@@ -21,23 +23,41 @@ jobs:
|
|||||||
jdk-version: [17, 21]
|
jdk-version: [17, 21]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
- name: Harden Runner
|
||||||
|
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
||||||
|
with:
|
||||||
|
egress-policy: audit
|
||||||
|
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
|
||||||
- name: Set up JDK ${{ matrix.jdk-version }}
|
- name: Set up JDK ${{ matrix.jdk-version }}
|
||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@7a6d8a8234af8eb26422e24e3006232cccaa061b # v4.6.0
|
||||||
with:
|
with:
|
||||||
java-version: ${{ matrix.jdk-version }}
|
java-version: ${{ matrix.jdk-version }}
|
||||||
distribution: "temurin"
|
distribution: "temurin"
|
||||||
|
|
||||||
- name: Set up Gradle
|
- name: Build with Gradle and no spring security
|
||||||
uses: gradle/actions/setup-gradle@v4
|
run: ./gradlew clean build
|
||||||
|
env:
|
||||||
|
DOCKER_ENABLE_SECURITY: false
|
||||||
|
|
||||||
|
- name: Build with Gradle and with spring security
|
||||||
|
run: ./gradlew clean build
|
||||||
|
env:
|
||||||
|
DOCKER_ENABLE_SECURITY: true
|
||||||
|
|
||||||
|
- name: Upload Test Reports
|
||||||
|
if: always()
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
gradle-version: 8.7
|
name: test-reports-jdk-${{ matrix.jdk-version }}
|
||||||
|
path: |
|
||||||
- name: Build with Gradle
|
build/reports/tests/
|
||||||
run: ./gradlew build --no-build-cache
|
build/test-results/
|
||||||
|
build/reports/problems/
|
||||||
|
retention-days: 3
|
||||||
|
|
||||||
docker-compose-tests:
|
docker-compose-tests:
|
||||||
# if: github.event_name == 'push' && github.ref == 'refs/heads/main' ||
|
# if: github.event_name == 'push' && github.ref == 'refs/heads/main' ||
|
||||||
# (github.event_name == 'pull_request' &&
|
# (github.event_name == 'pull_request' &&
|
||||||
@@ -56,25 +76,30 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
- name: Harden Runner
|
||||||
|
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
||||||
|
with:
|
||||||
|
egress-policy: audit
|
||||||
|
|
||||||
- name: Checkout Repository
|
- name: Checkout Repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
|
||||||
- name: Set up Java 17
|
- name: Set up Java 17
|
||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@7a6d8a8234af8eb26422e24e3006232cccaa061b # v4.6.0
|
||||||
with:
|
with:
|
||||||
java-version: "17"
|
java-version: "17"
|
||||||
distribution: "adopt"
|
distribution: "adopt"
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3
|
uses: docker/setup-buildx-action@6524bf65af31da8d45b59e8c27de4bd072b392f5 # v3.8.0
|
||||||
|
|
||||||
- name: Install Docker Compose
|
- name: Install Docker Compose
|
||||||
run: |
|
run: |
|
||||||
sudo curl -SL "https://github.com/docker/compose/releases/download/v2.29.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
|
sudo curl -SL "https://github.com/docker/compose/releases/download/v2.32.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
|
||||||
sudo chmod +x /usr/local/bin/docker-compose
|
sudo chmod +x /usr/local/bin/docker-compose
|
||||||
|
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v5
|
uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0
|
||||||
with:
|
with:
|
||||||
python-version: "3.12"
|
python-version: "3.12"
|
||||||
|
|
||||||
|
|||||||
257
.github/workflows/check_properties.yml
vendored
257
.github/workflows/check_properties.yml
vendored
@@ -1,42 +1,55 @@
|
|||||||
name: Check Properties Files
|
name: Check Properties Files on PR
|
||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request_target:
|
pull_request_target:
|
||||||
types: [opened, synchronize, reopened]
|
types: [opened, synchronize, reopened]
|
||||||
paths:
|
paths:
|
||||||
- "src/main/resources/messages_*.properties"
|
- "src/main/resources/messages_*.properties"
|
||||||
push:
|
|
||||||
branches: ["main"]
|
permissions:
|
||||||
paths:
|
contents: read # Allow read access to repository content
|
||||||
- "src/main/resources/messages_en_GB.properties"
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
check-files:
|
check-files:
|
||||||
if: github.event_name == 'pull_request_target'
|
if: github.event_name == 'pull_request_target'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
issues: write # Allow posting comments on issues/PRs
|
||||||
|
pull-requests: write
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout main branch first
|
- name: Harden Runner
|
||||||
uses: actions/checkout@v4
|
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
||||||
with:
|
with:
|
||||||
ref: main
|
egress-policy: audit
|
||||||
path: main-branch
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
- name: Checkout PR branch
|
- name: Checkout main branch first
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
|
||||||
repository: ${{ github.event.pull_request.head.repo.full_name }}
|
|
||||||
ref: ${{ github.event.pull_request.head.ref }}
|
|
||||||
path: pr-branch
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v5
|
uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0
|
||||||
with:
|
with:
|
||||||
python-version: "3.x"
|
python-version: "3.x"
|
||||||
|
|
||||||
- name: Install GitHub CLI
|
- name: Get PR data
|
||||||
run: sudo apt-get update && sudo apt-get install -y gh
|
id: get-pr-data
|
||||||
|
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const prNumber = context.payload.pull_request.number;
|
||||||
|
const repoOwner = context.payload.repository.owner.login;
|
||||||
|
const repoName = context.payload.repository.name;
|
||||||
|
const branch = context.payload.pull_request.head.ref;
|
||||||
|
|
||||||
|
console.log(`PR Number: ${prNumber}`);
|
||||||
|
console.log(`Repo Owner: ${repoOwner}`);
|
||||||
|
console.log(`Repo Name: ${repoName}`);
|
||||||
|
console.log(`Branch: ${branch}`);
|
||||||
|
|
||||||
|
core.setOutput("pr_number", prNumber);
|
||||||
|
core.setOutput("repo_owner", repoOwner);
|
||||||
|
core.setOutput("repo_name", repoName);
|
||||||
|
core.setOutput("branch", branch);
|
||||||
|
continue-on-error: true
|
||||||
|
|
||||||
- name: Fetch PR changed files
|
- name: Fetch PR changed files
|
||||||
id: fetch-pr-changes
|
id: fetch-pr-changes
|
||||||
@@ -44,45 +57,125 @@ jobs:
|
|||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
run: |
|
run: |
|
||||||
echo "Fetching PR changed files..."
|
echo "Fetching PR changed files..."
|
||||||
cd pr-branch
|
|
||||||
gh repo set-default ${{ github.repository }}
|
|
||||||
# Store files in a safe way, only allowing valid properties files
|
|
||||||
echo "Getting list of changed files from PR..."
|
echo "Getting list of changed files from PR..."
|
||||||
gh pr view ${{ github.event.pull_request.number }} --json files -q ".files[].path" | grep -E '^src/main/resources/messages_[a-zA-Z_]+\.properties$' > ../changed_files.txt
|
gh pr view ${{ steps.get-pr-data.outputs.pr_number }} --json files -q ".files[].path" | grep -E '^src/main/resources/messages_[a-zA-Z_]+\.properties$' > changed_files.txt # Filter only matching property files
|
||||||
cd ..
|
|
||||||
|
|
||||||
echo "Processing changed files..."
|
- name: Determine reference file test
|
||||||
mapfile -t CHANGED_FILES < changed_files.txt
|
|
||||||
|
|
||||||
CHANGED_FILES_STR="${CHANGED_FILES[*]}"
|
|
||||||
echo "CHANGED_FILES=${CHANGED_FILES_STR}" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
echo "Changed files: ${CHANGED_FILES_STR}"
|
|
||||||
|
|
||||||
- name: Determine reference file
|
|
||||||
id: determine-file
|
id: determine-file
|
||||||
run: |
|
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
|
||||||
echo "Determining reference file..."
|
with:
|
||||||
if grep -Fxq "src/main/resources/messages_en_GB.properties" changed_files.txt; then
|
script: |
|
||||||
echo "Using PR branch reference file"
|
const fs = require("fs");
|
||||||
echo "REFERENCE_FILE=pr-branch/src/main/resources/messages_en_GB.properties" >> $GITHUB_ENV
|
const path = require("path");
|
||||||
else
|
|
||||||
echo "Using main branch reference file"
|
|
||||||
echo "REFERENCE_FILE=main-branch/src/main/resources/messages_en_GB.properties" >> $GITHUB_ENV
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Show REFERENCE_FILE
|
const prNumber = ${{ steps.get-pr-data.outputs.pr_number }};
|
||||||
run: echo "Reference file is set to ${REFERENCE_FILE}"
|
const repoOwner = "${{ steps.get-pr-data.outputs.repo_owner }}";
|
||||||
|
const repoName = "${{ steps.get-pr-data.outputs.repo_name }}";
|
||||||
|
|
||||||
|
const prRepoOwner = "${{ github.event.pull_request.head.repo.owner.login }}";
|
||||||
|
const prRepoName = "${{ github.event.pull_request.head.repo.name }}";
|
||||||
|
const branch = "${{ steps.get-pr-data.outputs.branch }}";
|
||||||
|
|
||||||
|
console.log(`Determining reference file for PR #${prNumber}`);
|
||||||
|
|
||||||
|
// Validate inputs
|
||||||
|
const validateInput = (input, regex, name) => {
|
||||||
|
if (!regex.test(input)) {
|
||||||
|
throw new Error(`Invalid ${name}: ${input}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
validateInput(repoOwner, /^[a-zA-Z0-9_-]+$/, "repository owner");
|
||||||
|
validateInput(repoName, /^[a-zA-Z0-9._-]+$/, "repository name");
|
||||||
|
validateInput(branch, /^[a-zA-Z0-9._/-]+$/, "branch name");
|
||||||
|
|
||||||
|
// Get the list of changed files in the PR
|
||||||
|
const { data: files } = await github.rest.pulls.listFiles({
|
||||||
|
owner: repoOwner,
|
||||||
|
repo: repoName,
|
||||||
|
pull_number: prNumber,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Filter for relevant files based on the PR changes
|
||||||
|
const changedFiles = files
|
||||||
|
.map(file => file.filename)
|
||||||
|
.filter(file => /^src\/main\/resources\/messages_[a-zA-Z_]+\.properties$/.test(file));
|
||||||
|
|
||||||
|
console.log("Changed files:", changedFiles);
|
||||||
|
|
||||||
|
// Create a temporary directory for PR files
|
||||||
|
const tempDir = "pr-branch";
|
||||||
|
if (!fs.existsSync(tempDir)) {
|
||||||
|
fs.mkdirSync(tempDir, { recursive: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Download and save each changed file
|
||||||
|
for (const file of changedFiles) {
|
||||||
|
const { data: fileContent } = await github.rest.repos.getContent({
|
||||||
|
owner: prRepoOwner,
|
||||||
|
repo: prRepoName,
|
||||||
|
path: file,
|
||||||
|
ref: branch,
|
||||||
|
});
|
||||||
|
|
||||||
|
const content = Buffer.from(fileContent.content, "base64").toString("utf-8");
|
||||||
|
const filePath = path.join(tempDir, file);
|
||||||
|
const dirPath = path.dirname(filePath);
|
||||||
|
|
||||||
|
if (!fs.existsSync(dirPath)) {
|
||||||
|
fs.mkdirSync(dirPath, { recursive: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.writeFileSync(filePath, content);
|
||||||
|
console.log(`Saved file: ${filePath}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output the list of changed files for further processing
|
||||||
|
const fileList = changedFiles.join(" ");
|
||||||
|
core.exportVariable("FILES_LIST", fileList);
|
||||||
|
console.log("Files saved and listed in FILES_LIST.");
|
||||||
|
|
||||||
|
// Determine reference file
|
||||||
|
let referenceFilePath;
|
||||||
|
if (changedFiles.includes("src/main/resources/messages_en_GB.properties")) {
|
||||||
|
console.log("Using PR branch reference file.");
|
||||||
|
const { data: fileContent } = await github.rest.repos.getContent({
|
||||||
|
owner: prRepoOwner,
|
||||||
|
repo: prRepoName,
|
||||||
|
path: "src/main/resources/messages_en_GB.properties",
|
||||||
|
ref: branch,
|
||||||
|
});
|
||||||
|
|
||||||
|
referenceFilePath = "pr-branch-messages_en_GB.properties";
|
||||||
|
const content = Buffer.from(fileContent.content, "base64").toString("utf-8");
|
||||||
|
fs.writeFileSync(referenceFilePath, content);
|
||||||
|
} else {
|
||||||
|
console.log("Using main branch reference file.");
|
||||||
|
const { data: fileContent } = await github.rest.repos.getContent({
|
||||||
|
owner: repoOwner,
|
||||||
|
repo: repoName,
|
||||||
|
path: "src/main/resources/messages_en_GB.properties",
|
||||||
|
ref: "main",
|
||||||
|
});
|
||||||
|
|
||||||
|
referenceFilePath = "main-branch-messages_en_GB.properties";
|
||||||
|
const content = Buffer.from(fileContent.content, "base64").toString("utf-8");
|
||||||
|
fs.writeFileSync(referenceFilePath, content);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Reference file path: ${referenceFilePath}`);
|
||||||
|
core.exportVariable("REFERENCE_FILE", referenceFilePath);
|
||||||
|
|
||||||
- name: Run Python script to check files
|
- name: Run Python script to check files
|
||||||
id: run-check
|
id: run-check
|
||||||
run: |
|
run: |
|
||||||
echo "Running Python script to check files..."
|
echo "Running Python script to check files..."
|
||||||
python main-branch/.github/scripts/check_language_properties.py \
|
python .github/scripts/check_language_properties.py \
|
||||||
--actor ${{ github.event.pull_request.user.login }} \
|
--actor ${{ github.event.pull_request.user.login }} \
|
||||||
--reference-file "${REFERENCE_FILE}" \
|
--reference-file "${REFERENCE_FILE}" \
|
||||||
--branch pr-branch \
|
--branch "pr-branch" \
|
||||||
--files "${CHANGED_FILES[@]}" > result.txt || true
|
--files "${FILES_LIST[@]}" > result.txt
|
||||||
|
continue-on-error: true # Continue the job even if this step fails
|
||||||
|
|
||||||
- name: Capture output
|
- name: Capture output
|
||||||
id: capture-output
|
id: capture-output
|
||||||
@@ -95,7 +188,7 @@ jobs:
|
|||||||
echo "EOF" >> $GITHUB_ENV
|
echo "EOF" >> $GITHUB_ENV
|
||||||
echo "${SCRIPT_OUTPUT}"
|
echo "${SCRIPT_OUTPUT}"
|
||||||
|
|
||||||
# Set FAIL_JOB to true if SCRIPT_OUTPUT contains ❌
|
# Determine job failure based on script output
|
||||||
if [[ "$SCRIPT_OUTPUT" == *"❌"* ]]; then
|
if [[ "$SCRIPT_OUTPUT" == *"❌"* ]]; then
|
||||||
echo "FAIL_JOB=true" >> $GITHUB_ENV
|
echo "FAIL_JOB=true" >> $GITHUB_ENV
|
||||||
else
|
else
|
||||||
@@ -109,23 +202,23 @@ jobs:
|
|||||||
|
|
||||||
- name: Post comment on PR
|
- name: Post comment on PR
|
||||||
if: env.SCRIPT_OUTPUT != ''
|
if: env.SCRIPT_OUTPUT != ''
|
||||||
uses: actions/github-script@v7
|
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
const { GITHUB_REPOSITORY, SCRIPT_OUTPUT } = process.env;
|
const { GITHUB_REPOSITORY, SCRIPT_OUTPUT } = process.env;
|
||||||
const [repoOwner, repoName] = GITHUB_REPOSITORY.split('/');
|
const [repoOwner, repoName] = GITHUB_REPOSITORY.split('/');
|
||||||
const prNumber = context.issue.number;
|
const issueNumber = context.issue.number;
|
||||||
|
|
||||||
// Find existing comment
|
// Find existing comment
|
||||||
const comments = await github.rest.issues.listComments({
|
const comments = await github.rest.issues.listComments({
|
||||||
owner: repoOwner,
|
owner: repoOwner,
|
||||||
repo: repoName,
|
repo: repoName,
|
||||||
issue_number: prNumber
|
issue_number: issueNumber
|
||||||
});
|
});
|
||||||
|
|
||||||
const comment = comments.data.find(c => c.body.includes("## 🚀 Translation Verification Summary"));
|
const comment = comments.data.find(c => c.body.includes("## 🚀 Translation Verification Summary"));
|
||||||
|
|
||||||
// Only allow the action user to update comments
|
// Only update or create comments by the action user
|
||||||
const expectedActor = "github-actions[bot]";
|
const expectedActor = "github-actions[bot]";
|
||||||
|
|
||||||
if (comment && comment.user.login === expectedActor) {
|
if (comment && comment.user.login === expectedActor) {
|
||||||
@@ -142,7 +235,7 @@ jobs:
|
|||||||
await github.rest.issues.createComment({
|
await github.rest.issues.createComment({
|
||||||
owner: repoOwner,
|
owner: repoOwner,
|
||||||
repo: repoName,
|
repo: repoName,
|
||||||
issue_number: prNumber,
|
issue_number: issueNumber,
|
||||||
body: `## 🚀 Translation Verification Summary\n\n\n${SCRIPT_OUTPUT}\n`
|
body: `## 🚀 Translation Verification Summary\n\n\n${SCRIPT_OUTPUT}\n`
|
||||||
});
|
});
|
||||||
console.log("Created new comment.");
|
console.log("Created new comment.");
|
||||||
@@ -155,59 +248,3 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
echo "Failing the job because errors were detected."
|
echo "Failing the job because errors were detected."
|
||||||
exit 1
|
exit 1
|
||||||
|
|
||||||
update-translations-main:
|
|
||||||
if: github.event_name == 'push'
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
pull-requests: write
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Set up Python
|
|
||||||
uses: actions/setup-python@v5
|
|
||||||
with:
|
|
||||||
python-version: "3.x"
|
|
||||||
|
|
||||||
- name: Run Python script to check files
|
|
||||||
id: run-check
|
|
||||||
run: |
|
|
||||||
echo "Running Python script to check files..."
|
|
||||||
python .github/scripts/check_language_properties.py \
|
|
||||||
--reference-file src/main/resources/messages_en_GB.properties \
|
|
||||||
--branch main
|
|
||||||
|
|
||||||
- name: Set up git config
|
|
||||||
run: |
|
|
||||||
git config --global user.name "github-actions[bot]"
|
|
||||||
git config --global user.email "github-actions[bot]@users.noreply.github.com"
|
|
||||||
|
|
||||||
- name: Add translation keys
|
|
||||||
run: |
|
|
||||||
git add src/main/resources/messages_*.properties
|
|
||||||
git diff --staged --quiet || echo "CHANGES_DETECTED=true" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Create Pull Request
|
|
||||||
id: cpr
|
|
||||||
if: env.CHANGES_DETECTED == 'true'
|
|
||||||
uses: peter-evans/create-pull-request@v7
|
|
||||||
with:
|
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
commit-message: "Update translation files"
|
|
||||||
committer: GitHub Action <action@github.com>
|
|
||||||
author: GitHub Action <action@github.com>
|
|
||||||
signoff: true
|
|
||||||
branch: update_translation_files
|
|
||||||
title: "Update translation files"
|
|
||||||
add-paths: |
|
|
||||||
src/main/resources/messages_*.properties
|
|
||||||
body: |
|
|
||||||
Auto-generated by [create-pull-request][1]
|
|
||||||
|
|
||||||
[1]: https://github.com/peter-evans/create-pull-request
|
|
||||||
labels: Translation
|
|
||||||
draft: false
|
|
||||||
delete-branch: true
|
|
||||||
sign-commits: true
|
|
||||||
|
|||||||
79
.github/workflows/codeql.yml-disabled
vendored
Normal file
79
.github/workflows/codeql.yml-disabled
vendored
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
# For most projects, this workflow file will not need changing; you simply need
|
||||||
|
# to commit it to your repository.
|
||||||
|
#
|
||||||
|
# You may wish to alter this file to override the set of languages analyzed,
|
||||||
|
# or to provide custom queries or build logic.
|
||||||
|
#
|
||||||
|
# ******** NOTE ********
|
||||||
|
# We have attempted to detect the languages in your repository. Please check
|
||||||
|
# the `language` matrix defined below to confirm you have the correct set of
|
||||||
|
# supported CodeQL languages.
|
||||||
|
#
|
||||||
|
name: "CodeQL"
|
||||||
|
|
||||||
|
#disable for now
|
||||||
|
#on:
|
||||||
|
# push:
|
||||||
|
# branches: ["main"]
|
||||||
|
# pull_request:
|
||||||
|
# The branches below must be a subset of the branches above
|
||||||
|
# branches: ["main"]
|
||||||
|
# schedule:
|
||||||
|
# - cron: "0 0 * * 1"
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
analyze:
|
||||||
|
name: Analyze
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
actions: read
|
||||||
|
contents: read
|
||||||
|
security-events: write
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
language: ["java"]
|
||||||
|
# CodeQL supports [ $supported-codeql-languages ]
|
||||||
|
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Harden Runner
|
||||||
|
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
||||||
|
with:
|
||||||
|
egress-policy: audit
|
||||||
|
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
|
||||||
|
# Initializes the CodeQL tools for scanning.
|
||||||
|
- name: Initialize CodeQL
|
||||||
|
uses: github/codeql-action/init@48ab28a6f5dbc2a99bf1e0131198dd8f1df78169 # v3.28.0
|
||||||
|
with:
|
||||||
|
languages: ${{ matrix.language }}
|
||||||
|
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||||
|
# By default, queries listed here will override any specified in a config file.
|
||||||
|
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||||
|
|
||||||
|
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||||
|
# If this step fails, then you should remove it and run the build manually (see below)
|
||||||
|
- name: Autobuild
|
||||||
|
uses: github/codeql-action/autobuild@48ab28a6f5dbc2a99bf1e0131198dd8f1df78169 # v3.28.0
|
||||||
|
|
||||||
|
# ℹ️ Command-line programs to run using the OS shell.
|
||||||
|
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
||||||
|
|
||||||
|
# If the Autobuild fails above, remove it and uncomment the following three lines.
|
||||||
|
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
|
||||||
|
|
||||||
|
# - run: |
|
||||||
|
# echo "Run, Build Application using script"
|
||||||
|
# ./location_of_script_within_repo/buildscript.sh
|
||||||
|
|
||||||
|
- name: Perform CodeQL Analysis
|
||||||
|
uses: github/codeql-action/analyze@48ab28a6f5dbc2a99bf1e0131198dd8f1df78169 # v3.28.0
|
||||||
|
with:
|
||||||
|
category: "/language:${{matrix.language}}"
|
||||||
27
.github/workflows/dependency-review.yml
vendored
Normal file
27
.github/workflows/dependency-review.yml
vendored
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
# Dependency Review Action
|
||||||
|
#
|
||||||
|
# This Action will scan dependency manifest files that change as part of a Pull Request,
|
||||||
|
# surfacing known-vulnerable versions of the packages declared or updated in the PR.
|
||||||
|
# Once installed, if the workflow run is marked as required,
|
||||||
|
# PRs introducing known-vulnerable packages will be blocked from merging.
|
||||||
|
#
|
||||||
|
# Source repository: https://github.com/actions/dependency-review-action
|
||||||
|
name: 'Dependency Review'
|
||||||
|
on: [pull_request]
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
dependency-review:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Harden Runner
|
||||||
|
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
||||||
|
with:
|
||||||
|
egress-policy: audit
|
||||||
|
|
||||||
|
- name: 'Checkout Repository'
|
||||||
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
- name: 'Dependency Review'
|
||||||
|
uses: actions/dependency-review-action@3b139cfc5fae8b618d3eae3675e383bb1769c019 # v4.5.0
|
||||||
25
.github/workflows/licenses-update.yml
vendored
25
.github/workflows/licenses-update.yml
vendored
@@ -8,24 +8,30 @@ on:
|
|||||||
- "build.gradle"
|
- "build.gradle"
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: read
|
||||||
pull-requests: write
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
generate-license-report:
|
generate-license-report:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
pull-requests: write
|
||||||
steps:
|
steps:
|
||||||
|
- name: Harden Runner
|
||||||
|
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
||||||
|
with:
|
||||||
|
egress-policy: audit
|
||||||
|
|
||||||
- name: Check out code
|
- name: Check out code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
|
||||||
- name: Set up JDK 17
|
- name: Set up JDK 17
|
||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@7a6d8a8234af8eb26422e24e3006232cccaa061b # v4.6.0
|
||||||
with:
|
with:
|
||||||
java-version: "17"
|
java-version: "17"
|
||||||
distribution: "adopt"
|
distribution: "adopt"
|
||||||
|
|
||||||
- uses: gradle/actions/setup-gradle@v4
|
- uses: gradle/actions/setup-gradle@0bdd871935719febd78681f197cd39af5b6e16a6 # v4.2.2
|
||||||
|
|
||||||
- name: Run Gradle Command
|
- name: Run Gradle Command
|
||||||
run: ./gradlew clean generateLicenseReport
|
run: ./gradlew clean generateLicenseReport
|
||||||
@@ -47,7 +53,7 @@ jobs:
|
|||||||
- name: Create Pull Request
|
- name: Create Pull Request
|
||||||
id: cpr
|
id: cpr
|
||||||
if: env.CHANGES_DETECTED == 'true'
|
if: env.CHANGES_DETECTED == 'true'
|
||||||
uses: peter-evans/create-pull-request@v6
|
uses: peter-evans/create-pull-request@67ccf781d68cd99b580ae25a5c18a1cc84ffff1f # v7.0.6
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
commit-message: "Update 3rd Party Licenses"
|
commit-message: "Update 3rd Party Licenses"
|
||||||
@@ -60,9 +66,10 @@ jobs:
|
|||||||
Auto-generated by [create-pull-request][1]
|
Auto-generated by [create-pull-request][1]
|
||||||
|
|
||||||
[1]: https://github.com/peter-evans/create-pull-request
|
[1]: https://github.com/peter-evans/create-pull-request
|
||||||
labels: licenses
|
labels: licenses,github-actions
|
||||||
draft: false
|
draft: false
|
||||||
delete-branch: true
|
delete-branch: true
|
||||||
|
sign-commits: true
|
||||||
|
|
||||||
- name: Auto approve
|
- name: Auto approve
|
||||||
if: steps.cpr.outputs.pull-request-operation == 'created'
|
if: steps.cpr.outputs.pull-request-operation == 'created'
|
||||||
@@ -72,7 +79,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Enable auto-merge
|
- name: Enable auto-merge
|
||||||
if: steps.cpr.outputs.pull-request-operation == 'created'
|
if: steps.cpr.outputs.pull-request-operation == 'created'
|
||||||
uses: peter-evans/enable-pull-request-automerge@v3
|
uses: peter-evans/enable-pull-request-automerge@a660677d5469627102a1c1e11409dd063606628d # v3.0.0
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
pull-request-number: ${{ steps.cpr.outputs.pull-request-number }}
|
pull-request-number: ${{ steps.cpr.outputs.pull-request-number }}
|
||||||
|
|||||||
14
.github/workflows/manage-label.yml
vendored
14
.github/workflows/manage-label.yml
vendored
@@ -6,19 +6,25 @@ on:
|
|||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
issues: write
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
labeler:
|
labeler:
|
||||||
name: Labeler
|
name: Labeler
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
issues: write
|
||||||
steps:
|
steps:
|
||||||
|
- name: Harden Runner
|
||||||
|
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
||||||
|
with:
|
||||||
|
egress-policy: audit
|
||||||
|
|
||||||
- name: Check out the repository
|
- name: Check out the repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
|
||||||
- name: Run Labeler
|
- name: Run Labeler
|
||||||
uses: crazy-max/ghaction-github-labeler@v5
|
uses: crazy-max/ghaction-github-labeler@b54af0c25861143e7c8813d7cbbf46d2c341680c # v5.1.0
|
||||||
with:
|
with:
|
||||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
yaml-file: .github/labels.yml
|
yaml-file: .github/labels.yml
|
||||||
skip-delete: true
|
skip-delete: true
|
||||||
|
|||||||
31
.github/workflows/multiOSReleases.yml
vendored
31
.github/workflows/multiOSReleases.yml
vendored
@@ -4,9 +4,10 @@ on:
|
|||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
release:
|
release:
|
||||||
types: [created]
|
types: [created]
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: read
|
||||||
packages: write
|
|
||||||
jobs:
|
jobs:
|
||||||
build-installers:
|
build-installers:
|
||||||
strategy:
|
strategy:
|
||||||
@@ -22,19 +23,27 @@ jobs:
|
|||||||
# platform: linux
|
# platform: linux
|
||||||
# ext: deb
|
# ext: deb
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
packages: write
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- name: Harden Runner
|
||||||
|
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
||||||
|
with:
|
||||||
|
egress-policy: audit
|
||||||
|
|
||||||
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
|
||||||
- name: Set up JDK 21
|
- name: Set up JDK 21
|
||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@7a6d8a8234af8eb26422e24e3006232cccaa061b # v4.6.0
|
||||||
with:
|
with:
|
||||||
java-version: "21"
|
java-version: "21"
|
||||||
distribution: "temurin"
|
distribution: "temurin"
|
||||||
|
|
||||||
- uses: gradle/actions/setup-gradle@v4
|
- uses: gradle/actions/setup-gradle@0bdd871935719febd78681f197cd39af5b6e16a6 # v4.2.2
|
||||||
with:
|
with:
|
||||||
gradle-version: 8.7
|
gradle-version: 8.12
|
||||||
|
|
||||||
# Install Windows dependencies
|
# Install Windows dependencies
|
||||||
- name: Install WiX Toolset
|
- name: Install WiX Toolset
|
||||||
@@ -42,7 +51,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
curl -L -o wix.exe https://github.com/wixtoolset/wix3/releases/download/wix3141rtm/wix314.exe
|
curl -L -o wix.exe https://github.com/wixtoolset/wix3/releases/download/wix3141rtm/wix314.exe
|
||||||
.\wix.exe /install /quiet
|
.\wix.exe /install /quiet
|
||||||
|
|
||||||
# Install Linux dependencies
|
# Install Linux dependencies
|
||||||
- name: Install Linux Dependencies
|
- name: Install Linux Dependencies
|
||||||
if: matrix.os == 'ubuntu-latest'
|
if: matrix.os == 'ubuntu-latest'
|
||||||
@@ -83,14 +92,14 @@ jobs:
|
|||||||
|
|
||||||
# Upload installer as artifact for testing
|
# Upload installer as artifact for testing
|
||||||
- name: Upload Installer Artifact
|
- name: Upload Installer Artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||||
with:
|
with:
|
||||||
name: Stirling-PDF-${{ matrix.platform }}-installer.${{ matrix.ext }}
|
name: Stirling-PDF-${{ matrix.platform }}-installer.${{ matrix.ext }}
|
||||||
path: Stirling-PDF-${{ matrix.platform }}-installer.${{ matrix.ext }}
|
path: Stirling-PDF-${{ matrix.platform }}-installer.${{ matrix.ext }}
|
||||||
retention-days: 1
|
retention-days: 1
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
|
|
||||||
- name: Upload binaries to release
|
- name: Upload binaries to release
|
||||||
uses: softprops/action-gh-release@v2
|
uses: softprops/action-gh-release@01570a1f39cb168c169c802c3bceb9e93fb10974 # v2.1.0
|
||||||
with:
|
with:
|
||||||
files: ./Stirling-PDF-${{ matrix.platform }}-installer.${{ matrix.ext }}
|
files: ./Stirling-PDF-${{ matrix.platform }}-installer.${{ matrix.ext }}
|
||||||
|
|||||||
55
.github/workflows/pre_commit.yml
vendored
Normal file
55
.github/workflows/pre_commit.yml
vendored
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
name: Pre-commit
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
update:
|
||||||
|
if: ${{ github.event.pull_request.user.login != 'dependabot[bot]' }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
pull-requests: write
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
- name: Set up Python
|
||||||
|
uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0
|
||||||
|
with:
|
||||||
|
python-version: 3.12
|
||||||
|
- name: Run Pre-Commit Hooks
|
||||||
|
uses: pre-commit/action@2c7b3805fd2a0fd8c1884dcaebf91fc102a13ecd # v3.0.1
|
||||||
|
continue-on-error: true
|
||||||
|
- name: Set up git config
|
||||||
|
run: |
|
||||||
|
git config --global user.name "github-actions[bot]"
|
||||||
|
git config --global user.email "github-actions[bot]@users.noreply.github.com"
|
||||||
|
- name: git add
|
||||||
|
run: |
|
||||||
|
git add .
|
||||||
|
git diff --staged --quiet || git commit -m ":file_folder: pre-commit
|
||||||
|
> Made via .github/workflows/pre_commit.yml" || echo "pre-commit: no changes"
|
||||||
|
- name: Create Pull Request
|
||||||
|
uses: peter-evans/create-pull-request@67ccf781d68cd99b580ae25a5c18a1cc84ffff1f # v7.0.6
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
commit-message: "ci: 🤖 format everything with pre-commit"
|
||||||
|
committer: GitHub Action <action@github.com>
|
||||||
|
author: GitHub Action <action@github.com>
|
||||||
|
signoff: true
|
||||||
|
branch: pre-commit
|
||||||
|
title: "🤖 format everything with pre-commit by <github-actions[bot]>"
|
||||||
|
body: |
|
||||||
|
Auto-generated by [create-pull-request][1]
|
||||||
|
|
||||||
|
[1]: https://github.com/peter-evans/create-pull-request
|
||||||
|
draft: false
|
||||||
|
delete-branch: true
|
||||||
|
labels: github-actions
|
||||||
|
sign-commits: true
|
||||||
82
.github/workflows/push-docker.yml
vendored
82
.github/workflows/push-docker.yml
vendored
@@ -9,52 +9,65 @@ on:
|
|||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
packages: write
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
push:
|
push:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
packages: write
|
||||||
|
id-token: write
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- name: Harden Runner
|
||||||
|
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
||||||
|
with:
|
||||||
|
egress-policy: audit
|
||||||
|
|
||||||
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
|
||||||
- name: Set up JDK 17
|
- name: Set up JDK 17
|
||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@7a6d8a8234af8eb26422e24e3006232cccaa061b # v4.6.0
|
||||||
with:
|
with:
|
||||||
java-version: "17"
|
java-version: "17"
|
||||||
distribution: "temurin"
|
distribution: "temurin"
|
||||||
|
|
||||||
- uses: gradle/actions/setup-gradle@v4
|
- uses: gradle/actions/setup-gradle@0bdd871935719febd78681f197cd39af5b6e16a6 # v4.2.2
|
||||||
with:
|
with:
|
||||||
gradle-version: 8.7
|
gradle-version: 8.12
|
||||||
|
|
||||||
- name: Run Gradle Command
|
- name: Run Gradle Command
|
||||||
run: ./gradlew clean build
|
run: ./gradlew clean build
|
||||||
env:
|
env:
|
||||||
DOCKER_ENABLE_SECURITY: false
|
DOCKER_ENABLE_SECURITY: false
|
||||||
|
|
||||||
|
- name: Install cosign
|
||||||
|
if: github.ref == 'refs/heads/master'
|
||||||
|
uses: sigstore/cosign-installer@dc72c7d5c4d10cd6bcb8cf6e3fd625a9e5e537da # v3.7.0
|
||||||
|
with:
|
||||||
|
cosign-release: 'v2.4.1'
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
id: buildx
|
id: buildx
|
||||||
uses: docker/setup-buildx-action@v3
|
uses: docker/setup-buildx-action@6524bf65af31da8d45b59e8c27de4bd072b392f5 # v3.8.0
|
||||||
|
|
||||||
- name: Get version number
|
- name: Get version number
|
||||||
id: versionNumber
|
id: versionNumber
|
||||||
run: echo "versionNumber=$(./gradlew printVersion --quiet | tail -1)" >> $GITHUB_OUTPUT
|
run: echo "versionNumber=$(./gradlew printVersion --quiet | tail -1)" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Login to Docker Hub
|
- name: Login to Docker Hub
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||||
password: ${{ secrets.DOCKER_HUB_API }}
|
password: ${{ secrets.DOCKER_HUB_API }}
|
||||||
|
|
||||||
- name: Login to GitHub Container Registry
|
- name: Login to GitHub Container Registry
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ github.token }}
|
password: ${{ github.token }}
|
||||||
|
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v3
|
uses: docker/setup-qemu-action@49b3bc8e6bdd4a60e6116a5414239cba5943d3cf # v3.2.0
|
||||||
|
|
||||||
- name: Convert repository owner to lowercase
|
- name: Convert repository owner to lowercase
|
||||||
id: repoowner
|
id: repoowner
|
||||||
@@ -62,7 +75,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Generate tags
|
- name: Generate tags
|
||||||
id: meta
|
id: meta
|
||||||
uses: docker/metadata-action@v5
|
uses: docker/metadata-action@369eb591f429131d6889c46b94e711f089e6ca96 # v5.6.1
|
||||||
with:
|
with:
|
||||||
images: |
|
images: |
|
||||||
${{ secrets.DOCKER_HUB_USERNAME }}/s-pdf
|
${{ secrets.DOCKER_HUB_USERNAME }}/s-pdf
|
||||||
@@ -75,7 +88,8 @@ jobs:
|
|||||||
type=raw,value=alpha,enable=${{ github.ref == 'refs/heads/main' }}
|
type=raw,value=alpha,enable=${{ github.ref == 'refs/heads/main' }}
|
||||||
|
|
||||||
- name: Build and push main Dockerfile
|
- name: Build and push main Dockerfile
|
||||||
uses: docker/build-push-action@v6
|
id: build-push-regular
|
||||||
|
uses: docker/build-push-action@48aba3b46d1b1fec4febb7c5d0c644b249a11355 # v6.10.0
|
||||||
with:
|
with:
|
||||||
builder: ${{ steps.buildx.outputs.name }}
|
builder: ${{ steps.buildx.outputs.name }}
|
||||||
context: .
|
context: .
|
||||||
@@ -87,10 +101,26 @@ jobs:
|
|||||||
labels: ${{ steps.meta.outputs.labels }}
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
build-args: VERSION_TAG=${{ steps.versionNumber.outputs.versionNumber }}
|
build-args: VERSION_TAG=${{ steps.versionNumber.outputs.versionNumber }}
|
||||||
platforms: linux/amd64,linux/arm64/v8
|
platforms: linux/amd64,linux/arm64/v8
|
||||||
|
provenance: true
|
||||||
|
sbom: true
|
||||||
|
|
||||||
|
- name: Sign regular images
|
||||||
|
if: github.ref == 'refs/heads/master'
|
||||||
|
env:
|
||||||
|
DIGEST: ${{ steps.build-push-regular.outputs.digest }}
|
||||||
|
TAGS: ${{ steps.meta.outputs.tags }}
|
||||||
|
COSIGN_PRIVATE_KEY: ${{ secrets.COSIGN_PRIVATE_KEY }}
|
||||||
|
COSIGN_PASSWORD: ${{ secrets.COSIGN_PASSWORD }}
|
||||||
|
run: |
|
||||||
|
echo "$TAGS" | tr ',' '\n' | while read -r tag; do
|
||||||
|
cosign sign --yes \
|
||||||
|
--key env://COSIGN_PRIVATE_KEY \
|
||||||
|
"${tag}@${DIGEST}"
|
||||||
|
done
|
||||||
|
|
||||||
- name: Generate tags ultra-lite
|
- name: Generate tags ultra-lite
|
||||||
id: meta2
|
id: meta2
|
||||||
uses: docker/metadata-action@v5
|
uses: docker/metadata-action@369eb591f429131d6889c46b94e711f089e6ca96 # v5.6.1
|
||||||
if: github.ref != 'refs/heads/main'
|
if: github.ref != 'refs/heads/main'
|
||||||
with:
|
with:
|
||||||
images: |
|
images: |
|
||||||
@@ -103,11 +133,12 @@ jobs:
|
|||||||
type=raw,value=latest-ultra-lite,enable=${{ github.ref == 'refs/heads/master' }}
|
type=raw,value=latest-ultra-lite,enable=${{ github.ref == 'refs/heads/master' }}
|
||||||
|
|
||||||
- name: Build and push Dockerfile-ultra-lite
|
- name: Build and push Dockerfile-ultra-lite
|
||||||
uses: docker/build-push-action@v6
|
id: build-push-lite
|
||||||
|
uses: docker/build-push-action@48aba3b46d1b1fec4febb7c5d0c644b249a11355 # v6.10.0
|
||||||
if: github.ref != 'refs/heads/main'
|
if: github.ref != 'refs/heads/main'
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
file: ./Dockerfile-ultra-lite
|
file: ./Dockerfile.ultra-lite
|
||||||
push: true
|
push: true
|
||||||
cache-from: type=gha
|
cache-from: type=gha
|
||||||
cache-to: type=gha,mode=max
|
cache-to: type=gha,mode=max
|
||||||
@@ -115,10 +146,12 @@ jobs:
|
|||||||
labels: ${{ steps.meta2.outputs.labels }}
|
labels: ${{ steps.meta2.outputs.labels }}
|
||||||
build-args: VERSION_TAG=${{ steps.versionNumber.outputs.versionNumber }}
|
build-args: VERSION_TAG=${{ steps.versionNumber.outputs.versionNumber }}
|
||||||
platforms: linux/amd64,linux/arm64/v8
|
platforms: linux/amd64,linux/arm64/v8
|
||||||
|
provenance: true
|
||||||
|
sbom: true
|
||||||
|
|
||||||
- name: Generate tags fat
|
- name: Generate tags fat
|
||||||
id: meta3
|
id: meta3
|
||||||
uses: docker/metadata-action@v5
|
uses: docker/metadata-action@369eb591f429131d6889c46b94e711f089e6ca96 # v5.6.1
|
||||||
if: github.ref != 'refs/heads/main'
|
if: github.ref != 'refs/heads/main'
|
||||||
with:
|
with:
|
||||||
images: |
|
images: |
|
||||||
@@ -131,12 +164,13 @@ jobs:
|
|||||||
type=raw,value=latest-fat,enable=${{ github.ref == 'refs/heads/master' }}
|
type=raw,value=latest-fat,enable=${{ github.ref == 'refs/heads/master' }}
|
||||||
|
|
||||||
- name: Build and push main Dockerfile fat
|
- name: Build and push main Dockerfile fat
|
||||||
uses: docker/build-push-action@v6
|
id: build-push-fat
|
||||||
|
uses: docker/build-push-action@48aba3b46d1b1fec4febb7c5d0c644b249a11355 # v6.10.0
|
||||||
if: github.ref != 'refs/heads/main'
|
if: github.ref != 'refs/heads/main'
|
||||||
with:
|
with:
|
||||||
builder: ${{ steps.buildx.outputs.name }}
|
builder: ${{ steps.buildx.outputs.name }}
|
||||||
context: .
|
context: .
|
||||||
file: ./Dockerfile-fat
|
file: ./Dockerfile.fat
|
||||||
push: true
|
push: true
|
||||||
cache-from: type=gha
|
cache-from: type=gha
|
||||||
cache-to: type=gha,mode=max
|
cache-to: type=gha,mode=max
|
||||||
@@ -144,3 +178,17 @@ jobs:
|
|||||||
labels: ${{ steps.meta3.outputs.labels }}
|
labels: ${{ steps.meta3.outputs.labels }}
|
||||||
build-args: VERSION_TAG=${{ steps.versionNumber.outputs.versionNumber }}
|
build-args: VERSION_TAG=${{ steps.versionNumber.outputs.versionNumber }}
|
||||||
platforms: linux/amd64,linux/arm64/v8
|
platforms: linux/amd64,linux/arm64/v8
|
||||||
|
provenance: true
|
||||||
|
sbom: true
|
||||||
|
|
||||||
|
- name: Sign fat images
|
||||||
|
if: github.ref == 'refs/heads/master'
|
||||||
|
env:
|
||||||
|
DIGEST: ${{ steps.build-push-fat.outputs.digest }}
|
||||||
|
TAGS: ${{ steps.meta3.outputs.tags }}
|
||||||
|
COSIGN_PRIVATE_KEY: ${{ secrets.COSIGN_PRIVATE_KEY }}
|
||||||
|
COSIGN_PASSWORD: ${{ secrets.COSIGN_PASSWORD }}
|
||||||
|
run: |
|
||||||
|
echo "$TAGS" | tr ',' '\n' | while read -r tag; do
|
||||||
|
cosign sign --key env://COSIGN_PRIVATE_KEY --yes "${tag}@${DIGEST}"
|
||||||
|
done
|
||||||
|
|||||||
31
.github/workflows/releaseArtifacts.yml
vendored
31
.github/workflows/releaseArtifacts.yml
vendored
@@ -4,12 +4,16 @@ on:
|
|||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
release:
|
release:
|
||||||
types: [created]
|
types: [created]
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: read
|
||||||
packages: write
|
|
||||||
jobs:
|
jobs:
|
||||||
push:
|
push:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
packages: write
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
enable_security: [true, false]
|
enable_security: [true, false]
|
||||||
@@ -19,17 +23,22 @@ jobs:
|
|||||||
- enable_security: false
|
- enable_security: false
|
||||||
file_suffix: ""
|
file_suffix: ""
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- name: Harden Runner
|
||||||
|
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
||||||
|
with:
|
||||||
|
egress-policy: audit
|
||||||
|
|
||||||
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
|
||||||
- name: Set up JDK 17
|
- name: Set up JDK 17
|
||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@7a6d8a8234af8eb26422e24e3006232cccaa061b # v4.6.0
|
||||||
with:
|
with:
|
||||||
java-version: "17"
|
java-version: "17"
|
||||||
distribution: "temurin"
|
distribution: "temurin"
|
||||||
|
|
||||||
- uses: gradle/actions/setup-gradle@v4
|
- uses: gradle/actions/setup-gradle@0bdd871935719febd78681f197cd39af5b6e16a6 # v4.2.2
|
||||||
with:
|
with:
|
||||||
gradle-version: 8.7
|
gradle-version: 8.12
|
||||||
|
|
||||||
- name: Generate jar (With Security=${{ matrix.enable_security }})
|
- name: Generate jar (With Security=${{ matrix.enable_security }})
|
||||||
run: ./gradlew clean createExe
|
run: ./gradlew clean createExe
|
||||||
@@ -45,16 +54,16 @@ jobs:
|
|||||||
run: cp ./build/launch4j/Stirling-PDF.exe ./build/launch4j/Stirling-PDF-Server${{ matrix.file_suffix }}.exe
|
run: cp ./build/launch4j/Stirling-PDF.exe ./build/launch4j/Stirling-PDF-Server${{ matrix.file_suffix }}.exe
|
||||||
|
|
||||||
- name: Upload Assets binarie
|
- name: Upload Assets binarie
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||||
with:
|
with:
|
||||||
path: ./build/launch4j/Stirling-PDF-Server${{ matrix.file_suffix }}.exe
|
path: ./build/launch4j/Stirling-PDF-Server${{ matrix.file_suffix }}.exe
|
||||||
name: Stirling-PDF-Server${{ matrix.file_suffix }}.exe
|
name: Stirling-PDF-Server${{ matrix.file_suffix }}.exe
|
||||||
overwrite: true
|
overwrite: true
|
||||||
retention-days: 1
|
retention-days: 1
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
|
|
||||||
- name: Upload binaries to release
|
- name: Upload binaries to release
|
||||||
uses: softprops/action-gh-release@v2
|
uses: softprops/action-gh-release@01570a1f39cb168c169c802c3bceb9e93fb10974 # v2.1.0
|
||||||
with:
|
with:
|
||||||
files: ./build/launch4j/Stirling-PDF-Server${{ matrix.file_suffix }}.exe
|
files: ./build/launch4j/Stirling-PDF-Server${{ matrix.file_suffix }}.exe
|
||||||
|
|
||||||
@@ -62,7 +71,7 @@ jobs:
|
|||||||
run: cp ./build/libs/Stirling-PDF-${{ steps.versionNumber.outputs.versionNumber }}.jar ./build/libs/Stirling-PDF${{ matrix.file_suffix }}.jar
|
run: cp ./build/libs/Stirling-PDF-${{ steps.versionNumber.outputs.versionNumber }}.jar ./build/libs/Stirling-PDF${{ matrix.file_suffix }}.jar
|
||||||
|
|
||||||
- name: Upload Assets jar binaries
|
- name: Upload Assets jar binaries
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||||
with:
|
with:
|
||||||
path: ./build/libs/Stirling-PDF${{ matrix.file_suffix }}.jar
|
path: ./build/libs/Stirling-PDF${{ matrix.file_suffix }}.jar
|
||||||
name: Stirling-PDF${{ matrix.file_suffix }}.jar
|
name: Stirling-PDF${{ matrix.file_suffix }}.jar
|
||||||
@@ -71,6 +80,6 @@ jobs:
|
|||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
|
|
||||||
- name: Upload jar binaries to release
|
- name: Upload jar binaries to release
|
||||||
uses: softprops/action-gh-release@v2
|
uses: softprops/action-gh-release@01570a1f39cb168c169c802c3bceb9e93fb10974 # v2.1.0
|
||||||
with:
|
with:
|
||||||
files: ./build/libs/Stirling-PDF${{ matrix.file_suffix }}.jar
|
files: ./build/libs/Stirling-PDF${{ matrix.file_suffix }}.jar
|
||||||
|
|||||||
79
.github/workflows/scorecards.yml
vendored
Normal file
79
.github/workflows/scorecards.yml
vendored
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
# This workflow uses actions that are not certified by GitHub. They are provided
|
||||||
|
# by a third-party and are governed by separate terms of service, privacy
|
||||||
|
# policy, and support documentation.
|
||||||
|
|
||||||
|
name: Scorecard supply-chain security
|
||||||
|
on:
|
||||||
|
# For Branch-Protection check. Only the default branch is supported. See
|
||||||
|
# https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection
|
||||||
|
branch_protection_rule:
|
||||||
|
# To guarantee Maintained check is occasionally updated. See
|
||||||
|
# https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained
|
||||||
|
schedule:
|
||||||
|
- cron: '20 7 * * 2'
|
||||||
|
push:
|
||||||
|
branches: ["main"]
|
||||||
|
permissions: read-all
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
analysis:
|
||||||
|
name: Scorecard analysis
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
# Needed to upload the results to code-scanning dashboard.
|
||||||
|
security-events: write
|
||||||
|
# Needed to publish results and get a badge (see publish_results below).
|
||||||
|
id-token: write
|
||||||
|
contents: read
|
||||||
|
actions: read
|
||||||
|
# To allow GraphQL ListCommits to work
|
||||||
|
issues: read
|
||||||
|
pull-requests: read
|
||||||
|
# To detect SAST tools
|
||||||
|
checks: read
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Harden Runner
|
||||||
|
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
||||||
|
with:
|
||||||
|
egress-policy: audit
|
||||||
|
|
||||||
|
- name: "Checkout code"
|
||||||
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
|
- name: "Run analysis"
|
||||||
|
uses: ossf/scorecard-action@62b2cac7ed8198b15735ed49ab1e5cf35480ba46 # v2.4.0
|
||||||
|
with:
|
||||||
|
results_file: results.sarif
|
||||||
|
results_format: sarif
|
||||||
|
# (Optional) "write" PAT token. Uncomment the `repo_token` line below if:
|
||||||
|
# - you want to enable the Branch-Protection check on a *public* repository, or
|
||||||
|
# - you are installing Scorecards on a *private* repository
|
||||||
|
# To create the PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-pat.
|
||||||
|
# repo_token: ${{ secrets.SCORECARD_TOKEN }}
|
||||||
|
|
||||||
|
# Public repositories:
|
||||||
|
# - Publish results to OpenSSF REST API for easy access by consumers
|
||||||
|
# - Allows the repository to include the Scorecard badge.
|
||||||
|
# - See https://github.com/ossf/scorecard-action#publishing-results.
|
||||||
|
# For private repositories:
|
||||||
|
# - `publish_results` will always be set to `false`, regardless
|
||||||
|
# of the value entered here.
|
||||||
|
publish_results: true
|
||||||
|
|
||||||
|
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
|
||||||
|
# format to the repository Actions tab.
|
||||||
|
- name: "Upload artifact"
|
||||||
|
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||||
|
with:
|
||||||
|
name: SARIF file
|
||||||
|
path: results.sarif
|
||||||
|
retention-days: 5
|
||||||
|
|
||||||
|
# Upload the results to GitHub's code scanning dashboard.
|
||||||
|
- name: "Upload to code-scanning"
|
||||||
|
uses: github/codeql-action/upload-sarif@48ab28a6f5dbc2a99bf1e0131198dd8f1df78169 # v3.28.0
|
||||||
|
with:
|
||||||
|
sarif_file: results.sarif
|
||||||
10
.github/workflows/stale.yml
vendored
10
.github/workflows/stale.yml
vendored
@@ -5,6 +5,9 @@ on:
|
|||||||
- cron: "30 0 * * *"
|
- cron: "30 0 * * *"
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
stale:
|
stale:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -12,8 +15,13 @@ jobs:
|
|||||||
issues: write
|
issues: write
|
||||||
pull-requests: write
|
pull-requests: write
|
||||||
steps:
|
steps:
|
||||||
|
- name: Harden Runner
|
||||||
|
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
||||||
|
with:
|
||||||
|
egress-policy: audit
|
||||||
|
|
||||||
- name: 30 days stale issues
|
- name: 30 days stale issues
|
||||||
uses: actions/stale@v9
|
uses: actions/stale@28ca1036281a5e5922ead5184a1bbf96e5fc984e # v9.0.0
|
||||||
with:
|
with:
|
||||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
days-before-stale: 30
|
days-before-stale: 30
|
||||||
|
|||||||
14
.github/workflows/swagger.yml
vendored
14
.github/workflows/swagger.yml
vendored
@@ -6,19 +6,27 @@ on:
|
|||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
push:
|
push:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- name: Harden Runner
|
||||||
|
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
||||||
|
with:
|
||||||
|
egress-policy: audit
|
||||||
|
|
||||||
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
|
||||||
- name: Set up JDK 17
|
- name: Set up JDK 17
|
||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@7a6d8a8234af8eb26422e24e3006232cccaa061b # v4.6.0
|
||||||
with:
|
with:
|
||||||
java-version: "17"
|
java-version: "17"
|
||||||
distribution: "temurin"
|
distribution: "temurin"
|
||||||
|
|
||||||
- uses: gradle/actions/setup-gradle@v4
|
- uses: gradle/actions/setup-gradle@0bdd871935719febd78681f197cd39af5b6e16a6 # v4.2.2
|
||||||
|
|
||||||
- name: Generate Swagger documentation
|
- name: Generate Swagger documentation
|
||||||
run: ./gradlew generateOpenApiDocs
|
run: ./gradlew generateOpenApiDocs
|
||||||
|
|||||||
18
.github/workflows/sync_files.yml
vendored
18
.github/workflows/sync_files.yml
vendored
@@ -10,16 +10,23 @@ on:
|
|||||||
- "scripts/ignore_translation.toml"
|
- "scripts/ignore_translation.toml"
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: read
|
||||||
pull-requests: write
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
sync-readme:
|
sync-readme:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
pull-requests: write
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- name: Harden Runner
|
||||||
|
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
||||||
|
with:
|
||||||
|
egress-policy: audit
|
||||||
|
|
||||||
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v5
|
uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0
|
||||||
with:
|
with:
|
||||||
python-version: "3.x"
|
python-version: "3.x"
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
@@ -36,7 +43,7 @@ jobs:
|
|||||||
git diff --staged --quiet || git commit -m ":memo: Sync README
|
git diff --staged --quiet || git commit -m ":memo: Sync README
|
||||||
> Made via sync_files.yml" || echo "no changes"
|
> Made via sync_files.yml" || echo "no changes"
|
||||||
- name: Create Pull Request
|
- name: Create Pull Request
|
||||||
uses: peter-evans/create-pull-request@v6
|
uses: peter-evans/create-pull-request@67ccf781d68cd99b580ae25a5c18a1cc84ffff1f # v7.0.6
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
commit-message: Update files
|
commit-message: Update files
|
||||||
@@ -52,3 +59,4 @@ jobs:
|
|||||||
draft: false
|
draft: false
|
||||||
delete-branch: true
|
delete-branch: true
|
||||||
labels: Documentation,Translation,github-actions
|
labels: Documentation,Translation,github-actions
|
||||||
|
sign-commits: true
|
||||||
|
|||||||
72
.github/workflows/update-translations.yml
vendored
Normal file
72
.github/workflows/update-translations.yml
vendored
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
name: Update Translations
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: ["main"]
|
||||||
|
paths:
|
||||||
|
- "src/main/resources/messages_en_GB.properties"
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
update-translations-main:
|
||||||
|
if: github.event_name == 'push'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
pull-requests: write
|
||||||
|
steps:
|
||||||
|
- name: Harden Runner
|
||||||
|
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
||||||
|
with:
|
||||||
|
egress-policy: audit
|
||||||
|
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
|
||||||
|
- name: Set up Python
|
||||||
|
uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0
|
||||||
|
with:
|
||||||
|
python-version: "3.x"
|
||||||
|
|
||||||
|
- name: Run Python script to check files
|
||||||
|
id: run-check
|
||||||
|
run: |
|
||||||
|
echo "Running Python script to check files..."
|
||||||
|
python .github/scripts/check_language_properties.py \
|
||||||
|
--reference-file src/main/resources/messages_en_GB.properties \
|
||||||
|
--branch main
|
||||||
|
|
||||||
|
- name: Set up git config
|
||||||
|
run: |
|
||||||
|
git config --global user.name "github-actions[bot]"
|
||||||
|
git config --global user.email "github-actions[bot]@users.noreply.github.com"
|
||||||
|
|
||||||
|
- name: Add translation keys
|
||||||
|
run: |
|
||||||
|
git add src/main/resources/messages_*.properties
|
||||||
|
git diff --staged --quiet || echo "CHANGES_DETECTED=true" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Create Pull Request
|
||||||
|
id: cpr
|
||||||
|
if: env.CHANGES_DETECTED == 'true'
|
||||||
|
uses: peter-evans/create-pull-request@67ccf781d68cd99b580ae25a5c18a1cc84ffff1f # v7.0.6
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
commit-message: "Update translation files"
|
||||||
|
committer: GitHub Action <action@github.com>
|
||||||
|
author: GitHub Action <action@github.com>
|
||||||
|
signoff: true
|
||||||
|
branch: update_translation_files
|
||||||
|
title: "Update translation files"
|
||||||
|
add-paths: |
|
||||||
|
src/main/resources/messages_*.properties
|
||||||
|
body: |
|
||||||
|
Auto-generated by [create-pull-request][1]
|
||||||
|
|
||||||
|
[1]: https://github.com/peter-evans/create-pull-request
|
||||||
|
draft: false
|
||||||
|
delete-branch: true
|
||||||
|
labels: Translation,github-actions
|
||||||
|
sign-commits: true
|
||||||
22
.gitignore
vendored
22
.gitignore
vendored
@@ -146,19 +146,37 @@ out/
|
|||||||
# cucumber
|
# cucumber
|
||||||
/cucumber/reports/**
|
/cucumber/reports/**
|
||||||
|
|
||||||
# Certs
|
# Certs and Security Files
|
||||||
*.p12
|
*.p12
|
||||||
|
*.pk8
|
||||||
*.pem
|
*.pem
|
||||||
*.crt
|
*.crt
|
||||||
*.cer
|
*.cer
|
||||||
|
*.cert
|
||||||
*.der
|
*.der
|
||||||
*.key
|
*.key
|
||||||
*.csr
|
*.csr
|
||||||
|
*.kdbx
|
||||||
|
*.jks
|
||||||
|
*.asc
|
||||||
|
|
||||||
|
# SSH Keys
|
||||||
|
*.pub
|
||||||
|
*.priv
|
||||||
|
id_rsa
|
||||||
|
id_rsa.pub
|
||||||
|
id_ecdsa
|
||||||
|
id_ecdsa.pub
|
||||||
|
id_ed25519
|
||||||
|
id_ed25519.pub
|
||||||
|
.ssh/
|
||||||
|
*ssh
|
||||||
|
|
||||||
# cache
|
# cache
|
||||||
|
.cache
|
||||||
.ruff_cache
|
.ruff_cache
|
||||||
.mypy_cache
|
.mypy_cache
|
||||||
.pytest_cache
|
.pytest_cache
|
||||||
.ipynb_checkpoints
|
.ipynb_checkpoints
|
||||||
|
|
||||||
**/jcef-bundle/
|
**/jcef-bundle/
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
repos:
|
repos:
|
||||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||||
rev: v0.2.1
|
rev: v0.8.4
|
||||||
hooks:
|
hooks:
|
||||||
- id: ruff
|
- id: ruff
|
||||||
args:
|
args:
|
||||||
@@ -12,7 +12,7 @@ repos:
|
|||||||
files: ^((.github/scripts|scripts)/.+)?[^/]+\.py$
|
files: ^((.github/scripts|scripts)/.+)?[^/]+\.py$
|
||||||
exclude: (split_photos.py)
|
exclude: (split_photos.py)
|
||||||
- repo: https://github.com/codespell-project/codespell
|
- repo: https://github.com/codespell-project/codespell
|
||||||
rev: v2.2.6
|
rev: v2.3.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: codespell
|
- id: codespell
|
||||||
args:
|
args:
|
||||||
@@ -21,6 +21,25 @@ repos:
|
|||||||
- --quiet-level=2
|
- --quiet-level=2
|
||||||
files: \.(properties|html|css|js|py|md)$
|
files: \.(properties|html|css|js|py|md)$
|
||||||
exclude: (.vscode|.devcontainer|src/main/resources|Dockerfile)
|
exclude: (.vscode|.devcontainer|src/main/resources|Dockerfile)
|
||||||
|
- repo: https://github.com/gitleaks/gitleaks
|
||||||
|
rev: v8.22.0
|
||||||
|
hooks:
|
||||||
|
- id: gitleaks
|
||||||
|
- repo: https://github.com/jumanjihouse/pre-commit-hooks
|
||||||
|
rev: 3.0.0
|
||||||
|
hooks:
|
||||||
|
- id: shellcheck
|
||||||
|
files: ^.*(\.bash|\.sh|\.ksh|\.zsh)$
|
||||||
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
|
rev: v5.0.0
|
||||||
|
hooks:
|
||||||
|
- id: end-of-file-fixer
|
||||||
|
files: ^.*(\.js|\.java|\.py|\.yml)$
|
||||||
|
exclude: ^(.*/pdfjs.*|.*/thirdParty.*|bootstrap.*|.*\.min\..*|.*diff\.js$)
|
||||||
|
- id: trailing-whitespace
|
||||||
|
files: ^.*(\.js|\.java|\.py|\.yml)$
|
||||||
|
exclude: ^(.*/pdfjs.*|.*/thirdParty.*|bootstrap.*|.*\.min\..*|.*diff\.js$)
|
||||||
|
|
||||||
- repo: local
|
- repo: local
|
||||||
hooks:
|
hooks:
|
||||||
- id: check-duplicate-properties-keys
|
- id: check-duplicate-properties-keys
|
||||||
@@ -28,12 +47,11 @@ repos:
|
|||||||
entry: python .github/scripts/check_duplicates.py
|
entry: python .github/scripts/check_duplicates.py
|
||||||
language: python
|
language: python
|
||||||
files: ^(src)/.+\.properties$
|
files: ^(src)/.+\.properties$
|
||||||
- repo: local
|
|
||||||
hooks:
|
|
||||||
- id: check-html-tabs
|
- id: check-html-tabs
|
||||||
name: Check HTML for tabs
|
name: Check HTML for tabs
|
||||||
|
description: Ensures HTML/CSS/JS files do not contain tab characters
|
||||||
# args: ["--replace_with= "]
|
# args: ["--replace_with= "]
|
||||||
entry: python .github/scripts/check_tabulator.py
|
entry: python .github/scripts/check_tabulator.py
|
||||||
language: python
|
language: python
|
||||||
exclude: ^(src/main/resources/static/pdfjs|src/main/resources/static/pdfjs-legacy)
|
exclude: ^(.*/pdfjs.*|.*/thirdParty.*|bootstrap.*|.*\.min\..*|.*diff\.js$)
|
||||||
files: ^.*(\.html|\.css|\.js)$
|
files: ^.*(\.html|\.css|\.js)$
|
||||||
@@ -18,9 +18,9 @@ For a detailed pull request tutorial, see [this guide](https://www.digitalocean.
|
|||||||
Please make sure your Pull Request adheres to the following guidelines:
|
Please make sure your Pull Request adheres to the following guidelines:
|
||||||
|
|
||||||
- Use the PR template provided.
|
- Use the PR template provided.
|
||||||
- Keep your Pull Request title succinct, detailed and to the point.
|
- Keep your Pull Request title succinct, detailed, and to the point.
|
||||||
- Keep commits atomic. One commit should contain one change. If you want to make multiple changes, submit multiple Pull Requests.
|
- Keep commits atomic. One commit should contain one change. If you want to make multiple changes, submit multiple Pull Requests.
|
||||||
- Commits should be clear, concise and easy to understand.
|
- Commits should be clear, concise, and easy to understand.
|
||||||
- References to the Issue number in the Pull Request and/or Commit message.
|
- References to the Issue number in the Pull Request and/or Commit message.
|
||||||
|
|
||||||
## Translations
|
## Translations
|
||||||
@@ -29,15 +29,15 @@ If you would like to add or modify a translation, please see [How to add new lan
|
|||||||
|
|
||||||
## Docs
|
## Docs
|
||||||
|
|
||||||
Documentation for Stirling-PDF is handled in a separate repository. Please see [Docs repository](https://github.com/Stirling-Tools/Stirling-Tools.github.io) or use "edit this page"-button at the bottom of each page at [https://docs.stirlingpdf.com/](https://docs.stirlingpdf.com/).
|
Documentation for Stirling-PDF is handled in a separate repository. Please see [Docs repository](https://github.com/Stirling-Tools/Stirling-Tools.github.io) or use the "edit this page"-button at the bottom of each page at [https://docs.stirlingpdf.com/](https://docs.stirlingpdf.com/).
|
||||||
|
|
||||||
## Fixing Bugs or Adding a New Feature
|
## Fixing Bugs or Adding a New Feature
|
||||||
|
|
||||||
First, make sure you've read the section [Pull Requests](#pull-requests).
|
First, make sure you've read the section [Pull Requests](#pull-requests).
|
||||||
|
|
||||||
To build from source, please follow this [Guide](LocalRunGuide.md).
|
If, at any point in time, you have a question, please feel free to ask in the same issue thread or in our [Discord](https://discord.gg/FJUSXUSYec).
|
||||||
|
|
||||||
If, at any point of time, you have a question, please feel free to ask in the same issue thread or in our [Discord](https://discord.gg/FJUSXUSYec).
|
Developers should review our [Developer Guide](DeveloperGuide.md)
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
## 1. Introduction
|
## 1. Introduction
|
||||||
|
|
||||||
Stirling-PDF is a robust, locally hosted web-based PDF manipulation tool. This guide focuses on Docker-based development and testing, which is the recommended approach for working with the full version of Stirling-PDF.
|
Stirling-PDF is a robust, locally hosted, web-based PDF manipulation tool. This guide focuses on Docker-based development and testing, which is the recommended approach for working with the full version of Stirling-PDF.
|
||||||
|
|
||||||
## 2. Project Overview
|
## 2. Project Overview
|
||||||
|
|
||||||
@@ -25,7 +25,7 @@ Stirling-PDF is built using:
|
|||||||
- Docker
|
- Docker
|
||||||
- Git
|
- Git
|
||||||
- Java JDK 17 or later
|
- Java JDK 17 or later
|
||||||
- Gradle 7.0 or later (Included within repo)
|
- Gradle 7.0 or later (Included within the repo)
|
||||||
|
|
||||||
### Setup Steps
|
### Setup Steps
|
||||||
|
|
||||||
@@ -38,14 +38,14 @@ Stirling-PDF is built using:
|
|||||||
|
|
||||||
2. Install Docker and JDK17 if not already installed.
|
2. Install Docker and JDK17 if not already installed.
|
||||||
|
|
||||||
3. Install a recommended Java IDE such as Eclipse, IntelliJ or VSCode
|
3. Install a recommended Java IDE such as Eclipse, IntelliJ, or VSCode
|
||||||
|
|
||||||
4. Lombok Setup
|
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:
|
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:
|
||||||
Visit the [Lombok website](https://projectlombok.org/setup/) for installation instructions specific to your IDE.
|
Visit the [Lombok website](https://projectlombok.org/setup/) for installation instructions specific to your IDE.
|
||||||
|
|
||||||
5. Add environment variable
|
5. Add environment variable
|
||||||
For local testing you should generally be testing the full 'Security' version of Stirling-PDF to do this you must add the environment flag DOCKER_ENABLE_SECURITY=true to your system and/or IDE build/run step
|
For local testing, you should generally be testing the full 'Security' version of Stirling-PDF. To do this, you must add the environment flag DOCKER_ENABLE_SECURITY=true to your system and/or IDE build/run step.
|
||||||
|
|
||||||
## 4. Project Structure
|
## 4. Project Structure
|
||||||
|
|
||||||
@@ -86,8 +86,8 @@ Stirling-PDF/
|
|||||||
│ └── SPDF/
|
│ └── SPDF/
|
||||||
├── build.gradle # Gradle build configuration
|
├── build.gradle # Gradle build configuration
|
||||||
├── Dockerfile # Main Dockerfile
|
├── Dockerfile # Main Dockerfile
|
||||||
├── Dockerfile-ultra-lite # Dockerfile for ultra-lite version
|
├── Dockerfile.ultra-lite # Dockerfile for ultra-lite version
|
||||||
├── Dockerfile-fat # Dockerfile for fat version
|
├── Dockerfile.fat # Dockerfile for fat version
|
||||||
├── docker-compose.yml # Docker Compose configuration
|
├── docker-compose.yml # Docker Compose configuration
|
||||||
└── test.sh # Test script to deploy all docker versions and run cuke tests
|
└── test.sh # Test script to deploy all docker versions and run cuke tests
|
||||||
```
|
```
|
||||||
@@ -102,7 +102,7 @@ Stirling-PDF offers several Docker versions:
|
|||||||
|
|
||||||
### Example Docker Compose Files
|
### Example Docker Compose Files
|
||||||
|
|
||||||
Stirling-PDF provides several example Docker Compose files in the `exampleYmlFiles` directory such as :
|
Stirling-PDF provides several example Docker Compose files in the `exampleYmlFiles` directory, such as:
|
||||||
|
|
||||||
- `docker-compose-latest.yml`: Latest version without security features
|
- `docker-compose-latest.yml`: Latest version without security features
|
||||||
- `docker-compose-latest-security.yml`: Latest version with security features enabled
|
- `docker-compose-latest-security.yml`: Latest version with security features enabled
|
||||||
@@ -179,14 +179,14 @@ Stirling-PDF uses different Docker images for various configurations. The build
|
|||||||
For the ultra-lite version:
|
For the ultra-lite version:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
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-ultra-lite -f ./Dockerfile.ultra-lite .
|
||||||
```
|
```
|
||||||
|
|
||||||
For the fat version (with security enabled):
|
For the fat version (with security enabled):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
export DOCKER_ENABLE_SECURITY=true
|
export DOCKER_ENABLE_SECURITY=true
|
||||||
docker build --no-cache --pull --build-arg VERSION_TAG=alpha -t stirlingtools/stirling-pdf:latest-fat -f ./Dockerfile-fat .
|
docker build --no-cache --pull --build-arg VERSION_TAG=alpha -t stirlingtools/stirling-pdf:latest-fat -f ./Dockerfile.fat .
|
||||||
```
|
```
|
||||||
|
|
||||||
Note: The `--no-cache` and `--pull` flags ensure that the build process uses the latest base images and doesn't use cached layers, which is useful for testing and ensuring reproducible builds. however to improve build times these can often be removed depending on your usecase
|
Note: The `--no-cache` and `--pull` flags ensure that the build process uses the latest base images and doesn't use cached layers, which is useful for testing and ensuring reproducible builds. however to improve build times these can often be removed depending on your usecase
|
||||||
@@ -205,9 +205,9 @@ To run the test script:
|
|||||||
|
|
||||||
This script performs the following actions:
|
This script performs the following actions:
|
||||||
|
|
||||||
1. Builds all Docker images (full, ultra-lite, fat)
|
1. Builds all Docker images (full, ultra-lite, fat).
|
||||||
2. Runs each version to ensure it starts correctly
|
2. Runs each version to ensure it starts correctly.
|
||||||
3. Executes Cucumber tests against main version and ensures feature compatibility, in the event these tests fail your PR will not be merged
|
3. Executes Cucumber tests against the main version and ensures feature compatibility. In the event these tests fail, your PR will not be merged.
|
||||||
|
|
||||||
Note: The `test.sh` script will run automatically when you raise a PR. However, it's recommended to run it locally first to save resources and catch any issues early.
|
Note: The `test.sh` script will run automatically when you raise a PR. However, it's recommended to run it locally first to save resources and catch any issues early.
|
||||||
|
|
||||||
@@ -229,7 +229,7 @@ For quick iterations and development of Java backend, JavaScript, and UI compone
|
|||||||
|
|
||||||
To run Stirling-PDF locally:
|
To run Stirling-PDF locally:
|
||||||
|
|
||||||
1. Compile and run the project using built in IDE methods or by running:
|
1. Compile and run the project using built-in IDE methods or by running:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./gradlew bootRun
|
./gradlew bootRun
|
||||||
@@ -261,7 +261,7 @@ Important notes:
|
|||||||
|
|
||||||
6. Push your changes to your fork.
|
6. Push your changes to your fork.
|
||||||
7. Submit a pull request to the main repository.
|
7. Submit a pull request to the main repository.
|
||||||
8. See additional [contributing guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md)
|
8. See additional [contributing guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md).
|
||||||
|
|
||||||
When you raise a PR:
|
When you raise a PR:
|
||||||
|
|
||||||
@@ -317,7 +317,7 @@ Remember to test your changes thoroughly to ensure they don't break any existing
|
|||||||
|
|
||||||
### Overview of Thymeleaf
|
### Overview of Thymeleaf
|
||||||
|
|
||||||
Thymeleaf is a server-side Java HTML template engine. It is used in Stirling-PDF to render dynamic web pages. Thymeleaf integrates heavily with Spring Boot
|
Thymeleaf is a server-side Java HTML template engine. It is used in Stirling-PDF to render dynamic web pages. Thymeleaf integrates heavily with Spring Boot.
|
||||||
|
|
||||||
### Thymeleaf overview
|
### Thymeleaf overview
|
||||||
|
|
||||||
@@ -327,22 +327,24 @@ Some examples of this are:
|
|||||||
|
|
||||||
```html
|
```html
|
||||||
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
|
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
|
||||||
|
```
|
||||||
or
|
or
|
||||||
|
```html
|
||||||
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
|
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
|
||||||
```
|
```
|
||||||
|
|
||||||
Where it uses the th:block, th: indicating its a special thymeleaf element to be used serverside in generating the html, and block being the actual element type.
|
Where it uses the `th:block`, `th:` indicating it's a special Thymeleaf element to be used server-side in generating the HTML, and block being the actual element type.
|
||||||
In this case we are inserting the ``navbar`` entry within the ``fragments/navbar.html`` fragment into the ``th:block`` element.
|
In this case, we are inserting the `navbar` entry within the `fragments/navbar.html` fragment into the `th:block` element.
|
||||||
|
|
||||||
They can be more complex such as:
|
They can be more complex, such as:
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<th:block th:insert="~{fragments/common :: head(title=#{pageExtracter.title}, header=#{pageExtracter.header})}"></th:block>
|
<th:block th:insert="~{fragments/common :: head(title=#{pageExtracter.title}, header=#{pageExtracter.header})}"></th:block>
|
||||||
```
|
```
|
||||||
|
|
||||||
Which is the same as above but passes the parameters title and header into the fragment common.html to be used in its HTML generation
|
Which is the same as above but passes the parameters title and header into the fragment `common.html` to be used in its HTML generation.
|
||||||
|
|
||||||
Thymeleaf can also be used to loop through objects or pass things from java side into html side.
|
Thymeleaf can also be used to loop through objects or pass things from the Java side into the HTML side.
|
||||||
|
|
||||||
```java
|
```java
|
||||||
@GetMapping
|
@GetMapping
|
||||||
@@ -352,7 +354,7 @@ Thymeleaf can also be used to loop through objects or pass things from java side
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
in above example if exampleData is a list of plain java objects of class Person and within it you had id, name, age etc. You can reference it like so
|
In the above example, if exampleData is a list of plain java objects of class Person and within it, you had id, name, age, etc. You can reference it like so
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<tbody>
|
<tbody>
|
||||||
@@ -452,7 +454,7 @@ This would generate n entries of tr for each person in exampleData
|
|||||||
1. **Create a New Thymeleaf Template:**
|
1. **Create a New Thymeleaf Template:**
|
||||||
- Create a new HTML file in the `src/main/resources/templates` directory.
|
- Create a new HTML file in the `src/main/resources/templates` directory.
|
||||||
- Use Thymeleaf attributes to dynamically generate content.
|
- Use Thymeleaf attributes to dynamically generate content.
|
||||||
- Use `extract-page.html` as a base example for the HTML template, useful to ensure importing of the general layout, navbar and footer.
|
- Use `extract-page.html` as a base example for the HTML template, which is useful to ensure importing of the general layout, navbar, and footer.
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
|
|||||||
16
Dockerfile
16
Dockerfile
@@ -10,6 +10,18 @@ COPY build/libs/*.jar app.jar
|
|||||||
|
|
||||||
ARG VERSION_TAG
|
ARG VERSION_TAG
|
||||||
|
|
||||||
|
LABEL org.opencontainers.image.title="Stirling-PDF"
|
||||||
|
LABEL org.opencontainers.image.description="A powerful locally hosted web-based PDF manipulation tool supporting 50+ operations including merging, splitting, conversion, OCR, watermarking, and more."
|
||||||
|
LABEL org.opencontainers.image.source="https://github.com/Stirling-Tools/Stirling-PDF"
|
||||||
|
LABEL org.opencontainers.image.licenses="MIT"
|
||||||
|
LABEL org.opencontainers.image.vendor="Stirling-Tools"
|
||||||
|
LABEL org.opencontainers.image.url="https://www.stirlingpdf.com"
|
||||||
|
LABEL org.opencontainers.image.documentation="https://docs.stirlingpdf.com"
|
||||||
|
LABEL maintainer="Stirling-Tools"
|
||||||
|
LABEL org.opencontainers.image.authors="Stirling-Tools"
|
||||||
|
LABEL org.opencontainers.image.version="${VERSION_TAG}"
|
||||||
|
LABEL org.opencontainers.image.keywords="PDF, manipulation, merge, split, convert, OCR, watermark"
|
||||||
|
|
||||||
# Set Environment Variables
|
# Set Environment Variables
|
||||||
ENV DOCKER_ENABLE_SECURITY=false \
|
ENV DOCKER_ENABLE_SECURITY=false \
|
||||||
VERSION_TAG=$VERSION_TAG \
|
VERSION_TAG=$VERSION_TAG \
|
||||||
@@ -19,6 +31,7 @@ ENV DOCKER_ENABLE_SECURITY=false \
|
|||||||
PGID=1000 \
|
PGID=1000 \
|
||||||
UMASK=022
|
UMASK=022
|
||||||
|
|
||||||
|
|
||||||
# JDK for app
|
# JDK for app
|
||||||
RUN echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/main" | tee -a /etc/apk/repositories && \
|
RUN echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/main" | tee -a /etc/apk/repositories && \
|
||||||
echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/community" | tee -a /etc/apk/repositories && \
|
echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/community" | tee -a /etc/apk/repositories && \
|
||||||
@@ -57,8 +70,7 @@ RUN echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/main" | tee -a /et
|
|||||||
# User permissions
|
# User permissions
|
||||||
addgroup -S stirlingpdfgroup && adduser -S stirlingpdfuser -G stirlingpdfgroup && \
|
addgroup -S stirlingpdfgroup && adduser -S stirlingpdfuser -G stirlingpdfgroup && \
|
||||||
chown -R stirlingpdfuser:stirlingpdfgroup $HOME /scripts /usr/share/fonts/opentype/noto /configs /customFiles /pipeline && \
|
chown -R stirlingpdfuser:stirlingpdfgroup $HOME /scripts /usr/share/fonts/opentype/noto /configs /customFiles /pipeline && \
|
||||||
chown stirlingpdfuser:stirlingpdfgroup /app.jar && \
|
chown stirlingpdfuser:stirlingpdfgroup /app.jar
|
||||||
tesseract --list-langs
|
|
||||||
|
|
||||||
EXPOSE 8080/tcp
|
EXPOSE 8080/tcp
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Build the application
|
# Build the application
|
||||||
FROM gradle:8.11-jdk17 AS build
|
FROM gradle:8.12-jdk17 AS build
|
||||||
|
|
||||||
# Set the working directory
|
# Set the working directory
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
@@ -73,8 +73,7 @@ RUN echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/main" | tee -a /et
|
|||||||
# User permissions
|
# User permissions
|
||||||
addgroup -S stirlingpdfgroup && adduser -S stirlingpdfuser -G stirlingpdfgroup && \
|
addgroup -S stirlingpdfgroup && adduser -S stirlingpdfuser -G stirlingpdfgroup && \
|
||||||
chown -R stirlingpdfuser:stirlingpdfgroup $HOME /scripts /usr/share/fonts/opentype/noto /configs /customFiles /pipeline && \
|
chown -R stirlingpdfuser:stirlingpdfgroup $HOME /scripts /usr/share/fonts/opentype/noto /configs /customFiles /pipeline && \
|
||||||
chown stirlingpdfuser:stirlingpdfgroup /app.jar && \
|
chown stirlingpdfuser:stirlingpdfgroup /app.jar
|
||||||
tesseract --list-langs
|
|
||||||
|
|
||||||
EXPOSE 8080/tcp
|
EXPOSE 8080/tcp
|
||||||
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
# use alpine
|
# use alpine
|
||||||
FROM alpine:3.21.0
|
FROM alpine:3.21.0@sha256:21dc6063fd678b478f57c0e13f47560d0ea4eeba26dfc947b2a4f81f686b9f45
|
||||||
|
|
||||||
ARG VERSION_TAG
|
ARG VERSION_TAG
|
||||||
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
| Operation | PageOps | Convert | Security | Other | CLI | Python | OpenCV | LibreOffice | qpdf | Java | Javascript | Unoconv | tesseract |
|
|
||||||
| ------------------- | ------- | ------- | -------- | ----- | --- | ------ | ------ | ----------- | -------- | ---- | ---------- | ------- | ----------- |
|
|
||||||
| adjust-contrast | ✔️ | | | | | | | | | | ✔️ | | |
|
|
||||||
| auto-split-pdf | ✔️ | | | | | | | | | ✔️ | | | |
|
|
||||||
| crop | ✔️ | | | | | | | | | ✔️ | | | |
|
|
||||||
| extract-page | ✔️ | | | | | | | | | ✔️ | | | |
|
|
||||||
| merge-pdfs | ✔️ | | | | | | | | | ✔️ | | | |
|
|
||||||
| multi-page-layout | ✔️ | | | | | | | | | ✔️ | | | |
|
|
||||||
| pdf-organizer | ✔️ | | | | | | | | | ✔️ | ✔️ | | |
|
|
||||||
| pdf-to-single-page | ✔️ | | | | | | | | | ✔️ | | | |
|
|
||||||
| remove-pages | ✔️ | | | | | | | | | ✔️ | | | |
|
|
||||||
| rotate-pdf | ✔️ | | | | | | | | | ✔️ | | | |
|
|
||||||
| scale-pages | ✔️ | | | | | | | | | ✔️ | | | |
|
|
||||||
| split-pdfs | ✔️ | | | | | | | | | ✔️ | | | |
|
|
||||||
| file-to-pdf | | ✔️ | | | ✔️ | ✔️ | | ✔️ | | | | ✔️ | |
|
|
||||||
| img-to-pdf | | ✔️ | | | | | | | | ✔️ | | | |
|
|
||||||
| pdf-to-html | | ✔️ | | | ✔️ | | | ✔️ | | | | | |
|
|
||||||
| pdf-to-img | | ✔️ | | | | ✔️ | | | | ✔️ | | | |
|
|
||||||
| pdf-to-pdfa | | ✔️ | | | ✔️ | | | | ✔️ | | | | |
|
|
||||||
| pdf-to-markdown | | ✔️ | | | | | | | | ✔️ | | | |
|
|
||||||
| pdf-to-presentation | | ✔️ | | | ✔️ | | | ✔️ | | | | | |
|
|
||||||
| pdf-to-text | | ✔️ | | | ✔️ | | | ✔️ | | | | | |
|
|
||||||
| pdf-to-word | | ✔️ | | | ✔️ | | | ✔️ | | | | | |
|
|
||||||
| pdf-to-xml | | ✔️ | | | ✔️ | | | ✔️ | | | | | |
|
|
||||||
| add-password | | | ✔️ | | | | | | | ✔️ | | | |
|
|
||||||
| add-watermark | | | ✔️ | | | | | | | ✔️ | | | |
|
|
||||||
| cert-sign | | | ✔️ | | | | | | | ✔️ | | | |
|
|
||||||
| remove-cert-sign | | | ✔️ | | | | | | | ✔️ | | | |
|
|
||||||
| change-permissions | | | ✔️ | | | | | | | ✔️ | | | |
|
|
||||||
| remove-password | | | ✔️ | | | | | | | ✔️ | | | |
|
|
||||||
| sanitize-pdf | | | ✔️ | | | | | | | ✔️ | | | |
|
|
||||||
| add-image | | | | ✔️ | | | | | | ✔️ | | | |
|
|
||||||
| add-page-numbers | | | | ✔️ | | | | | | ✔️ | | | |
|
|
||||||
| auto-rename | | | | ✔️ | | | | | | ✔️ | | | |
|
|
||||||
| change-metadata | | | | ✔️ | | | | | | ✔️ | | | |
|
|
||||||
| compare | | | | ✔️ | | | | | | | ✔️ | | |
|
|
||||||
| compress-pdf | | | | ✔️ | ✔️ | | | | ✔️ | | | | |
|
|
||||||
| extract-image-scans | | | | ✔️ | ✔️ | ✔️ | ✔️ | | | | | | |
|
|
||||||
| extract-images | | | | ✔️ | | | | | | ✔️ | | | |
|
|
||||||
| flatten | | | | ✔️ | | | | | | | ✔️ | | |
|
|
||||||
| get-info-on-pdf | | | | ✔️ | | | | | | ✔️ | | | |
|
|
||||||
| ocr-pdf | | | | ✔️ | ✔️ | | | | | | | | ✔ |
|
|
||||||
| remove-blanks | | | | ✔️ | ✔️ | ✔️ | ✔️ | | | | | | |
|
|
||||||
| repair | | | | ✔️ | ✔️ | | | ✔️ | ✔ | | | | |
|
|
||||||
| show-javascript | | | | ✔️ | | | | | | | ✔️ | | |
|
|
||||||
| sign | | | | ✔️ | | | | | | | ✔️ | | |
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
## User Guide for Local Directory Scanning and File Processing
|
|
||||||
|
|
||||||
### Setting Up Watched Folders
|
|
||||||
|
|
||||||
- Create a folder where you want your files to be monitored. This is your 'watched folder'.
|
|
||||||
- The default directory for this is `./pipeline/watchedFolders/`.
|
|
||||||
- Place any directories you want to be scanned into this folder. This folder should contain multiple folders, each for their own tasks and pipelines.
|
|
||||||
|
|
||||||
### Configuring Processing with JSON Files
|
|
||||||
|
|
||||||
- In each directory you want processed (e.g., `./pipeline/watchedFolders/officePrinter`), include a JSON configuration file.
|
|
||||||
- This JSON file should specify how you want the files in the directory to be handled (e.g., what operations to perform on them). This can be made, configured, and downloaded from the Stirling-PDF Pipeline interface.
|
|
||||||
|
|
||||||
### Automatic Scanning and Processing
|
|
||||||
|
|
||||||
- The system automatically checks the watched folder every minute for new directories and files to process.
|
|
||||||
- When a directory with a valid JSON configuration file is found, it begins processing the files inside according to the configuration.
|
|
||||||
|
|
||||||
### Processing Steps
|
|
||||||
|
|
||||||
- Files in each directory are processed according to the instructions in the JSON file.
|
|
||||||
- This might involve file conversions, data filtering, renaming files, etc. If the output of a step is a zip, this zip will be automatically unzipped as it passes to the next process.
|
|
||||||
|
|
||||||
### Results and Output
|
|
||||||
|
|
||||||
- After processing, the results are saved in a specified output location. This could be a different folder or location as defined in the JSON file or the default location `./pipeline/finishedFolders/`.
|
|
||||||
- Each processed file is named and organized according to the rules set in the JSON configuration.
|
|
||||||
|
|
||||||
### Completion and Cleanup
|
|
||||||
|
|
||||||
- Once processing is complete, the original files in the watched folder's directory are removed.
|
|
||||||
- You can find the processed files in the designated output location.
|
|
||||||
|
|
||||||
### Error Handling
|
|
||||||
|
|
||||||
- If there's an error during processing, the system will not delete the original files, allowing you to check and retry if necessary.
|
|
||||||
|
|
||||||
### User Interaction
|
|
||||||
|
|
||||||
- As a user, your main tasks are to set up the watched folders, place directories with files for processing, and create the corresponding JSON configuration files.
|
|
||||||
- The system handles the rest, including scanning, processing, and outputting results.
|
|
||||||
@@ -92,8 +92,9 @@ Verify installation:
|
|||||||
``tesseract --list-langs``
|
``tesseract --list-langs``
|
||||||
|
|
||||||
You must then edit your ``/configs/settings.yml`` and change the system.tessdataDir to match the directory containing lang files
|
You must then edit your ``/configs/settings.yml`` and change the system.tessdataDir to match the directory containing lang files
|
||||||
|
|
||||||
```
|
```
|
||||||
system:
|
system:
|
||||||
tessdataDir: C:/Program Files/Tesseract-OCR/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.
|
tessdataDir: C:/Program Files/Tesseract-OCR/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.
|
||||||
```
|
```
|
||||||
|
|
||||||
327
LocalRunGuide.md
327
LocalRunGuide.md
@@ -1,327 +0,0 @@
|
|||||||
To run the application without Docker/Podman, you will need to manually install all dependencies and build the necessary components.
|
|
||||||
|
|
||||||
Note that some dependencies might not be available in the standard repositories of all Linux distributions, and may require additional steps to install.
|
|
||||||
|
|
||||||
The following guide assumes you have a basic understanding of using a command line interface in your operating system.
|
|
||||||
|
|
||||||
It should work on most Linux distributions and MacOS. For Windows, you might need to use Windows Subsystem for Linux (WSL) for certain steps. The amount of dependencies is to actually reduce overall size, i.e., installing LibreOffice subcomponents rather than the full LibreOffice package.
|
|
||||||
|
|
||||||
You could theoretically use a Distrobox/Toolbox if your distribution has old or not all packages. But you might just as well use the Docker container then.
|
|
||||||
|
|
||||||
### Step 1: Prerequisites
|
|
||||||
|
|
||||||
Install the following software, if not already installed:
|
|
||||||
|
|
||||||
- Java 17 or later (21 recommended)
|
|
||||||
- Gradle 7.0 or later (included within repo so not needed on server)
|
|
||||||
- Git
|
|
||||||
- Python 3.8 (with pip)
|
|
||||||
- Make
|
|
||||||
- GCC/G++
|
|
||||||
- Automake
|
|
||||||
- Autoconf
|
|
||||||
- libtool
|
|
||||||
- pkg-config
|
|
||||||
- zlib1g-dev
|
|
||||||
- libleptonica-dev
|
|
||||||
|
|
||||||
For Debian-based systems, you can use the following command:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install -y git automake autoconf libtool libleptonica-dev pkg-config zlib1g-dev make g++ openjdk-21-jdk python3 python3-pip
|
|
||||||
```
|
|
||||||
|
|
||||||
For Fedora-based systems use this command:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo dnf install -y git automake autoconf libtool leptonica-devel pkg-config zlib-devel make gcc-c++ java-21-openjdk python3 python3-pip
|
|
||||||
```
|
|
||||||
|
|
||||||
For non-root users with Nix Package Manager, use the following command:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
nix-channel --update
|
|
||||||
nix-env -iA nixpkgs.jdk21 nixpkgs.git nixpkgs.python38 nixpkgs.gnumake nixpkgs.libgcc nixpkgs.automake nixpkgs.autoconf nixpkgs.libtool nixpkgs.pkg-config nixpkgs.zlib nixpkgs.leptonica
|
|
||||||
```
|
|
||||||
|
|
||||||
### Step 2: Clone and Build jbig2enc (Only required for certain OCR functionality)
|
|
||||||
|
|
||||||
For Debian and Fedora, you can build it from source using the following commands:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
mkdir ~/.git
|
|
||||||
cd ~/.git && \
|
|
||||||
git clone https://github.com/agl/jbig2enc.git && \
|
|
||||||
cd jbig2enc && \
|
|
||||||
./autogen.sh && \
|
|
||||||
./configure && \
|
|
||||||
make && \
|
|
||||||
sudo make install
|
|
||||||
```
|
|
||||||
|
|
||||||
For Nix, you will face `Leptonica not detected`. Bypass this by installing it directly using the following command:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
nix-env -iA nixpkgs.jbig2enc
|
|
||||||
```
|
|
||||||
|
|
||||||
### Step 3: Install Additional Software
|
|
||||||
|
|
||||||
Next we need to install LibreOffice for conversions, qpdf for OCR, and OpenCV for pattern recognition functionality.
|
|
||||||
|
|
||||||
Install the following software:
|
|
||||||
|
|
||||||
- libreoffice-core
|
|
||||||
- libreoffice-common
|
|
||||||
- libreoffice-writer
|
|
||||||
- libreoffice-calc
|
|
||||||
- libreoffice-impress
|
|
||||||
- python3-uno
|
|
||||||
- unoconv
|
|
||||||
- pngquant
|
|
||||||
- unpaper
|
|
||||||
- qpdf
|
|
||||||
- opencv-python-headless
|
|
||||||
|
|
||||||
For Debian-based systems, you can use the following command:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo apt-get install -y libreoffice-writer libreoffice-calc libreoffice-impress unpaper qpdf
|
|
||||||
pip3 install uno opencv-python-headless unoconv pngquant WeasyPrint --break-system-packages
|
|
||||||
```
|
|
||||||
|
|
||||||
For Fedora:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo dnf install -y libreoffice-writer libreoffice-calc libreoffice-impress unpaper qpdf
|
|
||||||
pip3 install uno opencv-python-headless unoconv pngquant WeasyPrint
|
|
||||||
```
|
|
||||||
|
|
||||||
For Nix:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
nix-env -iA nixpkgs.unpaper nixpkgs.libreoffice nixpkgs.qpdf nixpkgs.poppler_utils
|
|
||||||
pip3 install uno opencv-python-headless unoconv pngquant WeasyPrint
|
|
||||||
```
|
|
||||||
|
|
||||||
### Step 4: Clone and Build Stirling-PDF
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd ~/.git && \
|
|
||||||
git clone https://github.com/Stirling-Tools/Stirling-PDF.git && \
|
|
||||||
cd Stirling-PDF && \
|
|
||||||
chmod +x ./gradlew && \
|
|
||||||
./gradlew build
|
|
||||||
```
|
|
||||||
|
|
||||||
### Step 5: Move Jar to Desired Location
|
|
||||||
|
|
||||||
After the build process, a `.jar` file will be generated in the `build/libs` directory. You can move this file to a desired location, for example, `/opt/Stirling-PDF/`. You must also move the Script folder within the Stirling-PDF repo that you have downloaded to this directory. This folder is required for the Python scripts using OpenCV.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo mkdir /opt/Stirling-PDF && \
|
|
||||||
sudo mv ./build/libs/Stirling-PDF-*.jar /opt/Stirling-PDF/ && \
|
|
||||||
sudo mv scripts /opt/Stirling-PDF/ && \
|
|
||||||
echo "Scripts installed."
|
|
||||||
```
|
|
||||||
|
|
||||||
For non-root users, you can just keep the jar in the main directory of Stirling-PDF using the following command:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
mv ./build/libs/Stirling-PDF-*.jar ./Stirling-PDF-*.jar
|
|
||||||
```
|
|
||||||
|
|
||||||
### Step 6: Other Files
|
|
||||||
|
|
||||||
#### OCR
|
|
||||||
|
|
||||||
If you plan to use the OCR (Optical Character Recognition) functionality, you might need to install language packs for Tesseract if running non-English scanning.
|
|
||||||
|
|
||||||
##### Installing Language Packs
|
|
||||||
|
|
||||||
The easiest method is to use the language packs provided by your repositories. Skip the other steps if they are available.
|
|
||||||
|
|
||||||
**Manual:**
|
|
||||||
|
|
||||||
1. Download the desired language pack(s) by selecting the `.traineddata` file(s) for the language(s) you need.
|
|
||||||
2. Place the `.traineddata` files in the Tesseract tessdata directory: `/usr/share/tessdata`
|
|
||||||
|
|
||||||
**IMPORTANT:** DO NOT REMOVE EXISTING `eng.traineddata`, IT'S REQUIRED.
|
|
||||||
|
|
||||||
**Debian-based systems**, install languages with this command:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo apt update && \
|
|
||||||
# All languages
|
|
||||||
# sudo apt install -y 'tesseract-ocr-*'
|
|
||||||
|
|
||||||
# Find languages:
|
|
||||||
apt search tesseract-ocr-
|
|
||||||
|
|
||||||
# View installed languages:
|
|
||||||
dpkg-query -W tesseract-ocr- | sed 's/tesseract-ocr-//g'
|
|
||||||
```
|
|
||||||
|
|
||||||
**Fedora:**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# All languages
|
|
||||||
# sudo dnf install -y tesseract-langpack-*
|
|
||||||
|
|
||||||
# Find languages:
|
|
||||||
dnf search -C tesseract-langpack-
|
|
||||||
|
|
||||||
# View installed languages:
|
|
||||||
rpm -qa | grep tesseract-langpack | sed 's/tesseract-langpack-//g'
|
|
||||||
```
|
|
||||||
|
|
||||||
**Nix:**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
nix-env -iA nixpkgs.tesseract
|
|
||||||
```
|
|
||||||
|
|
||||||
**Note:** Nix Package Manager pre-installs almost all the language packs when Tesseract is installed.
|
|
||||||
|
|
||||||
### Step 7: Run Stirling-PDF
|
|
||||||
|
|
||||||
Those who have pushed to the root directory, run the following commands:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
./gradlew bootRun
|
|
||||||
or
|
|
||||||
java -jar /opt/Stirling-PDF/Stirling-PDF-*.jar
|
|
||||||
```
|
|
||||||
|
|
||||||
Since LibreOffice, soffice, and conversion tools have their dbus_tmp_dir set as `dbus_tmp_dir="/run/user/$(id -u)/libreoffice-dbus"`, you might get the following error when using their endpoints:
|
|
||||||
|
|
||||||
```
|
|
||||||
[Thread-7] INFO s.s.SPDF.utils.ProcessExecutor - mkdir: cannot create directory ‘/run/user/1501’: Permission denied
|
|
||||||
```
|
|
||||||
|
|
||||||
To resolve this, before starting Stirling-PDF, you have to set the environment variable to a directory you have write access to by using the following commands:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
mkdir temp
|
|
||||||
export DBUS_SESSION_BUS_ADDRESS="unix:path=./temp"
|
|
||||||
./gradlew bootRun
|
|
||||||
or
|
|
||||||
java -jar ./Stirling-PDF-*.jar
|
|
||||||
```
|
|
||||||
|
|
||||||
### Step 8: Adding a Desktop Icon
|
|
||||||
|
|
||||||
This will add a modified app starter to your app menu.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
location=$(pwd)/gradlew
|
|
||||||
image=$(pwd)/docs/stirling-transparent.svg
|
|
||||||
|
|
||||||
cat > ~/.local/share/applications/Stirling-PDF.desktop <<EOF
|
|
||||||
[Desktop Entry]
|
|
||||||
Name=Stirling PDF;
|
|
||||||
GenericName=Launch StirlingPDF and open its WebGUI;
|
|
||||||
Category=Office;
|
|
||||||
Exec=xdg-open http://localhost:8080 && nohup $location bootRun &;
|
|
||||||
Icon=$image;
|
|
||||||
Keywords=pdf;
|
|
||||||
Type=Application;
|
|
||||||
NoDisplay=false;
|
|
||||||
Terminal=true;
|
|
||||||
EOF
|
|
||||||
```
|
|
||||||
|
|
||||||
Note: Currently the app will run in the background until manually closed.
|
|
||||||
|
|
||||||
### Optional: Changing the Host and Port of the Application
|
|
||||||
|
|
||||||
To override the default configuration, you can add the following to `/.git/Stirling-PDF/configs/custom_settings.yml` file:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
server:
|
|
||||||
host: 0.0.0.0 # Not working - use instead address
|
|
||||||
address: 0.0.0.0
|
|
||||||
port: 3000
|
|
||||||
```
|
|
||||||
|
|
||||||
`-Djava.net.preferIPv4Stack=true` --> To force IPv4 only in the Java starting command
|
|
||||||
|
|
||||||
**Note:** This file is created after the first application launch. To have it before that, you can create the directory and add the file yourself.
|
|
||||||
|
|
||||||
### Optional: Run Stirling-PDF as a Service (requires root)
|
|
||||||
|
|
||||||
First create a `.env` file, where you can store environment variables:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
touch /opt/Stirling-PDF/.env
|
|
||||||
```
|
|
||||||
|
|
||||||
In this file, you can add all variables, one variable per line, as stated in the main readme (for example `SYSTEM_DEFAULTLOCALE="de-DE"`).
|
|
||||||
|
|
||||||
Create a new file where we store our service settings and open it with the nano editor:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
nano /etc/systemd/system/stirlingpdf.service
|
|
||||||
```
|
|
||||||
|
|
||||||
Paste this content, make sure to update the filename of the jar file. Press `Ctrl+S` and `Ctrl+X` to save and exit the nano editor:
|
|
||||||
|
|
||||||
```ini
|
|
||||||
[Unit]
|
|
||||||
Description=Stirling-PDF service
|
|
||||||
After=syslog.target network.target
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
SuccessExitStatus=143
|
|
||||||
|
|
||||||
User=root
|
|
||||||
Group=root
|
|
||||||
|
|
||||||
Type=simple
|
|
||||||
|
|
||||||
EnvironmentFile=/opt/Stirling-PDF/.env
|
|
||||||
WorkingDirectory=/opt/Stirling-PDF
|
|
||||||
ExecStart=/usr/bin/java -jar Stirling-PDF-0.17.2.jar
|
|
||||||
ExecStop=/bin/kill -15 $MAINPID
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=multi-user.target
|
|
||||||
```
|
|
||||||
|
|
||||||
Notify systemd that it has to rebuild its internal service database (you have to run this command every time you make a change in the service file):
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo systemctl daemon-reload
|
|
||||||
```
|
|
||||||
|
|
||||||
Enable the service to tell it to start automatically:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo systemctl enable stirlingpdf.service
|
|
||||||
```
|
|
||||||
|
|
||||||
See the status of the service:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo systemctl status stirlingpdf.service
|
|
||||||
```
|
|
||||||
|
|
||||||
Manually start/stop/restart the service:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo systemctl start stirlingpdf.service
|
|
||||||
sudo systemctl stop stirlingpdf.service
|
|
||||||
sudo systemctl restart stirlingpdf.service
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
Remember to set the necessary environment variables before running the project if you want to customize the application. The list can be seen in the main readme.
|
|
||||||
|
|
||||||
You can do this in the terminal by using the `export` command or `-D` argument to the Java `-jar` command:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
export APP_HOME_NAME="Stirling PDF"
|
|
||||||
or
|
|
||||||
-DAPP_HOME_NAME="Stirling PDF"
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
# Pipeline Configuration and Usage Tutorial
|
|
||||||
|
|
||||||
- Configure the pipeline config file and input files to run files against it.
|
|
||||||
- For reuse, download the config file and re-upload it when needed, or place it in `/pipeline/defaultWebUIConfigs/` to auto-load in the web UI for all users.
|
|
||||||
|
|
||||||
## Steps to Configure and Use Your Pipeline
|
|
||||||
|
|
||||||
1. **Access Configuration**
|
|
||||||
- Upon entering the screen, click on the **Configure** button.
|
|
||||||
|
|
||||||
2. **Enter Pipeline Name**
|
|
||||||
- Provide a name for your pipeline in the designated field.
|
|
||||||
|
|
||||||
3. **Select Operations**
|
|
||||||
- Choose the operations for your pipeline (e.g., **Split Pages**), then click **Add Operation**.
|
|
||||||
|
|
||||||
4. **Configure Operation Settings**
|
|
||||||
- Input the necessary settings for each added operation. Settings are highlighted in yellow if customization is needed.
|
|
||||||
|
|
||||||
5. **Add More Operations**
|
|
||||||
- You can add and adjust the order of multiple operations. Ensure each operation's settings are customized.
|
|
||||||
|
|
||||||
6. **Save Settings**
|
|
||||||
- Click **Save Operation Settings** after customizing settings for each operation.
|
|
||||||
|
|
||||||
7. **Validate Pipeline**
|
|
||||||
- Use the **Validation** button to check your pipeline. A green indicator signifies correct setup; a pop-out error indicates issues.
|
|
||||||
|
|
||||||
8. **Download Pipeline Configuration**
|
|
||||||
- To use the configuration for folder scanning (or save it for future use and re-upload it), download a JSON file in this menu. You can also pre-load it for future use by placing it in `/pipeline/defaultWebUIConfigs/`. It will then appear in the dropdown menu for all users to use.
|
|
||||||
|
|
||||||
9. **Submit Files for Processing**
|
|
||||||
- If your pipeline is correctly set up, close the configure menu, input the files, and hit **Submit**.
|
|
||||||
|
|
||||||
10. **Note on Web UI Limitations**
|
|
||||||
- The current web UI version does not support operations that require multiple different types of inputs, such as adding a separate image to a PDF.
|
|
||||||
|
|
||||||
### Current Limitations
|
|
||||||
|
|
||||||
- Cannot have more than one of the same operation.
|
|
||||||
- Cannot input additional files via UI.
|
|
||||||
- All files and operations run in serial mode.
|
|
||||||
348
README.md
348
README.md
@@ -4,6 +4,7 @@
|
|||||||
[](https://hub.docker.com/r/frooodle/s-pdf)
|
[](https://hub.docker.com/r/frooodle/s-pdf)
|
||||||
[](https://discord.gg/HYmhKj45pU)
|
[](https://discord.gg/HYmhKj45pU)
|
||||||
[](https://github.com/Stirling-Tools/Stirling-PDF/)
|
[](https://github.com/Stirling-Tools/Stirling-PDF/)
|
||||||
|
[](https://scorecard.dev/viewer/?uri=github.com/Stirling-Tools/Stirling-PDF)
|
||||||
[](https://github.com/Stirling-Tools/stirling-pdf)
|
[](https://github.com/Stirling-Tools/stirling-pdf)
|
||||||
|
|
||||||
<a href="https://www.producthunt.com/posts/stirling-pdf?embed=true&utm_source=badge-featured&utm_medium=badge&utm_souce=badge-stirling-pdf" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=641239&theme=light" alt="Stirling PDF - Open source locally hosted web PDF editor | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a>
|
<a href="https://www.producthunt.com/posts/stirling-pdf?embed=true&utm_source=badge-featured&utm_medium=badge&utm_souce=badge-stirling-pdf" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=641239&theme=light" alt="Stirling PDF - Open source locally hosted web PDF editor | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a>
|
||||||
@@ -11,29 +12,30 @@
|
|||||||
|
|
||||||
[Stirling-PDF](https://www.stirlingpdf.com) is a robust, locally hosted web-based PDF manipulation tool using Docker. It enables you to carry out various operations on PDF files, including splitting, merging, converting, reorganizing, adding images, rotating, compressing, and more. This locally hosted web application has evolved to encompass a comprehensive set of features, addressing all your PDF requirements.
|
[Stirling-PDF](https://www.stirlingpdf.com) is a robust, locally hosted web-based PDF manipulation tool using Docker. It enables you to carry out various operations on PDF files, including splitting, merging, converting, reorganizing, adding images, rotating, compressing, and more. This locally hosted web application has evolved to encompass a comprehensive set of features, addressing all your PDF requirements.
|
||||||
|
|
||||||
Stirling-PDF does not initiate any outbound calls for record-keeping or tracking purposes.
|
|
||||||
|
|
||||||
All files and PDFs exist either exclusively on the client side, reside in server memory only during task execution, or temporarily reside in a file solely for the execution of the task. Any file downloaded by the user will have been deleted from the server by that point.
|
All files and PDFs exist either exclusively on the client side, reside in server memory only during task execution, or temporarily reside in a file solely for the execution of the task. Any file downloaded by the user will have been deleted from the server by that point.
|
||||||
|
|
||||||
|
|
||||||
|
All information available at [https://docs.stirlingpdf.com/](https://docs.stirlingpdf.com/)
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- Enterprise features like SSO Check [here](https://docs.stirlingpdf.com/Enterprise%20Edition)
|
- 50+ PDF Operations
|
||||||
|
- Parallel file processing and downloads
|
||||||
- Dark mode support
|
- Dark mode support
|
||||||
- Custom download options
|
- Custom download options
|
||||||
- Parallel file processing and downloads
|
- Custom 'Pipelines' to run multiple features in a automated queue
|
||||||
- Custom 'Pipelines' to run multiple features in a queue
|
|
||||||
- API for integration with external scripts
|
- API for integration with external scripts
|
||||||
- Optional Login and Authentication support (see [here](https://github.com/Stirling-Tools/Stirling-PDF/tree/main#login-authentication) for documentation)
|
- Optional Login and Authentication support (see [here](https://docs.stirlingpdf.com/Advanced%20Configuration/System%20and%20Security) for documentation)
|
||||||
- Database Backup and Import (see [here](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DATABASE.md) for documentation)
|
- Database Backup and Import (see [here](https://docs.stirlingpdf.com/Advanced%20Configuration/DATABASE) for documentation)
|
||||||
|
- Enterprise features like SSO see [here](https://docs.stirlingpdf.com/Enterprise%20Edition)
|
||||||
|
|
||||||
## PDF Features
|
## PDF Features
|
||||||
|
|
||||||
### Page Operations
|
### Page Operations
|
||||||
|
|
||||||
- View and modify PDFs - View multi-page PDFs with custom viewing, sorting, and searching. Plus on-page edit features like annotate, draw, and adding text and images. (Using PDF.js with Joxit and Liberation fonts)
|
- View and modify PDFs - View multi-page PDFs with custom viewing, sorting, and searching. Plus, on-page edit features like annotating, drawing, and adding text and images. (Using PDF.js with Joxit and Liberation fonts)
|
||||||
- Full interactive GUI for merging/splitting/rotating/moving PDFs and their pages
|
- Full interactive GUI for merging/splitting/rotating/moving PDFs and their pages
|
||||||
- Merge multiple PDFs into a single resultant file
|
- Merge multiple PDFs into a single resultant file
|
||||||
- Split PDFs into multiple files at specified page numbers or extract all pages as individual files
|
- Split PDFs into multiple files at specified page numbers or extract all pages as individual files
|
||||||
@@ -44,11 +46,11 @@ All files and PDFs exist either exclusively on the client side, reside in server
|
|||||||
- Scale page contents size by set percentage
|
- Scale page contents size by set percentage
|
||||||
- Adjust contrast
|
- Adjust contrast
|
||||||
- Crop PDF
|
- Crop PDF
|
||||||
- Auto split PDF (with physically scanned page dividers)
|
- Auto-split PDF (with physically scanned page dividers)
|
||||||
- Extract page(s)
|
- Extract page(s)
|
||||||
- Convert PDF to a single page
|
- Convert PDF to a single page
|
||||||
- Overlay PDFs on top of each other
|
- Overlay PDFs on top of each other
|
||||||
- PDF to single page
|
- PDF to a single page
|
||||||
- Split PDF by sections
|
- Split PDF by sections
|
||||||
|
|
||||||
### Conversion Operations
|
### Conversion Operations
|
||||||
@@ -57,7 +59,7 @@ All files and PDFs exist either exclusively on the client side, reside in server
|
|||||||
- Convert any common file to PDF (using LibreOffice)
|
- Convert any common file to PDF (using LibreOffice)
|
||||||
- Convert PDF to Word/PowerPoint/others (using LibreOffice)
|
- Convert PDF to Word/PowerPoint/others (using LibreOffice)
|
||||||
- Convert HTML to PDF
|
- Convert HTML to PDF
|
||||||
- Convert PDF to xml
|
- Convert PDF to XML
|
||||||
- Convert PDF to CSV
|
- Convert PDF to CSV
|
||||||
- URL to PDF
|
- URL to PDF
|
||||||
- Markdown to PDF
|
- Markdown to PDF
|
||||||
@@ -85,105 +87,28 @@ All files and PDFs exist either exclusively on the client side, reside in server
|
|||||||
- Extract images from scans
|
- Extract images from scans
|
||||||
- Remove annotations
|
- Remove annotations
|
||||||
- Add page numbers
|
- Add page numbers
|
||||||
- Auto rename file by detecting PDF header text
|
- Auto-rename files by detecting PDF header text
|
||||||
- OCR on PDF (using tesseract)
|
- OCR on PDF (using Tesseract OCR)
|
||||||
- PDF/A conversion (using libreoffice)
|
- PDF/A conversion (using LibreOffice)
|
||||||
- Edit metadata
|
- Edit metadata
|
||||||
- Flatten PDFs
|
- Flatten PDFs
|
||||||
- Get all information on a PDF to view or export as JSON
|
- Get all information on a PDF to view or export as JSON
|
||||||
- Show/detect embedded JavaScript
|
- Show/detect embedded JavaScript
|
||||||
|
|
||||||
For an overview of the tasks and the technology each uses, please view [Endpoint-groups.md](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/Endpoint-groups.md).
|
|
||||||
|
|
||||||
A demo of the app is available [here](https://stirlingpdf.io).
|
|
||||||
|
|
||||||
## Technologies Used
|
|
||||||
|
|
||||||
- Spring Boot + Thymeleaf
|
# 📖 Get Started
|
||||||
- [PDFBox](https://github.com/apache/pdfbox/tree/trunk)
|
|
||||||
- [LibreOffice](https://www.libreoffice.org/discover/libreoffice/) for advanced conversions
|
|
||||||
- [qpdf](https://github.com/qpdf/qpdf)
|
|
||||||
- HTML, CSS, JavaScript
|
|
||||||
- Docker
|
|
||||||
- [PDF.js](https://github.com/mozilla/pdf.js)
|
|
||||||
- [PDF-LIB.js](https://github.com/Hopding/pdf-lib)
|
|
||||||
|
|
||||||
## How to Use
|
Visit our comprehensive documentation at [docs.stirlingpdf.com](https://docs.stirlingpdf.com) for:
|
||||||
|
|
||||||
### Windows
|
- Installation guides for all platforms
|
||||||
|
- Configuration options
|
||||||
|
- Feature documentation
|
||||||
|
- API reference
|
||||||
|
- Security setup
|
||||||
|
- Enterprise features
|
||||||
|
|
||||||
For Windows users, download the latest Stirling-PDF.exe from our [release](https://github.com/Stirling-Tools/Stirling-PDF/releases) section or by clicking [here](https://github.com/Stirling-Tools/Stirling-PDF/releases/latest/download/Stirling-PDF.exe).
|
|
||||||
|
|
||||||
### Locally
|
|
||||||
|
|
||||||
Please view the [LocalRunGuide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/LocalRunGuide.md).
|
|
||||||
|
|
||||||
### Docker / Podman
|
|
||||||
|
|
||||||
> [!NOTE]
|
|
||||||
> <https://hub.docker.com/r/stirlingtools/stirling-pdf>
|
|
||||||
|
|
||||||
Stirling-PDF has three different versions: a full version, an ultra-lite version, and a 'fat' version. Depending on the types of features you use, you may want a smaller image to save on space. To see what the different versions offer, please look at our [version mapping](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/Version-groups.md). For people that don't mind space optimization, just use the latest tag.
|
|
||||||
|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||
|
|
||||||
Please note in the examples below, you may need to change the volume paths as needed, e.g., `./extraConfigs:/configs` to `/opt/stirlingpdf/extraConfigs:/configs`.
|
|
||||||
|
|
||||||
### Docker Run
|
|
||||||
|
|
||||||
```bash
|
|
||||||
docker run -d \
|
|
||||||
-p 8080:8080 \
|
|
||||||
-v ./trainingData:/usr/share/tessdata \
|
|
||||||
-v ./extraConfigs:/configs \
|
|
||||||
-v ./logs:/logs \
|
|
||||||
# Optional customization (not required)
|
|
||||||
# -v /location/of/customFiles:/customFiles \
|
|
||||||
-e DOCKER_ENABLE_SECURITY=false \
|
|
||||||
-e INSTALL_BOOK_AND_ADVANCED_HTML_OPS=false \
|
|
||||||
-e LANGS=en_GB \
|
|
||||||
--name stirling-pdf \
|
|
||||||
stirlingtools/stirling-pdf:latest
|
|
||||||
```
|
|
||||||
|
|
||||||
### Docker Compose
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
version: '3.3'
|
|
||||||
services:
|
|
||||||
stirling-pdf:
|
|
||||||
image: stirlingtools/stirling-pdf:latest
|
|
||||||
ports:
|
|
||||||
- '8080:8080'
|
|
||||||
volumes:
|
|
||||||
- ./trainingData:/usr/share/tessdata # Required for extra OCR languages
|
|
||||||
- ./extraConfigs:/configs
|
|
||||||
# - ./customFiles:/customFiles/
|
|
||||||
# - ./logs:/logs/
|
|
||||||
environment:
|
|
||||||
- DOCKER_ENABLE_SECURITY=false
|
|
||||||
- INSTALL_BOOK_AND_ADVANCED_HTML_OPS=false
|
|
||||||
- LANGS=en_GB
|
|
||||||
```
|
|
||||||
|
|
||||||
Note: Podman is CLI-compatible with Docker, so simply replace "docker" with "podman".
|
|
||||||
|
|
||||||
### Kubernetes
|
|
||||||
|
|
||||||
See the kubernetes helm chart [here](https://github.com/Stirling-Tools/Stirling-PDF-chart)
|
|
||||||
|
|
||||||
## Enable OCR/Compression Feature
|
|
||||||
|
|
||||||
Please view the [HowToUseOCR.md](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToUseOCR.md).
|
|
||||||
|
|
||||||
## Reuse Stored Files
|
|
||||||
|
|
||||||
Certain functionality like `Sign` supports pre-saved files stored at `/customFiles/signatures/`. Image files placed within here will be accessible to be used via the web UI. Currently, this supports two folder types:
|
|
||||||
|
|
||||||
- `/customFiles/signatures/ALL_USERS`: Accessible to all users, useful for organizations where many users use the same files or for users not using authentication
|
|
||||||
- `/customFiles/signatures/{username}`: Such as `/customFiles/signatures/froodle`, accessible only to the `froodle` username, private for all others
|
|
||||||
|
|
||||||
## Supported Languages
|
## Supported Languages
|
||||||
|
|
||||||
@@ -191,238 +116,57 @@ Stirling-PDF currently supports 38 languages!
|
|||||||
|
|
||||||
| Language | Progress |
|
| Language | Progress |
|
||||||
| -------------------------------------------- | -------------------------------------- |
|
| -------------------------------------------- | -------------------------------------- |
|
||||||
| Arabic (العربية) (ar_AR) |  |
|
| Arabic (العربية) (ar_AR) |  |
|
||||||
| Azerbaijani (Azərbaycan Dili) (az_AZ) |  |
|
| Azerbaijani (Azərbaycan Dili) (az_AZ) |  |
|
||||||
| Basque (Euskara) (eu_ES) |  |
|
| Basque (Euskara) (eu_ES) |  |
|
||||||
| Bulgarian (Български) (bg_BG) |  |
|
| Bulgarian (Български) (bg_BG) |  |
|
||||||
| Catalan (Català) (ca_CA) |  |
|
| Catalan (Català) (ca_CA) |  |
|
||||||
| Croatian (Hrvatski) (hr_HR) |  |
|
| Croatian (Hrvatski) (hr_HR) |  |
|
||||||
| Czech (Česky) (cs_CZ) |  |
|
| Czech (Česky) (cs_CZ) |  |
|
||||||
| Danish (Dansk) (da_DK) |  |
|
| Danish (Dansk) (da_DK) |  |
|
||||||
| Dutch (Nederlands) (nl_NL) |  |
|
| Dutch (Nederlands) (nl_NL) |  |
|
||||||
| English (English) (en_GB) |  |
|
| English (English) (en_GB) |  |
|
||||||
| English (US) (en_US) |  |
|
| English (US) (en_US) |  |
|
||||||
| French (Français) (fr_FR) |  |
|
| French (Français) (fr_FR) |  |
|
||||||
| German (Deutsch) (de_DE) |  |
|
| German (Deutsch) (de_DE) |  |
|
||||||
| Greek (Ελληνικά) (el_GR) |  |
|
| Greek (Ελληνικά) (el_GR) |  |
|
||||||
| Hindi (हिंदी) (hi_IN) |  |
|
| Hindi (हिंदी) (hi_IN) |  |
|
||||||
| Hungarian (Magyar) (hu_HU) |  |
|
| Hungarian (Magyar) (hu_HU) |  |
|
||||||
| Indonesian (Bahasa Indonesia) (id_ID) |  |
|
| Indonesian (Bahasa Indonesia) (id_ID) |  |
|
||||||
| Irish (Gaeilge) (ga_IE) |  |
|
| Irish (Gaeilge) (ga_IE) |  |
|
||||||
| Italian (Italiano) (it_IT) |  |
|
| Italian (Italiano) (it_IT) |  |
|
||||||
| Japanese (日本語) (ja_JP) |  |
|
| Japanese (日本語) (ja_JP) |  |
|
||||||
| Korean (한국어) (ko_KR) |  |
|
| Korean (한국어) (ko_KR) |  |
|
||||||
| Norwegian (Norsk) (no_NB) |  |
|
| Norwegian (Norsk) (no_NB) |  |
|
||||||
| Persian (فارسی) (fa_IR) |  |
|
| Persian (فارسی) (fa_IR) |  |
|
||||||
| Polish (Polski) (pl_PL) |  |
|
| Polish (Polski) (pl_PL) |  |
|
||||||
| Portuguese (Português) (pt_PT) |  |
|
| Portuguese (Português) (pt_PT) |  |
|
||||||
| Portuguese Brazilian (Português) (pt_BR) |  |
|
| Portuguese Brazilian (Português) (pt_BR) |  |
|
||||||
| Romanian (Română) (ro_RO) |  |
|
| Romanian (Română) (ro_RO) |  |
|
||||||
| Russian (Русский) (ru_RU) |  |
|
| Russian (Русский) (ru_RU) |  |
|
||||||
| Serbian Latin alphabet (Srpski) (sr_LATN_RS) |  |
|
| Serbian Latin alphabet (Srpski) (sr_LATN_RS) |  |
|
||||||
| Simplified Chinese (简体中文) (zh_CN) |  |
|
| Simplified Chinese (简体中文) (zh_CN) |  |
|
||||||
| Slovakian (Slovensky) (sk_SK) |  |
|
| Slovakian (Slovensky) (sk_SK) |  |
|
||||||
| Spanish (Español) (es_ES) |  |
|
| Spanish (Español) (es_ES) |  |
|
||||||
| Swedish (Svenska) (sv_SE) |  |
|
| Swedish (Svenska) (sv_SE) |  |
|
||||||
| Thai (ไทย) (th_TH) |  |
|
| Thai (ไทย) (th_TH) |  |
|
||||||
| Traditional Chinese (繁體中文) (zh_TW) |  |
|
| Traditional Chinese (繁體中文) (zh_TW) |  |
|
||||||
| Turkish (Türkçe) (tr_TR) |  |
|
| Turkish (Türkçe) (tr_TR) |  |
|
||||||
| Ukrainian (Українська) (uk_UA) |  |
|
| Ukrainian (Українська) (uk_UA) |  |
|
||||||
| Vietnamese (Tiếng Việt) (vi_VN) |  |
|
| Vietnamese (Tiếng Việt) (vi_VN) |  |
|
||||||
|
|
||||||
## Contributing (Creating Issues, Translations, Fixing Bugs, etc.)
|
|
||||||
|
|
||||||
Please see our [Contributing Guide](CONTRIBUTING.md).
|
|
||||||
|
|
||||||
## Stirling PDF Enterprise
|
## Stirling PDF Enterprise
|
||||||
|
|
||||||
Stirling PDF offers a Enterprise edition of its software, This is the same great software but with added features and comforts
|
Stirling PDF offers an Enterprise edition of its software. This is the same great software but with added features, support and comforts.
|
||||||
|
Check out our [Enterprise docs](https://docs.stirlingpdf.com/Enterprise%20Edition)
|
||||||
### Whats included
|
|
||||||
|
|
||||||
- Prioritised Support tickets via support@stirlingpdf.com to reach directly to Stirling-PDF team for support and 1:1 meetings where applicable (Provided they come from same email domain registered with us)
|
|
||||||
- Prioritised Enhancements to Stirling-PDF where applicable
|
|
||||||
- Base SSO support
|
|
||||||
- Advanced SSO such as automated login handling (Coming very soon)
|
|
||||||
- SAML SSO (Coming very soon)
|
|
||||||
- Custom automated metadata handling
|
|
||||||
- Advanced user configurations (Coming soon)
|
|
||||||
- Plus other exciting features to come
|
|
||||||
|
|
||||||
Check out of [docs](https://docs.stirlingpdf.com/Enterprise%20Edition) on it or our official [website](https://www.stirlingpdf.com)
|
|
||||||
|
|
||||||
## Customization
|
|
||||||
|
|
||||||
Stirling-PDF allows easy customization of the app, including things like:
|
|
||||||
|
|
||||||
- Custom application name
|
|
||||||
- Custom slogans, icons, HTML, images, CSS, etc. (via file overrides)
|
|
||||||
|
|
||||||
There are two options for this, either using the generated settings file `settings.yml`, which is located in the `/configs` directory and follows standard YAML formatting, or using environment variables, which would override the settings file.
|
|
||||||
|
|
||||||
For example, in `settings.yml`, you might have:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
security:
|
|
||||||
enableLogin: 'true'
|
|
||||||
```
|
|
||||||
|
|
||||||
To have this via an environment variable, you would use `SECURITY_ENABLELOGIN`.
|
|
||||||
|
|
||||||
The current list of settings is:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
security:
|
|
||||||
enableLogin: false # set to 'true' to enable login
|
|
||||||
csrfDisabled: true # set to 'true' to disable CSRF protection (not recommended for production)
|
|
||||||
loginAttemptCount: 5 # lock user account after 5 tries; when using e.g. Fail2Ban you can deactivate the function with -1
|
|
||||||
loginResetTimeMinutes: 120 # lock account for 2 hours after x attempts
|
|
||||||
loginMethod: all # 'all' (Login Username/Password and OAuth2[must be enabled and configured]), 'normal'(only Login with Username/Password) or 'oauth2'(only Login with OAuth2)
|
|
||||||
initialLogin:
|
|
||||||
username: '' # initial username for the first login
|
|
||||||
password: '' # initial password for the first login
|
|
||||||
oauth2:
|
|
||||||
enabled: false # set to 'true' to enable login (Note: enableLogin must also be 'true' for this to work)
|
|
||||||
client:
|
|
||||||
keycloak:
|
|
||||||
issuer: '' # URL of the Keycloak realm's OpenID Connect Discovery endpoint
|
|
||||||
clientId: '' # client ID for Keycloak OAuth2
|
|
||||||
clientSecret: '' # client secret for Keycloak OAuth2
|
|
||||||
scopes: openid, profile, email # scopes for Keycloak OAuth2
|
|
||||||
useAsUsername: preferred_username # field to use as the username for Keycloak OAuth2
|
|
||||||
google:
|
|
||||||
clientId: '' # client ID for Google OAuth2
|
|
||||||
clientSecret: '' # client secret for Google OAuth2
|
|
||||||
scopes: https://www.googleapis.com/auth/userinfo.email, https://www.googleapis.com/auth/userinfo.profile # scopes for Google OAuth2
|
|
||||||
useAsUsername: email # field to use as the username for Google OAuth2
|
|
||||||
github:
|
|
||||||
clientId: '' # client ID for GitHub OAuth2
|
|
||||||
clientSecret: '' # client secret for GitHub OAuth2
|
|
||||||
scopes: read:user # scope for GitHub OAuth2
|
|
||||||
useAsUsername: login # field to use as the username for GitHub OAuth2
|
|
||||||
issuer: '' # set to any provider that supports OpenID Connect Discovery (/.well-known/openid-configuration) endpoint
|
|
||||||
clientId: '' # client ID from your provider
|
|
||||||
clientSecret: '' # client secret from your provider
|
|
||||||
autoCreateUser: false # set to 'true' to allow auto-creation of non-existing users
|
|
||||||
blockRegistration: false # set to 'true' to deny login with SSO without prior registration by an admin
|
|
||||||
useAsUsername: email # default is 'email'; custom fields can be used as the username
|
|
||||||
scopes: openid, profile, email # specify the scopes for which the application will request permissions
|
|
||||||
provider: google # set this to your OAuth provider's name, e.g., 'google' or 'keycloak'
|
|
||||||
saml2:
|
|
||||||
enabled: false # currently in alpha, not recommended for use yet, enableAlphaFunctionality must be set to true
|
|
||||||
autoCreateUser: false # set to 'true' to allow auto-creation of non-existing users
|
|
||||||
blockRegistration: false # set to 'true' to deny login with SSO without prior registration by an admin
|
|
||||||
registrationId: stirling
|
|
||||||
idpMetadataUri: https://dev-XXXXXXXX.okta.com/app/externalKey/sso/saml/metadata
|
|
||||||
idpSingleLogoutUrl: https://dev-XXXXXXXX.okta.com/app/dev-XXXXXXXX_stirlingpdf_1/externalKey/slo/saml
|
|
||||||
idpSingleLoginUrl: https://dev-XXXXXXXX.okta.com/app/dev-XXXXXXXX_stirlingpdf_1/externalKey/sso/saml
|
|
||||||
idpIssuer: http://www.okta.com/externalKey
|
|
||||||
idpCert: classpath:okta.crt
|
|
||||||
privateKey: classpath:saml-private-key.key
|
|
||||||
spCert: classpath:saml-public-cert.crt
|
|
||||||
|
|
||||||
enterpriseEdition:
|
|
||||||
enabled: false # set to 'true' to enable enterprise edition
|
|
||||||
key: 00000000-0000-0000-0000-000000000000
|
|
||||||
CustomMetadata:
|
|
||||||
autoUpdateMetadata: false # set to 'true' to automatically update metadata with below values
|
|
||||||
author: username # supports text such as 'John Doe' or types such as username to autopopulate with user's username
|
|
||||||
creator: Stirling-PDF # supports text such as 'Company-PDF'
|
|
||||||
producer: Stirling-PDF # supports text such as 'Company-PDF'
|
|
||||||
|
|
||||||
legal:
|
|
||||||
termsAndConditions: https://www.stirlingpdf.com/terms-and-conditions # URL to the terms and conditions of your application (e.g. https://example.com/terms). Empty string to disable or filename to load from local file in static folder
|
|
||||||
privacyPolicy: https://www.stirlingpdf.com/privacy-policy # URL to the privacy policy of your application (e.g. https://example.com/privacy). Empty string to disable or filename to load from local file in static folder
|
|
||||||
accessibilityStatement: '' # URL to the accessibility statement of your application (e.g. https://example.com/accessibility). Empty string to disable or filename to load from local file in static folder
|
|
||||||
cookiePolicy: '' # URL to the cookie policy of your application (e.g. https://example.com/cookie). Empty string to disable or filename to load from local file in static folder
|
|
||||||
impressum: '' # URL to the impressum of your application (e.g. https://example.com/impressum). Empty string to disable or filename to load from local file in static folder
|
|
||||||
|
|
||||||
system:
|
|
||||||
defaultLocale: en-US # set the default language (e.g. 'de-DE', 'fr-FR', etc)
|
|
||||||
googlevisibility: false # 'true' to allow Google visibility (via robots.txt), 'false' to disallow
|
|
||||||
enableAlphaFunctionality: false # set to enable functionality which might need more testing before it fully goes live (this feature might make no changes)
|
|
||||||
showUpdate: false # see when a new update is available
|
|
||||||
showUpdateOnlyAdmin: false # only admins can see when a new update is available, depending on showUpdate it must be set to 'true'
|
|
||||||
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: undefined # set to 'true' to enable analytics, set to 'false' to disable analytics; for enterprise users, this is set to true
|
|
||||||
|
|
||||||
ui:
|
|
||||||
appName: '' # application's visible name
|
|
||||||
homeDescription: '' # short description or tagline shown on the homepage
|
|
||||||
appNameNavbar: '' # name displayed on the navigation bar
|
|
||||||
|
|
||||||
endpoints:
|
|
||||||
toRemove: [] # list endpoints to disable (e.g. ['img-to-pdf', 'remove-pages'])
|
|
||||||
groupsToRemove: [] # list groups to disable (e.g. ['LibreOffice'])
|
|
||||||
|
|
||||||
metrics:
|
|
||||||
enabled: true # 'true' to enable Info APIs (`/api/*`) endpoints, 'false' to disable
|
|
||||||
|
|
||||||
# Automatically Generated Settings (Do Not Edit Directly)
|
|
||||||
AutomaticallyGenerated:
|
|
||||||
key: example
|
|
||||||
UUID: example
|
|
||||||
```
|
|
||||||
|
|
||||||
There is an additional config file `/configs/custom_settings.yml` where users familiar with Java and Spring `application.properties` can input their own settings on top of Stirling-PDF's existing ones.
|
|
||||||
|
|
||||||
|
|
||||||
|
## 🤝 Looking to contribute?
|
||||||
|
|
||||||
### Extra Notes
|
Join our community:
|
||||||
|
- [Contribution Guidelines](CONTRIBUTING.md)
|
||||||
- **Endpoints**: Currently, the `ENDPOINTS_TO_REMOVE` and `GROUPS_TO_REMOVE` endpoints can include comma-separated lists of endpoints and groups to disable. For example, `ENDPOINTS_TO_REMOVE=img-to-pdf,remove-pages` would disable both image-to-pdf and remove pages, while `GROUPS_TO_REMOVE=LibreOffice` would disable all things that use LibreOffice. You can see a list of all endpoints and groups [here](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/Endpoint-groups.md).
|
- [Translation Guide (How to add custom languages)](HowToAddNewLanguage.md)
|
||||||
- **customStaticFilePath**: Customize static files such as the app logo by placing files in the `/customFiles/static/` directory. An example of customizing the app logo is placing `/customFiles/static/favicon.svg` to override the current SVG. This can be used to change any `images/icons/css/fonts/js`, etc. in Stirling-PDF.
|
- [Issue Tracker](https://github.com/Stirling-Tools/Stirling-PDF/issues)
|
||||||
|
- [Discord Community](https://discord.gg/HYmhKj45pU)
|
||||||
### Environment-Only Parameters
|
- [Developer Guide](DeveloperGuide.md)
|
||||||
|
|
||||||
- `SYSTEM_ROOTURIPATH` - Set the application's root URI (e.g. `/pdf-app` to set the root URI to `localhost:8080/pdf-app`)
|
|
||||||
- `SYSTEM_CONNECTIONTIMEOUTMINUTES` - Set custom connection timeout values
|
|
||||||
- `DOCKER_ENABLE_SECURITY` - Set to `true` to download security jar (required for authentication login)
|
|
||||||
- `INSTALL_BOOK_AND_ADVANCED_HTML_OPS` - Download Calibre onto Stirling-PDF to enable PDF to/from book and advanced HTML conversion
|
|
||||||
- `LANGS` - Define custom font libraries to install for document conversions
|
|
||||||
|
|
||||||
## API
|
|
||||||
|
|
||||||
For those wanting to use Stirling-PDF's backend API to link with their own custom scripting to edit PDFs, you can view all existing API documentation [here](https://app.swaggerhub.com/apis-docs/Stirling-Tools/Stirling-PDF/), or navigate to `/swagger-ui/index.html` of your Stirling-PDF instance for your version's documentation (or by following the API button in the settings of Stirling-PDF).
|
|
||||||
|
|
||||||
## Login Authentication
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
### Prerequisites
|
|
||||||
|
|
||||||
- User must have the folder `./configs` volumed within Docker so that it is retained during updates.
|
|
||||||
- Docker users must download the security jar version by setting `DOCKER_ENABLE_SECURITY` to `true` in environment variables.
|
|
||||||
- Then either enable login via the `settings.yml` file or set `SECURITY_ENABLE_LOGIN` to `true`.
|
|
||||||
- Now the initial user will be generated with username `admin` and password `stirling`. On login, you will be forced to change the password to a new one. You can also use the environment variables `SECURITY_INITIALLOGIN_USERNAME` and `SECURITY_INITIALLOGIN_PASSWORD` to set your own credentials straight away (recommended to remove them after user creation).
|
|
||||||
|
|
||||||
Once the above has been done, on restart, a new `stirling-pdf-DB.mv.db` will show if everything worked.
|
|
||||||
|
|
||||||
When you log in to Stirling-PDF, you will be redirected to the `/login` page to log in with those default credentials. After login, everything should function as normal.
|
|
||||||
|
|
||||||
To access your account settings, go to Account Settings in the settings cog menu (top right in the navbar). This Account Settings menu is also where you find your API key.
|
|
||||||
|
|
||||||
To add new users, go to the bottom of Account Settings and hit 'Admin Settings'. Here you can add new users. The different roles mentioned within this are for rate limiting. This is a work in progress and will be expanded on more in the future.
|
|
||||||
|
|
||||||
For API usage, you must provide a header with `X-API-KEY` and the associated API key for that user.
|
|
||||||
|
|
||||||
## FAQ
|
|
||||||
|
|
||||||
### Q1: What are your planned features?
|
|
||||||
|
|
||||||
- Progress bar/tracking
|
|
||||||
- Full custom logic pipelines to combine multiple operations together
|
|
||||||
- Folder support with auto-scanning to perform operations on
|
|
||||||
- Redact text (via UI, not just automated)
|
|
||||||
- Add forms
|
|
||||||
- Multi-page layout (stitch PDF pages together) support x rows y columns and custom page sizing
|
|
||||||
- Fill forms manually or automatically
|
|
||||||
|
|
||||||
### Q2: Why is my application downloading .htm files? Why am i getting HTTP error 413?
|
|
||||||
|
|
||||||
This is an issue commonly caused by your NGINX configuration. The default file upload size for NGINX is 1MB. You need to add the following in your Nginx sites-available file: `client_max_body_size SIZE;` (where "SIZE" is 50M for example for 50MB files).
|
|
||||||
|
|
||||||
### Q3: Why is my download timing out?
|
|
||||||
|
|
||||||
NGINX has timeout values by default, so if you are running Stirling-PDF behind NGINX, you may need to set a timeout value, such as adding the config `proxy_read_timeout 3600;`.
|
|
||||||
|
|||||||
63
SECURITY.md
Normal file
63
SECURITY.md
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
# Security Policy
|
||||||
|
|
||||||
|
## Reporting a Vulnerability
|
||||||
|
|
||||||
|
The Stirling-PDF team takes security vulnerabilities seriously. We appreciate your efforts to responsibly disclose your findings.
|
||||||
|
|
||||||
|
### How to Report
|
||||||
|
|
||||||
|
You can report security vulnerabilities through two channels:
|
||||||
|
|
||||||
|
1. **GitHub Security Advisory**:
|
||||||
|
- Navigate to the [Security tab](https://github.com/Stirling-Tools/Stirling-PDF/security) in our repository
|
||||||
|
- Click on "Report a vulnerability"
|
||||||
|
- Provide a detailed description of the vulnerability
|
||||||
|
|
||||||
|
2. **Direct Email**:
|
||||||
|
- Send your report to security@stirlingpdf.com
|
||||||
|
- Please include as much information as possible about the vulnerability
|
||||||
|
|
||||||
|
### What to Include
|
||||||
|
|
||||||
|
When reporting a vulnerability, please provide:
|
||||||
|
|
||||||
|
- A clear description of the vulnerability
|
||||||
|
- Steps to reproduce the issue
|
||||||
|
- Any potential impact
|
||||||
|
- If possible, suggestions for addressing the vulnerability
|
||||||
|
- Your contact information for follow-up questions
|
||||||
|
|
||||||
|
### Response Time
|
||||||
|
|
||||||
|
We aim to acknowledge receipt of your vulnerability report within 48 hours
|
||||||
|
|
||||||
|
### Process
|
||||||
|
|
||||||
|
1. Submit your report through one of the channels above
|
||||||
|
2. Receive an acknowledgment from our team
|
||||||
|
3. Our team will investigate and validate the issue
|
||||||
|
4. We will work on a fix and keep you updated on our progress
|
||||||
|
5. Once resolved, we will publish the fix and acknowledge your contribution (if desired)
|
||||||
|
|
||||||
|
### Bug Bounty
|
||||||
|
|
||||||
|
At this time, we do not offer a bug bounty program. However, we greatly appreciate your efforts in making Stirling-PDF more secure and will acknowledge your contribution in our release notes (unless you prefer to remain anonymous).
|
||||||
|
|
||||||
|
## Supported Versions
|
||||||
|
|
||||||
|
Only the latest version of Stirling-PDF is supported for security updates. We do not backport security fixes to older versions.
|
||||||
|
|
||||||
|
| Version | Supported |
|
||||||
|
| ------- | ------------------ |
|
||||||
|
| Latest | :white_check_mark: |
|
||||||
|
| Older | :x: |
|
||||||
|
|
||||||
|
**Please note:** Before reporting a security issue, ensure you are using the latest version of Stirling-PDF. Security reports for older versions will not be accepted.
|
||||||
|
|
||||||
|
## Security Best Practices
|
||||||
|
|
||||||
|
When deploying Stirling-PDF:
|
||||||
|
|
||||||
|
1. Always use the latest version
|
||||||
|
2. Follow our deployment guidelines
|
||||||
|
3. Regularly check for and apply updates
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
|All versions in a Docker environment can download Calibre as a optional extra at runtime to support `book-to-pdf` and `pdf-to-book` using parameter ``INSTALL_BOOK_AND_ADVANCED_HTML_OPS``.
|
|
||||||
The 'Fat' container contains all those found in 'Full' with security jar along with this Calibre install.
|
|
||||||
|
|
||||||
| Technology | Ultra-Lite | Full |
|
|
||||||
| ---------- | :--------: | :---: |
|
|
||||||
| Java | ✔️ | ✔️ |
|
|
||||||
| JavaScript | ✔️ | ✔️ |
|
|
||||||
| Libre | | ✔️ |
|
|
||||||
| Python | | ✔️ |
|
|
||||||
| OpenCV | | ✔️ |
|
|
||||||
| qpdf | | ✔️ |
|
|
||||||
|
|
||||||
| Operation | Ultra-Lite | Full |
|
|
||||||
| ---------------------- | ---------- | ---- |
|
|
||||||
| add-page-numbers | ✔️ | ✔️ |
|
|
||||||
| add-password | ✔️ | ✔️ |
|
|
||||||
| add-image | ✔️ | ✔️ |
|
|
||||||
| add-watermark | ✔️ | ✔️ |
|
|
||||||
| adjust-contrast | ✔️ | ✔️ |
|
|
||||||
| auto-split-pdf | ✔️ | ✔️ |
|
|
||||||
| auto-redact | ✔️ | ✔️ |
|
|
||||||
| auto-rename | ✔️ | ✔️ |
|
|
||||||
| cert-sign | ✔️ | ✔️ |
|
|
||||||
| remove-cert-sign | ✔️ | ✔️ |
|
|
||||||
| crop | ✔️ | ✔️ |
|
|
||||||
| change-metadata | ✔️ | ✔️ |
|
|
||||||
| change-permissions | ✔️ | ✔️ |
|
|
||||||
| compare | ✔️ | ✔️ |
|
|
||||||
| extract-page | ✔️ | ✔️ |
|
|
||||||
| extract-images | ✔️ | ✔️ |
|
|
||||||
| flatten | ✔️ | ✔️ |
|
|
||||||
| get-info-on-pdf | ✔️ | ✔️ |
|
|
||||||
| img-to-pdf | ✔️ | ✔️ |
|
|
||||||
| markdown-to-pdf | ✔️ | ✔️ |
|
|
||||||
| merge-pdfs | ✔️ | ✔️ |
|
|
||||||
| multi-page-layout | ✔️ | ✔️ |
|
|
||||||
| overlay-pdf | ✔️ | ✔️ |
|
|
||||||
| pdf-organizer | ✔️ | ✔️ |
|
|
||||||
| pdf-to-csv | ✔️ | ✔️ |
|
|
||||||
| pdf-to-img | ✔️ | ✔️ |
|
|
||||||
| pdf-to-single-page | ✔️ | ✔️ |
|
|
||||||
| remove-pages | ✔️ | ✔️ |
|
|
||||||
| remove-password | ✔️ | ✔️ |
|
|
||||||
| rotate-pdf | ✔️ | ✔️ |
|
|
||||||
| sanitize-pdf | ✔️ | ✔️ |
|
|
||||||
| scale-pages | ✔️ | ✔️ |
|
|
||||||
| sign | ✔️ | ✔️ |
|
|
||||||
| show-javascript | ✔️ | ✔️ |
|
|
||||||
| split-by-size-or-count | ✔️ | ✔️ |
|
|
||||||
| split-pdf-by-sections | ✔️ | ✔️ |
|
|
||||||
| split-pdfs | ✔️ | ✔️ |
|
|
||||||
| compress-pdf | | ✔️ |
|
|
||||||
| extract-image-scans | | ✔️ |
|
|
||||||
| ocr-pdf | | ✔️ |
|
|
||||||
| pdf-to-pdfa | | ✔️ |
|
|
||||||
| remove-blanks | | ✔️ |
|
|
||||||
pdf-to-text | ✔️ | ✔️
|
|
||||||
pdf-to-html | | ✔️
|
|
||||||
pdf-to-word | | ✔️
|
|
||||||
pdf-to-presentation | | ✔️
|
|
||||||
pdf-to-xml | | ✔️
|
|
||||||
remove-annotations | ✔️ | ✔️
|
|
||||||
remove-cert-sign | ✔️ | ✔️
|
|
||||||
remove-image-pdf | ✔️ | ✔️
|
|
||||||
file-to-pdf | | ✔️
|
|
||||||
html-to-pdf | | ✔️
|
|
||||||
url-to-pdf | | ✔️
|
|
||||||
repair | | ✔️
|
|
||||||
22
build.gradle
22
build.gradle
@@ -1,7 +1,7 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id "java"
|
id "java"
|
||||||
id "org.springframework.boot" version "3.4.0"
|
id "org.springframework.boot" version "3.4.1"
|
||||||
id "io.spring.dependency-management" version "1.1.6"
|
id "io.spring.dependency-management" version "1.1.7"
|
||||||
id "org.springdoc.openapi-gradle-plugin" version "1.8.0"
|
id "org.springdoc.openapi-gradle-plugin" version "1.8.0"
|
||||||
id "io.swagger.swaggerhub" version "1.3.2"
|
id "io.swagger.swaggerhub" version "1.3.2"
|
||||||
id "edu.sc.seis.launch4j" version "3.0.6"
|
id "edu.sc.seis.launch4j" version "3.0.6"
|
||||||
@@ -16,7 +16,7 @@ plugins {
|
|||||||
import com.github.jk1.license.render.*
|
import com.github.jk1.license.render.*
|
||||||
|
|
||||||
ext {
|
ext {
|
||||||
springBootVersion = "3.4.0"
|
springBootVersion = "3.4.1"
|
||||||
pdfboxVersion = "3.0.3"
|
pdfboxVersion = "3.0.3"
|
||||||
logbackVersion = "1.5.7"
|
logbackVersion = "1.5.7"
|
||||||
imageioVersion = "3.12.0"
|
imageioVersion = "3.12.0"
|
||||||
@@ -27,7 +27,7 @@ ext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
group = "stirling.software"
|
group = "stirling.software"
|
||||||
version = "0.36.5"
|
version = "0.36.6"
|
||||||
|
|
||||||
|
|
||||||
java {
|
java {
|
||||||
@@ -123,11 +123,13 @@ jpackage {
|
|||||||
windows {
|
windows {
|
||||||
launcherAsService = false
|
launcherAsService = false
|
||||||
appVersion = project.version
|
appVersion = project.version
|
||||||
winConsole = false
|
|
||||||
winDirChooser = true
|
winConsole = false
|
||||||
winMenu = true
|
winMenu = true // Creates start menu entry
|
||||||
winShortcut = true
|
winShortcut = true // Creates desktop shortcut
|
||||||
winPerUserInstall = true
|
winShortcutPrompt = true // Lets user choose whether to create shortcuts
|
||||||
|
winDirChooser = true // Allows users to choose installation directory
|
||||||
|
winPerUserInstall = false
|
||||||
winMenuGroup = "Stirling Software"
|
winMenuGroup = "Stirling Software"
|
||||||
winUpgradeUuid = "2a43ed0c-b8c2-40cf-89e1-751129b87641" // Unique identifier for updates
|
winUpgradeUuid = "2a43ed0c-b8c2-40cf-89e1-751129b87641" // Unique identifier for updates
|
||||||
winHelpUrl = "https://github.com/Stirling-Tools/Stirling-PDF"
|
winHelpUrl = "https://github.com/Stirling-Tools/Stirling-PDF"
|
||||||
@@ -257,7 +259,7 @@ spotless {
|
|||||||
// rules=['unused-dependency']
|
// rules=['unused-dependency']
|
||||||
// }
|
// }
|
||||||
tasks.wrapper {
|
tasks.wrapper {
|
||||||
gradleVersion = "8.7"
|
gradleVersion = "8.12"
|
||||||
}
|
}
|
||||||
//tasks.withType(JavaCompile) {
|
//tasks.withType(JavaCompile) {
|
||||||
// options.compilerArgs << "-Xlint:deprecation"
|
// options.compilerArgs << "-Xlint:deprecation"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
@general
|
@general
|
||||||
Feature: API Validation
|
Feature: API Validation
|
||||||
|
|
||||||
|
|
||||||
@split-pdf-by-sections @positive
|
@split-pdf-by-sections @positive
|
||||||
Scenario Outline: split-pdf-by-sections with different parameters
|
Scenario Outline: split-pdf-by-sections with different parameters
|
||||||
Given I generate a PDF file as "fileInput"
|
Given I generate a PDF file as "fileInput"
|
||||||
@@ -66,7 +66,7 @@ Feature: API Validation
|
|||||||
| pageNumbers | file_count |
|
| pageNumbers | file_count |
|
||||||
| 1,3,5-9 | 8 |
|
| 1,3,5-9 | 8 |
|
||||||
| all | 20 |
|
| all | 20 |
|
||||||
| 2n+1 | 11 |
|
| 2n+1 | 10 |
|
||||||
| 3n | 7 |
|
| 3n | 7 |
|
||||||
|
|
||||||
|
|
||||||
@@ -106,9 +106,9 @@ Feature: API Validation
|
|||||||
And the response ZIP should contain 2 files
|
And the response ZIP should contain 2 files
|
||||||
And the response file should have size greater than 0
|
And the response file should have size greater than 0
|
||||||
And the response status code should be 200
|
And the response status code should be 200
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
| format |
|
| format |
|
||||||
| png |
|
| png |
|
||||||
| gif |
|
| gif |
|
||||||
| jpeg |
|
| jpeg |
|
||||||
|
|||||||
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,5 +1,5 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package stirling.software.SPDF;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.ServerSocket;
|
import java.net.ServerSocket;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
@@ -24,6 +25,7 @@ import jakarta.annotation.PreDestroy;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import stirling.software.SPDF.UI.WebBrowser;
|
import stirling.software.SPDF.UI.WebBrowser;
|
||||||
import stirling.software.SPDF.config.ConfigInitializer;
|
import stirling.software.SPDF.config.ConfigInitializer;
|
||||||
|
import stirling.software.SPDF.config.InstallationPathConfig;
|
||||||
import stirling.software.SPDF.model.ApplicationProperties;
|
import stirling.software.SPDF.model.ApplicationProperties;
|
||||||
|
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
@@ -76,15 +78,27 @@ public class SPdfApplication {
|
|||||||
props.put("spring.main.web-application-type", "servlet");
|
props.put("spring.main.web-application-type", "servlet");
|
||||||
}
|
}
|
||||||
app.setAdditionalProfiles("default");
|
app.setAdditionalProfiles("default");
|
||||||
app.addInitializers(new ConfigInitializer());
|
|
||||||
|
ConfigInitializer initializer = new ConfigInitializer();
|
||||||
|
try {
|
||||||
|
initializer.ensureConfigExists();
|
||||||
|
} catch (IOException | URISyntaxException e) {
|
||||||
|
log.error("Error initialising configuration", e);
|
||||||
|
}
|
||||||
|
|
||||||
Map<String, String> propertyFiles = new HashMap<>();
|
Map<String, String> propertyFiles = new HashMap<>();
|
||||||
// External config files
|
// External config files
|
||||||
if (Files.exists(Paths.get("configs/settings.yml"))) {
|
log.info("Settings file: {}", InstallationPathConfig.getSettingsPath());
|
||||||
propertyFiles.put("spring.config.additional-location", "file:configs/settings.yml");
|
if (Files.exists(Paths.get(InstallationPathConfig.getSettingsPath()))) {
|
||||||
|
propertyFiles.put(
|
||||||
|
"spring.config.additional-location",
|
||||||
|
"file:" + InstallationPathConfig.getSettingsPath());
|
||||||
} else {
|
} else {
|
||||||
log.warn("External configuration file 'configs/settings.yml' does not exist.");
|
log.warn(
|
||||||
|
"External configuration file '{}' does not exist.",
|
||||||
|
InstallationPathConfig.getSettingsPath());
|
||||||
}
|
}
|
||||||
if (Files.exists(Paths.get("configs/custom_settings.yml"))) {
|
if (Files.exists(Paths.get(InstallationPathConfig.getCustomSettingsPath()))) {
|
||||||
String existingLocation =
|
String existingLocation =
|
||||||
propertyFiles.getOrDefault("spring.config.additional-location", "");
|
propertyFiles.getOrDefault("spring.config.additional-location", "");
|
||||||
if (!existingLocation.isEmpty()) {
|
if (!existingLocation.isEmpty()) {
|
||||||
@@ -92,9 +106,11 @@ public class SPdfApplication {
|
|||||||
}
|
}
|
||||||
propertyFiles.put(
|
propertyFiles.put(
|
||||||
"spring.config.additional-location",
|
"spring.config.additional-location",
|
||||||
existingLocation + "file:configs/custom_settings.yml");
|
existingLocation + "file:" + InstallationPathConfig.getCustomSettingsPath());
|
||||||
} else {
|
} else {
|
||||||
log.warn("Custom configuration file 'configs/custom_settings.yml' does not exist.");
|
log.warn(
|
||||||
|
"Custom configuration file '{}' does not exist.",
|
||||||
|
InstallationPathConfig.getCustomSettingsPath());
|
||||||
}
|
}
|
||||||
Properties finalProps = new Properties();
|
Properties finalProps = new Properties();
|
||||||
if (!propertyFiles.isEmpty()) {
|
if (!propertyFiles.isEmpty()) {
|
||||||
@@ -110,8 +126,8 @@ public class SPdfApplication {
|
|||||||
app.run(args);
|
app.run(args);
|
||||||
// Ensure directories are created
|
// Ensure directories are created
|
||||||
try {
|
try {
|
||||||
Files.createDirectories(Path.of("customFiles/static/"));
|
Files.createDirectories(Path.of(InstallationPathConfig.getTemplatesPath()));
|
||||||
Files.createDirectories(Path.of("customFiles/templates/"));
|
Files.createDirectories(Path.of(InstallationPathConfig.getStaticPath()));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("Error creating directories: {}", e.getMessage());
|
log.error("Error creating directories: {}", e.getMessage());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ import me.friwi.jcefmaven.EnumProgress;
|
|||||||
import me.friwi.jcefmaven.MavenCefAppHandlerAdapter;
|
import me.friwi.jcefmaven.MavenCefAppHandlerAdapter;
|
||||||
import me.friwi.jcefmaven.impl.progress.ConsoleProgressHandler;
|
import me.friwi.jcefmaven.impl.progress.ConsoleProgressHandler;
|
||||||
import stirling.software.SPDF.UI.WebBrowser;
|
import stirling.software.SPDF.UI.WebBrowser;
|
||||||
|
import stirling.software.SPDF.config.InstallationPathConfig;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@@ -72,7 +73,8 @@ public class DesktopBrowser implements WebBrowser {
|
|||||||
CefAppBuilder builder = new CefAppBuilder();
|
CefAppBuilder builder = new CefAppBuilder();
|
||||||
configureCefSettings(builder);
|
configureCefSettings(builder);
|
||||||
builder.setProgressHandler(createProgressHandler());
|
builder.setProgressHandler(createProgressHandler());
|
||||||
|
builder.setInstallDir(
|
||||||
|
new File(InstallationPathConfig.getClientWebUIPath()));
|
||||||
// Build and initialize CEF
|
// Build and initialize CEF
|
||||||
cefApp = builder.build();
|
cefApp = builder.build();
|
||||||
client = cefApp.createClient();
|
client = cefApp.createClient();
|
||||||
@@ -99,8 +101,16 @@ public class DesktopBrowser implements WebBrowser {
|
|||||||
|
|
||||||
private void configureCefSettings(CefAppBuilder builder) {
|
private void configureCefSettings(CefAppBuilder builder) {
|
||||||
CefSettings settings = builder.getCefSettings();
|
CefSettings settings = builder.getCefSettings();
|
||||||
settings.cache_path = new File("jcef-bundle").getAbsolutePath();
|
String basePath = InstallationPathConfig.getClientWebUIPath();
|
||||||
settings.root_cache_path = new File("jcef-bundle").getAbsolutePath();
|
log.info("basePath " + basePath);
|
||||||
|
settings.cache_path = new File(basePath + "cache").getAbsolutePath();
|
||||||
|
settings.root_cache_path = new File(basePath + "root_cache").getAbsolutePath();
|
||||||
|
// settings.browser_subprocess_path = new File(basePath +
|
||||||
|
// "subprocess").getAbsolutePath();
|
||||||
|
// settings.resources_dir_path = new File(basePath + "resources").getAbsolutePath();
|
||||||
|
// settings.locales_dir_path = new File(basePath + "locales").getAbsolutePath();
|
||||||
|
settings.log_file = new File(basePath, "debug.log").getAbsolutePath();
|
||||||
|
|
||||||
settings.persist_session_cookies = true;
|
settings.persist_session_cookies = true;
|
||||||
settings.windowless_rendering_enabled = false;
|
settings.windowless_rendering_enabled = false;
|
||||||
settings.log_severity = CefSettings.LogSeverity.LOGSEVERITY_INFO;
|
settings.log_severity = CefSettings.LogSeverity.LOGSEVERITY_INFO;
|
||||||
@@ -212,6 +222,9 @@ public class DesktopBrowser implements WebBrowser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void setupLoadHandler() {
|
private void setupLoadHandler() {
|
||||||
|
final long initStartTime = System.currentTimeMillis();
|
||||||
|
log.info("Setting up load handler at: {}", initStartTime);
|
||||||
|
|
||||||
client.addLoadHandler(
|
client.addLoadHandler(
|
||||||
new CefLoadHandlerAdapter() {
|
new CefLoadHandlerAdapter() {
|
||||||
@Override
|
@Override
|
||||||
@@ -220,32 +233,77 @@ public class DesktopBrowser implements WebBrowser {
|
|||||||
boolean isLoading,
|
boolean isLoading,
|
||||||
boolean canGoBack,
|
boolean canGoBack,
|
||||||
boolean canGoForward) {
|
boolean canGoForward) {
|
||||||
|
log.debug(
|
||||||
|
"Loading state change - isLoading: {}, canGoBack: {}, canGoForward: {}, "
|
||||||
|
+ "browserInitialized: {}, Time elapsed: {}ms",
|
||||||
|
isLoading,
|
||||||
|
canGoBack,
|
||||||
|
canGoForward,
|
||||||
|
browserInitialized,
|
||||||
|
System.currentTimeMillis() - initStartTime);
|
||||||
|
|
||||||
if (!isLoading && !browserInitialized) {
|
if (!isLoading && !browserInitialized) {
|
||||||
|
log.info(
|
||||||
|
"Browser finished loading, preparing to initialize UI components");
|
||||||
browserInitialized = true;
|
browserInitialized = true;
|
||||||
SwingUtilities.invokeLater(
|
SwingUtilities.invokeLater(
|
||||||
() -> {
|
() -> {
|
||||||
if (loadingWindow != null) {
|
try {
|
||||||
Timer timer =
|
if (loadingWindow != null) {
|
||||||
new Timer(
|
log.info("Starting UI initialization sequence");
|
||||||
500,
|
|
||||||
e -> {
|
|
||||||
loadingWindow.dispose();
|
|
||||||
loadingWindow = null;
|
|
||||||
|
|
||||||
frame.dispose();
|
// Close loading window first
|
||||||
frame.setOpacity(1.0f);
|
loadingWindow.setVisible(false);
|
||||||
frame.setUndecorated(false);
|
loadingWindow.dispose();
|
||||||
frame.pack();
|
loadingWindow = null;
|
||||||
frame.setSize(1280, 800);
|
log.info("Loading window disposed");
|
||||||
frame.setLocationRelativeTo(null);
|
|
||||||
frame.setVisible(true);
|
// Then setup the main frame
|
||||||
frame.requestFocus();
|
frame.setVisible(false);
|
||||||
frame.toFront();
|
frame.dispose();
|
||||||
browser.getUIComponent()
|
frame.setOpacity(1.0f);
|
||||||
.requestFocus();
|
frame.setUndecorated(false);
|
||||||
});
|
frame.pack();
|
||||||
timer.setRepeats(false);
|
frame.setSize(1280, 800);
|
||||||
timer.start();
|
frame.setLocationRelativeTo(null);
|
||||||
|
log.debug("Frame reconfigured");
|
||||||
|
|
||||||
|
// Show the main frame
|
||||||
|
frame.setVisible(true);
|
||||||
|
frame.requestFocus();
|
||||||
|
frame.toFront();
|
||||||
|
log.info("Main frame displayed and focused");
|
||||||
|
|
||||||
|
// Focus the browser component
|
||||||
|
Timer focusTimer =
|
||||||
|
new Timer(
|
||||||
|
100,
|
||||||
|
e -> {
|
||||||
|
try {
|
||||||
|
browser.getUIComponent()
|
||||||
|
.requestFocus();
|
||||||
|
log.info(
|
||||||
|
"Browser component focused");
|
||||||
|
} catch (Exception ex) {
|
||||||
|
log.error(
|
||||||
|
"Error focusing browser",
|
||||||
|
ex);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
focusTimer.setRepeats(false);
|
||||||
|
focusTimer.start();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Error during UI initialization", e);
|
||||||
|
// Attempt cleanup on error
|
||||||
|
if (loadingWindow != null) {
|
||||||
|
loadingWindow.dispose();
|
||||||
|
loadingWindow = null;
|
||||||
|
}
|
||||||
|
if (frame != null) {
|
||||||
|
frame.setVisible(true);
|
||||||
|
frame.requestFocus();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,9 +14,12 @@ public class LoadingWindow extends JDialog {
|
|||||||
private final JLabel statusLabel;
|
private final JLabel statusLabel;
|
||||||
private final JPanel mainPanel;
|
private final JPanel mainPanel;
|
||||||
private final JLabel brandLabel;
|
private final JLabel brandLabel;
|
||||||
|
private long startTime;
|
||||||
|
|
||||||
public LoadingWindow(Frame parent, String initialUrl) {
|
public LoadingWindow(Frame parent, String initialUrl) {
|
||||||
super(parent, "Initializing Stirling-PDF", true);
|
super(parent, "Initializing Stirling-PDF", true);
|
||||||
|
startTime = System.currentTimeMillis();
|
||||||
|
log.info("Creating LoadingWindow - initialization started at: {}", startTime);
|
||||||
|
|
||||||
// Initialize components
|
// Initialize components
|
||||||
mainPanel = new JPanel();
|
mainPanel = new JPanel();
|
||||||
@@ -29,8 +32,8 @@ public class LoadingWindow extends JDialog {
|
|||||||
gbc.gridwidth = GridBagConstraints.REMAINDER;
|
gbc.gridwidth = GridBagConstraints.REMAINDER;
|
||||||
gbc.fill = GridBagConstraints.HORIZONTAL;
|
gbc.fill = GridBagConstraints.HORIZONTAL;
|
||||||
gbc.insets = new Insets(5, 5, 5, 5);
|
gbc.insets = new Insets(5, 5, 5, 5);
|
||||||
gbc.weightx = 1.0; // Add horizontal weight
|
gbc.weightx = 1.0;
|
||||||
gbc.weighty = 0.0; // Add vertical weight
|
gbc.weighty = 0.0;
|
||||||
|
|
||||||
// Add icon
|
// Add icon
|
||||||
try {
|
try {
|
||||||
@@ -43,12 +46,14 @@ public class LoadingWindow extends JDialog {
|
|||||||
iconLabel.setHorizontalAlignment(SwingConstants.CENTER);
|
iconLabel.setHorizontalAlignment(SwingConstants.CENTER);
|
||||||
gbc.gridy = 0;
|
gbc.gridy = 0;
|
||||||
mainPanel.add(iconLabel, gbc);
|
mainPanel.add(iconLabel, gbc);
|
||||||
|
log.debug("Icon loaded and scaled successfully");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("Failed to load icon", e);
|
log.error("Failed to load icon", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
// URL Label with explicit size
|
// URL Label with explicit size
|
||||||
brandLabel = new JLabel(initialUrl);
|
brandLabel = new JLabel(initialUrl);
|
||||||
brandLabel.setHorizontalAlignment(SwingConstants.CENTER);
|
brandLabel.setHorizontalAlignment(SwingConstants.CENTER);
|
||||||
@@ -63,6 +68,7 @@ public class LoadingWindow extends JDialog {
|
|||||||
statusLabel.setPreferredSize(new Dimension(300, 25));
|
statusLabel.setPreferredSize(new Dimension(300, 25));
|
||||||
gbc.gridy = 2;
|
gbc.gridy = 2;
|
||||||
mainPanel.add(statusLabel, gbc);
|
mainPanel.add(statusLabel, gbc);
|
||||||
|
|
||||||
// Progress bar with explicit size
|
// Progress bar with explicit size
|
||||||
progressBar = new JProgressBar(0, 100);
|
progressBar = new JProgressBar(0, 100);
|
||||||
progressBar.setStringPainted(true);
|
progressBar.setStringPainted(true);
|
||||||
@@ -82,33 +88,78 @@ public class LoadingWindow extends JDialog {
|
|||||||
setAlwaysOnTop(true);
|
setAlwaysOnTop(true);
|
||||||
setProgress(0);
|
setProgress(0);
|
||||||
setStatus("Starting...");
|
setStatus("Starting...");
|
||||||
|
|
||||||
|
log.info(
|
||||||
|
"LoadingWindow initialization completed in {}ms",
|
||||||
|
System.currentTimeMillis() - startTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setProgress(final int progress) {
|
public void setProgress(final int progress) {
|
||||||
SwingUtilities.invokeLater(
|
SwingUtilities.invokeLater(
|
||||||
() -> {
|
() -> {
|
||||||
try {
|
try {
|
||||||
progressBar.setValue(Math.min(Math.max(progress, 0), 100));
|
int validProgress = Math.min(Math.max(progress, 0), 100);
|
||||||
progressBar.setString(progress + "%");
|
log.info(
|
||||||
|
"Setting progress to {}% at {}ms since start",
|
||||||
|
validProgress, System.currentTimeMillis() - startTime);
|
||||||
|
|
||||||
|
// Log additional details when near 90%
|
||||||
|
if (validProgress >= 85 && validProgress <= 95) {
|
||||||
|
log.info(
|
||||||
|
"Near 90% progress - Current status: {}, Window visible: {}, "
|
||||||
|
+ "Progress bar responding: {}, Memory usage: {}MB",
|
||||||
|
statusLabel.getText(),
|
||||||
|
isVisible(),
|
||||||
|
progressBar.isEnabled(),
|
||||||
|
Runtime.getRuntime().totalMemory() / (1024 * 1024));
|
||||||
|
|
||||||
|
// Add thread state logging
|
||||||
|
Thread currentThread = Thread.currentThread();
|
||||||
|
log.debug(
|
||||||
|
"Current thread state - Name: {}, State: {}, Priority: {}",
|
||||||
|
currentThread.getName(),
|
||||||
|
currentThread.getState(),
|
||||||
|
currentThread.getPriority());
|
||||||
|
}
|
||||||
|
|
||||||
|
progressBar.setValue(validProgress);
|
||||||
|
progressBar.setString(validProgress + "%");
|
||||||
mainPanel.revalidate();
|
mainPanel.revalidate();
|
||||||
mainPanel.repaint();
|
mainPanel.repaint();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("Error updating progress", e);
|
log.error("Error updating progress to " + progress, e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setStatus(final String status) {
|
public void setStatus(final String status) {
|
||||||
log.info(status);
|
log.info(
|
||||||
|
"Status update at {}ms - Setting status to: {}",
|
||||||
|
System.currentTimeMillis() - startTime,
|
||||||
|
status);
|
||||||
|
|
||||||
SwingUtilities.invokeLater(
|
SwingUtilities.invokeLater(
|
||||||
() -> {
|
() -> {
|
||||||
try {
|
try {
|
||||||
statusLabel.setText(status != null ? status : "");
|
String validStatus = status != null ? status : "";
|
||||||
|
statusLabel.setText(validStatus);
|
||||||
|
|
||||||
|
// Log UI state when status changes
|
||||||
|
log.debug(
|
||||||
|
"UI State - Window visible: {}, Progress: {}%, Status: {}",
|
||||||
|
isVisible(), progressBar.getValue(), validStatus);
|
||||||
|
|
||||||
mainPanel.revalidate();
|
mainPanel.revalidate();
|
||||||
mainPanel.repaint();
|
mainPanel.repaint();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("Error updating status", e);
|
log.error("Error updating status to: " + status, e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void dispose() {
|
||||||
|
log.info("LoadingWindow disposing after {}ms", System.currentTimeMillis() - startTime);
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -136,16 +136,6 @@ public class AppConfig {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean(name = "watchedFoldersDir")
|
|
||||||
public String watchedFoldersDir() {
|
|
||||||
return "./pipeline/watchedFolders/";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean(name = "finishedFoldersDir")
|
|
||||||
public String finishedFoldersDir() {
|
|
||||||
return "./pipeline/finishedFolders/";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean(name = "directoryFilter")
|
@Bean(name = "directoryFilter")
|
||||||
public Predicate<Path> processOnlyFiles() {
|
public Predicate<Path> processOnlyFiles() {
|
||||||
return path -> {
|
return path -> {
|
||||||
|
|||||||
@@ -16,27 +16,15 @@ import org.simpleyaml.configuration.comments.CommentType;
|
|||||||
import org.simpleyaml.configuration.file.YamlFile;
|
import org.simpleyaml.configuration.file.YamlFile;
|
||||||
import org.simpleyaml.configuration.implementation.SimpleYamlImplementation;
|
import org.simpleyaml.configuration.implementation.SimpleYamlImplementation;
|
||||||
import org.simpleyaml.configuration.implementation.snakeyaml.lib.DumperOptions;
|
import org.simpleyaml.configuration.implementation.snakeyaml.lib.DumperOptions;
|
||||||
import org.springframework.context.ApplicationContextInitializer;
|
|
||||||
import org.springframework.context.ConfigurableApplicationContext;
|
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class ConfigInitializer
|
public class ConfigInitializer {
|
||||||
implements ApplicationContextInitializer<ConfigurableApplicationContext> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void initialize(ConfigurableApplicationContext applicationContext) {
|
|
||||||
try {
|
|
||||||
ensureConfigExists();
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException("Failed to initialize application configuration", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ensureConfigExists() throws IOException, URISyntaxException {
|
public void ensureConfigExists() throws IOException, URISyntaxException {
|
||||||
// Define the path to the external config directory
|
// Define the path to the external config directory
|
||||||
Path destPath = Paths.get("configs", "settings.yml");
|
Path destPath = Paths.get(InstallationPathConfig.getSettingsPath());
|
||||||
|
|
||||||
// Check if the file already exists
|
// Check if the file already exists
|
||||||
if (Files.notExists(destPath)) {
|
if (Files.notExists(destPath)) {
|
||||||
@@ -53,10 +41,11 @@ public class ConfigInitializer
|
|||||||
"Resource file not found: settings.yml.template");
|
"Resource file not found: settings.yml.template");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
log.info("Created settings file from template");
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
// Define the path to the config settings file
|
// Define the path to the config settings file
|
||||||
Path settingsPath = Paths.get("configs", "settings.yml");
|
Path settingsPath = Paths.get(InstallationPathConfig.getSettingsPath());
|
||||||
// Load the template resource
|
// Load the template resource
|
||||||
URL settingsTemplateResource =
|
URL settingsTemplateResource =
|
||||||
getClass().getClassLoader().getResource("settings.yml.template");
|
getClass().getClassLoader().getResource("settings.yml.template");
|
||||||
@@ -120,7 +109,7 @@ public class ConfigInitializer
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create custom settings file if it doesn't exist
|
// Create custom settings file if it doesn't exist
|
||||||
Path customSettingsPath = Paths.get("configs", "custom_settings.yml");
|
Path customSettingsPath = Paths.get(InstallationPathConfig.getCustomSettingsPath());
|
||||||
if (!Files.exists(customSettingsPath)) {
|
if (!Files.exists(customSettingsPath)) {
|
||||||
Files.createFile(customSettingsPath);
|
Files.createFile(customSettingsPath);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package stirling.software.SPDF.config;
|
package stirling.software.SPDF.config;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -265,20 +266,26 @@ public class EndpointConfiguration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void processEnvironmentConfigs() {
|
private void processEnvironmentConfigs() {
|
||||||
List<String> endpointsToRemove = applicationProperties.getEndpoints().getToRemove();
|
if (applicationProperties != null && applicationProperties.getEndpoints() != null) {
|
||||||
List<String> groupsToRemove = applicationProperties.getEndpoints().getGroupsToRemove();
|
List<String> endpointsToRemove = applicationProperties.getEndpoints().getToRemove();
|
||||||
if (!bookAndHtmlFormatsInstalled) {
|
List<String> groupsToRemove = applicationProperties.getEndpoints().getGroupsToRemove();
|
||||||
groupsToRemove.add("Calibre");
|
|
||||||
}
|
|
||||||
if (endpointsToRemove != null) {
|
|
||||||
for (String endpoint : endpointsToRemove) {
|
|
||||||
disableEndpoint(endpoint.trim());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (groupsToRemove != null) {
|
if (!bookAndHtmlFormatsInstalled) {
|
||||||
for (String group : groupsToRemove) {
|
if (groupsToRemove == null) {
|
||||||
disableGroup(group.trim());
|
groupsToRemove = new ArrayList<>();
|
||||||
|
}
|
||||||
|
groupsToRemove.add("Calibre");
|
||||||
|
}
|
||||||
|
if (endpointsToRemove != null) {
|
||||||
|
for (String endpoint : endpointsToRemove) {
|
||||||
|
disableEndpoint(endpoint.trim());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (groupsToRemove != null) {
|
||||||
|
for (String group : groupsToRemove) {
|
||||||
|
disableGroup(group.trim());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,8 @@ public class FileFallbackTemplateResolver extends AbstractConfigurableTemplateRe
|
|||||||
String characterEncoding,
|
String characterEncoding,
|
||||||
Map<String, Object> templateResolutionAttributes) {
|
Map<String, Object> templateResolutionAttributes) {
|
||||||
Resource resource =
|
Resource resource =
|
||||||
resourceLoader.getResource("file:./customFiles/templates/" + resourceName);
|
resourceLoader.getResource(
|
||||||
|
"file:" + InstallationPathConfig.getTemplatesPath() + resourceName);
|
||||||
try {
|
try {
|
||||||
if (resource.exists() && resource.isReadable()) {
|
if (resource.exists() && resource.isReadable()) {
|
||||||
return new FileTemplateResource(resource.getFile().getPath(), characterEncoding);
|
return new FileTemplateResource(resource.getFile().getPath(), characterEncoding);
|
||||||
|
|||||||
@@ -0,0 +1,132 @@
|
|||||||
|
package stirling.software.SPDF.config;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
public class InstallationPathConfig {
|
||||||
|
private static final String BASE_PATH;
|
||||||
|
|
||||||
|
// Root paths
|
||||||
|
private static final String LOG_PATH;
|
||||||
|
private static final String CONFIG_PATH;
|
||||||
|
private static final String PIPELINE_PATH;
|
||||||
|
private static final String CUSTOM_FILES_PATH;
|
||||||
|
private static final String CLIENT_WEBUI_PATH;
|
||||||
|
|
||||||
|
// Config paths
|
||||||
|
private static final String SETTINGS_PATH;
|
||||||
|
private static final String CUSTOM_SETTINGS_PATH;
|
||||||
|
|
||||||
|
// Pipeline paths
|
||||||
|
private static final String PIPELINE_WATCHED_FOLDERS_PATH;
|
||||||
|
private static final String PIPELINE_FINISHED_FOLDERS_PATH;
|
||||||
|
|
||||||
|
// Custom file paths
|
||||||
|
private static final String STATIC_PATH;
|
||||||
|
private static final String TEMPLATES_PATH;
|
||||||
|
private static final String SIGNATURES_PATH;
|
||||||
|
|
||||||
|
static {
|
||||||
|
BASE_PATH = initializeBasePath();
|
||||||
|
|
||||||
|
// Initialize root paths
|
||||||
|
LOG_PATH = BASE_PATH + "logs" + File.separator;
|
||||||
|
CONFIG_PATH = BASE_PATH + "configs" + File.separator;
|
||||||
|
PIPELINE_PATH = BASE_PATH + "pipeline" + File.separator;
|
||||||
|
CUSTOM_FILES_PATH = BASE_PATH + "customFiles" + File.separator;
|
||||||
|
CLIENT_WEBUI_PATH = BASE_PATH + "clientWebUI" + File.separator;
|
||||||
|
|
||||||
|
// Initialize config paths
|
||||||
|
SETTINGS_PATH = CONFIG_PATH + "settings.yml";
|
||||||
|
CUSTOM_SETTINGS_PATH = CONFIG_PATH + "custom_settings.yml";
|
||||||
|
|
||||||
|
// Initialize pipeline paths
|
||||||
|
PIPELINE_WATCHED_FOLDERS_PATH = PIPELINE_PATH + "watchedFolders" + File.separator;
|
||||||
|
PIPELINE_FINISHED_FOLDERS_PATH = PIPELINE_PATH + "finishedFolders" + File.separator;
|
||||||
|
|
||||||
|
// Initialize custom file paths
|
||||||
|
STATIC_PATH = CUSTOM_FILES_PATH + "static" + File.separator;
|
||||||
|
TEMPLATES_PATH = CUSTOM_FILES_PATH + "templates" + File.separator;
|
||||||
|
SIGNATURES_PATH = CUSTOM_FILES_PATH + "signatures" + File.separator;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String initializeBasePath() {
|
||||||
|
if (Boolean.parseBoolean(System.getProperty("STIRLING_PDF_DESKTOP_UI", "false"))) {
|
||||||
|
String os = System.getProperty("os.name").toLowerCase();
|
||||||
|
if (os.contains("win")) {
|
||||||
|
return System.getenv("APPDATA") + File.separator + "Stirling-PDF" + File.separator;
|
||||||
|
} else if (os.contains("mac")) {
|
||||||
|
return System.getProperty("user.home")
|
||||||
|
+ File.separator
|
||||||
|
+ "Library"
|
||||||
|
+ File.separator
|
||||||
|
+ "Application Support"
|
||||||
|
+ File.separator
|
||||||
|
+ "Stirling-PDF"
|
||||||
|
+ File.separator;
|
||||||
|
} else {
|
||||||
|
return System.getProperty("user.home")
|
||||||
|
+ File.separator
|
||||||
|
+ ".config"
|
||||||
|
+ File.separator
|
||||||
|
+ "Stirling-PDF"
|
||||||
|
+ File.separator;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "./";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getPath() {
|
||||||
|
return BASE_PATH;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getLogPath() {
|
||||||
|
return LOG_PATH;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getConfigPath() {
|
||||||
|
return CONFIG_PATH;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getPipelinePath() {
|
||||||
|
return PIPELINE_PATH;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getCustomFilesPath() {
|
||||||
|
return CUSTOM_FILES_PATH;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getClientWebUIPath() {
|
||||||
|
return CLIENT_WEBUI_PATH;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getSettingsPath() {
|
||||||
|
return SETTINGS_PATH;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getCustomSettingsPath() {
|
||||||
|
return CUSTOM_SETTINGS_PATH;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getPipelineWatchedFoldersDir() {
|
||||||
|
return PIPELINE_WATCHED_FOLDERS_PATH;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getPipelineFinishedFoldersDir() {
|
||||||
|
return PIPELINE_FINISHED_FOLDERS_PATH;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getStaticPath() {
|
||||||
|
return STATIC_PATH;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getTemplatesPath() {
|
||||||
|
return TEMPLATES_PATH;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getSignaturesPath() {
|
||||||
|
return SIGNATURES_PATH;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package stirling.software.SPDF.config;
|
||||||
|
|
||||||
|
import ch.qos.logback.core.PropertyDefinerBase;
|
||||||
|
|
||||||
|
public class LogbackPropertyLoader extends PropertyDefinerBase {
|
||||||
|
@Override
|
||||||
|
public String getPropertyValue() {
|
||||||
|
return InstallationPathConfig.getLogPath();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -23,7 +23,8 @@ public class WebMvcConfig implements WebMvcConfigurer {
|
|||||||
public void addResourceHandlers(ResourceHandlerRegistry registry) {
|
public void addResourceHandlers(ResourceHandlerRegistry registry) {
|
||||||
// Handler for external static resources
|
// Handler for external static resources
|
||||||
registry.addResourceHandler("/**")
|
registry.addResourceHandler("/**")
|
||||||
.addResourceLocations("file:customFiles/static/", "classpath:/static/");
|
.addResourceLocations(
|
||||||
|
"file:" + InstallationPathConfig.getStaticPath(), "classpath:/static/");
|
||||||
// .setCachePeriod(0); // Optional: disable caching
|
// .setCachePeriod(0); // Optional: disable caching
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ public class YamlPropertySourceFactory implements PropertySourceFactory {
|
|||||||
throws IOException {
|
throws IOException {
|
||||||
YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
|
YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
|
||||||
factory.setResources(encodedResource.getResource());
|
factory.setResources(encodedResource.getResource());
|
||||||
|
|
||||||
Properties properties = factory.getObject();
|
Properties properties = factory.getObject();
|
||||||
|
|
||||||
return new PropertiesPropertySource(
|
return new PropertiesPropertySource(
|
||||||
|
|||||||
@@ -22,22 +22,31 @@ import jakarta.servlet.FilterChain;
|
|||||||
import jakarta.servlet.ServletException;
|
import jakarta.servlet.ServletException;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import stirling.software.SPDF.config.security.saml2.CustomSaml2AuthenticatedPrincipal;
|
import stirling.software.SPDF.config.security.saml2.CustomSaml2AuthenticatedPrincipal;
|
||||||
import stirling.software.SPDF.config.security.session.SessionPersistentRegistry;
|
import stirling.software.SPDF.config.security.session.SessionPersistentRegistry;
|
||||||
import stirling.software.SPDF.model.ApiKeyAuthenticationToken;
|
import stirling.software.SPDF.model.ApiKeyAuthenticationToken;
|
||||||
|
import stirling.software.SPDF.model.ApplicationProperties;
|
||||||
|
import stirling.software.SPDF.model.ApplicationProperties.Security;
|
||||||
|
import stirling.software.SPDF.model.ApplicationProperties.Security.OAUTH2;
|
||||||
|
import stirling.software.SPDF.model.ApplicationProperties.Security.SAML2;
|
||||||
import stirling.software.SPDF.model.User;
|
import stirling.software.SPDF.model.User;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
@Component
|
@Component
|
||||||
public class UserAuthenticationFilter extends OncePerRequestFilter {
|
public class UserAuthenticationFilter extends OncePerRequestFilter {
|
||||||
|
|
||||||
|
private final ApplicationProperties applicationProperties;
|
||||||
private final UserService userService;
|
private final UserService userService;
|
||||||
private final SessionPersistentRegistry sessionPersistentRegistry;
|
private final SessionPersistentRegistry sessionPersistentRegistry;
|
||||||
private final boolean loginEnabledValue;
|
private final boolean loginEnabledValue;
|
||||||
|
|
||||||
public UserAuthenticationFilter(
|
public UserAuthenticationFilter(
|
||||||
|
@Lazy ApplicationProperties applicationProperties,
|
||||||
@Lazy UserService userService,
|
@Lazy UserService userService,
|
||||||
SessionPersistentRegistry sessionPersistentRegistry,
|
SessionPersistentRegistry sessionPersistentRegistry,
|
||||||
@Qualifier("loginEnabled") boolean loginEnabledValue) {
|
@Qualifier("loginEnabled") boolean loginEnabledValue) {
|
||||||
|
this.applicationProperties = applicationProperties;
|
||||||
this.userService = userService;
|
this.userService = userService;
|
||||||
this.sessionPersistentRegistry = sessionPersistentRegistry;
|
this.sessionPersistentRegistry = sessionPersistentRegistry;
|
||||||
this.loginEnabledValue = loginEnabledValue;
|
this.loginEnabledValue = loginEnabledValue;
|
||||||
@@ -121,33 +130,67 @@ public class UserAuthenticationFilter extends OncePerRequestFilter {
|
|||||||
|
|
||||||
// Check if the authenticated user is disabled and invalidate their session if so
|
// Check if the authenticated user is disabled and invalidate their session if so
|
||||||
if (authentication != null && authentication.isAuthenticated()) {
|
if (authentication != null && authentication.isAuthenticated()) {
|
||||||
|
|
||||||
|
Security securityProp = applicationProperties.getSecurity();
|
||||||
|
LoginMethod loginMethod = LoginMethod.UNKNOWN;
|
||||||
|
|
||||||
|
boolean blockRegistration = false;
|
||||||
|
|
||||||
|
// Extract username and determine the login method
|
||||||
Object principal = authentication.getPrincipal();
|
Object principal = authentication.getPrincipal();
|
||||||
String username = null;
|
String username = null;
|
||||||
if (principal instanceof UserDetails) {
|
if (principal instanceof UserDetails) {
|
||||||
username = ((UserDetails) principal).getUsername();
|
username = ((UserDetails) principal).getUsername();
|
||||||
|
loginMethod = LoginMethod.USERDETAILS;
|
||||||
} else if (principal instanceof OAuth2User) {
|
} else if (principal instanceof OAuth2User) {
|
||||||
username = ((OAuth2User) principal).getName();
|
username = ((OAuth2User) principal).getName();
|
||||||
|
loginMethod = LoginMethod.OAUTH2USER;
|
||||||
|
OAUTH2 oAuth = securityProp.getOauth2();
|
||||||
|
blockRegistration = oAuth != null && oAuth.getBlockRegistration();
|
||||||
} else if (principal instanceof CustomSaml2AuthenticatedPrincipal) {
|
} else if (principal instanceof CustomSaml2AuthenticatedPrincipal) {
|
||||||
username = ((CustomSaml2AuthenticatedPrincipal) principal).getName();
|
username = ((CustomSaml2AuthenticatedPrincipal) principal).getName();
|
||||||
|
loginMethod = LoginMethod.SAML2USER;
|
||||||
|
SAML2 saml2 = securityProp.getSaml2();
|
||||||
|
blockRegistration = saml2 != null && saml2.getBlockRegistration();
|
||||||
} else if (principal instanceof String) {
|
} else if (principal instanceof String) {
|
||||||
username = (String) principal;
|
username = (String) principal;
|
||||||
|
loginMethod = LoginMethod.STRINGUSER;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Retrieve all active sessions for the user
|
||||||
List<SessionInformation> sessionsInformations =
|
List<SessionInformation> sessionsInformations =
|
||||||
sessionPersistentRegistry.getAllSessions(principal, false);
|
sessionPersistentRegistry.getAllSessions(principal, false);
|
||||||
|
|
||||||
|
// Check if the user exists, is disabled, or needs session invalidation
|
||||||
if (username != null) {
|
if (username != null) {
|
||||||
|
log.debug("Validating user: {}", username);
|
||||||
boolean isUserExists = userService.usernameExistsIgnoreCase(username);
|
boolean isUserExists = userService.usernameExistsIgnoreCase(username);
|
||||||
boolean isUserDisabled = userService.isUserDisabled(username);
|
boolean isUserDisabled = userService.isUserDisabled(username);
|
||||||
|
|
||||||
|
boolean notSsoLogin =
|
||||||
|
!loginMethod.equals(LoginMethod.OAUTH2USER)
|
||||||
|
&& !loginMethod.equals(LoginMethod.SAML2USER);
|
||||||
|
|
||||||
|
// Block user registration if not allowed by configuration
|
||||||
|
if (blockRegistration && !isUserExists) {
|
||||||
|
log.warn("Blocked registration for OAuth2/SAML user: {}", username);
|
||||||
|
response.sendRedirect(
|
||||||
|
request.getContextPath() + "/logout?oauth2_admin_blocked_user=true");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Expire sessions and logout if the user does not exist or is disabled
|
||||||
if (!isUserExists || isUserDisabled) {
|
if (!isUserExists || isUserDisabled) {
|
||||||
|
log.info(
|
||||||
|
"Invalidating session for disabled or non-existent user: {}", username);
|
||||||
for (SessionInformation sessionsInformation : sessionsInformations) {
|
for (SessionInformation sessionsInformation : sessionsInformations) {
|
||||||
sessionsInformation.expireNow();
|
sessionsInformation.expireNow();
|
||||||
sessionPersistentRegistry.expireSession(sessionsInformation.getSessionId());
|
sessionPersistentRegistry.expireSession(sessionsInformation.getSessionId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isUserExists) {
|
// Redirect to logout if credentials are invalid
|
||||||
|
if (!isUserExists && notSsoLogin) {
|
||||||
response.sendRedirect(request.getContextPath() + "/logout?badcredentials=true");
|
response.sendRedirect(request.getContextPath() + "/logout?badcredentials=true");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -161,6 +204,25 @@ public class UserAuthenticationFilter extends OncePerRequestFilter {
|
|||||||
filterChain.doFilter(request, response);
|
filterChain.doFilter(request, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private enum LoginMethod {
|
||||||
|
USERDETAILS("UserDetails"),
|
||||||
|
OAUTH2USER("OAuth2User"),
|
||||||
|
STRINGUSER("StringUser"),
|
||||||
|
UNKNOWN("Unknown"),
|
||||||
|
SAML2USER("Saml2User");
|
||||||
|
|
||||||
|
private String method;
|
||||||
|
|
||||||
|
LoginMethod(String method) {
|
||||||
|
this.method = method;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return method;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
|
protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
|
||||||
String uri = request.getRequestURI();
|
String uri = request.getRequestURI();
|
||||||
|
|||||||
@@ -11,13 +11,11 @@ import org.springframework.core.convert.converter.Converter;
|
|||||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||||
import org.springframework.security.saml2.provider.service.authentication.OpenSaml4AuthenticationProvider.ResponseToken;
|
import org.springframework.security.saml2.provider.service.authentication.OpenSaml4AuthenticationProvider.ResponseToken;
|
||||||
import org.springframework.security.saml2.provider.service.authentication.Saml2Authentication;
|
import org.springframework.security.saml2.provider.service.authentication.Saml2Authentication;
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import stirling.software.SPDF.config.security.UserService;
|
import stirling.software.SPDF.config.security.UserService;
|
||||||
import stirling.software.SPDF.model.User;
|
import stirling.software.SPDF.model.User;
|
||||||
|
|
||||||
@Component
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class CustomSaml2ResponseAuthenticationConverter
|
public class CustomSaml2ResponseAuthenticationConverter
|
||||||
implements Converter<ResponseToken, Saml2Authentication> {
|
implements Converter<ResponseToken, Saml2Authentication> {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||||||
import io.swagger.v3.oas.annotations.Hidden;
|
import io.swagger.v3.oas.annotations.Hidden;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
|
||||||
|
import stirling.software.SPDF.config.InstallationPathConfig;
|
||||||
import stirling.software.SPDF.model.ApplicationProperties;
|
import stirling.software.SPDF.model.ApplicationProperties;
|
||||||
import stirling.software.SPDF.utils.GeneralUtils;
|
import stirling.software.SPDF.utils.GeneralUtils;
|
||||||
|
|
||||||
@@ -33,7 +34,8 @@ public class SettingsController {
|
|||||||
if (!"undefined".equals(applicationProperties.getSystem().getEnableAnalytics())) {
|
if (!"undefined".equals(applicationProperties.getSystem().getEnableAnalytics())) {
|
||||||
return ResponseEntity.status(HttpStatus.ALREADY_REPORTED)
|
return ResponseEntity.status(HttpStatus.ALREADY_REPORTED)
|
||||||
.body(
|
.body(
|
||||||
"Setting has already been set, To adjust please edit /config/settings.yml");
|
"Setting has already been set, To adjust please edit "
|
||||||
|
+ InstallationPathConfig.getSettingsPath());
|
||||||
}
|
}
|
||||||
GeneralUtils.saveKeyToConfig("system.enableAnalytics", String.valueOf(enabled), false);
|
GeneralUtils.saveKeyToConfig("system.enableAnalytics", String.valueOf(enabled), false);
|
||||||
applicationProperties.getSystem().setEnableAnalytics(String.valueOf(enabled));
|
applicationProperties.getSystem().setEnableAnalytics(String.valueOf(enabled));
|
||||||
|
|||||||
@@ -13,6 +13,9 @@ import java.util.zip.ZipEntry;
|
|||||||
import java.util.zip.ZipOutputStream;
|
import java.util.zip.ZipOutputStream;
|
||||||
|
|
||||||
import org.apache.commons.io.FileUtils;
|
import org.apache.commons.io.FileUtils;
|
||||||
|
import org.apache.pdfbox.Loader;
|
||||||
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
|
import org.apache.pdfbox.pdmodel.PDPage;
|
||||||
import org.apache.pdfbox.rendering.ImageType;
|
import org.apache.pdfbox.rendering.ImageType;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
@@ -31,11 +34,8 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
import stirling.software.SPDF.model.api.converters.ConvertToImageRequest;
|
import stirling.software.SPDF.model.api.converters.ConvertToImageRequest;
|
||||||
import stirling.software.SPDF.model.api.converters.ConvertToPdfRequest;
|
import stirling.software.SPDF.model.api.converters.ConvertToPdfRequest;
|
||||||
import stirling.software.SPDF.service.CustomPDDocumentFactory;
|
import stirling.software.SPDF.service.CustomPDDocumentFactory;
|
||||||
import stirling.software.SPDF.utils.CheckProgramInstall;
|
import stirling.software.SPDF.utils.*;
|
||||||
import stirling.software.SPDF.utils.PdfUtils;
|
|
||||||
import stirling.software.SPDF.utils.ProcessExecutor;
|
|
||||||
import stirling.software.SPDF.utils.ProcessExecutor.ProcessExecutorResult;
|
import stirling.software.SPDF.utils.ProcessExecutor.ProcessExecutorResult;
|
||||||
import stirling.software.SPDF.utils.WebResponseUtils;
|
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/api/v1/convert")
|
@RequestMapping("/api/v1/convert")
|
||||||
@@ -62,14 +62,20 @@ public class ConvertImgPDFController {
|
|||||||
String singleOrMultiple = request.getSingleOrMultiple();
|
String singleOrMultiple = request.getSingleOrMultiple();
|
||||||
String colorType = request.getColorType();
|
String colorType = request.getColorType();
|
||||||
String dpi = request.getDpi();
|
String dpi = request.getDpi();
|
||||||
|
String pageNumbers = request.getPageNumbers();
|
||||||
Path tempFile = null;
|
Path tempFile = null;
|
||||||
Path tempOutputDir = null;
|
Path tempOutputDir = null;
|
||||||
Path tempPdfPath = null;
|
Path tempPdfPath = null;
|
||||||
byte[] result = null;
|
byte[] result = null;
|
||||||
|
String[] pageOrderArr =
|
||||||
|
(pageNumbers != null && !pageNumbers.trim().isEmpty())
|
||||||
|
? pageNumbers.split(",")
|
||||||
|
: new String[] {"all"};
|
||||||
|
;
|
||||||
try {
|
try {
|
||||||
byte[] pdfBytes = file.getBytes();
|
// Load the input PDF
|
||||||
|
byte[] newPdfBytes = rearrangePdfPages(file.getBytes(), pageOrderArr);
|
||||||
|
|
||||||
ImageType colorTypeResult = ImageType.RGB;
|
ImageType colorTypeResult = ImageType.RGB;
|
||||||
if ("greyscale".equals(colorType)) {
|
if ("greyscale".equals(colorType)) {
|
||||||
colorTypeResult = ImageType.GRAY;
|
colorTypeResult = ImageType.GRAY;
|
||||||
@@ -84,7 +90,7 @@ public class ConvertImgPDFController {
|
|||||||
|
|
||||||
result =
|
result =
|
||||||
PdfUtils.convertFromPdf(
|
PdfUtils.convertFromPdf(
|
||||||
pdfBytes,
|
newPdfBytes,
|
||||||
"webp".equalsIgnoreCase(imageFormat)
|
"webp".equalsIgnoreCase(imageFormat)
|
||||||
? "png"
|
? "png"
|
||||||
: imageFormat.toUpperCase(),
|
: imageFormat.toUpperCase(),
|
||||||
@@ -227,4 +233,46 @@ public class ConvertImgPDFController {
|
|||||||
String mimeType = URLConnection.guessContentTypeFromName("." + imageFormat);
|
String mimeType = URLConnection.guessContentTypeFromName("." + imageFormat);
|
||||||
return "null".equals(mimeType) ? "application/octet-stream" : mimeType;
|
return "null".equals(mimeType) ? "application/octet-stream" : mimeType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rearranges the pages of the given PDF document based on the specified page order.
|
||||||
|
*
|
||||||
|
* @param pdfBytes The byte array of the original PDF file.
|
||||||
|
* @param pageOrderArr An array of page numbers indicating the new order.
|
||||||
|
* @return A byte array of the rearranged PDF.
|
||||||
|
* @throws IOException If an error occurs while processing the PDF.
|
||||||
|
*/
|
||||||
|
private byte[] rearrangePdfPages(byte[] pdfBytes, String[] pageOrderArr) throws IOException {
|
||||||
|
// Load the input PDF
|
||||||
|
PDDocument document = Loader.loadPDF(pdfBytes);
|
||||||
|
int totalPages = document.getNumberOfPages();
|
||||||
|
List<Integer> newPageOrder = GeneralUtils.parsePageList(pageOrderArr, totalPages, false);
|
||||||
|
|
||||||
|
// Create a new list to hold the pages in the new order
|
||||||
|
List<PDPage> newPages = new ArrayList<>();
|
||||||
|
for (int pageIndex : newPageOrder) {
|
||||||
|
newPages.add(document.getPage(pageIndex));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove all the pages from the original document
|
||||||
|
for (int i = document.getNumberOfPages() - 1; i >= 0; i--) {
|
||||||
|
document.removePage(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the pages in the new order
|
||||||
|
for (PDPage page : newPages) {
|
||||||
|
document.addPage(page);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert PDDocument to byte array
|
||||||
|
byte[] newPdfBytes;
|
||||||
|
try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
|
||||||
|
document.save(baos);
|
||||||
|
newPdfBytes = baos.toByteArray();
|
||||||
|
} finally {
|
||||||
|
document.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
return newPdfBytes;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
|||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import stirling.software.SPDF.model.ApplicationProperties;
|
|
||||||
import stirling.software.SPDF.model.PipelineConfig;
|
import stirling.software.SPDF.model.PipelineConfig;
|
||||||
import stirling.software.SPDF.model.api.HandleDataRequest;
|
import stirling.software.SPDF.model.api.HandleDataRequest;
|
||||||
import stirling.software.SPDF.utils.WebResponseUtils;
|
import stirling.software.SPDF.utils.WebResponseUtils;
|
||||||
@@ -35,22 +34,12 @@ import stirling.software.SPDF.utils.WebResponseUtils;
|
|||||||
@Tag(name = "Pipeline", description = "Pipeline APIs")
|
@Tag(name = "Pipeline", description = "Pipeline APIs")
|
||||||
public class PipelineController {
|
public class PipelineController {
|
||||||
|
|
||||||
final String watchedFoldersDir = "./pipeline/watchedFolders/";
|
|
||||||
|
|
||||||
final String finishedFoldersDir = "./pipeline/finishedFolders/";
|
|
||||||
|
|
||||||
private final PipelineProcessor processor;
|
private final PipelineProcessor processor;
|
||||||
|
|
||||||
private final ApplicationProperties applicationProperties;
|
|
||||||
|
|
||||||
private final ObjectMapper objectMapper;
|
private final ObjectMapper objectMapper;
|
||||||
|
|
||||||
public PipelineController(
|
public PipelineController(PipelineProcessor processor, ObjectMapper objectMapper) {
|
||||||
PipelineProcessor processor,
|
|
||||||
ApplicationProperties applicationProperties,
|
|
||||||
ObjectMapper objectMapper) {
|
|
||||||
this.processor = processor;
|
this.processor = processor;
|
||||||
this.applicationProperties = applicationProperties;
|
|
||||||
this.objectMapper = objectMapper;
|
this.objectMapper = objectMapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ import java.util.List;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Qualifier;
|
|
||||||
import org.springframework.core.io.ByteArrayResource;
|
import org.springframework.core.io.ByteArrayResource;
|
||||||
import org.springframework.core.io.Resource;
|
import org.springframework.core.io.Resource;
|
||||||
import org.springframework.scheduling.annotation.Scheduled;
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
@@ -25,6 +24,7 @@ import org.springframework.stereotype.Service;
|
|||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import stirling.software.SPDF.config.InstallationPathConfig;
|
||||||
import stirling.software.SPDF.model.PipelineConfig;
|
import stirling.software.SPDF.model.PipelineConfig;
|
||||||
import stirling.software.SPDF.model.PipelineOperation;
|
import stirling.software.SPDF.model.PipelineOperation;
|
||||||
import stirling.software.SPDF.utils.FileMonitor;
|
import stirling.software.SPDF.utils.FileMonitor;
|
||||||
@@ -48,14 +48,12 @@ public class PipelineDirectoryProcessor {
|
|||||||
public PipelineDirectoryProcessor(
|
public PipelineDirectoryProcessor(
|
||||||
ObjectMapper objectMapper,
|
ObjectMapper objectMapper,
|
||||||
ApiDocService apiDocService,
|
ApiDocService apiDocService,
|
||||||
@Qualifier("watchedFoldersDir") String watchedFoldersDir,
|
|
||||||
@Qualifier("finishedFoldersDir") String finishedFoldersDir,
|
|
||||||
PipelineProcessor processor,
|
PipelineProcessor processor,
|
||||||
FileMonitor fileMonitor) {
|
FileMonitor fileMonitor) {
|
||||||
this.objectMapper = objectMapper;
|
this.objectMapper = objectMapper;
|
||||||
this.apiDocService = apiDocService;
|
this.apiDocService = apiDocService;
|
||||||
this.watchedFoldersDir = watchedFoldersDir;
|
this.watchedFoldersDir = InstallationPathConfig.getPipelineWatchedFoldersDir();
|
||||||
this.finishedFoldersDir = finishedFoldersDir;
|
this.finishedFoldersDir = InstallationPathConfig.getPipelineFinishedFoldersDir();
|
||||||
this.processor = processor;
|
this.processor = processor;
|
||||||
this.fileMonitor = fileMonitor;
|
this.fileMonitor = fileMonitor;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -163,6 +163,9 @@ public class AccountWebController {
|
|||||||
case "invalid_destination":
|
case "invalid_destination":
|
||||||
erroroauth = "login.invalid_destination";
|
erroroauth = "login.invalid_destination";
|
||||||
break;
|
break;
|
||||||
|
case "relying_party_registration_not_found":
|
||||||
|
erroroauth = "login.relyingPartyRegistrationNotFound";
|
||||||
|
break;
|
||||||
// Valid InResponseTo was not available from the validation context, unable to
|
// Valid InResponseTo was not available from the validation context, unable to
|
||||||
// evaluate
|
// evaluate
|
||||||
case "invalid_in_response_to":
|
case "invalid_in_response_to":
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import io.swagger.v3.oas.annotations.Hidden;
|
|||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import stirling.software.SPDF.config.InstallationPathConfig;
|
||||||
import stirling.software.SPDF.controller.api.pipeline.UserServiceInterface;
|
import stirling.software.SPDF.controller.api.pipeline.UserServiceInterface;
|
||||||
import stirling.software.SPDF.model.SignatureFile;
|
import stirling.software.SPDF.model.SignatureFile;
|
||||||
import stirling.software.SPDF.service.SignatureService;
|
import stirling.software.SPDF.service.SignatureService;
|
||||||
@@ -34,8 +35,6 @@ import stirling.software.SPDF.service.SignatureService;
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
public class GeneralWebController {
|
public class GeneralWebController {
|
||||||
|
|
||||||
private static final String SIGNATURE_BASE_PATH = "customFiles/static/signatures/";
|
|
||||||
private static final String ALL_USERS_FOLDER = "ALL_USERS";
|
|
||||||
private final SignatureService signatureService;
|
private final SignatureService signatureService;
|
||||||
private final UserServiceInterface userService;
|
private final UserServiceInterface userService;
|
||||||
private final ResourceLoader resourceLoader;
|
private final ResourceLoader resourceLoader;
|
||||||
@@ -223,7 +222,9 @@ public class GeneralWebController {
|
|||||||
// Extract font names from classpath
|
// Extract font names from classpath
|
||||||
fontNames.addAll(getFontNamesFromLocation("classpath:static/fonts/*.woff2"));
|
fontNames.addAll(getFontNamesFromLocation("classpath:static/fonts/*.woff2"));
|
||||||
// Extract font names from external directory
|
// Extract font names from external directory
|
||||||
fontNames.addAll(getFontNamesFromLocation("file:customFiles/static/fonts/*"));
|
fontNames.addAll(
|
||||||
|
getFontNamesFromLocation(
|
||||||
|
"file:" + InstallationPathConfig.getStaticPath() + "fonts/*"));
|
||||||
return fontNames;
|
return fontNames;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package stirling.software.SPDF.model;
|
package stirling.software.SPDF.model;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.net.HttpURLConnection;
|
import java.net.HttpURLConnection;
|
||||||
@@ -13,18 +15,23 @@ import java.util.List;
|
|||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.context.annotation.PropertySource;
|
|
||||||
import org.springframework.core.Ordered;
|
import org.springframework.core.Ordered;
|
||||||
import org.springframework.core.annotation.Order;
|
import org.springframework.core.annotation.Order;
|
||||||
|
import org.springframework.core.env.ConfigurableEnvironment;
|
||||||
|
import org.springframework.core.env.PropertySource;
|
||||||
import org.springframework.core.io.ClassPathResource;
|
import org.springframework.core.io.ClassPathResource;
|
||||||
import org.springframework.core.io.FileSystemResource;
|
import org.springframework.core.io.FileSystemResource;
|
||||||
import org.springframework.core.io.Resource;
|
import org.springframework.core.io.Resource;
|
||||||
|
import org.springframework.core.io.support.EncodedResource;
|
||||||
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import lombok.ToString;
|
import lombok.ToString;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import stirling.software.SPDF.config.InstallationPathConfig;
|
||||||
import stirling.software.SPDF.config.YamlPropertySourceFactory;
|
import stirling.software.SPDF.config.YamlPropertySourceFactory;
|
||||||
import stirling.software.SPDF.model.provider.GithubProvider;
|
import stirling.software.SPDF.model.provider.GithubProvider;
|
||||||
import stirling.software.SPDF.model.provider.GoogleProvider;
|
import stirling.software.SPDF.model.provider.GoogleProvider;
|
||||||
@@ -33,11 +40,37 @@ import stirling.software.SPDF.model.provider.UnsupportedProviderException;
|
|||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@ConfigurationProperties(prefix = "")
|
@ConfigurationProperties(prefix = "")
|
||||||
@PropertySource(value = "file:./configs/settings.yml", factory = YamlPropertySourceFactory.class)
|
|
||||||
@Data
|
@Data
|
||||||
@Order(Ordered.HIGHEST_PRECEDENCE)
|
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||||
|
@Slf4j
|
||||||
public class ApplicationProperties {
|
public class ApplicationProperties {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public PropertySource<?> dynamicYamlPropertySource(ConfigurableEnvironment environment)
|
||||||
|
throws IOException {
|
||||||
|
String configPath = InstallationPathConfig.getSettingsPath();
|
||||||
|
log.debug("Attempting to load settings from: " + configPath);
|
||||||
|
|
||||||
|
File file = new File(configPath);
|
||||||
|
if (!file.exists()) {
|
||||||
|
log.error("Warning: Settings file does not exist at: " + configPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
Resource resource = new FileSystemResource(configPath);
|
||||||
|
if (!resource.exists()) {
|
||||||
|
throw new FileNotFoundException("Settings file not found at: " + configPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
EncodedResource encodedResource = new EncodedResource(resource);
|
||||||
|
PropertySource<?> propertySource =
|
||||||
|
new YamlPropertySourceFactory().createPropertySource(null, encodedResource);
|
||||||
|
environment.getPropertySources().addFirst(propertySource);
|
||||||
|
|
||||||
|
log.debug("Loaded properties: " + propertySource.getSource());
|
||||||
|
|
||||||
|
return propertySource;
|
||||||
|
}
|
||||||
|
|
||||||
private Legal legal = new Legal();
|
private Legal legal = new Legal();
|
||||||
private Security security = new Security();
|
private Security security = new Security();
|
||||||
private System system = new System();
|
private System system = new System();
|
||||||
@@ -153,6 +186,7 @@ public class ApplicationProperties {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Resource getSpCert() {
|
public Resource getSpCert() {
|
||||||
|
if (spCert == null) return null;
|
||||||
if (spCert.startsWith("classpath:")) {
|
if (spCert.startsWith("classpath:")) {
|
||||||
return new ClassPathResource(spCert.substring("classpath:".length()));
|
return new ClassPathResource(spCert.substring("classpath:".length()));
|
||||||
} else {
|
} else {
|
||||||
@@ -161,6 +195,7 @@ public class ApplicationProperties {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Resource getidpCert() {
|
public Resource getidpCert() {
|
||||||
|
if (idpCert == null) return null;
|
||||||
if (idpCert.startsWith("classpath:")) {
|
if (idpCert.startsWith("classpath:")) {
|
||||||
return new ClassPathResource(idpCert.substring("classpath:".length()));
|
return new ClassPathResource(idpCert.substring("classpath:".length()));
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -21,6 +21,11 @@ public class ConvertToImageRequest extends PDFFile {
|
|||||||
allowableValues = {"single", "multiple"})
|
allowableValues = {"single", "multiple"})
|
||||||
private String singleOrMultiple;
|
private String singleOrMultiple;
|
||||||
|
|
||||||
|
@Schema(
|
||||||
|
description =
|
||||||
|
"The pages to select, Supports ranges (e.g., '1,3,5-9'), or 'all' or functions in the format 'an+b' where 'a' is the multiplier of the page number 'n', and 'b' is a constant (e.g., '2n+1', '3n', '6n-5')\"")
|
||||||
|
private String pageNumbers;
|
||||||
|
|
||||||
@Schema(
|
@Schema(
|
||||||
description = "The color type of the output image(s)",
|
description = "The color type of the output image(s)",
|
||||||
allowableValues = {"color", "greyscale", "blackwhite"})
|
allowableValues = {"color", "greyscale", "blackwhite"})
|
||||||
|
|||||||
@@ -13,14 +13,19 @@ import org.springframework.stereotype.Service;
|
|||||||
import org.thymeleaf.util.StringUtils;
|
import org.thymeleaf.util.StringUtils;
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import stirling.software.SPDF.config.InstallationPathConfig;
|
||||||
import stirling.software.SPDF.model.SignatureFile;
|
import stirling.software.SPDF.model.SignatureFile;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class SignatureService {
|
public class SignatureService {
|
||||||
|
|
||||||
private static final String SIGNATURE_BASE_PATH = "customFiles/signatures/";
|
private final String SIGNATURE_BASE_PATH;
|
||||||
private static final String ALL_USERS_FOLDER = "ALL_USERS";
|
private final String ALL_USERS_FOLDER = "ALL_USERS";
|
||||||
|
|
||||||
|
public SignatureService() {
|
||||||
|
SIGNATURE_BASE_PATH = InstallationPathConfig.getSignaturesPath();
|
||||||
|
}
|
||||||
|
|
||||||
public boolean hasAccessToFile(String username, String fileName) throws IOException {
|
public boolean hasAccessToFile(String username, String fileName) throws IOException {
|
||||||
validateFileName(fileName);
|
validateFileName(fileName);
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import org.springframework.scheduling.annotation.Scheduled;
|
|||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import stirling.software.SPDF.config.InstallationPathConfig;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@@ -34,9 +35,7 @@ public class FileMonitor {
|
|||||||
* monitored, false otherwise
|
* monitored, false otherwise
|
||||||
*/
|
*/
|
||||||
@Autowired
|
@Autowired
|
||||||
public FileMonitor(
|
public FileMonitor(@Qualifier("directoryFilter") Predicate<Path> pathFilter)
|
||||||
@Qualifier("watchedFoldersDir") String rootDirectory,
|
|
||||||
@Qualifier("directoryFilter") Predicate<Path> pathFilter)
|
|
||||||
throws IOException {
|
throws IOException {
|
||||||
this.newlyDiscoveredFiles = new HashSet<>();
|
this.newlyDiscoveredFiles = new HashSet<>();
|
||||||
this.path2KeyMapping = new HashMap<>();
|
this.path2KeyMapping = new HashMap<>();
|
||||||
@@ -44,7 +43,7 @@ public class FileMonitor {
|
|||||||
this.pathFilter = pathFilter;
|
this.pathFilter = pathFilter;
|
||||||
this.readyForProcessingFiles = ConcurrentHashMap.newKeySet();
|
this.readyForProcessingFiles = ConcurrentHashMap.newKeySet();
|
||||||
this.watchService = FileSystems.getDefault().newWatchService();
|
this.watchService = FileSystems.getDefault().newWatchService();
|
||||||
this.rootDir = Path.of(rootDirectory);
|
this.rootDir = Path.of(InstallationPathConfig.getPipelineWatchedFoldersDir());
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean shouldNotProcess(Path path) {
|
private boolean shouldNotProcess(Path path) {
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import io.github.pixee.security.HostValidator;
|
|||||||
import io.github.pixee.security.Urls;
|
import io.github.pixee.security.Urls;
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import stirling.software.SPDF.config.InstallationPathConfig;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class GeneralUtils {
|
public class GeneralUtils {
|
||||||
@@ -83,7 +84,7 @@ public class GeneralUtils {
|
|||||||
|
|
||||||
// Allow only http and https protocols
|
// Allow only http and https protocols
|
||||||
String protocol = url.getProtocol();
|
String protocol = url.getProtocol();
|
||||||
if (!protocol.equals("http") && !protocol.equals("https")) {
|
if (!"http".equals(protocol) && !"https".equals(protocol)) {
|
||||||
return false; // Disallow other protocols
|
return false; // Disallow other protocols
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -220,32 +221,54 @@ public class GeneralUtils {
|
|||||||
throw new IllegalArgumentException("Invalid expression");
|
throw new IllegalArgumentException("Invalid expression");
|
||||||
}
|
}
|
||||||
|
|
||||||
int n = 0;
|
for (int n = 1; n <= maxValue; n++) {
|
||||||
while (true) {
|
|
||||||
// Replace 'n' with the current value of n, correctly handling numbers before
|
// Replace 'n' with the current value of n, correctly handling numbers before
|
||||||
// 'n'
|
// 'n'
|
||||||
String sanitizedExpression = insertMultiplicationBeforeN(expression, n);
|
String sanitizedExpression = sanitizeNFunction(expression, n);
|
||||||
Double result = evaluator.evaluate(sanitizedExpression);
|
Double result = evaluator.evaluate(sanitizedExpression);
|
||||||
|
|
||||||
// Check if the result is null or not within bounds
|
// Check if the result is null or not within bounds
|
||||||
if (result == null || result <= 0 || result.intValue() > maxValue) {
|
if (result == null) break;
|
||||||
if (n != 0) break;
|
|
||||||
} else {
|
if (result.intValue() > 0 && result.intValue() <= maxValue)
|
||||||
results.add(result.intValue());
|
results.add(result.intValue());
|
||||||
}
|
|
||||||
n++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String sanitizeNFunction(String expression, int nValue) {
|
||||||
|
String sanitizedExpression = expression.replace(" ", "");
|
||||||
|
String multiplyByOpeningRoundBracketPattern =
|
||||||
|
"([0-9n)])\\("; // example: n(n-1), 9(n-1), (n-1)(n-2)
|
||||||
|
sanitizedExpression =
|
||||||
|
sanitizedExpression.replaceAll(multiplyByOpeningRoundBracketPattern, "$1*(");
|
||||||
|
|
||||||
|
String multiplyByClosingRoundBracketPattern =
|
||||||
|
"\\)([0-9n)])"; // example: (n-1)n, (n-1)9, (n-1)(n-2)
|
||||||
|
sanitizedExpression =
|
||||||
|
sanitizedExpression.replaceAll(multiplyByClosingRoundBracketPattern, ")*$1");
|
||||||
|
|
||||||
|
sanitizedExpression = insertMultiplicationBeforeN(sanitizedExpression, nValue);
|
||||||
|
return sanitizedExpression;
|
||||||
|
}
|
||||||
|
|
||||||
private static String insertMultiplicationBeforeN(String expression, int nValue) {
|
private static String insertMultiplicationBeforeN(String expression, int nValue) {
|
||||||
// Insert multiplication between a number and 'n' (e.g., "4n" becomes "4*n")
|
// Insert multiplication between a number and 'n' (e.g., "4n" becomes "4*n")
|
||||||
String withMultiplication = expression.replaceAll("(\\d)n", "$1*n");
|
String withMultiplication = expression.replaceAll("(\\d)n", "$1*n");
|
||||||
|
withMultiplication = formatConsecutiveNsForNFunction(withMultiplication);
|
||||||
// Now replace 'n' with its current value
|
// Now replace 'n' with its current value
|
||||||
return withMultiplication.replace("n", String.valueOf(nValue));
|
return withMultiplication.replace("n", String.valueOf(nValue));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String formatConsecutiveNsForNFunction(String expression) {
|
||||||
|
String text = expression;
|
||||||
|
while (text.matches(".*n{2,}.*")) {
|
||||||
|
text = text.replaceAll("(?<!n)n{2}", "n*n");
|
||||||
|
}
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
private static List<Integer> handlePart(String part, int totalPages, int offset) {
|
private static List<Integer> handlePart(String part, int totalPages, int offset) {
|
||||||
List<Integer> partResult = new ArrayList<>();
|
List<Integer> partResult = new ArrayList<>();
|
||||||
|
|
||||||
@@ -320,7 +343,10 @@ public class GeneralUtils {
|
|||||||
|
|
||||||
public static void saveKeyToConfig(String id, String key, boolean autoGenerated)
|
public static void saveKeyToConfig(String id, String key, boolean autoGenerated)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
Path path = Paths.get("configs", "settings.yml"); // Target the configs/settings.yml
|
Path path =
|
||||||
|
Paths.get(
|
||||||
|
InstallationPathConfig
|
||||||
|
.getSettingsPath()); // Target the configs/settings.yml
|
||||||
|
|
||||||
final YamlFile settingsYml = new YamlFile(path.toFile());
|
final YamlFile settingsYml = new YamlFile(path.toFile());
|
||||||
DumperOptions yamlOptionssettingsYml =
|
DumperOptions yamlOptionssettingsYml =
|
||||||
@@ -338,7 +364,7 @@ public class GeneralUtils {
|
|||||||
|
|
||||||
public static void saveKeyToConfig(String id, boolean key, boolean autoGenerated)
|
public static void saveKeyToConfig(String id, boolean key, boolean autoGenerated)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
Path path = Paths.get("configs", "settings.yml");
|
Path path = Paths.get(InstallationPathConfig.getSettingsPath());
|
||||||
|
|
||||||
final YamlFile settingsYml = new YamlFile(path.toFile());
|
final YamlFile settingsYml = new YamlFile(path.toFile());
|
||||||
DumperOptions yamlOptionssettingsYml =
|
DumperOptions yamlOptionssettingsYml =
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
package stirling.software.SPDF.utils;
|
|
||||||
|
|
||||||
public class PDFManipulationUtils {}
|
|
||||||
@@ -9,6 +9,7 @@ import java.nio.file.Path;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
import java.util.zip.ZipOutputStream;
|
import java.util.zip.ZipOutputStream;
|
||||||
|
|
||||||
@@ -64,7 +65,7 @@ public class PDFToFile {
|
|||||||
.runCommandWithOutputHandling(command, tempOutputDir.toFile());
|
.runCommandWithOutputHandling(command, tempOutputDir.toFile());
|
||||||
|
|
||||||
// Get output files
|
// Get output files
|
||||||
List<File> outputFiles = Arrays.asList(tempOutputDir.toFile().listFiles());
|
File[] outputFiles = Objects.requireNonNull(tempOutputDir.toFile().listFiles());
|
||||||
|
|
||||||
// Return output files in a ZIP archive
|
// Return output files in a ZIP archive
|
||||||
fileName = pdfBaseName + "ToHtml.zip";
|
fileName = pdfBaseName + "ToHtml.zip";
|
||||||
|
|||||||
@@ -22,9 +22,11 @@ import org.apache.pdfbox.text.TextPosition;
|
|||||||
import org.springframework.core.io.InputStreamResource;
|
import org.springframework.core.io.InputStreamResource;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import stirling.software.SPDF.model.api.misc.HighContrastColorCombination;
|
import stirling.software.SPDF.model.api.misc.HighContrastColorCombination;
|
||||||
import stirling.software.SPDF.model.api.misc.ReplaceAndInvert;
|
import stirling.software.SPDF.model.api.misc.ReplaceAndInvert;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
public class CustomColorReplaceStrategy extends ReplaceAndInvertColorStrategy {
|
public class CustomColorReplaceStrategy extends ReplaceAndInvertColorStrategy {
|
||||||
|
|
||||||
private String textColor;
|
private String textColor;
|
||||||
@@ -93,17 +95,17 @@ public class CustomColorReplaceStrategy extends ReplaceAndInvertColorStrategy {
|
|||||||
try {
|
try {
|
||||||
font = PDFontFactory.createFont(text.getFont().getCOSObject());
|
font = PDFontFactory.createFont(text.getFont().getCOSObject());
|
||||||
} catch (IOException io) {
|
} catch (IOException io) {
|
||||||
System.out.println("Primary font not found, using fallback font.");
|
log.info("Primary font not found, using fallback font.");
|
||||||
font = new PDType1Font(Standard14Fonts.FontName.HELVETICA);
|
font = new PDType1Font(Standard14Fonts.FontName.HELVETICA);
|
||||||
}
|
}
|
||||||
// if a character is not supported by font, then look for supported font
|
// if a character is not supported by font, then look for supported font
|
||||||
try {
|
try {
|
||||||
byte[] bytes = font.encode(unicodeText);
|
byte[] bytes = font.encode(unicodeText);
|
||||||
} catch (IOException io) {
|
} catch (IOException io) {
|
||||||
System.out.println("text could not be encoded ");
|
log.info("text could not be encoded ");
|
||||||
font = checkSupportedFontForCharacter(unicodeText);
|
font = checkSupportedFontForCharacter(unicodeText);
|
||||||
} catch (IllegalArgumentException ie) {
|
} catch (IllegalArgumentException ie) {
|
||||||
System.out.println("text not supported by font ");
|
log.info("text not supported by font ");
|
||||||
font = checkSupportedFontForCharacter(unicodeText);
|
font = checkSupportedFontForCharacter(unicodeText);
|
||||||
} finally {
|
} finally {
|
||||||
// if any other font is not supported, then replace default character *
|
// if any other font is not supported, then replace default character *
|
||||||
@@ -157,9 +159,9 @@ public class CustomColorReplaceStrategy extends ReplaceAndInvertColorStrategy {
|
|||||||
byte[] bytes = currentFont.encode(unicodeText);
|
byte[] bytes = currentFont.encode(unicodeText);
|
||||||
return currentFont;
|
return currentFont;
|
||||||
} catch (IOException io) {
|
} catch (IOException io) {
|
||||||
System.out.println("text could not be encoded ");
|
log.info("text could not be encoded ");
|
||||||
} catch (IllegalArgumentException ie) {
|
} catch (IllegalArgumentException ie) {
|
||||||
System.out.println("text not supported by font ");
|
log.info("text not supported by font ");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -27,8 +27,7 @@ spring.devtools.restart.exclude=stirling.software.SPDF.config.security/**
|
|||||||
spring.thymeleaf.encoding=UTF-8
|
spring.thymeleaf.encoding=UTF-8
|
||||||
spring.web.resources.mime-mappings.webmanifest=application/manifest+json
|
spring.web.resources.mime-mappings.webmanifest=application/manifest+json
|
||||||
spring.mvc.async.request-timeout=${SYSTEM_CONNECTIONTIMEOUTMILLISECONDS:1200000}
|
spring.mvc.async.request-timeout=${SYSTEM_CONNECTIONTIMEOUTMILLISECONDS:1200000}
|
||||||
#spring.thymeleaf.prefix=file:/customFiles/templates/,classpath:/templates/
|
|
||||||
#spring.thymeleaf.cache=false
|
|
||||||
spring.datasource.url=jdbc:h2:file:./configs/stirling-pdf-DB-2.3.232;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
|
spring.datasource.url=jdbc:h2:file:./configs/stirling-pdf-DB-2.3.232;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
|
||||||
spring.datasource.driver-class-name=org.h2.Driver
|
spring.datasource.driver-class-name=org.h2.Driver
|
||||||
spring.datasource.username=sa
|
spring.datasource.username=sa
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<configuration>
|
<configuration>
|
||||||
|
<define name="LOG_PATH" class="stirling.software.SPDF.config.LogbackPropertyLoader" />
|
||||||
|
|
||||||
<!-- Console Appender -->
|
<!-- Console Appender -->
|
||||||
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
|
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
|
||||||
@@ -7,35 +9,30 @@
|
|||||||
</encoder>
|
</encoder>
|
||||||
</appender>
|
</appender>
|
||||||
|
|
||||||
<!-- Rolling File Appender -->
|
<!-- Rolling File Appender for Auth Logs -->
|
||||||
<appender name="AUTHLOG" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
<appender name="AUTHLOG" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||||
<file>logs/invalid-auths.log</file>
|
<file>${LOG_PATH}/invalid-auths.log</file>
|
||||||
<encoder>
|
<encoder>
|
||||||
<pattern>%d %p %c{1} [%thread] %m%n</pattern>
|
<pattern>%d %p %c{1} [%thread] %m%n</pattern>
|
||||||
</encoder>
|
</encoder>
|
||||||
|
|
||||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||||
<!-- daily rollover and keep 7 days' worth of history -->
|
<fileNamePattern>${LOG_PATH}/auth-%d{yyyy-MM-dd}.log</fileNamePattern>
|
||||||
<fileNamePattern>logs/auth-%d{yyyy-MM-dd}.log</fileNamePattern>
|
|
||||||
<maxHistory>1</maxHistory>
|
<maxHistory>1</maxHistory>
|
||||||
</rollingPolicy>
|
</rollingPolicy>
|
||||||
</appender>
|
</appender>
|
||||||
|
|
||||||
<!-- Rolling File Appender -->
|
<!-- Rolling File Appender for General Logs -->
|
||||||
<appender name="GENERAL" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
<appender name="GENERAL" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||||
<file>logs/info.log</file>
|
<file>${LOG_PATH}/info.log</file>
|
||||||
<encoder>
|
<encoder>
|
||||||
<pattern>%d %p %c{1} [%thread] %m%n</pattern>
|
<pattern>%d %p %c{1} [%thread] %m%n</pattern>
|
||||||
</encoder>
|
</encoder>
|
||||||
|
|
||||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||||
<!-- daily rollover and keep 7 days' worth of history -->
|
<fileNamePattern>${LOG_PATH}/info-%d{yyyy-MM-dd}.log</fileNamePattern>
|
||||||
<fileNamePattern>logs/info-%d{yyyy-MM-dd}.log</fileNamePattern>
|
|
||||||
<maxHistory>1</maxHistory>
|
<maxHistory>1</maxHistory>
|
||||||
</rollingPolicy>
|
</rollingPolicy>
|
||||||
</appender>
|
</appender>
|
||||||
|
|
||||||
|
|
||||||
<!-- Root Logger -->
|
<!-- Root Logger -->
|
||||||
<root level="INFO">
|
<root level="INFO">
|
||||||
<appender-ref ref="CONSOLE"/>
|
<appender-ref ref="CONSOLE"/>
|
||||||
@@ -43,10 +40,9 @@
|
|||||||
</root>
|
</root>
|
||||||
|
|
||||||
<!-- Specific Logger -->
|
<!-- Specific Logger -->
|
||||||
<logger name="stirling.software.SPDF.config.security.CustomAuthenticationFailureHandler" level="ERROR"
|
<logger name="stirling.software.SPDF.config.security.CustomAuthenticationFailureHandler"
|
||||||
additivity="false">
|
level="ERROR" additivity="false">
|
||||||
<appender-ref ref="CONSOLE"/>
|
<appender-ref ref="CONSOLE"/>
|
||||||
<appender-ref ref="AUTHLOG"/>
|
<appender-ref ref="AUTHLOG"/>
|
||||||
</logger>
|
</logger>
|
||||||
|
</configuration>
|
||||||
</configuration>
|
|
||||||
@@ -561,6 +561,7 @@ login.oauth2invalidRequest=طلب غير صالح
|
|||||||
login.oauth2AccessDenied=تم رفض الوصول
|
login.oauth2AccessDenied=تم رفض الوصول
|
||||||
login.oauth2InvalidTokenResponse=استجابة الرمز المميز غير صالحة
|
login.oauth2InvalidTokenResponse=استجابة الرمز المميز غير صالحة
|
||||||
login.oauth2InvalidIdToken=رمز الهوية غير صالح
|
login.oauth2InvalidIdToken=رمز الهوية غير صالح
|
||||||
|
login.relyingPartyRegistrationNotFound=No relying party registration found
|
||||||
login.userIsDisabled=تم تعطيل المستخدم، تم حظر تسجيل الدخول حاليًا باستخدام اسم المستخدم هذا. يرجى الاتصال بالمسؤول.
|
login.userIsDisabled=تم تعطيل المستخدم، تم حظر تسجيل الدخول حاليًا باستخدام اسم المستخدم هذا. يرجى الاتصال بالمسؤول.
|
||||||
login.alreadyLoggedIn=لقد تسجل دخولًا إلى
|
login.alreadyLoggedIn=لقد تسجل دخولًا إلى
|
||||||
login.alreadyLoggedIn2=أجهزة أخرى. يرجى تسجيل الخروج من الأجهزة وحاول مرة أخرى.
|
login.alreadyLoggedIn2=أجهزة أخرى. يرجى تسجيل الخروج من الأجهزة وحاول مرة أخرى.
|
||||||
@@ -830,7 +831,7 @@ sign.first=First page
|
|||||||
sign.last=Last page
|
sign.last=Last page
|
||||||
sign.next=Next page
|
sign.next=Next page
|
||||||
sign.previous=Previous page
|
sign.previous=Previous page
|
||||||
|
sign.maintainRatio=Toggle maintain aspect ratio
|
||||||
#repair
|
#repair
|
||||||
repair.title=إصلاح
|
repair.title=إصلاح
|
||||||
repair.header=إصلاح ملفات PDF
|
repair.header=إصلاح ملفات PDF
|
||||||
@@ -1041,6 +1042,7 @@ pdfToImage.grey=تدرج الرمادي
|
|||||||
pdfToImage.blackwhite=أبيض وأسود (قد يفقد البيانات!)
|
pdfToImage.blackwhite=أبيض وأسود (قد يفقد البيانات!)
|
||||||
pdfToImage.submit=تحويل
|
pdfToImage.submit=تحويل
|
||||||
pdfToImage.info=Python غير مثبت. مطلوب لتحويل WebP.
|
pdfToImage.info=Python غير مثبت. مطلوب لتحويل WebP.
|
||||||
|
pdfToImage.placeholder=(مثال: 1,2,8 أو 4,7,12-16 أو 2n-1)
|
||||||
|
|
||||||
|
|
||||||
#addPassword
|
#addPassword
|
||||||
@@ -1284,6 +1286,8 @@ splitByChapters.submit=تقطيع ملف PDF
|
|||||||
fileChooser.click=انقر هنا
|
fileChooser.click=انقر هنا
|
||||||
fileChooser.or=أو
|
fileChooser.or=أو
|
||||||
fileChooser.dragAndDrop=قم بسحب الملفات وإفلاتها
|
fileChooser.dragAndDrop=قم بسحب الملفات وإفلاتها
|
||||||
|
fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||||
|
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||||
fileChooser.hoveredDragAndDrop=قم بسحب المفات وإفلاتها هنا
|
fileChooser.hoveredDragAndDrop=قم بسحب المفات وإفلاتها هنا
|
||||||
|
|
||||||
#release notes
|
#release notes
|
||||||
|
|||||||
@@ -561,6 +561,7 @@ login.oauth2invalidRequest=Etibarsız Sorğu
|
|||||||
login.oauth2AccessDenied=Giriş rədd edildi
|
login.oauth2AccessDenied=Giriş rədd edildi
|
||||||
login.oauth2InvalidTokenResponse=Etibarsız Token Cavabı
|
login.oauth2InvalidTokenResponse=Etibarsız Token Cavabı
|
||||||
login.oauth2InvalidIdToken=Etibarsız Id Token
|
login.oauth2InvalidIdToken=Etibarsız Id Token
|
||||||
|
login.relyingPartyRegistrationNotFound=No relying party registration found
|
||||||
login.userIsDisabled=İstifadəçi deaktivləşdirilmişdir, bu istifadəçi adı ilə giriş hal-hazırda bloklanmışdır. Zəhmət olmasa, administratorla əlaqə saxlayın.
|
login.userIsDisabled=İstifadəçi deaktivləşdirilmişdir, bu istifadəçi adı ilə giriş hal-hazırda bloklanmışdır. Zəhmət olmasa, administratorla əlaqə saxlayın.
|
||||||
login.alreadyLoggedIn=Siz artıq daxil olmusunuz
|
login.alreadyLoggedIn=Siz artıq daxil olmusunuz
|
||||||
login.alreadyLoggedIn2=cihazlar. Zəhmət olmasa, cihazlardan çıxış edin və yenidən cəhd edin.
|
login.alreadyLoggedIn2=cihazlar. Zəhmət olmasa, cihazlardan çıxış edin və yenidən cəhd edin.
|
||||||
@@ -830,7 +831,7 @@ sign.first=İlk səhifə
|
|||||||
sign.last=Son səhifə
|
sign.last=Son səhifə
|
||||||
sign.next=Növbəti səhifə
|
sign.next=Növbəti səhifə
|
||||||
sign.previous=Əvvəlki səhifə
|
sign.previous=Əvvəlki səhifə
|
||||||
|
sign.maintainRatio=Toggle maintain aspect ratio
|
||||||
#repair
|
#repair
|
||||||
repair.title=Bərpa Et
|
repair.title=Bərpa Et
|
||||||
repair.header=PDFləri Bərpa Et
|
repair.header=PDFləri Bərpa Et
|
||||||
@@ -1041,6 +1042,7 @@ pdfToImage.grey=Boz Tonlama
|
|||||||
pdfToImage.blackwhite=Qara və Ağ (Data İtə Bilər)
|
pdfToImage.blackwhite=Qara və Ağ (Data İtə Bilər)
|
||||||
pdfToImage.submit=Çevir
|
pdfToImage.submit=Çevir
|
||||||
pdfToImage.info=Python Yüklü Deyil.WebP Çevirməsi Üçün Vacibdir
|
pdfToImage.info=Python Yüklü Deyil.WebP Çevirməsi Üçün Vacibdir
|
||||||
|
pdfToImage.placeholder=(məsələn, 1,2,8 və ya 4,7,12-16 və ya 2n-1)
|
||||||
|
|
||||||
|
|
||||||
#addPassword
|
#addPassword
|
||||||
@@ -1284,6 +1286,8 @@ splitByChapters.submit=PDF-i Ayır
|
|||||||
fileChooser.click=Click
|
fileChooser.click=Click
|
||||||
fileChooser.or=or
|
fileChooser.or=or
|
||||||
fileChooser.dragAndDrop=Drag & Drop
|
fileChooser.dragAndDrop=Drag & Drop
|
||||||
|
fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||||
|
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||||
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
||||||
|
|
||||||
#release notes
|
#release notes
|
||||||
|
|||||||
@@ -561,6 +561,7 @@ login.oauth2invalidRequest=Невалидна заявка
|
|||||||
login.oauth2AccessDenied=Отказан достъп
|
login.oauth2AccessDenied=Отказан достъп
|
||||||
login.oauth2InvalidTokenResponse=Невалиден отговор на токена
|
login.oauth2InvalidTokenResponse=Невалиден отговор на токена
|
||||||
login.oauth2InvalidIdToken=Невалиден токен за идентификатор
|
login.oauth2InvalidIdToken=Невалиден токен за идентификатор
|
||||||
|
login.relyingPartyRegistrationNotFound=No relying party registration found
|
||||||
login.userIsDisabled=Потребителят е деактивиран, влизането в момента е блокирано с това потребителско име. Моля, свържете се с администратора.
|
login.userIsDisabled=Потребителят е деактивиран, влизането в момента е блокирано с това потребителско име. Моля, свържете се с администратора.
|
||||||
login.alreadyLoggedIn=Вече сте влезли в
|
login.alreadyLoggedIn=Вече сте влезли в
|
||||||
login.alreadyLoggedIn2=устройства. Моля, излезте от устройствата и опитайте отново.
|
login.alreadyLoggedIn2=устройства. Моля, излезте от устройствата и опитайте отново.
|
||||||
@@ -830,7 +831,7 @@ sign.first=First page
|
|||||||
sign.last=Last page
|
sign.last=Last page
|
||||||
sign.next=Next page
|
sign.next=Next page
|
||||||
sign.previous=Previous page
|
sign.previous=Previous page
|
||||||
|
sign.maintainRatio=Toggle maintain aspect ratio
|
||||||
#repair
|
#repair
|
||||||
repair.title=Поправи
|
repair.title=Поправи
|
||||||
repair.header=Поправи PDF-и
|
repair.header=Поправи PDF-и
|
||||||
@@ -1041,6 +1042,7 @@ pdfToImage.grey=Скала на сивото
|
|||||||
pdfToImage.blackwhite=Черно и бяло (може да загубите данни!)
|
pdfToImage.blackwhite=Черно и бяло (може да загубите данни!)
|
||||||
pdfToImage.submit=Преобразуване
|
pdfToImage.submit=Преобразуване
|
||||||
pdfToImage.info=Python не е инсталиран. Изисква се за конвертиране на WebP.
|
pdfToImage.info=Python не е инсталиран. Изисква се за конвертиране на WebP.
|
||||||
|
pdfToImage.placeholder=(e.g. 1,2,8 or 4,7,12-16 or 2n-1)
|
||||||
|
|
||||||
|
|
||||||
#addPassword
|
#addPassword
|
||||||
@@ -1284,6 +1286,8 @@ splitByChapters.submit=Разделяне на PDF
|
|||||||
fileChooser.click=Click
|
fileChooser.click=Click
|
||||||
fileChooser.or=or
|
fileChooser.or=or
|
||||||
fileChooser.dragAndDrop=Drag & Drop
|
fileChooser.dragAndDrop=Drag & Drop
|
||||||
|
fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||||
|
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||||
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
||||||
|
|
||||||
#release notes
|
#release notes
|
||||||
|
|||||||
@@ -561,6 +561,7 @@ login.oauth2invalidRequest=Sol·licitud no vàlida
|
|||||||
login.oauth2AccessDenied=Accés denegat
|
login.oauth2AccessDenied=Accés denegat
|
||||||
login.oauth2InvalidTokenResponse=Resposta de token no vàlida
|
login.oauth2InvalidTokenResponse=Resposta de token no vàlida
|
||||||
login.oauth2InvalidIdToken=ID Token no vàlid
|
login.oauth2InvalidIdToken=ID Token no vàlid
|
||||||
|
login.relyingPartyRegistrationNotFound=No relying party registration found
|
||||||
login.userIsDisabled=L'usuari està desactivat, l'inici de sessió està actualment bloquejat amb aquest nom d'usuari. Si us plau, contacta amb l'administrador.
|
login.userIsDisabled=L'usuari està desactivat, l'inici de sessió està actualment bloquejat amb aquest nom d'usuari. Si us plau, contacta amb l'administrador.
|
||||||
login.alreadyLoggedIn=Ja has iniciat sessió a
|
login.alreadyLoggedIn=Ja has iniciat sessió a
|
||||||
login.alreadyLoggedIn2=dispositius. Si us plau, tanca la sessió en els dispositius i torna-ho a intentar.
|
login.alreadyLoggedIn2=dispositius. Si us plau, tanca la sessió en els dispositius i torna-ho a intentar.
|
||||||
@@ -830,7 +831,7 @@ sign.first=First page
|
|||||||
sign.last=Last page
|
sign.last=Last page
|
||||||
sign.next=Next page
|
sign.next=Next page
|
||||||
sign.previous=Previous page
|
sign.previous=Previous page
|
||||||
|
sign.maintainRatio=Toggle maintain aspect ratio
|
||||||
#repair
|
#repair
|
||||||
repair.title=Reparar
|
repair.title=Reparar
|
||||||
repair.header=Repara els PDF
|
repair.header=Repara els PDF
|
||||||
@@ -1041,6 +1042,7 @@ pdfToImage.grey=Escala de Grisos
|
|||||||
pdfToImage.blackwhite=Blanc i Negre (Pot perdre dades!)
|
pdfToImage.blackwhite=Blanc i Negre (Pot perdre dades!)
|
||||||
pdfToImage.submit=Converteix
|
pdfToImage.submit=Converteix
|
||||||
pdfToImage.info=Python no està instal·lat. És necessari per a la conversió a WebP.
|
pdfToImage.info=Python no està instal·lat. És necessari per a la conversió a WebP.
|
||||||
|
pdfToImage.placeholder=(p. ex. 1,2,8 o 4,7,12-16 o 2n-1)
|
||||||
|
|
||||||
|
|
||||||
#addPassword
|
#addPassword
|
||||||
@@ -1284,6 +1286,8 @@ splitByChapters.submit=Divideix PDF
|
|||||||
fileChooser.click=Click
|
fileChooser.click=Click
|
||||||
fileChooser.or=or
|
fileChooser.or=or
|
||||||
fileChooser.dragAndDrop=Drag & Drop
|
fileChooser.dragAndDrop=Drag & Drop
|
||||||
|
fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||||
|
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||||
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
||||||
|
|
||||||
#release notes
|
#release notes
|
||||||
|
|||||||
@@ -561,6 +561,7 @@ login.oauth2invalidRequest=Neplatný požadavek
|
|||||||
login.oauth2AccessDenied=Přístup zazobán
|
login.oauth2AccessDenied=Přístup zazobán
|
||||||
login.oauth2InvalidTokenResponse=Neplatná odpověď tokenu
|
login.oauth2InvalidTokenResponse=Neplatná odpověď tokenu
|
||||||
login.oauth2InvalidIdToken=Neplatný identifikační token
|
login.oauth2InvalidIdToken=Neplatný identifikační token
|
||||||
|
login.relyingPartyRegistrationNotFound=No relying party registration found
|
||||||
login.userIsDisabled=Uživatel je deaktivován, přihlášení aktuálně pro tuto uživatelskou jmena je zakázáno. Kontaktujte správce.
|
login.userIsDisabled=Uživatel je deaktivován, přihlášení aktuálně pro tuto uživatelskou jmena je zakázáno. Kontaktujte správce.
|
||||||
login.alreadyLoggedIn=Jste již přihlášeni na
|
login.alreadyLoggedIn=Jste již přihlášeni na
|
||||||
login.alreadyLoggedIn2=zariadení. Odhlašujte se z těchto zařízení a zkuste to znovu.
|
login.alreadyLoggedIn2=zariadení. Odhlašujte se z těchto zařízení a zkuste to znovu.
|
||||||
@@ -830,7 +831,7 @@ sign.first=First page
|
|||||||
sign.last=Last page
|
sign.last=Last page
|
||||||
sign.next=Next page
|
sign.next=Next page
|
||||||
sign.previous=Previous page
|
sign.previous=Previous page
|
||||||
|
sign.maintainRatio=Toggle maintain aspect ratio
|
||||||
#repair
|
#repair
|
||||||
repair.title=Opravit
|
repair.title=Opravit
|
||||||
repair.header=Opravit PDF
|
repair.header=Opravit PDF
|
||||||
@@ -1041,6 +1042,7 @@ pdfToImage.grey=Stupně šedi
|
|||||||
pdfToImage.blackwhite=Černobílý (Může dojít k ztrátě dat!)
|
pdfToImage.blackwhite=Černobílý (Může dojít k ztrátě dat!)
|
||||||
pdfToImage.submit=Převést
|
pdfToImage.submit=Převést
|
||||||
pdfToImage.info=Python není nainstalován. Potřebuje se pro konverzi do WebP.
|
pdfToImage.info=Python není nainstalován. Potřebuje se pro konverzi do WebP.
|
||||||
|
pdfToImage.placeholder=(např. 1,2,8 nebo 4,7,12-16 nebo 2n-1)
|
||||||
|
|
||||||
|
|
||||||
#addPassword
|
#addPassword
|
||||||
@@ -1284,6 +1286,8 @@ splitByChapters.submit=Podělit se PDF
|
|||||||
fileChooser.click=Click
|
fileChooser.click=Click
|
||||||
fileChooser.or=or
|
fileChooser.or=or
|
||||||
fileChooser.dragAndDrop=Drag & Drop
|
fileChooser.dragAndDrop=Drag & Drop
|
||||||
|
fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||||
|
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||||
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
||||||
|
|
||||||
#release notes
|
#release notes
|
||||||
|
|||||||
@@ -561,6 +561,7 @@ login.oauth2invalidRequest=Ugyldig Anmodning
|
|||||||
login.oauth2AccessDenied=Adgang Nægtet
|
login.oauth2AccessDenied=Adgang Nægtet
|
||||||
login.oauth2InvalidTokenResponse=Ugyldigt Token Svar
|
login.oauth2InvalidTokenResponse=Ugyldigt Token Svar
|
||||||
login.oauth2InvalidIdToken=Ugyldigt Id Token
|
login.oauth2InvalidIdToken=Ugyldigt Id Token
|
||||||
|
login.relyingPartyRegistrationNotFound=No relying party registration found
|
||||||
login.userIsDisabled=Bruger er deaktiveret, login er i øjeblikket blokeret med dette brugernavn. Kontakt venligst administratoren.
|
login.userIsDisabled=Bruger er deaktiveret, login er i øjeblikket blokeret med dette brugernavn. Kontakt venligst administratoren.
|
||||||
login.alreadyLoggedIn=Du er allerede logget ind på
|
login.alreadyLoggedIn=Du er allerede logget ind på
|
||||||
login.alreadyLoggedIn2=enheder. Log ud af disse enheder og prøv igen.
|
login.alreadyLoggedIn2=enheder. Log ud af disse enheder og prøv igen.
|
||||||
@@ -830,7 +831,7 @@ sign.first=First page
|
|||||||
sign.last=Last page
|
sign.last=Last page
|
||||||
sign.next=Next page
|
sign.next=Next page
|
||||||
sign.previous=Previous page
|
sign.previous=Previous page
|
||||||
|
sign.maintainRatio=Toggle maintain aspect ratio
|
||||||
#repair
|
#repair
|
||||||
repair.title=Reparér
|
repair.title=Reparér
|
||||||
repair.header=Reparér PDF'er
|
repair.header=Reparér PDF'er
|
||||||
@@ -1041,6 +1042,7 @@ pdfToImage.grey=Gråtone
|
|||||||
pdfToImage.blackwhite=Sort og Hvid (Kan miste data!)
|
pdfToImage.blackwhite=Sort og Hvid (Kan miste data!)
|
||||||
pdfToImage.submit=Konvertér
|
pdfToImage.submit=Konvertér
|
||||||
pdfToImage.info=Python er ikke installeret. Påkrævet for WebP-konvertering.
|
pdfToImage.info=Python er ikke installeret. Påkrævet for WebP-konvertering.
|
||||||
|
pdfToImage.placeholder=(f.eks. 1,2,8 eller 4,7,12-16 eller 2n-1)
|
||||||
|
|
||||||
|
|
||||||
#addPassword
|
#addPassword
|
||||||
@@ -1284,6 +1286,8 @@ splitByChapters.submit=Splitter PDF
|
|||||||
fileChooser.click=Click
|
fileChooser.click=Click
|
||||||
fileChooser.or=or
|
fileChooser.or=or
|
||||||
fileChooser.dragAndDrop=Drag & Drop
|
fileChooser.dragAndDrop=Drag & Drop
|
||||||
|
fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||||
|
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||||
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
||||||
|
|
||||||
#release notes
|
#release notes
|
||||||
|
|||||||
@@ -561,6 +561,7 @@ login.oauth2invalidRequest=ungültige Anfrage
|
|||||||
login.oauth2AccessDenied=Zugriff abgelehnt
|
login.oauth2AccessDenied=Zugriff abgelehnt
|
||||||
login.oauth2InvalidTokenResponse=Ungültige Token-Antwort
|
login.oauth2InvalidTokenResponse=Ungültige Token-Antwort
|
||||||
login.oauth2InvalidIdToken=Ungültiges ID-Token
|
login.oauth2InvalidIdToken=Ungültiges ID-Token
|
||||||
|
login.relyingPartyRegistrationNotFound=Keine Relying-Party-Registrierung gefunden
|
||||||
login.userIsDisabled=Benutzer ist deaktiviert, die Anmeldung ist mit diesem Benutzernamen derzeit gesperrt. Bitte wenden Sie sich an den Administrator.
|
login.userIsDisabled=Benutzer ist deaktiviert, die Anmeldung ist mit diesem Benutzernamen derzeit gesperrt. Bitte wenden Sie sich an den Administrator.
|
||||||
login.alreadyLoggedIn=Sie sind bereits an
|
login.alreadyLoggedIn=Sie sind bereits an
|
||||||
login.alreadyLoggedIn2=Geräten angemeldet. Bitte melden Sie sich dort ab und versuchen es dann erneut.
|
login.alreadyLoggedIn2=Geräten angemeldet. Bitte melden Sie sich dort ab und versuchen es dann erneut.
|
||||||
@@ -830,7 +831,7 @@ sign.first=Erste Seite
|
|||||||
sign.last=Letzte Seite
|
sign.last=Letzte Seite
|
||||||
sign.next=Nächste Seite
|
sign.next=Nächste Seite
|
||||||
sign.previous=Vorherige Seite
|
sign.previous=Vorherige Seite
|
||||||
|
sign.maintainRatio=Toggle maintain aspect ratio
|
||||||
#repair
|
#repair
|
||||||
repair.title=Reparieren
|
repair.title=Reparieren
|
||||||
repair.header=PDFs reparieren
|
repair.header=PDFs reparieren
|
||||||
@@ -1041,6 +1042,7 @@ pdfToImage.grey=Graustufen
|
|||||||
pdfToImage.blackwhite=Schwarzweiß (Datenverlust möglich!)
|
pdfToImage.blackwhite=Schwarzweiß (Datenverlust möglich!)
|
||||||
pdfToImage.submit=Umwandeln
|
pdfToImage.submit=Umwandeln
|
||||||
pdfToImage.info=Python ist nicht installiert. Erforderlich für die WebP-Konvertierung.
|
pdfToImage.info=Python ist nicht installiert. Erforderlich für die WebP-Konvertierung.
|
||||||
|
pdfToImage.placeholder=(z.B. 1,2,8 oder 4,7,12-16 oder 2n-1)
|
||||||
|
|
||||||
|
|
||||||
#addPassword
|
#addPassword
|
||||||
@@ -1284,6 +1286,8 @@ splitByChapters.submit=PDF teilen
|
|||||||
fileChooser.click=Klicken
|
fileChooser.click=Klicken
|
||||||
fileChooser.or=oder
|
fileChooser.or=oder
|
||||||
fileChooser.dragAndDrop=Drag & Drop
|
fileChooser.dragAndDrop=Drag & Drop
|
||||||
|
fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||||
|
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||||
fileChooser.hoveredDragAndDrop=Datei(en) hierhin Ziehen & Fallenlassen
|
fileChooser.hoveredDragAndDrop=Datei(en) hierhin Ziehen & Fallenlassen
|
||||||
|
|
||||||
#release notes
|
#release notes
|
||||||
|
|||||||
@@ -561,6 +561,7 @@ login.oauth2invalidRequest=Αρχαίως απαιτήσεις πληροφορ
|
|||||||
login.oauth2AccessDenied=Πρόσβαση ορισμένες τοποθετήσεις
|
login.oauth2AccessDenied=Πρόσβαση ορισμένες τοποθετήσεις
|
||||||
login.oauth2InvalidTokenResponse=Απάντηση άρκετο ρόταμα
|
login.oauth2InvalidTokenResponse=Απάντηση άρκετο ρόταμα
|
||||||
login.oauth2InvalidIdToken=Λιδός όρκου μη αρκετός
|
login.oauth2InvalidIdToken=Λιδός όρκου μη αρκετός
|
||||||
|
login.relyingPartyRegistrationNotFound=No relying party registration found
|
||||||
login.userIsDisabled=Ο χρήστης είναι δευτεροβαθμιακά διακωμένος, το σύστημα άλλαξε τον καθώς βρεθεί. Παρακαλείστε τους αρχηγούς να επιβεβαιώσουν τη συμπεριφορά χρήστη.
|
login.userIsDisabled=Ο χρήστης είναι δευτεροβαθμιακά διακωμένος, το σύστημα άλλαξε τον καθώς βρεθεί. Παρακαλείστε τους αρχηγούς να επιβεβαιώσουν τη συμπεριφορά χρήστη.
|
||||||
login.alreadyLoggedIn=Είστε ήδη σύνδεδες σε
|
login.alreadyLoggedIn=Είστε ήδη σύνδεδες σε
|
||||||
login.alreadyLoggedIn2=κατοχόι. Παρακαλώ δυσκέντρωση τους και προσπαθήστε ξανά.
|
login.alreadyLoggedIn2=κατοχόι. Παρακαλώ δυσκέντρωση τους και προσπαθήστε ξανά.
|
||||||
@@ -830,7 +831,7 @@ sign.first=First page
|
|||||||
sign.last=Last page
|
sign.last=Last page
|
||||||
sign.next=Next page
|
sign.next=Next page
|
||||||
sign.previous=Previous page
|
sign.previous=Previous page
|
||||||
|
sign.maintainRatio=Toggle maintain aspect ratio
|
||||||
#repair
|
#repair
|
||||||
repair.title=Επιδιόρθωση
|
repair.title=Επιδιόρθωση
|
||||||
repair.header=Επιδιόρθωση PDFs
|
repair.header=Επιδιόρθωση PDFs
|
||||||
@@ -1041,6 +1042,7 @@ pdfToImage.grey=Κλίμακα του γκρι
|
|||||||
pdfToImage.blackwhite=Ασπρόμαυρο (Μπορεί να χαθούν δεδομένα!)
|
pdfToImage.blackwhite=Ασπρόμαυρο (Μπορεί να χαθούν δεδομένα!)
|
||||||
pdfToImage.submit=Μετατροπή
|
pdfToImage.submit=Μετατροπή
|
||||||
pdfToImage.info=Δεν είναι ιστάμενος Python. Είναι απαιτήτων για τη μετατροπή σε WebP.
|
pdfToImage.info=Δεν είναι ιστάμενος Python. Είναι απαιτήτων για τη μετατροπή σε WebP.
|
||||||
|
pdfToImage.placeholder=(π.χ. 1,2,8 ή 4,7,12-16 ή 2n-1)
|
||||||
|
|
||||||
|
|
||||||
#addPassword
|
#addPassword
|
||||||
@@ -1284,6 +1286,8 @@ splitByChapters.submit=Διαλύστε το PDF
|
|||||||
fileChooser.click=Click
|
fileChooser.click=Click
|
||||||
fileChooser.or=or
|
fileChooser.or=or
|
||||||
fileChooser.dragAndDrop=Drag & Drop
|
fileChooser.dragAndDrop=Drag & Drop
|
||||||
|
fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||||
|
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||||
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
||||||
|
|
||||||
#release notes
|
#release notes
|
||||||
|
|||||||
@@ -561,6 +561,7 @@ login.oauth2invalidRequest=Invalid Request
|
|||||||
login.oauth2AccessDenied=Access Denied
|
login.oauth2AccessDenied=Access Denied
|
||||||
login.oauth2InvalidTokenResponse=Invalid Token Response
|
login.oauth2InvalidTokenResponse=Invalid Token Response
|
||||||
login.oauth2InvalidIdToken=Invalid Id Token
|
login.oauth2InvalidIdToken=Invalid Id Token
|
||||||
|
login.relyingPartyRegistrationNotFound=No relying party registration found
|
||||||
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
|
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
|
||||||
login.alreadyLoggedIn=You are already logged in to
|
login.alreadyLoggedIn=You are already logged in to
|
||||||
login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
|
login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
|
||||||
@@ -830,7 +831,7 @@ sign.first=First page
|
|||||||
sign.last=Last page
|
sign.last=Last page
|
||||||
sign.next=Next page
|
sign.next=Next page
|
||||||
sign.previous=Previous page
|
sign.previous=Previous page
|
||||||
|
sign.maintainRatio=Toggle maintain aspect ratio
|
||||||
#repair
|
#repair
|
||||||
repair.title=Repair
|
repair.title=Repair
|
||||||
repair.header=Repair PDFs
|
repair.header=Repair PDFs
|
||||||
@@ -1041,6 +1042,7 @@ pdfToImage.grey=Greyscale
|
|||||||
pdfToImage.blackwhite=Black and White (May lose data!)
|
pdfToImage.blackwhite=Black and White (May lose data!)
|
||||||
pdfToImage.submit=Convert
|
pdfToImage.submit=Convert
|
||||||
pdfToImage.info=Python is not installed. Required for WebP conversion.
|
pdfToImage.info=Python is not installed. Required for WebP conversion.
|
||||||
|
pdfToImage.placeholder=(e.g. 1,2,8 or 4,7,12-16 or 2n-1)
|
||||||
|
|
||||||
|
|
||||||
#addPassword
|
#addPassword
|
||||||
@@ -1284,6 +1286,8 @@ splitByChapters.submit=Split PDF
|
|||||||
fileChooser.click=Click
|
fileChooser.click=Click
|
||||||
fileChooser.or=or
|
fileChooser.or=or
|
||||||
fileChooser.dragAndDrop=Drag & Drop
|
fileChooser.dragAndDrop=Drag & Drop
|
||||||
|
fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||||
|
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||||
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
||||||
|
|
||||||
#release notes
|
#release notes
|
||||||
|
|||||||
@@ -561,6 +561,7 @@ login.oauth2invalidRequest=Invalid Request
|
|||||||
login.oauth2AccessDenied=Access Denied
|
login.oauth2AccessDenied=Access Denied
|
||||||
login.oauth2InvalidTokenResponse=Invalid Token Response
|
login.oauth2InvalidTokenResponse=Invalid Token Response
|
||||||
login.oauth2InvalidIdToken=Invalid Id Token
|
login.oauth2InvalidIdToken=Invalid Id Token
|
||||||
|
login.relyingPartyRegistrationNotFound=No relying party registration found
|
||||||
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
|
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
|
||||||
login.alreadyLoggedIn=You are already logged in to
|
login.alreadyLoggedIn=You are already logged in to
|
||||||
login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
|
login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
|
||||||
@@ -830,7 +831,7 @@ sign.first=First page
|
|||||||
sign.last=Last page
|
sign.last=Last page
|
||||||
sign.next=Next page
|
sign.next=Next page
|
||||||
sign.previous=Previous page
|
sign.previous=Previous page
|
||||||
|
sign.maintainRatio=Toggle maintain aspect ratio
|
||||||
#repair
|
#repair
|
||||||
repair.title=Repair
|
repair.title=Repair
|
||||||
repair.header=Repair PDFs
|
repair.header=Repair PDFs
|
||||||
@@ -1041,6 +1042,7 @@ pdfToImage.grey=Grayscale
|
|||||||
pdfToImage.blackwhite=Black and White (May lose data!)
|
pdfToImage.blackwhite=Black and White (May lose data!)
|
||||||
pdfToImage.submit=Convert
|
pdfToImage.submit=Convert
|
||||||
pdfToImage.info=Python is not installed. Required for WebP conversion.
|
pdfToImage.info=Python is not installed. Required for WebP conversion.
|
||||||
|
pdfToImage.placeholder=(e.g. 1,2,8 or 4,7,12-16 or 2n-1)
|
||||||
|
|
||||||
|
|
||||||
#addPassword
|
#addPassword
|
||||||
@@ -1284,6 +1286,8 @@ splitByChapters.submit=Split PDF
|
|||||||
fileChooser.click=Click
|
fileChooser.click=Click
|
||||||
fileChooser.or=or
|
fileChooser.or=or
|
||||||
fileChooser.dragAndDrop=Drag & Drop
|
fileChooser.dragAndDrop=Drag & Drop
|
||||||
|
fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||||
|
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||||
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
||||||
|
|
||||||
#release notes
|
#release notes
|
||||||
|
|||||||
@@ -561,6 +561,7 @@ login.oauth2invalidRequest=Solicitud no válida
|
|||||||
login.oauth2AccessDenied=Acceso denegado
|
login.oauth2AccessDenied=Acceso denegado
|
||||||
login.oauth2InvalidTokenResponse=Respuesta de token no válida
|
login.oauth2InvalidTokenResponse=Respuesta de token no válida
|
||||||
login.oauth2InvalidIdToken=Token de identificación no válido
|
login.oauth2InvalidIdToken=Token de identificación no válido
|
||||||
|
login.relyingPartyRegistrationNotFound=No relying party registration found
|
||||||
login.userIsDisabled=El usuario está desactivado, actualmente el acceso está bloqueado para ese nombre de usuario. Por favor, póngase en contacto con el administrador.
|
login.userIsDisabled=El usuario está desactivado, actualmente el acceso está bloqueado para ese nombre de usuario. Por favor, póngase en contacto con el administrador.
|
||||||
login.alreadyLoggedIn=Ya has iniciado sesión en
|
login.alreadyLoggedIn=Ya has iniciado sesión en
|
||||||
login.alreadyLoggedIn2=dispositivos. Cierra sesión en los dispositivos y vuelve a intentarlo.
|
login.alreadyLoggedIn2=dispositivos. Cierra sesión en los dispositivos y vuelve a intentarlo.
|
||||||
@@ -830,7 +831,7 @@ sign.first=First page
|
|||||||
sign.last=Last page
|
sign.last=Last page
|
||||||
sign.next=Next page
|
sign.next=Next page
|
||||||
sign.previous=Previous page
|
sign.previous=Previous page
|
||||||
|
sign.maintainRatio=Toggle maintain aspect ratio
|
||||||
#repair
|
#repair
|
||||||
repair.title=Reparar
|
repair.title=Reparar
|
||||||
repair.header=Reparar archivos PDF
|
repair.header=Reparar archivos PDF
|
||||||
@@ -1041,6 +1042,7 @@ pdfToImage.grey=Escala de grises
|
|||||||
pdfToImage.blackwhite=Blanco y Negro (¡Puede perder datos!)
|
pdfToImage.blackwhite=Blanco y Negro (¡Puede perder datos!)
|
||||||
pdfToImage.submit=Convertir
|
pdfToImage.submit=Convertir
|
||||||
pdfToImage.info=Python no está instalado. Se requiere para la conversión WebP.
|
pdfToImage.info=Python no está instalado. Se requiere para la conversión WebP.
|
||||||
|
pdfToImage.placeholder=(e.g. 1,2,8 or 4,7,12-16 or 2n-1)
|
||||||
|
|
||||||
|
|
||||||
#addPassword
|
#addPassword
|
||||||
@@ -1284,6 +1286,8 @@ splitByChapters.submit=Dividir PDF
|
|||||||
fileChooser.click=Click
|
fileChooser.click=Click
|
||||||
fileChooser.or=or
|
fileChooser.or=or
|
||||||
fileChooser.dragAndDrop=Drag & Drop
|
fileChooser.dragAndDrop=Drag & Drop
|
||||||
|
fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||||
|
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||||
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
||||||
|
|
||||||
#release notes
|
#release notes
|
||||||
|
|||||||
@@ -561,6 +561,7 @@ login.oauth2invalidRequest=Invalid Request
|
|||||||
login.oauth2AccessDenied=Access Denied
|
login.oauth2AccessDenied=Access Denied
|
||||||
login.oauth2InvalidTokenResponse=Invalid Token Response
|
login.oauth2InvalidTokenResponse=Invalid Token Response
|
||||||
login.oauth2InvalidIdToken=Invalid Id Token
|
login.oauth2InvalidIdToken=Invalid Id Token
|
||||||
|
login.relyingPartyRegistrationNotFound=No relying party registration found
|
||||||
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
|
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
|
||||||
login.alreadyLoggedIn=You are already logged in to
|
login.alreadyLoggedIn=You are already logged in to
|
||||||
login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
|
login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
|
||||||
@@ -830,7 +831,7 @@ sign.first=First page
|
|||||||
sign.last=Last page
|
sign.last=Last page
|
||||||
sign.next=Next page
|
sign.next=Next page
|
||||||
sign.previous=Previous page
|
sign.previous=Previous page
|
||||||
|
sign.maintainRatio=Toggle maintain aspect ratio
|
||||||
#repair
|
#repair
|
||||||
repair.title=Konpondu
|
repair.title=Konpondu
|
||||||
repair.header=Konpondu PDF fitxategiak
|
repair.header=Konpondu PDF fitxategiak
|
||||||
@@ -1041,6 +1042,7 @@ pdfToImage.grey=Gris-eskala
|
|||||||
pdfToImage.blackwhite=Zuria eta Beltza (Datuak galdu ditzake!)
|
pdfToImage.blackwhite=Zuria eta Beltza (Datuak galdu ditzake!)
|
||||||
pdfToImage.submit=Bihurtu
|
pdfToImage.submit=Bihurtu
|
||||||
pdfToImage.info=Python is not installed. Required for WebP conversion.
|
pdfToImage.info=Python is not installed. Required for WebP conversion.
|
||||||
|
pdfToImage.placeholder=(e.g. 1,2,8 or 4,7,12-16 or 2n-1)
|
||||||
|
|
||||||
|
|
||||||
#addPassword
|
#addPassword
|
||||||
@@ -1284,6 +1286,8 @@ splitByChapters.submit=Split PDF
|
|||||||
fileChooser.click=Click
|
fileChooser.click=Click
|
||||||
fileChooser.or=or
|
fileChooser.or=or
|
||||||
fileChooser.dragAndDrop=Drag & Drop
|
fileChooser.dragAndDrop=Drag & Drop
|
||||||
|
fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||||
|
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||||
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
||||||
|
|
||||||
#release notes
|
#release notes
|
||||||
|
|||||||
@@ -561,6 +561,7 @@ login.oauth2invalidRequest=درخواست نامعتبر
|
|||||||
login.oauth2AccessDenied=دسترسی ممنوع
|
login.oauth2AccessDenied=دسترسی ممنوع
|
||||||
login.oauth2InvalidTokenResponse=پاسخ توکن نامعتبر است
|
login.oauth2InvalidTokenResponse=پاسخ توکن نامعتبر است
|
||||||
login.oauth2InvalidIdToken=توکن شناسه نامعتبر است
|
login.oauth2InvalidIdToken=توکن شناسه نامعتبر است
|
||||||
|
login.relyingPartyRegistrationNotFound=No relying party registration found
|
||||||
login.userIsDisabled=کاربر غیرفعال شده است، ورود با این نام کاربری در حال حاضر مسدود است. لطفاً با مدیر تماس بگیرید.
|
login.userIsDisabled=کاربر غیرفعال شده است، ورود با این نام کاربری در حال حاضر مسدود است. لطفاً با مدیر تماس بگیرید.
|
||||||
login.alreadyLoggedIn=شما قبلاً وارد شدهاید در
|
login.alreadyLoggedIn=شما قبلاً وارد شدهاید در
|
||||||
login.alreadyLoggedIn2=دستگاهها. لطفاً از دستگاهها خارج شده و دوباره تلاش کنید.
|
login.alreadyLoggedIn2=دستگاهها. لطفاً از دستگاهها خارج شده و دوباره تلاش کنید.
|
||||||
@@ -830,7 +831,7 @@ sign.first=صفحه اول
|
|||||||
sign.last=صفحه آخر
|
sign.last=صفحه آخر
|
||||||
sign.next=صفحه بعدی
|
sign.next=صفحه بعدی
|
||||||
sign.previous=صفحه قبلی
|
sign.previous=صفحه قبلی
|
||||||
|
sign.maintainRatio=Toggle maintain aspect ratio
|
||||||
#repair
|
#repair
|
||||||
repair.title=تعمیر
|
repair.title=تعمیر
|
||||||
repair.header=تعمیر PDFها
|
repair.header=تعمیر PDFها
|
||||||
@@ -1041,6 +1042,7 @@ pdfToImage.grey=خاکستری
|
|||||||
pdfToImage.blackwhite=سیاه و سفید (ممکن است اطلاعات از دست برود!)
|
pdfToImage.blackwhite=سیاه و سفید (ممکن است اطلاعات از دست برود!)
|
||||||
pdfToImage.submit=تبدیل
|
pdfToImage.submit=تبدیل
|
||||||
pdfToImage.info=پایتون نصب نشده است. برای تبدیل WebP لازم است.
|
pdfToImage.info=پایتون نصب نشده است. برای تبدیل WebP لازم است.
|
||||||
|
pdfToImage.placeholder=(مثال: 1,2,8 یا 4,7,12-16 یا 2n-1)
|
||||||
|
|
||||||
|
|
||||||
#addPassword
|
#addPassword
|
||||||
@@ -1284,6 +1286,8 @@ splitByChapters.submit=تقسیم PDF
|
|||||||
fileChooser.click=کلیک کنید
|
fileChooser.click=کلیک کنید
|
||||||
fileChooser.or=یا
|
fileChooser.or=یا
|
||||||
fileChooser.dragAndDrop=بکشید و رها کنید
|
fileChooser.dragAndDrop=بکشید و رها کنید
|
||||||
|
fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||||
|
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||||
fileChooser.hoveredDragAndDrop=فایل(های) خود را اینجا بکشید و رها کنید
|
fileChooser.hoveredDragAndDrop=فایل(های) خود را اینجا بکشید و رها کنید
|
||||||
|
|
||||||
#release notes
|
#release notes
|
||||||
|
|||||||
@@ -561,6 +561,7 @@ login.oauth2invalidRequest=Requête invalide
|
|||||||
login.oauth2AccessDenied=Accès refusé
|
login.oauth2AccessDenied=Accès refusé
|
||||||
login.oauth2InvalidTokenResponse=Réponse contenant le jeton est invalide
|
login.oauth2InvalidTokenResponse=Réponse contenant le jeton est invalide
|
||||||
login.oauth2InvalidIdToken=Jeton d'identification invalide
|
login.oauth2InvalidIdToken=Jeton d'identification invalide
|
||||||
|
login.relyingPartyRegistrationNotFound=No relying party registration found
|
||||||
login.userIsDisabled=L'utilisateur est désactivé, la connexion est actuellement bloquée avec ce nom d'utilisateur. Veuillez contacter l'administrateur.
|
login.userIsDisabled=L'utilisateur est désactivé, la connexion est actuellement bloquée avec ce nom d'utilisateur. Veuillez contacter l'administrateur.
|
||||||
login.alreadyLoggedIn=Vous êtes déjà connecté sur
|
login.alreadyLoggedIn=Vous êtes déjà connecté sur
|
||||||
login.alreadyLoggedIn2=appareils. Veuillez vous déconnecter des appareils et réessayer.
|
login.alreadyLoggedIn2=appareils. Veuillez vous déconnecter des appareils et réessayer.
|
||||||
@@ -830,7 +831,7 @@ sign.first=First page
|
|||||||
sign.last=Last page
|
sign.last=Last page
|
||||||
sign.next=Next page
|
sign.next=Next page
|
||||||
sign.previous=Previous page
|
sign.previous=Previous page
|
||||||
|
sign.maintainRatio=Toggle maintain aspect ratio
|
||||||
#repair
|
#repair
|
||||||
repair.title=Réparer
|
repair.title=Réparer
|
||||||
repair.header=Réparer
|
repair.header=Réparer
|
||||||
@@ -1041,6 +1042,7 @@ pdfToImage.grey=Niveaux de gris
|
|||||||
pdfToImage.blackwhite=Noir et blanc (peut engendrer une perte de données !)
|
pdfToImage.blackwhite=Noir et blanc (peut engendrer une perte de données !)
|
||||||
pdfToImage.submit=Convertir
|
pdfToImage.submit=Convertir
|
||||||
pdfToImage.info=Python n’est pas installé. Nécessaire pour la conversion WebP.
|
pdfToImage.info=Python n’est pas installé. Nécessaire pour la conversion WebP.
|
||||||
|
pdfToImage.placeholder=(par exemple : 1,2,8 ou 4,7,12-16 ou 2n-1)
|
||||||
|
|
||||||
|
|
||||||
#addPassword
|
#addPassword
|
||||||
@@ -1281,49 +1283,51 @@ splitByChapters.desc.4=Autoriser les Doublons : Si coché, permet à plusieurs s
|
|||||||
splitByChapters.submit=Diviser le PDF
|
splitByChapters.submit=Diviser le PDF
|
||||||
|
|
||||||
#File Chooser
|
#File Chooser
|
||||||
fileChooser.click=Click
|
fileChooser.click=Cliquez
|
||||||
fileChooser.or=or
|
fileChooser.or=ou
|
||||||
fileChooser.dragAndDrop=Drag & Drop
|
fileChooser.dragAndDrop=Glisser & Déposer
|
||||||
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||||
|
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||||
|
fileChooser.hoveredDragAndDrop=Glisser & Déposer le(s) fichier(s) ici
|
||||||
|
|
||||||
#release notes
|
#release notes
|
||||||
releases.footer=Releases
|
releases.footer=Versions
|
||||||
releases.title=Release Notes
|
releases.title=Notes de version
|
||||||
releases.header=Release Notes
|
releases.header=Notes de version
|
||||||
releases.current.version=Current Release
|
releases.current.version=Version actuelle
|
||||||
releases.note=Release notes are only available in English
|
releases.note=Les notes de version sont uniquement disponibles en anglais
|
||||||
|
|
||||||
#Validate Signature
|
#Validate Signature
|
||||||
validateSignature.title=Validate PDF Signatures
|
validateSignature.title=Valider les signatures PDF
|
||||||
validateSignature.header=Validate Digital Signatures
|
validateSignature.header=Valider les signatures numériques
|
||||||
validateSignature.selectPDF=Select signed PDF file
|
validateSignature.selectPDF=Sélectionnez un fichier PDF signé
|
||||||
validateSignature.submit=Validate Signatures
|
validateSignature.submit=Valider les signatures
|
||||||
validateSignature.results=Validation Results
|
validateSignature.results=Résultats de la validation
|
||||||
validateSignature.status=Status
|
validateSignature.status=Statut
|
||||||
validateSignature.signer=Signer
|
validateSignature.signer=Signataire
|
||||||
validateSignature.date=Date
|
validateSignature.date=Date
|
||||||
validateSignature.reason=Reason
|
validateSignature.reason=Raison
|
||||||
validateSignature.location=Location
|
validateSignature.location=Localisation
|
||||||
validateSignature.noSignatures=No digital signatures found in this document
|
validateSignature.noSignatures=Aucune signature numérique trouvée dans ce document
|
||||||
validateSignature.status.valid=Valid
|
validateSignature.status.valid=Valide
|
||||||
validateSignature.status.invalid=Invalid
|
validateSignature.status.invalid=Invalide
|
||||||
validateSignature.chain.invalid=Certificate chain validation failed - cannot verify signer's identity
|
validateSignature.chain.invalid=La validation de la chaîne de certificats a échoué - impossible de vérifier l'identité du signataire
|
||||||
validateSignature.trust.invalid=Certificate not in trust store - source cannot be verified
|
validateSignature.trust.invalid=Le certificat n'est pas dans le magasin de confiance - la source ne peut pas être vérifiée
|
||||||
validateSignature.cert.expired=Certificate has expired
|
validateSignature.cert.expired=Le certificat a expiré
|
||||||
validateSignature.cert.revoked=Certificate has been revoked
|
validateSignature.cert.revoked=Le certificat a été révoqué
|
||||||
validateSignature.signature.info=Signature Information
|
validateSignature.signature.info=Informations sur la signature
|
||||||
validateSignature.signature=Signature
|
validateSignature.signature=Signature
|
||||||
validateSignature.signature.mathValid=Signature is mathematically valid BUT:
|
validateSignature.signature.mathValid=La signature est mathématiquement valide MAIS :
|
||||||
validateSignature.selectCustomCert=Custom Certificate File X.509 (Optional)
|
validateSignature.selectCustomCert=Fichier de certificat personnalisé X.509 (Optionnel)
|
||||||
validateSignature.cert.info=Certificate Details
|
validateSignature.cert.info=Détails du certificat
|
||||||
validateSignature.cert.issuer=Issuer
|
validateSignature.cert.issuer=Émetteur
|
||||||
validateSignature.cert.subject=Subject
|
validateSignature.cert.subject=Sujet
|
||||||
validateSignature.cert.serialNumber=Serial Number
|
validateSignature.cert.serialNumber=Numéro de série
|
||||||
validateSignature.cert.validFrom=Valid From
|
validateSignature.cert.validFrom=Valide à partir du
|
||||||
validateSignature.cert.validUntil=Valid Until
|
validateSignature.cert.validUntil=Valide jusqu'au
|
||||||
validateSignature.cert.algorithm=Algorithm
|
validateSignature.cert.algorithm=Algorithme
|
||||||
validateSignature.cert.keySize=Key Size
|
validateSignature.cert.keySize=Taille de la clé
|
||||||
validateSignature.cert.version=Version
|
validateSignature.cert.version=Version
|
||||||
validateSignature.cert.keyUsage=Key Usage
|
validateSignature.cert.keyUsage=Usage de la clé
|
||||||
validateSignature.cert.selfSigned=Self-Signed
|
validateSignature.cert.selfSigned=Auto-signé
|
||||||
validateSignature.cert.bits=bits
|
validateSignature.cert.bits=bits
|
||||||
|
|||||||
@@ -561,6 +561,7 @@ login.oauth2invalidRequest=Iarratas Neamhbhailí
|
|||||||
login.oauth2AccessDenied=Rochtain Diúltaithe
|
login.oauth2AccessDenied=Rochtain Diúltaithe
|
||||||
login.oauth2InvalidTokenResponse=Freagra Comhartha Neamhbhailí
|
login.oauth2InvalidTokenResponse=Freagra Comhartha Neamhbhailí
|
||||||
login.oauth2InvalidIdToken=Comhartha Aitheantais Neamhbhailí
|
login.oauth2InvalidIdToken=Comhartha Aitheantais Neamhbhailí
|
||||||
|
login.relyingPartyRegistrationNotFound=No relying party registration found
|
||||||
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
|
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
|
||||||
login.alreadyLoggedIn=You are already logged in to
|
login.alreadyLoggedIn=You are already logged in to
|
||||||
login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
|
login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
|
||||||
@@ -830,7 +831,7 @@ sign.first=First page
|
|||||||
sign.last=Last page
|
sign.last=Last page
|
||||||
sign.next=Next page
|
sign.next=Next page
|
||||||
sign.previous=Previous page
|
sign.previous=Previous page
|
||||||
|
sign.maintainRatio=Toggle maintain aspect ratio
|
||||||
#repair
|
#repair
|
||||||
repair.title=Deisiúchán
|
repair.title=Deisiúchán
|
||||||
repair.header=PDF a dheisiú
|
repair.header=PDF a dheisiú
|
||||||
@@ -1041,6 +1042,7 @@ pdfToImage.grey=Scála Liath
|
|||||||
pdfToImage.blackwhite=Dubh agus Bán (D’fhéadfadh sonraí a chailleadh!)
|
pdfToImage.blackwhite=Dubh agus Bán (D’fhéadfadh sonraí a chailleadh!)
|
||||||
pdfToImage.submit=Tiontaigh
|
pdfToImage.submit=Tiontaigh
|
||||||
pdfToImage.info=Python is not installed. Required for WebP conversion.
|
pdfToImage.info=Python is not installed. Required for WebP conversion.
|
||||||
|
pdfToImage.placeholder=(m.sh. 1,2,8 nó 4,7,12-16 nó 2n-1)
|
||||||
|
|
||||||
|
|
||||||
#addPassword
|
#addPassword
|
||||||
@@ -1284,6 +1286,8 @@ splitByChapters.submit=Split PDF
|
|||||||
fileChooser.click=Click
|
fileChooser.click=Click
|
||||||
fileChooser.or=or
|
fileChooser.or=or
|
||||||
fileChooser.dragAndDrop=Drag & Drop
|
fileChooser.dragAndDrop=Drag & Drop
|
||||||
|
fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||||
|
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||||
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
||||||
|
|
||||||
#release notes
|
#release notes
|
||||||
|
|||||||
@@ -561,6 +561,7 @@ login.oauth2invalidRequest=गलत याचना
|
|||||||
login.oauth2AccessDenied=इनपुट उम्मीदवार डिसकार
|
login.oauth2AccessDenied=इनपुट उम्मीदवार डिसकार
|
||||||
login.oauth2InvalidTokenResponse=अमान्तरित सिक्वेंस जवाब कैसे है
|
login.oauth2InvalidTokenResponse=अमान्तरित सिक्वेंस जवाब कैसे है
|
||||||
login.oauth2InvalidIdToken=गलत इड टोकन
|
login.oauth2InvalidIdToken=गलत इड टोकन
|
||||||
|
login.relyingPartyRegistrationNotFound=No relying party registration found
|
||||||
login.userIsDisabled=उपयोगकर्ता डिसबाल, यह वर्षा सभी उपयोगकर्ता जूझे वर्षाकरण बारा मिल गई है। कृपया संबवादक से संपर्क करें.
|
login.userIsDisabled=उपयोगकर्ता डिसबाल, यह वर्षा सभी उपयोगकर्ता जूझे वर्षाकरण बारा मिल गई है। कृपया संबवादक से संपर्क करें.
|
||||||
login.alreadyLoggedIn=आप पहले से ही
|
login.alreadyLoggedIn=आप पहले से ही
|
||||||
login.alreadyLoggedIn2=सुनिश्चित करने वाले डिवाइस्स पर लॉग-इन हैं। कृपया इन डिवाइस से लॉगआउट करें और पुनः प्रयास करें
|
login.alreadyLoggedIn2=सुनिश्चित करने वाले डिवाइस्स पर लॉग-इन हैं। कृपया इन डिवाइस से लॉगआउट करें और पुनः प्रयास करें
|
||||||
@@ -830,7 +831,7 @@ sign.first=First page
|
|||||||
sign.last=Last page
|
sign.last=Last page
|
||||||
sign.next=Next page
|
sign.next=Next page
|
||||||
sign.previous=Previous page
|
sign.previous=Previous page
|
||||||
|
sign.maintainRatio=Toggle maintain aspect ratio
|
||||||
#repair
|
#repair
|
||||||
repair.title=मरम्मत
|
repair.title=मरम्मत
|
||||||
repair.header=पीडीएफ़ मरम्मत करें
|
repair.header=पीडीएफ़ मरम्मत करें
|
||||||
@@ -1041,6 +1042,7 @@ pdfToImage.grey=ग्रे स्केल
|
|||||||
pdfToImage.blackwhite=काला और सफेद (डेटा खो सकता है!)
|
pdfToImage.blackwhite=काला और सफेद (डेटा खो सकता है!)
|
||||||
pdfToImage.submit=परिवर्तित करें
|
pdfToImage.submit=परिवर्तित करें
|
||||||
pdfToImage.info=पायथन नहीं अनिस्तारित है। वेबP परिवर्तन के लिए आवश्यक है।
|
pdfToImage.info=पायथन नहीं अनिस्तारित है। वेबP परिवर्तन के लिए आवश्यक है।
|
||||||
|
pdfToImage.placeholder=(उदाहरण के लिए 1,2,8 या 4,7,12-16 या 2n-1)
|
||||||
|
|
||||||
|
|
||||||
#addPassword
|
#addPassword
|
||||||
@@ -1284,6 +1286,8 @@ splitByChapters.submit=PDF विभाजित
|
|||||||
fileChooser.click=Click
|
fileChooser.click=Click
|
||||||
fileChooser.or=or
|
fileChooser.or=or
|
||||||
fileChooser.dragAndDrop=Drag & Drop
|
fileChooser.dragAndDrop=Drag & Drop
|
||||||
|
fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||||
|
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||||
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
||||||
|
|
||||||
#release notes
|
#release notes
|
||||||
|
|||||||
@@ -561,6 +561,7 @@ login.oauth2invalidRequest=Neispravan zahtjev
|
|||||||
login.oauth2AccessDenied=Pristup odbijen
|
login.oauth2AccessDenied=Pristup odbijen
|
||||||
login.oauth2InvalidTokenResponse=Nevažeći odgovor tokena
|
login.oauth2InvalidTokenResponse=Nevažeći odgovor tokena
|
||||||
login.oauth2InvalidIdToken=Nevažeći ID token
|
login.oauth2InvalidIdToken=Nevažeći ID token
|
||||||
|
login.relyingPartyRegistrationNotFound=No relying party registration found
|
||||||
login.userIsDisabled=Korisnik je deaktiviran, prijava sa ovim korisničkim imenom je trenutno zakazana. Molimo Vas da kontaktirate administratorske osobe.
|
login.userIsDisabled=Korisnik je deaktiviran, prijava sa ovim korisničkim imenom je trenutno zakazana. Molimo Vas da kontaktirate administratorske osobe.
|
||||||
login.alreadyLoggedIn=Već ste se prijavili na
|
login.alreadyLoggedIn=Već ste se prijavili na
|
||||||
login.alreadyLoggedIn2=ure. Odjavite se s ure i pokušajte ponovo.
|
login.alreadyLoggedIn2=ure. Odjavite se s ure i pokušajte ponovo.
|
||||||
@@ -830,7 +831,7 @@ sign.first=First page
|
|||||||
sign.last=Last page
|
sign.last=Last page
|
||||||
sign.next=Next page
|
sign.next=Next page
|
||||||
sign.previous=Previous page
|
sign.previous=Previous page
|
||||||
|
sign.maintainRatio=Toggle maintain aspect ratio
|
||||||
#repair
|
#repair
|
||||||
repair.title=Popravi
|
repair.title=Popravi
|
||||||
repair.header=Popravi PDF datoteku
|
repair.header=Popravi PDF datoteku
|
||||||
@@ -1041,6 +1042,7 @@ pdfToImage.grey=Sivi tonovi
|
|||||||
pdfToImage.blackwhite=Crno-bijelo (mogu se izgubiti podaci!)
|
pdfToImage.blackwhite=Crno-bijelo (mogu se izgubiti podaci!)
|
||||||
pdfToImage.submit=Pretvori
|
pdfToImage.submit=Pretvori
|
||||||
pdfToImage.info=Python nije instaliran. Treba je za konverziju na WebP.
|
pdfToImage.info=Python nije instaliran. Treba je za konverziju na WebP.
|
||||||
|
pdfToImage.placeholder=(t.j. 1,2,8 ili 4,7,12-16 ili 2n-1)
|
||||||
|
|
||||||
|
|
||||||
#addPassword
|
#addPassword
|
||||||
@@ -1284,6 +1286,8 @@ splitByChapters.submit=Podijeli PDF
|
|||||||
fileChooser.click=Click
|
fileChooser.click=Click
|
||||||
fileChooser.or=or
|
fileChooser.or=or
|
||||||
fileChooser.dragAndDrop=Drag & Drop
|
fileChooser.dragAndDrop=Drag & Drop
|
||||||
|
fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||||
|
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||||
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
||||||
|
|
||||||
#release notes
|
#release notes
|
||||||
|
|||||||
@@ -561,6 +561,7 @@ login.oauth2invalidRequest=Érvénytelen kérelem
|
|||||||
login.oauth2AccessDenied=Hozzáférés megtagadva
|
login.oauth2AccessDenied=Hozzáférés megtagadva
|
||||||
login.oauth2InvalidTokenResponse=Érvénytelen token-válasz
|
login.oauth2InvalidTokenResponse=Érvénytelen token-válasz
|
||||||
login.oauth2InvalidIdToken=Érvénytelen azonosító token
|
login.oauth2InvalidIdToken=Érvénytelen azonosító token
|
||||||
|
login.relyingPartyRegistrationNotFound=No relying party registration found
|
||||||
login.userIsDisabled=A felhasználó deaktivált, a bejelentkezés jelenleg megszakítva ezzel a felhasználónévvel. Kérjen segítséget a rendszergazdától.
|
login.userIsDisabled=A felhasználó deaktivált, a bejelentkezés jelenleg megszakítva ezzel a felhasználónévvel. Kérjen segítséget a rendszergazdától.
|
||||||
login.alreadyLoggedIn=Már be van jelentkezve az
|
login.alreadyLoggedIn=Már be van jelentkezve az
|
||||||
login.alreadyLoggedIn2=eszközökre. Kijelentkezzen ezekből a eszközökből, majd próbálja újra bejelentkezni.
|
login.alreadyLoggedIn2=eszközökre. Kijelentkezzen ezekből a eszközökből, majd próbálja újra bejelentkezni.
|
||||||
@@ -830,7 +831,7 @@ sign.first=First page
|
|||||||
sign.last=Last page
|
sign.last=Last page
|
||||||
sign.next=Next page
|
sign.next=Next page
|
||||||
sign.previous=Previous page
|
sign.previous=Previous page
|
||||||
|
sign.maintainRatio=Toggle maintain aspect ratio
|
||||||
#repair
|
#repair
|
||||||
repair.title=Javítás
|
repair.title=Javítás
|
||||||
repair.header=PDF-ek javítása
|
repair.header=PDF-ek javítása
|
||||||
@@ -1041,6 +1042,7 @@ pdfToImage.grey=szürkeárnyalatos
|
|||||||
pdfToImage.blackwhite=fekete-fehér (adatvesztéssel járhat!)
|
pdfToImage.blackwhite=fekete-fehér (adatvesztéssel járhat!)
|
||||||
pdfToImage.submit=Átalakítás
|
pdfToImage.submit=Átalakítás
|
||||||
pdfToImage.info=Nincs telepítve a Python. Szükséges a WebP konverzióhoz.
|
pdfToImage.info=Nincs telepítve a Python. Szükséges a WebP konverzióhoz.
|
||||||
|
pdfToImage.placeholder=(pl. 1,2,8 vagy 4,7,12-16 vagy 2n-1)
|
||||||
|
|
||||||
|
|
||||||
#addPassword
|
#addPassword
|
||||||
@@ -1284,6 +1286,8 @@ splitByChapters.submit=PDF osztás
|
|||||||
fileChooser.click=Click
|
fileChooser.click=Click
|
||||||
fileChooser.or=or
|
fileChooser.or=or
|
||||||
fileChooser.dragAndDrop=Drag & Drop
|
fileChooser.dragAndDrop=Drag & Drop
|
||||||
|
fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||||
|
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||||
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
||||||
|
|
||||||
#release notes
|
#release notes
|
||||||
|
|||||||
@@ -561,6 +561,7 @@ login.oauth2invalidRequest=Permintaan Tidak Valid
|
|||||||
login.oauth2AccessDenied=Akses Ditolak
|
login.oauth2AccessDenied=Akses Ditolak
|
||||||
login.oauth2InvalidTokenResponse=Respons Token Tidak Valid
|
login.oauth2InvalidTokenResponse=Respons Token Tidak Valid
|
||||||
login.oauth2InvalidIdToken=Token ID Tidak Valid
|
login.oauth2InvalidIdToken=Token ID Tidak Valid
|
||||||
|
login.relyingPartyRegistrationNotFound=No relying party registration found
|
||||||
login.userIsDisabled=Pengguna dinonaktifkan, login saat ini diblokir dengan nama pengguna ini. Silakan hubungi administrator.
|
login.userIsDisabled=Pengguna dinonaktifkan, login saat ini diblokir dengan nama pengguna ini. Silakan hubungi administrator.
|
||||||
login.alreadyLoggedIn=Anda sudah login ke
|
login.alreadyLoggedIn=Anda sudah login ke
|
||||||
login.alreadyLoggedIn2=perangkat. Silakan keluar dari perangkat dan coba lagi.
|
login.alreadyLoggedIn2=perangkat. Silakan keluar dari perangkat dan coba lagi.
|
||||||
@@ -830,7 +831,7 @@ sign.first=First page
|
|||||||
sign.last=Last page
|
sign.last=Last page
|
||||||
sign.next=Next page
|
sign.next=Next page
|
||||||
sign.previous=Previous page
|
sign.previous=Previous page
|
||||||
|
sign.maintainRatio=Toggle maintain aspect ratio
|
||||||
#repair
|
#repair
|
||||||
repair.title=Perbaiki
|
repair.title=Perbaiki
|
||||||
repair.header=Perbaiki PDF
|
repair.header=Perbaiki PDF
|
||||||
@@ -1041,6 +1042,7 @@ pdfToImage.grey=Skala abu-abu
|
|||||||
pdfToImage.blackwhite=Black and White (Bisa kehilangan data!)
|
pdfToImage.blackwhite=Black and White (Bisa kehilangan data!)
|
||||||
pdfToImage.submit=Konversi
|
pdfToImage.submit=Konversi
|
||||||
pdfToImage.info=Python tidak terinstal. Diperlukan untuk konversi WebP.
|
pdfToImage.info=Python tidak terinstal. Diperlukan untuk konversi WebP.
|
||||||
|
pdfToImage.placeholder=(misalnya 1,2,8 atau 4,7,12-16 atau 2n-1)
|
||||||
|
|
||||||
|
|
||||||
#addPassword
|
#addPassword
|
||||||
@@ -1284,6 +1286,8 @@ splitByChapters.submit=Pecah PDF
|
|||||||
fileChooser.click=Click
|
fileChooser.click=Click
|
||||||
fileChooser.or=or
|
fileChooser.or=or
|
||||||
fileChooser.dragAndDrop=Drag & Drop
|
fileChooser.dragAndDrop=Drag & Drop
|
||||||
|
fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||||
|
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||||
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
||||||
|
|
||||||
#release notes
|
#release notes
|
||||||
|
|||||||
@@ -561,6 +561,7 @@ login.oauth2invalidRequest=Richiesta non valida
|
|||||||
login.oauth2AccessDenied=Accesso negato
|
login.oauth2AccessDenied=Accesso negato
|
||||||
login.oauth2InvalidTokenResponse=Risposta token non valida
|
login.oauth2InvalidTokenResponse=Risposta token non valida
|
||||||
login.oauth2InvalidIdToken=Id Token non valido
|
login.oauth2InvalidIdToken=Id Token non valido
|
||||||
|
login.relyingPartyRegistrationNotFound=Nessuna registrazione di parte affidabile trovata
|
||||||
login.userIsDisabled=L'utente è disattivato, l'accesso è attualmente bloccato con questo nome utente. Si prega di contattare l'amministratore.
|
login.userIsDisabled=L'utente è disattivato, l'accesso è attualmente bloccato con questo nome utente. Si prega di contattare l'amministratore.
|
||||||
login.alreadyLoggedIn=Hai già effettuato l'accesso a
|
login.alreadyLoggedIn=Hai già effettuato l'accesso a
|
||||||
login.alreadyLoggedIn2=dispositivi. Esci dai dispositivi e riprova.
|
login.alreadyLoggedIn2=dispositivi. Esci dai dispositivi e riprova.
|
||||||
@@ -830,7 +831,7 @@ sign.first=Prima pagina
|
|||||||
sign.last=Ultima pagina
|
sign.last=Ultima pagina
|
||||||
sign.next=Prossima pagina
|
sign.next=Prossima pagina
|
||||||
sign.previous=Pagina precedente
|
sign.previous=Pagina precedente
|
||||||
|
sign.maintainRatio=Attiva il mantenimento delle proporzioni
|
||||||
#repair
|
#repair
|
||||||
repair.title=Ripara
|
repair.title=Ripara
|
||||||
repair.header=Ripara PDF
|
repair.header=Ripara PDF
|
||||||
@@ -1041,6 +1042,7 @@ pdfToImage.grey=Scala di grigi
|
|||||||
pdfToImage.blackwhite=Bianco e Nero (potresti perdere dettagli!)
|
pdfToImage.blackwhite=Bianco e Nero (potresti perdere dettagli!)
|
||||||
pdfToImage.submit=Converti
|
pdfToImage.submit=Converti
|
||||||
pdfToImage.info=Python non è installato.È richiesto per la conversione WebP.
|
pdfToImage.info=Python non è installato.È richiesto per la conversione WebP.
|
||||||
|
pdfToImage.placeholder=(es. 1,2,8 o 4,7,12-16 o 2n-1)
|
||||||
|
|
||||||
|
|
||||||
#addPassword
|
#addPassword
|
||||||
@@ -1284,6 +1286,8 @@ splitByChapters.submit=Dividi PDF
|
|||||||
fileChooser.click=Clicca
|
fileChooser.click=Clicca
|
||||||
fileChooser.or=o
|
fileChooser.or=o
|
||||||
fileChooser.dragAndDrop=Trascina & Rilascia
|
fileChooser.dragAndDrop=Trascina & Rilascia
|
||||||
|
fileChooser.dragAndDropPDF=Trascina & rilascia il file PDF
|
||||||
|
fileChooser.dragAndDropImage=Trascina & rilascia il file immagine
|
||||||
fileChooser.hoveredDragAndDrop=Trascina & rilascia i file qui
|
fileChooser.hoveredDragAndDrop=Trascina & rilascia i file qui
|
||||||
|
|
||||||
#release notes
|
#release notes
|
||||||
|
|||||||
@@ -561,6 +561,7 @@ login.oauth2invalidRequest=無効なリクエスト
|
|||||||
login.oauth2AccessDenied=アクセス拒否
|
login.oauth2AccessDenied=アクセス拒否
|
||||||
login.oauth2InvalidTokenResponse=無効なトークン応答
|
login.oauth2InvalidTokenResponse=無効なトークン応答
|
||||||
login.oauth2InvalidIdToken=無効なIDトークン
|
login.oauth2InvalidIdToken=無効なIDトークン
|
||||||
|
login.relyingPartyRegistrationNotFound=No relying party registration found
|
||||||
login.userIsDisabled=ユーザーは非アクティブ化されており、現在このユーザー名でのログインはブロックされています。管理者に連絡してください。
|
login.userIsDisabled=ユーザーは非アクティブ化されており、現在このユーザー名でのログインはブロックされています。管理者に連絡してください。
|
||||||
login.alreadyLoggedIn=すでにログインしています
|
login.alreadyLoggedIn=すでにログインしています
|
||||||
login.alreadyLoggedIn2=デバイスからログアウトしてもう一度お試しください。
|
login.alreadyLoggedIn2=デバイスからログアウトしてもう一度お試しください。
|
||||||
@@ -830,7 +831,7 @@ sign.first=最初のページ
|
|||||||
sign.last=最後のページ
|
sign.last=最後のページ
|
||||||
sign.next=次のページ
|
sign.next=次のページ
|
||||||
sign.previous=前のページ
|
sign.previous=前のページ
|
||||||
|
sign.maintainRatio=Toggle maintain aspect ratio
|
||||||
#repair
|
#repair
|
||||||
repair.title=修復
|
repair.title=修復
|
||||||
repair.header=PDFを修復
|
repair.header=PDFを修復
|
||||||
@@ -1041,6 +1042,7 @@ pdfToImage.grey=グレースケール
|
|||||||
pdfToImage.blackwhite=白黒 (データが失われる可能性があります!)
|
pdfToImage.blackwhite=白黒 (データが失われる可能性があります!)
|
||||||
pdfToImage.submit=変換
|
pdfToImage.submit=変換
|
||||||
pdfToImage.info=Pythonがインストールされていません。WebPの変換に必要です。
|
pdfToImage.info=Pythonがインストールされていません。WebPの変換に必要です。
|
||||||
|
pdfToImage.placeholder=(例:1,2,8、4,7,12-16、2n-1)
|
||||||
|
|
||||||
|
|
||||||
#addPassword
|
#addPassword
|
||||||
@@ -1284,6 +1286,8 @@ splitByChapters.submit=PDFを分割
|
|||||||
fileChooser.click=クリック
|
fileChooser.click=クリック
|
||||||
fileChooser.or=または
|
fileChooser.or=または
|
||||||
fileChooser.dragAndDrop=ドラッグ&ドロップ
|
fileChooser.dragAndDrop=ドラッグ&ドロップ
|
||||||
|
fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||||
|
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||||
fileChooser.hoveredDragAndDrop=ファイルをここにドラッグ&ドロップ
|
fileChooser.hoveredDragAndDrop=ファイルをここにドラッグ&ドロップ
|
||||||
|
|
||||||
#release notes
|
#release notes
|
||||||
|
|||||||
@@ -561,6 +561,7 @@ login.oauth2invalidRequest=Invalid Request
|
|||||||
login.oauth2AccessDenied=Access Denied
|
login.oauth2AccessDenied=Access Denied
|
||||||
login.oauth2InvalidTokenResponse=Invalid Token Response
|
login.oauth2InvalidTokenResponse=Invalid Token Response
|
||||||
login.oauth2InvalidIdToken=Invalid Id Token
|
login.oauth2InvalidIdToken=Invalid Id Token
|
||||||
|
login.relyingPartyRegistrationNotFound=No relying party registration found
|
||||||
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
|
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
|
||||||
login.alreadyLoggedIn=You are already logged in to
|
login.alreadyLoggedIn=You are already logged in to
|
||||||
login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
|
login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
|
||||||
@@ -830,7 +831,7 @@ sign.first=First page
|
|||||||
sign.last=Last page
|
sign.last=Last page
|
||||||
sign.next=Next page
|
sign.next=Next page
|
||||||
sign.previous=Previous page
|
sign.previous=Previous page
|
||||||
|
sign.maintainRatio=Toggle maintain aspect ratio
|
||||||
#repair
|
#repair
|
||||||
repair.title=복구
|
repair.title=복구
|
||||||
repair.header=PDF 복구
|
repair.header=PDF 복구
|
||||||
@@ -1041,6 +1042,7 @@ pdfToImage.grey=그레이스케일
|
|||||||
pdfToImage.blackwhite=흑백 (데이터 손실 가능성 있음!)
|
pdfToImage.blackwhite=흑백 (데이터 손실 가능성 있음!)
|
||||||
pdfToImage.submit=변환
|
pdfToImage.submit=변환
|
||||||
pdfToImage.info=Python이 설치되어 있지 않습니다. WebP 변환에 필요합니다.
|
pdfToImage.info=Python이 설치되어 있지 않습니다. WebP 변환에 필요합니다.
|
||||||
|
pdfToImage.placeholder=(예: 1,2,8 또는 4,7,12-16 또는 2n-1)
|
||||||
|
|
||||||
|
|
||||||
#addPassword
|
#addPassword
|
||||||
@@ -1284,6 +1286,8 @@ splitByChapters.submit=PDF 분할
|
|||||||
fileChooser.click=Click
|
fileChooser.click=Click
|
||||||
fileChooser.or=or
|
fileChooser.or=or
|
||||||
fileChooser.dragAndDrop=Drag & Drop
|
fileChooser.dragAndDrop=Drag & Drop
|
||||||
|
fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||||
|
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||||
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
||||||
|
|
||||||
#release notes
|
#release notes
|
||||||
|
|||||||
@@ -561,6 +561,7 @@ login.oauth2invalidRequest=Ongeldig verzoek
|
|||||||
login.oauth2AccessDenied=Toegang geweigerd
|
login.oauth2AccessDenied=Toegang geweigerd
|
||||||
login.oauth2InvalidTokenResponse=Ongeldige tokenreactie
|
login.oauth2InvalidTokenResponse=Ongeldige tokenreactie
|
||||||
login.oauth2InvalidIdToken=Ongeldige ID token
|
login.oauth2InvalidIdToken=Ongeldige ID token
|
||||||
|
login.relyingPartyRegistrationNotFound=No relying party registration found
|
||||||
login.userIsDisabled=De gebruiker is gedesactiveerd, inloggen is momenteel geblokkeerd voor deze gebruikersnaam. Neem contact op met de beheerder.
|
login.userIsDisabled=De gebruiker is gedesactiveerd, inloggen is momenteel geblokkeerd voor deze gebruikersnaam. Neem contact op met de beheerder.
|
||||||
login.alreadyLoggedIn=U zit reeds ingelogd bij
|
login.alreadyLoggedIn=U zit reeds ingelogd bij
|
||||||
login.alreadyLoggedIn2=apparaten. U moet u a.u.b. uitloggen van de apparaten en opnieuw proberen.
|
login.alreadyLoggedIn2=apparaten. U moet u a.u.b. uitloggen van de apparaten en opnieuw proberen.
|
||||||
@@ -830,7 +831,7 @@ sign.first=First page
|
|||||||
sign.last=Last page
|
sign.last=Last page
|
||||||
sign.next=Next page
|
sign.next=Next page
|
||||||
sign.previous=Previous page
|
sign.previous=Previous page
|
||||||
|
sign.maintainRatio=Toggle maintain aspect ratio
|
||||||
#repair
|
#repair
|
||||||
repair.title=Repareren
|
repair.title=Repareren
|
||||||
repair.header=PDF's repareren
|
repair.header=PDF's repareren
|
||||||
@@ -1041,6 +1042,7 @@ pdfToImage.grey=Grijstinten
|
|||||||
pdfToImage.blackwhite=Zwart en wit (kan data verliezen!)
|
pdfToImage.blackwhite=Zwart en wit (kan data verliezen!)
|
||||||
pdfToImage.submit=Omzetten
|
pdfToImage.submit=Omzetten
|
||||||
pdfToImage.info=Python is niet geïnstalleerd. Vereist voor WebP-conversie.
|
pdfToImage.info=Python is niet geïnstalleerd. Vereist voor WebP-conversie.
|
||||||
|
pdfToImage.placeholder=(bijv. 1,2,8 of 4,7,12-16 of 2n-1)
|
||||||
|
|
||||||
|
|
||||||
#addPassword
|
#addPassword
|
||||||
@@ -1284,6 +1286,8 @@ splitByChapters.submit=PDF splitsen
|
|||||||
fileChooser.click=Click
|
fileChooser.click=Click
|
||||||
fileChooser.or=or
|
fileChooser.or=or
|
||||||
fileChooser.dragAndDrop=Drag & Drop
|
fileChooser.dragAndDrop=Drag & Drop
|
||||||
|
fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||||
|
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||||
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
||||||
|
|
||||||
#release notes
|
#release notes
|
||||||
|
|||||||
@@ -561,6 +561,7 @@ login.oauth2invalidRequest=Ugyldig forespørsel
|
|||||||
login.oauth2AccessDenied=Tilgang nektet
|
login.oauth2AccessDenied=Tilgang nektet
|
||||||
login.oauth2InvalidTokenResponse=Ugyldig tokenrespons
|
login.oauth2InvalidTokenResponse=Ugyldig tokenrespons
|
||||||
login.oauth2InvalidIdToken=Ugyldig Id Token
|
login.oauth2InvalidIdToken=Ugyldig Id Token
|
||||||
|
login.relyingPartyRegistrationNotFound=No relying party registration found
|
||||||
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
|
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
|
||||||
login.alreadyLoggedIn=You are already logged in to
|
login.alreadyLoggedIn=You are already logged in to
|
||||||
login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
|
login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
|
||||||
@@ -830,7 +831,7 @@ sign.first=First page
|
|||||||
sign.last=Last page
|
sign.last=Last page
|
||||||
sign.next=Next page
|
sign.next=Next page
|
||||||
sign.previous=Previous page
|
sign.previous=Previous page
|
||||||
|
sign.maintainRatio=Toggle maintain aspect ratio
|
||||||
#repair
|
#repair
|
||||||
repair.title=Reparer
|
repair.title=Reparer
|
||||||
repair.header=Reparer PDF-er
|
repair.header=Reparer PDF-er
|
||||||
@@ -1041,6 +1042,7 @@ pdfToImage.grey=Gråtone
|
|||||||
pdfToImage.blackwhite=Svart-hvitt (kan miste data!)
|
pdfToImage.blackwhite=Svart-hvitt (kan miste data!)
|
||||||
pdfToImage.submit=Konverter
|
pdfToImage.submit=Konverter
|
||||||
pdfToImage.info=Python is not installed. Required for WebP conversion.
|
pdfToImage.info=Python is not installed. Required for WebP conversion.
|
||||||
|
pdfToImage.placeholder=(f.eks. 1,2,8 eller 4,7,12-16 eller 2n-1)
|
||||||
|
|
||||||
|
|
||||||
#addPassword
|
#addPassword
|
||||||
@@ -1284,6 +1286,8 @@ splitByChapters.submit=Split PDF
|
|||||||
fileChooser.click=Click
|
fileChooser.click=Click
|
||||||
fileChooser.or=or
|
fileChooser.or=or
|
||||||
fileChooser.dragAndDrop=Drag & Drop
|
fileChooser.dragAndDrop=Drag & Drop
|
||||||
|
fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||||
|
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||||
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
||||||
|
|
||||||
#release notes
|
#release notes
|
||||||
|
|||||||
@@ -561,6 +561,7 @@ login.oauth2invalidRequest=Nieprawidłowe żądanie
|
|||||||
login.oauth2AccessDenied=Brak dostępu
|
login.oauth2AccessDenied=Brak dostępu
|
||||||
login.oauth2InvalidTokenResponse=Nieprawidłowa odpowiedź na token
|
login.oauth2InvalidTokenResponse=Nieprawidłowa odpowiedź na token
|
||||||
login.oauth2InvalidIdToken=Nieprawidłowa wartość tokenu
|
login.oauth2InvalidIdToken=Nieprawidłowa wartość tokenu
|
||||||
|
login.relyingPartyRegistrationNotFound=No relying party registration found
|
||||||
login.userIsDisabled=Użytkownik jest nieaktywny, logowanie przy użyciu tej nazwy użytkownika jest obecnie zablokowane. Prosimy o kontakt z administratorem.
|
login.userIsDisabled=Użytkownik jest nieaktywny, logowanie przy użyciu tej nazwy użytkownika jest obecnie zablokowane. Prosimy o kontakt z administratorem.
|
||||||
login.alreadyLoggedIn=Jesteś już zalogowany na
|
login.alreadyLoggedIn=Jesteś już zalogowany na
|
||||||
login.alreadyLoggedIn2=urządzeniach. Wyloguj się z tych urządzeń i spróbuj ponownie.
|
login.alreadyLoggedIn2=urządzeniach. Wyloguj się z tych urządzeń i spróbuj ponownie.
|
||||||
@@ -830,7 +831,7 @@ sign.first=First page
|
|||||||
sign.last=Last page
|
sign.last=Last page
|
||||||
sign.next=Next page
|
sign.next=Next page
|
||||||
sign.previous=Previous page
|
sign.previous=Previous page
|
||||||
|
sign.maintainRatio=Toggle maintain aspect ratio
|
||||||
#repair
|
#repair
|
||||||
repair.title=Napraw
|
repair.title=Napraw
|
||||||
repair.header=Napraw dokument(y) PDF
|
repair.header=Napraw dokument(y) PDF
|
||||||
@@ -1041,6 +1042,7 @@ pdfToImage.grey=Odcień szarości
|
|||||||
pdfToImage.blackwhite=Czarno-biały (może spowodować utratę danych!)
|
pdfToImage.blackwhite=Czarno-biały (może spowodować utratę danych!)
|
||||||
pdfToImage.submit=Konwertuj
|
pdfToImage.submit=Konwertuj
|
||||||
pdfToImage.info=Python nie został zainstalowany. Jest wymagany do konwersji WebP.
|
pdfToImage.info=Python nie został zainstalowany. Jest wymagany do konwersji WebP.
|
||||||
|
pdfToImage.placeholder=(przykład 1,2,8 lub 2n-1)
|
||||||
|
|
||||||
|
|
||||||
#addPassword
|
#addPassword
|
||||||
@@ -1284,6 +1286,8 @@ splitByChapters.submit=Podziel PDF
|
|||||||
fileChooser.click=Click
|
fileChooser.click=Click
|
||||||
fileChooser.or=or
|
fileChooser.or=or
|
||||||
fileChooser.dragAndDrop=Drag & Drop
|
fileChooser.dragAndDrop=Drag & Drop
|
||||||
|
fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||||
|
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||||
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
||||||
|
|
||||||
#release notes
|
#release notes
|
||||||
|
|||||||
@@ -561,6 +561,7 @@ login.oauth2invalidRequest=Requisição Inválida
|
|||||||
login.oauth2AccessDenied=Acesso Negado
|
login.oauth2AccessDenied=Acesso Negado
|
||||||
login.oauth2InvalidTokenResponse=Resposta de Token Inválida
|
login.oauth2InvalidTokenResponse=Resposta de Token Inválida
|
||||||
login.oauth2InvalidIdToken=Id de Token Inválido
|
login.oauth2InvalidIdToken=Id de Token Inválido
|
||||||
|
login.relyingPartyRegistrationNotFound=No relying party registration found
|
||||||
login.userIsDisabled=O usuário está desativado, o login está atualmente bloqueado com este nome de usuário. Entre em contato com o administrador.
|
login.userIsDisabled=O usuário está desativado, o login está atualmente bloqueado com este nome de usuário. Entre em contato com o administrador.
|
||||||
login.alreadyLoggedIn=Você já está conectado em
|
login.alreadyLoggedIn=Você já está conectado em
|
||||||
login.alreadyLoggedIn2=aparelhos. Por favor saia dos aparelhos e tente novamente.
|
login.alreadyLoggedIn2=aparelhos. Por favor saia dos aparelhos e tente novamente.
|
||||||
@@ -830,7 +831,7 @@ sign.first=Primeira página
|
|||||||
sign.last=Última página
|
sign.last=Última página
|
||||||
sign.next=Próxima página
|
sign.next=Próxima página
|
||||||
sign.previous=Página anterior
|
sign.previous=Página anterior
|
||||||
|
sign.maintainRatio=Toggle maintain aspect ratio
|
||||||
#repair
|
#repair
|
||||||
repair.title=Reparar
|
repair.title=Reparar
|
||||||
repair.header=Reparar
|
repair.header=Reparar
|
||||||
@@ -1041,6 +1042,7 @@ pdfToImage.grey=Escala de Cinza
|
|||||||
pdfToImage.blackwhite=Preto e Branco (pode perder informações!)
|
pdfToImage.blackwhite=Preto e Branco (pode perder informações!)
|
||||||
pdfToImage.submit=Converter
|
pdfToImage.submit=Converter
|
||||||
pdfToImage.info=Python não está instalado. Necessário para conversão WebP.
|
pdfToImage.info=Python não está instalado. Necessário para conversão WebP.
|
||||||
|
pdfToImage.placeholder=(por exemplo 1,2,8 or 4,7,12-16 ou 2n-1)
|
||||||
|
|
||||||
|
|
||||||
#addPassword
|
#addPassword
|
||||||
@@ -1284,6 +1286,8 @@ splitByChapters.submit=Dividir
|
|||||||
fileChooser.click=Clique
|
fileChooser.click=Clique
|
||||||
fileChooser.or=ou
|
fileChooser.or=ou
|
||||||
fileChooser.dragAndDrop=Arraste & Solte
|
fileChooser.dragAndDrop=Arraste & Solte
|
||||||
|
fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||||
|
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||||
fileChooser.hoveredDragAndDrop=Arraste & Solte arquivo(s) aqui
|
fileChooser.hoveredDragAndDrop=Arraste & Solte arquivo(s) aqui
|
||||||
|
|
||||||
#release notes
|
#release notes
|
||||||
|
|||||||
@@ -561,6 +561,7 @@ login.oauth2invalidRequest=Requisito inválido
|
|||||||
login.oauth2AccessDenied=Acesso negado
|
login.oauth2AccessDenied=Acesso negado
|
||||||
login.oauth2InvalidTokenResponse=Resposta de token inválida
|
login.oauth2InvalidTokenResponse=Resposta de token inválida
|
||||||
login.oauth2InvalidIdToken=Token de identificação inválido
|
login.oauth2InvalidIdToken=Token de identificação inválido
|
||||||
|
login.relyingPartyRegistrationNotFound=No relying party registration found
|
||||||
login.userIsDisabled=O utilizador foi desativado, o login está atualmente bloqueado com esta conta. Por favor, contacte o administrador.
|
login.userIsDisabled=O utilizador foi desativado, o login está atualmente bloqueado com esta conta. Por favor, contacte o administrador.
|
||||||
login.alreadyLoggedIn=Já está logado em
|
login.alreadyLoggedIn=Já está logado em
|
||||||
login.alreadyLoggedIn2=dispositivos. Por favor, faça logout nos dispositivos e tente novamente.
|
login.alreadyLoggedIn2=dispositivos. Por favor, faça logout nos dispositivos e tente novamente.
|
||||||
@@ -830,7 +831,7 @@ sign.first=First page
|
|||||||
sign.last=Last page
|
sign.last=Last page
|
||||||
sign.next=Next page
|
sign.next=Next page
|
||||||
sign.previous=Previous page
|
sign.previous=Previous page
|
||||||
|
sign.maintainRatio=Toggle maintain aspect ratio
|
||||||
#repair
|
#repair
|
||||||
repair.title=Reparar
|
repair.title=Reparar
|
||||||
repair.header=Reparar PDFs
|
repair.header=Reparar PDFs
|
||||||
@@ -1041,6 +1042,7 @@ pdfToImage.grey=Escala de Cinza
|
|||||||
pdfToImage.blackwhite=Preto e Branco (pode resultar em perda de dados!)
|
pdfToImage.blackwhite=Preto e Branco (pode resultar em perda de dados!)
|
||||||
pdfToImage.submit=Converter
|
pdfToImage.submit=Converter
|
||||||
pdfToImage.info=O Python não está instalado. Necessário para a conversão de WebP.
|
pdfToImage.info=O Python não está instalado. Necessário para a conversão de WebP.
|
||||||
|
pdfToImage.placeholder=(ex: 1,2,8 ou 4,7,12-16 ou 2n-1)
|
||||||
|
|
||||||
|
|
||||||
#addPassword
|
#addPassword
|
||||||
@@ -1284,6 +1286,8 @@ splitByChapters.submit=Dividir o PDF
|
|||||||
fileChooser.click=Click
|
fileChooser.click=Click
|
||||||
fileChooser.or=or
|
fileChooser.or=or
|
||||||
fileChooser.dragAndDrop=Drag & Drop
|
fileChooser.dragAndDrop=Drag & Drop
|
||||||
|
fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||||
|
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||||
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
||||||
|
|
||||||
#release notes
|
#release notes
|
||||||
|
|||||||
@@ -561,6 +561,7 @@ login.oauth2invalidRequest=Cerere Invalidă
|
|||||||
login.oauth2AccessDenied=Acces Refuzat
|
login.oauth2AccessDenied=Acces Refuzat
|
||||||
login.oauth2InvalidTokenResponse=Răspuns Invalid la Token
|
login.oauth2InvalidTokenResponse=Răspuns Invalid la Token
|
||||||
login.oauth2InvalidIdToken=Token de Id Invalid
|
login.oauth2InvalidIdToken=Token de Id Invalid
|
||||||
|
login.relyingPartyRegistrationNotFound=No relying party registration found
|
||||||
login.userIsDisabled=Utilizatorul este dezactivat, conectarea este în prezent blocată cu acest nume de utilizator. Te rugăm să contactezi administratorul.
|
login.userIsDisabled=Utilizatorul este dezactivat, conectarea este în prezent blocată cu acest nume de utilizator. Te rugăm să contactezi administratorul.
|
||||||
login.alreadyLoggedIn=You are already logged in to
|
login.alreadyLoggedIn=You are already logged in to
|
||||||
login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
|
login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
|
||||||
@@ -830,7 +831,7 @@ sign.first=First page
|
|||||||
sign.last=Last page
|
sign.last=Last page
|
||||||
sign.next=Next page
|
sign.next=Next page
|
||||||
sign.previous=Previous page
|
sign.previous=Previous page
|
||||||
|
sign.maintainRatio=Toggle maintain aspect ratio
|
||||||
#repair
|
#repair
|
||||||
repair.title=Repară
|
repair.title=Repară
|
||||||
repair.header=Repară documente PDF
|
repair.header=Repară documente PDF
|
||||||
@@ -1041,6 +1042,7 @@ pdfToImage.grey=Scală de gri
|
|||||||
pdfToImage.blackwhite=Alb și negru (Poate pierde date!)
|
pdfToImage.blackwhite=Alb și negru (Poate pierde date!)
|
||||||
pdfToImage.submit=Convertește
|
pdfToImage.submit=Convertește
|
||||||
pdfToImage.info=Python nu este instalat. Necesar pentru conversia WebP.
|
pdfToImage.info=Python nu este instalat. Necesar pentru conversia WebP.
|
||||||
|
pdfToImage.placeholder=(ex. 1,2,8 sau 4,7,12-16 sau 2n-1)
|
||||||
|
|
||||||
|
|
||||||
#addPassword
|
#addPassword
|
||||||
@@ -1284,6 +1286,8 @@ splitByChapters.submit=Split PDF
|
|||||||
fileChooser.click=Click
|
fileChooser.click=Click
|
||||||
fileChooser.or=or
|
fileChooser.or=or
|
||||||
fileChooser.dragAndDrop=Drag & Drop
|
fileChooser.dragAndDrop=Drag & Drop
|
||||||
|
fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||||
|
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||||
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
||||||
|
|
||||||
#release notes
|
#release notes
|
||||||
|
|||||||
@@ -561,6 +561,7 @@ login.oauth2invalidRequest=Неверный запрос
|
|||||||
login.oauth2AccessDenied=Доступ запрещен
|
login.oauth2AccessDenied=Доступ запрещен
|
||||||
login.oauth2InvalidTokenResponse=Недействительный ответ токена
|
login.oauth2InvalidTokenResponse=Недействительный ответ токена
|
||||||
login.oauth2InvalidIdToken=Недействительный идентификационный токен
|
login.oauth2InvalidIdToken=Недействительный идентификационный токен
|
||||||
|
login.relyingPartyRegistrationNotFound=No relying party registration found
|
||||||
login.userIsDisabled=Пользователь деактивирован, вход с данным именем пользователя заблокирован. Пожалуйста, обратитесь к администратору.
|
login.userIsDisabled=Пользователь деактивирован, вход с данным именем пользователя заблокирован. Пожалуйста, обратитесь к администратору.
|
||||||
login.alreadyLoggedIn=Вы уже вошли в
|
login.alreadyLoggedIn=Вы уже вошли в
|
||||||
login.alreadyLoggedIn2=устройства. Выполните выход из устройств и попробуйте снова.
|
login.alreadyLoggedIn2=устройства. Выполните выход из устройств и попробуйте снова.
|
||||||
@@ -830,7 +831,7 @@ sign.first=First page
|
|||||||
sign.last=Last page
|
sign.last=Last page
|
||||||
sign.next=Next page
|
sign.next=Next page
|
||||||
sign.previous=Previous page
|
sign.previous=Previous page
|
||||||
|
sign.maintainRatio=Toggle maintain aspect ratio
|
||||||
#repair
|
#repair
|
||||||
repair.title=Ремонт
|
repair.title=Ремонт
|
||||||
repair.header=Ремонт PDF ов
|
repair.header=Ремонт PDF ов
|
||||||
@@ -1041,6 +1042,7 @@ pdfToImage.grey=Оттенки серого
|
|||||||
pdfToImage.blackwhite=Черно-белый (может потерять данные!)
|
pdfToImage.blackwhite=Черно-белый (может потерять данные!)
|
||||||
pdfToImage.submit=Конвертировать
|
pdfToImage.submit=Конвертировать
|
||||||
pdfToImage.info=Питон не установлен. Необходим для конвертации в WebP.
|
pdfToImage.info=Питон не установлен. Необходим для конвертации в WebP.
|
||||||
|
pdfToImage.placeholder=(например 1,2,8 или 4,7,12-16 или 2n-1)
|
||||||
|
|
||||||
|
|
||||||
#addPassword
|
#addPassword
|
||||||
@@ -1284,6 +1286,8 @@ splitByChapters.submit=Разделить PDF
|
|||||||
fileChooser.click=Click
|
fileChooser.click=Click
|
||||||
fileChooser.or=or
|
fileChooser.or=or
|
||||||
fileChooser.dragAndDrop=Drag & Drop
|
fileChooser.dragAndDrop=Drag & Drop
|
||||||
|
fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||||
|
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||||
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
||||||
|
|
||||||
#release notes
|
#release notes
|
||||||
|
|||||||
@@ -561,6 +561,7 @@ login.oauth2invalidRequest=Invalid Request
|
|||||||
login.oauth2AccessDenied=Access Denied
|
login.oauth2AccessDenied=Access Denied
|
||||||
login.oauth2InvalidTokenResponse=Invalid Token Response
|
login.oauth2InvalidTokenResponse=Invalid Token Response
|
||||||
login.oauth2InvalidIdToken=Invalid Id Token
|
login.oauth2InvalidIdToken=Invalid Id Token
|
||||||
|
login.relyingPartyRegistrationNotFound=No relying party registration found
|
||||||
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
|
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
|
||||||
login.alreadyLoggedIn=You are already logged in to
|
login.alreadyLoggedIn=You are already logged in to
|
||||||
login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
|
login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
|
||||||
@@ -830,7 +831,7 @@ sign.first=First page
|
|||||||
sign.last=Last page
|
sign.last=Last page
|
||||||
sign.next=Next page
|
sign.next=Next page
|
||||||
sign.previous=Previous page
|
sign.previous=Previous page
|
||||||
|
sign.maintainRatio=Toggle maintain aspect ratio
|
||||||
#repair
|
#repair
|
||||||
repair.title=Opraviť
|
repair.title=Opraviť
|
||||||
repair.header=Opraviť PDF
|
repair.header=Opraviť PDF
|
||||||
@@ -1041,6 +1042,7 @@ pdfToImage.grey=Odtiene šedej
|
|||||||
pdfToImage.blackwhite=Čierno-biele (Môže stratiť údaje!)
|
pdfToImage.blackwhite=Čierno-biele (Môže stratiť údaje!)
|
||||||
pdfToImage.submit=Konvertovať
|
pdfToImage.submit=Konvertovať
|
||||||
pdfToImage.info=Python is not installed. Required for WebP conversion.
|
pdfToImage.info=Python is not installed. Required for WebP conversion.
|
||||||
|
pdfToImage.placeholder=(napr. 1,2,8 alebo 4,7,12-16 alebo 2n-1)
|
||||||
|
|
||||||
|
|
||||||
#addPassword
|
#addPassword
|
||||||
@@ -1284,6 +1286,8 @@ splitByChapters.submit=Split PDF
|
|||||||
fileChooser.click=Click
|
fileChooser.click=Click
|
||||||
fileChooser.or=or
|
fileChooser.or=or
|
||||||
fileChooser.dragAndDrop=Drag & Drop
|
fileChooser.dragAndDrop=Drag & Drop
|
||||||
|
fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||||
|
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||||
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
||||||
|
|
||||||
#release notes
|
#release notes
|
||||||
|
|||||||
@@ -561,6 +561,7 @@ login.oauth2invalidRequest=Invalid Request
|
|||||||
login.oauth2AccessDenied=Access Denied
|
login.oauth2AccessDenied=Access Denied
|
||||||
login.oauth2InvalidTokenResponse=Invalid Token Response
|
login.oauth2InvalidTokenResponse=Invalid Token Response
|
||||||
login.oauth2InvalidIdToken=Invalid Id Token
|
login.oauth2InvalidIdToken=Invalid Id Token
|
||||||
|
login.relyingPartyRegistrationNotFound=No relying party registration found
|
||||||
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
|
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
|
||||||
login.alreadyLoggedIn=You are already logged in to
|
login.alreadyLoggedIn=You are already logged in to
|
||||||
login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
|
login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
|
||||||
@@ -830,7 +831,7 @@ sign.first=First page
|
|||||||
sign.last=Last page
|
sign.last=Last page
|
||||||
sign.next=Next page
|
sign.next=Next page
|
||||||
sign.previous=Previous page
|
sign.previous=Previous page
|
||||||
|
sign.maintainRatio=Toggle maintain aspect ratio
|
||||||
#repair
|
#repair
|
||||||
repair.title=Popravi
|
repair.title=Popravi
|
||||||
repair.header=Popravi PDF fajlove
|
repair.header=Popravi PDF fajlove
|
||||||
@@ -1041,6 +1042,7 @@ pdfToImage.grey=Nijanse sive
|
|||||||
pdfToImage.blackwhite=Crno-belo (Može izgubiti podatke!)
|
pdfToImage.blackwhite=Crno-belo (Može izgubiti podatke!)
|
||||||
pdfToImage.submit=Konvertuj
|
pdfToImage.submit=Konvertuj
|
||||||
pdfToImage.info=Python is not installed. Required for WebP conversion.
|
pdfToImage.info=Python is not installed. Required for WebP conversion.
|
||||||
|
pdfToImage.placeholder=(e.g. 1,2,8 or 4,7,12-16 or 2n-1)
|
||||||
|
|
||||||
|
|
||||||
#addPassword
|
#addPassword
|
||||||
@@ -1284,6 +1286,8 @@ splitByChapters.submit=Split PDF
|
|||||||
fileChooser.click=Click
|
fileChooser.click=Click
|
||||||
fileChooser.or=or
|
fileChooser.or=or
|
||||||
fileChooser.dragAndDrop=Drag & Drop
|
fileChooser.dragAndDrop=Drag & Drop
|
||||||
|
fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||||
|
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||||
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
||||||
|
|
||||||
#release notes
|
#release notes
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user