Compare commits

..

2 Commits

Author SHA1 Message Date
Connor Yoh
1ad348b9a7 Removed duplicate package paths 2025-02-15 18:45:29 +00:00
Connor Yoh
84e0b4caa7 Replaced pip breaking installs of dependencies with APK Edge versions 2025-02-15 16:58:41 +00:00
639 changed files with 131874 additions and 2073 deletions

View File

@@ -37,7 +37,7 @@ jobs:
steps:
- name: Harden Runner
uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
with:
egress-policy: audit
@@ -82,7 +82,7 @@ jobs:
steps:
- name: Harden Runner
uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
with:
egress-policy: audit
@@ -103,7 +103,6 @@ jobs:
run: ./gradlew clean build
env:
DOCKER_ENABLE_SECURITY: false
STIRLING_PDF_DESKTOP_UI: false
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@f7ce87c1d6bead3e36075b2ce75da1f6cc28aaca # v3.9.0
@@ -121,7 +120,7 @@ jobs:
password: ${{ secrets.DOCKER_HUB_API }}
- name: Build and push PR-specific image
uses: docker/build-push-action@0adf9959216b96bec444f325f1e493d4aa344497 # v6.14.0
uses: docker/build-push-action@ca877d9245402d1537745e0e356eab47c3520991 # v6.13.0
with:
context: .
file: ./Dockerfile

View File

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

View File

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

View File

@@ -24,7 +24,7 @@ jobs:
steps:
- name: Harden Runner
uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
with:
egress-policy: audit
@@ -49,7 +49,7 @@ jobs:
- name: Upload Test Reports
if: always()
uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
with:
name: test-reports-jdk-${{ matrix.jdk-version }}
path: |
@@ -62,7 +62,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Harden Runner
uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
with:
egress-policy: audit
@@ -80,7 +80,7 @@ jobs:
- name: FAILED - check the licenses for compatibility
if: failure()
uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
with:
name: dependencies-without-allowed-license.json
path: |
@@ -106,7 +106,7 @@ jobs:
steps:
- name: Harden Runner
uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
with:
egress-policy: audit

View File

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

View File

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

View File

@@ -18,13 +18,13 @@ jobs:
pull-requests: write
steps:
- name: Harden Runner
uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
with:
egress-policy: audit
- name: Generate GitHub App Token
id: generate-token
uses: actions/create-github-app-token@0d564482f06ca65fa9e77e2510873638c82206f2 # v1.11.5
uses: actions/create-github-app-token@136412a57a7081aa63c935a2cc2918f76c34f514 # v1.11.2
with:
app-id: ${{ secrets.GH_APP_ID }}
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
@@ -45,7 +45,7 @@ jobs:
- name: FAILED - check the licenses for compatibility
if: failure()
uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
with:
name: dependencies-without-allowed-license.json
path: |

View File

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

View File

@@ -16,7 +16,7 @@ jobs:
versionMac: ${{ steps.versionNumberMac.outputs.versionNumberMac }}
steps:
- name: Harden Runner
uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
with:
egress-policy: audit
@@ -51,7 +51,7 @@ jobs:
file_suffix: ""
steps:
- name: Harden Runner
uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
with:
egress-policy: audit
@@ -80,7 +80,7 @@ jobs:
mv ./build/libs/Stirling-PDF-${{ needs.read_versions.outputs.version }}.jar ./binaries/Stirling-PDF${{ matrix.file_suffix }}.jar
- name: Upload build artifacts
uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
with:
retention-days: 1
if-no-files-found: error
@@ -101,7 +101,7 @@ jobs:
file_suffix: ""
steps:
- name: Harden Runner
uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
with:
egress-policy: audit
@@ -114,7 +114,7 @@ jobs:
run: ls -R
- name: Upload signed artifacts
uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
with:
retention-days: 1
if-no-files-found: error
@@ -139,7 +139,7 @@ jobs:
contents: write
steps:
- name: Harden Runner
uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
with:
egress-policy: audit
@@ -188,7 +188,7 @@ jobs:
run: ls -R ./binaries
- name: Upload build artifacts
uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
with:
retention-days: 1
if-no-files-found: error
@@ -210,7 +210,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Harden Runner
uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
with:
egress-policy: audit
@@ -224,7 +224,7 @@ jobs:
- name: Install Cosign
if: matrix.os == 'windows-latest'
uses: sigstore/cosign-installer@d7d6bc7722e3daa8354c50bcb52f4837da5e9b6a # v3.8.1
uses: sigstore/cosign-installer@dc72c7d5c4d10cd6bcb8cf6e3fd625a9e5e537da # v3.7.0
- name: Generate key pair
if: matrix.os == 'windows-latest'
@@ -255,7 +255,7 @@ jobs:
run: ls -R
- name: Upload signed artifacts
uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
with:
retention-days: 1
if-no-files-found: error
@@ -271,7 +271,7 @@ jobs:
contents: write
steps:
- name: Harden Runner
uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
with:
egress-policy: audit

View File

@@ -16,13 +16,13 @@ jobs:
pull-requests: write
steps:
- name: Harden Runner
uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
with:
egress-policy: audit
- name: Generate GitHub App Token
id: generate-token
uses: actions/create-github-app-token@0d564482f06ca65fa9e77e2510873638c82206f2 # v1.11.5
uses: actions/create-github-app-token@136412a57a7081aa63c935a2cc2918f76c34f514 # v1.11.2
with:
app-id: ${{ secrets.GH_APP_ID }}
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}

View File

@@ -6,7 +6,6 @@ on:
branches:
- master
- main
- aws
permissions:
contents: read
@@ -19,7 +18,7 @@ jobs:
id-token: write
steps:
- name: Harden Runner
uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
with:
egress-policy: audit
@@ -39,11 +38,10 @@ jobs:
run: ./gradlew clean build
env:
DOCKER_ENABLE_SECURITY: false
STIRLING_PDF_DESKTOP_UI: false
- name: Install cosign
if: github.ref == 'refs/heads/master'
uses: sigstore/cosign-installer@d7d6bc7722e3daa8354c50bcb52f4837da5e9b6a # v3.8.1
uses: sigstore/cosign-installer@dc72c7d5c4d10cd6bcb8cf6e3fd625a9e5e537da # v3.7.0
with:
cosign-release: "v2.4.1"
@@ -75,30 +73,100 @@ jobs:
id: repoowner
run: echo "lowercase=$(echo ${{ github.repository_owner }} | awk '{print tolower($0)}')" >> $GITHUB_OUTPUT
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_GITHUB_ROLE }}
aws-region: ${{ secrets.AWS_REGION }}
- name: Login to AWS Public ECR
uses: aws-actions/amazon-ecr-login@v2
with:
registry-type: public
- name: Generate tags fat
id: meta3
- name: Generate tags
id: meta
uses: docker/metadata-action@369eb591f429131d6889c46b94e711f089e6ca96 # v5.6.1
with:
images: |
public.ecr.aws/${{ secrets.AWS_PUBLIC_ECR_ALIAS }}/stirling-pdf
${{ secrets.DOCKER_HUB_USERNAME }}/s-pdf
ghcr.io/${{ steps.repoowner.outputs.lowercase }}/s-pdf
ghcr.io/${{ steps.repoowner.outputs.lowercase }}/stirling-pdf
${{ secrets.DOCKER_HUB_ORG_USERNAME }}/stirling-pdf
tags: |
type=raw,value=${{ steps.versionNumber.outputs.versionNumber }}-fat,enable=${{ github.ref == 'refs/heads/aws' }}
type=raw,value=latest-fat,enable=${{ github.ref == 'refs/heads/aws' }}
type=raw,value=${{ steps.versionNumber.outputs.versionNumber }},enable=${{ github.ref == 'refs/heads/master' }}
type=raw,value=latest,enable=${{ github.ref == 'refs/heads/master' }}
type=raw,value=alpha,enable=${{ github.ref == 'refs/heads/main' }}
- name: Build and push main Dockerfile
id: build-push-regular
uses: docker/build-push-action@ca877d9245402d1537745e0e356eab47c3520991 # v6.13.0
with:
builder: ${{ steps.buildx.outputs.name }}
context: .
file: ./Dockerfile
push: true
cache-from: type=gha
cache-to: type=gha,mode=max
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-args: VERSION_TAG=${{ steps.versionNumber.outputs.versionNumber }}
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
id: meta2
uses: docker/metadata-action@369eb591f429131d6889c46b94e711f089e6ca96 # v5.6.1
if: github.ref != 'refs/heads/main'
with:
images: |
${{ secrets.DOCKER_HUB_USERNAME }}/s-pdf
ghcr.io/${{ steps.repoowner.outputs.lowercase }}/s-pdf
ghcr.io/${{ steps.repoowner.outputs.lowercase }}/stirling-pdf
${{ secrets.DOCKER_HUB_ORG_USERNAME }}/stirling-pdf
tags: |
type=raw,value=${{ steps.versionNumber.outputs.versionNumber }}-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
id: build-push-lite
uses: docker/build-push-action@ca877d9245402d1537745e0e356eab47c3520991 # v6.13.0
if: github.ref != 'refs/heads/main'
with:
context: .
file: ./Dockerfile.ultra-lite
push: true
cache-from: type=gha
cache-to: type=gha,mode=max
tags: ${{ steps.meta2.outputs.tags }}
labels: ${{ steps.meta2.outputs.labels }}
build-args: VERSION_TAG=${{ steps.versionNumber.outputs.versionNumber }}
platforms: linux/amd64,linux/arm64/v8
provenance: true
sbom: true
- name: Generate tags fat
id: meta3
uses: docker/metadata-action@369eb591f429131d6889c46b94e711f089e6ca96 # v5.6.1
if: github.ref != 'refs/heads/main'
with:
images: |
${{ secrets.DOCKER_HUB_USERNAME }}/s-pdf
ghcr.io/${{ steps.repoowner.outputs.lowercase }}/s-pdf
ghcr.io/${{ steps.repoowner.outputs.lowercase }}/stirling-pdf
${{ secrets.DOCKER_HUB_ORG_USERNAME }}/stirling-pdf
tags: |
type=raw,value=${{ steps.versionNumber.outputs.versionNumber }}-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
id: build-push-fat
uses: docker/build-push-action@0adf9959216b96bec444f325f1e493d4aa344497 # v6.14.0
uses: docker/build-push-action@ca877d9245402d1537745e0e356eab47c3520991 # v6.13.0
if: github.ref != 'refs/heads/main'
with:
builder: ${{ steps.buildx.outputs.name }}
context: .
@@ -113,5 +181,14 @@ jobs:
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

View File

@@ -23,7 +23,7 @@ jobs:
version: ${{ steps.versionNumber.outputs.versionNumber }}
steps:
- name: Harden Runner
uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
with:
egress-policy: audit
@@ -63,7 +63,7 @@ jobs:
ls -R ./build/launch4j
- name: Upload build artifacts
uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
with:
name: binaries${{ matrix.file_suffix }}
path: |
@@ -83,7 +83,7 @@ jobs:
file_suffix: ""
steps:
- name: Harden Runner
uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
with:
egress-policy: audit
@@ -95,7 +95,7 @@ jobs:
run: ls -R
- name: Install Cosign
uses: sigstore/cosign-installer@d7d6bc7722e3daa8354c50bcb52f4837da5e9b6a # v3.8.1
uses: sigstore/cosign-installer@dc72c7d5c4d10cd6bcb8cf6e3fd625a9e5e537da # v3.7.0
- name: Generate key pair
run: cosign generate-key-pair
@@ -139,7 +139,7 @@ jobs:
./launch4j/Stirling-PDF-Server${{ matrix.file_suffix }}.exe
- name: Upload signed artifacts
uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
with:
name: signed${{ matrix.file_suffix }}
path: |
@@ -161,7 +161,7 @@ jobs:
file_suffix: ""
steps:
- name: Harden Runner
uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
with:
egress-policy: audit

View File

@@ -34,7 +34,7 @@ jobs:
steps:
- name: Harden Runner
uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
with:
egress-policy: audit
@@ -44,7 +44,7 @@ jobs:
persist-credentials: false
- name: "Run analysis"
uses: ossf/scorecard-action@f49aabe0b5af0936a0987cfb85d86b75731b0186 # v2.4.1
uses: ossf/scorecard-action@62b2cac7ed8198b15735ed49ab1e5cf35480ba46 # v2.4.0
with:
results_file: results.sarif
results_format: sarif
@@ -66,7 +66,7 @@ jobs:
# 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@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
with:
name: SARIF file
path: results.sarif
@@ -74,6 +74,6 @@ jobs:
# Upload the results to GitHub's code scanning dashboard.
- name: "Upload to code-scanning"
uses: github/codeql-action/upload-sarif@b56ba49b26e50535fa1e7f7db0f4f7b4bf65d80d # v3.28.10
uses: github/codeql-action/upload-sarif@9e8d0789d4a0fa9ceb6b1738f7e269594bdd67f0 # v3.28.9
with:
sarif_file: results.sarif

View File

@@ -1,24 +1,23 @@
name: Run Sonarqube
on:
push:
branches:
- master
pull_request_target:
branches:
- main
pull_request:
branches: [ "main" ]
workflow_dispatch:
permissions:
pull-requests: read
actions: read
name: Run Sonarqube
jobs:
sonarqube:
runs-on: ubuntu-latest
steps:
- name: Harden Runner
uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
with:
egress-policy: audit
@@ -46,7 +45,7 @@ jobs:
- name: Upload Problems Report on Failure
if: failure()
uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
with:
name: gradle-problems-report
path: build/reports/problems/problems-report.html
@@ -54,7 +53,7 @@ jobs:
- name: Upload Sonar Logs on Failure
if: failure()
uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
with:
name: sonar-logs
path: |

View File

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

View File

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

View File

@@ -24,13 +24,13 @@ jobs:
committer: ${{ steps.committer.outputs.committer }}
steps:
- name: Harden Runner
uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
with:
egress-policy: audit
- name: Generate GitHub App Token
id: generate-token
uses: actions/create-github-app-token@0d564482f06ca65fa9e77e2510873638c82206f2 # v1.11.5
uses: actions/create-github-app-token@136412a57a7081aa63c935a2cc2918f76c34f514 # v1.11.2
with:
app-id: ${{ secrets.GH_APP_ID }}
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
@@ -57,13 +57,13 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Harden Runner
uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
with:
egress-policy: audit
- name: Generate GitHub App Token
id: generate-token
uses: actions/create-github-app-token@0d564482f06ca65fa9e77e2510873638c82206f2 # v1.11.5
uses: actions/create-github-app-token@136412a57a7081aa63c935a2cc2918f76c34f514 # v1.11.2
with:
app-id: ${{ vars.GH_APP_ID }}
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}

View File

@@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Harden Runner
uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
with:
egress-policy: audit
@@ -46,7 +46,7 @@ jobs:
password: ${{ secrets.DOCKER_HUB_API }}
- name: Build and push test image
uses: docker/build-push-action@0adf9959216b96bec444f325f1e493d4aa344497 # v6.14.0
uses: docker/build-push-action@ca877d9245402d1537745e0e356eab47c3520991 # v6.13.0
with:
context: .
file: ./Dockerfile
@@ -105,7 +105,7 @@ jobs:
steps:
- name: Harden Runner
uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
with:
egress-policy: audit
@@ -134,7 +134,7 @@ jobs:
steps:
- name: Harden Runner
uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
with:
egress-policy: audit

View File

@@ -9,6 +9,7 @@
// "ms-vscode-remote.vscode-remote-extensionpack", // Remote Development Pack for SSH, WSL, and Containers
"Oracle.oracle-java", // Oracle Java extension with additional features for Java development
"redhat.java", // Java support by Red Hat with IntelliSense, debugging, and code navigation
"shengchen.vscode-checkstyle", // Checkstyle integration for Java code quality checks
"streetsidesoftware.code-spell-checker", // Spell checker for code to avoid typos
"vmware.vscode-boot-dev-pack", // Developer tools for Spring Boot by VMware
"vmware.vscode-spring-boot", // Spring Boot tools by VMware for enhanced Spring development

121
.vscode/settings.json vendored
View File

@@ -2,147 +2,54 @@
"java.compile.nullAnalysis.mode": "automatic",
"files.eol": "auto",
"java.configuration.updateBuildConfiguration": "interactive",
"black-formatter.args": [
"--line-length",
"127"
],
"flake8.args": [
"--max-line-length",
"127"
],
"black-formatter.args": ["--line-length", "127"],
"flake8.args": ["--max-line-length", "127"],
"pylint.args": ["max-line-length", "127"],
"[java]": {
"editor.tabSize": 4,
"editor.detectIndentation": false,
"editor.rulers": [
127
],
"editor.defaultFormatter": "josevseb.google-java-format-for-vs-code"
"editor.rulers": [127]
},
"[python]": {
"editor.tabSize": 2,
"editor.detectIndentation": false,
"editor.rulers": [
127
]
"editor.rulers": [127]
},
"[gradle-build]": {
"editor.tabSize": 4,
"editor.detectIndentation": false,
"editor.rulers": [
127
]
"editor.rulers": [127]
},
"[gradle]": {
"editor.tabSize": 4,
"editor.detectIndentation": false,
"editor.rulers": [
127
]
"editor.rulers": [127]
},
"[html]": {
"editor.tabSize": 2,
"editor.rulers": [
127
],
"editor.rulers": [127],
"files.trimFinalNewlines": false,
"files.insertFinalNewline": false
},
"[javascript]": {
"editor.tabSize": 2,
"editor.rulers": [
127
]
"editor.rulers": [127]
},
"[yaml]": {
"files.trimFinalNewlines": false,
"files.insertFinalNewline": false
},
"diffEditor.maxComputationTime": 0,
"editor.wordSegmenterLocales": null,
"editor.guides.bracketPairs": "active",
"editor.guides.bracketPairsHorizontal": "active",
"files.insertFinalNewline": true,
"files.trimFinalNewlines": true,
"files.trimTrailingWhitespace": true,
"files.autoSave": "onFocusChange",
"files.autoSaveWhenNoErrors": true,
"diffEditor.maxComputationTime": 0,
"editor.wordSegmenterLocales": "",
"editor.guides.bracketPairs": "active",
"editor.guides.bracketPairsHorizontal": "active",
"editor.indentSize": "tabSize",
"editor.stickyScroll.enabled": false,
"editor.minimap.enabled": false,
"editor.formatOnSave": true,
"editor.insertSpaces": true,
"java.format.enabled": true,
"java.format.settings.profile": "GoogleStyle",
"java.format.settings.google.version": "1.25.2",
"java.format.settings.google.mode": "jar-file",
"java.format.settings.google.extra": "--aosp --skip-sorting-imports --skip-javadoc-formatting",
// (DE) Aktiviert Kommentare im Java-Format.
// (EN) Enables comments in Java formatting.
// "java.format.comments.enabled": true,
// (DE) Generiert automatisch Kommentare im Code.
// (EN) Automatically generates comments in code.
// "java.codeGeneration.generateComments": true,
// https://github.com/redhat-developer/vscode-java/blob/master/document/_java.learnMoreAboutCleanUps.md#java-clean-ups
"java.saveActions.cleanup": true,
"java.cleanup.actions": [
"invertEquals", // Inverts calls to Object.equals(Object) and String.equalsIgnoreCase(String) to avoid useless null pointer exception.
"instanceofPatternMatch" // Replaces instanceof checks with pattern matching.
],
// (DE) Aktiviert die Code-Vervollständigung für Java.
// (EN) Enables code completion for Java.
"java.completion.engine": "dom",
"java.completion.enabled": true,
"java.completion.importOrder": [
"java",
"javax",
"org",
"com",
"net",
"io",
"jakarta",
"lombok",
"me",
"stirling",
],
"java.project.resourceFilters": [
".devcontainer/",
".git/",
".github/",
".gradle/",
".venv/",
".venv*/",
".vscode/",
"bin/",
"build/",
"configs/",
"customFiles/",
"docs/",
"exampleYmlFiles",
"gradle/",
"images/",
"logs/",
"pipeline/",
"scripts/",
"testings/",
".git-blame-ignore-revs",
".gitattributes",
".gitignore",
".pre-commit-config.yaml",
],
// Enables signature help in Java.
"java.signatureHelp.enabled": true,
// Enables detailed signature help descriptions.
"java.signatureHelp.description.enabled": true,
// Downloads sources for Maven dependencies.
"java.maven.downloadSources": true,
// Enables Gradle project import.
"java.import.gradle.enabled": true,
// Downloads sources for Eclipse projects.
"java.eclipse.downloadSources": true,
// Enables import of the Gradle wrapper.
"java.import.gradle.wrapper.enabled": true,
"spring.initializr.defaultLanguage": "Java",
"spring.initializr.defaultGroupId": "stirling.software.SPDF",
"spring.initializr.defaultArtifactId": "SPDF",
"cSpell.enabled": false,
"java.format.settings.google.extra": "--aosp --skip-sorting-imports"
}

View File

@@ -1,5 +1,5 @@
# Main stage
FROM alpine:3.21.3@sha256:a8560b36e8b8210634f77d9f7f9efd7ffa463e380b75e2e74aff4511df3ef88c
FROM alpine:3.21.2@sha256:56fa17d2a7e7f168a043a2712e63aed1f8543aeafdcee47c58dcffe38ed51099
# Copy necessary files
COPY scripts /scripts
@@ -35,10 +35,7 @@ ENV DOCKER_ENABLE_SECURITY=false \
HOME=/home/stirlingpdfuser \
PUID=1000 \
PGID=1000 \
UMASK=022 \
PYTHONPATH=/usr/lib/libreoffice/program:/opt/venv/lib/python3.12/site-packages \
UNO_PATH=/usr/lib/libreoffice/program \
URE_BOOTSTRAP=file:///usr/lib/libreoffice/program/fundamentalrc
UMASK=022
# JDK for app
@@ -69,16 +66,10 @@ RUN echo "@main https://dl-cdn.alpinelinux.org/alpine/edge/main" | tee -a /etc/a
# CV
py3-opencv \
python3 \
py3-pip \
py3-unoconv@testing \
py3-pillow@testing \
py3-pdf2image@testing && \
python3 -m venv /opt/venv && \
export PATH="/opt/venv/bin:$PATH" && \
pip install --upgrade pip && \
pip install --no-cache-dir --upgrade unoserver weasyprint && \
ln -s /usr/lib/libreoffice/program/uno.py /opt/venv/lib/python3.12/site-packages/ && \
ln -s /usr/lib/libreoffice/program/unohelper.py /opt/venv/lib/python3.12/site-packages/ && \
ln -s /usr/lib/libreoffice/program /opt/venv/lib/python3.12/site-packages/LibreOffice && \
py3-pdf2image@testing \
weasyprint@community && \
mv /usr/share/tessdata /usr/share/tessdata-original && \
mkdir -p $HOME /configs /logs /customFiles /pipeline/watchedFolders /pipeline/finishedFolders && \
fc-cache -f -v && \
@@ -93,4 +84,4 @@ EXPOSE 8080/tcp
# Set user and run command
ENTRYPOINT ["tini", "--", "/scripts/init.sh"]
CMD ["sh", "-c", "java -Dfile.encoding=UTF-8 -jar /app.jar & /opt/venv/bin/unoserver --port 2003 --interface 0.0.0.0"]
CMD ["java", "-Dfile.encoding=UTF-8", "-jar", "/app.jar"]

View File

@@ -9,11 +9,10 @@ COPY . .
# Build the application with DOCKER_ENABLE_SECURITY=false
RUN DOCKER_ENABLE_SECURITY=true \
STIRLING_PDF_DESKTOP_UI=false \
./gradlew clean build
# Main stage
FROM alpine:3.21.3@sha256:a8560b36e8b8210634f77d9f7f9efd7ffa463e380b75e2e74aff4511df3ef88c
FROM alpine:3.21.2@sha256:56fa17d2a7e7f168a043a2712e63aed1f8543aeafdcee47c58dcffe38ed51099
# Copy necessary files
COPY scripts /scripts
@@ -38,10 +37,7 @@ ENV DOCKER_ENABLE_SECURITY=false \
PGID=1000 \
UMASK=022 \
FAT_DOCKER=true \
INSTALL_BOOK_AND_ADVANCED_HTML_OPS=false \
PYTHONPATH=/usr/lib/libreoffice/program:/opt/venv/lib/python3.12/site-packages \
UNO_PATH=/usr/lib/libreoffice/program \
URE_BOOTSTRAP=file:///usr/lib/libreoffice/program/fundamentalrc
INSTALL_BOOK_AND_ADVANCED_HTML_OPS=false
# JDK for app
@@ -69,21 +65,16 @@ RUN echo "@main https://dl-cdn.alpinelinux.org/alpine/edge/main" | tee -a /etc/a
# OCR MY PDF (unpaper for descew and other advanced featues)
qpdf \
tesseract-ocr-data-eng \
font-terminus font-dejavu font-noto font-noto-cjk font-awesome font-noto-extra font-liberation font-linux-libertine \
font-terminus font-dejavu font-noto font-noto-cjk font-awesome font-noto-extra \
# CV
py3-opencv \
# python3/pip
python3 \
py3-pip \
py3-unoconv@testing \
py3-pillow@testing \
py3-pdf2image@testing && \
python3 -m venv /opt/venv && \
export PATH="/opt/venv/bin:$PATH" && \
pip install --upgrade pip && \
pip install --no-cache-dir --upgrade unoserver weasyprint && \
ln -s /usr/lib/libreoffice/program/uno.py /opt/venv/lib/python3.12/site-packages/ && \
ln -s /usr/lib/libreoffice/program/unohelper.py /opt/venv/lib/python3.12/site-packages/ && \
ln -s /usr/lib/libreoffice/program /opt/venv/lib/python3.12/site-packages/LibreOffice && \
py3-pdf2image@testing \
weasyprint@community && \
# uno unoconv and HTML
mv /usr/share/tessdata /usr/share/tessdata-original && \
mkdir -p $HOME /configs /logs /customFiles /pipeline/watchedFolders /pipeline/finishedFolders && \
fc-cache -f -v && \
@@ -95,6 +86,7 @@ RUN echo "@main https://dl-cdn.alpinelinux.org/alpine/edge/main" | tee -a /etc/a
chown stirlingpdfuser:stirlingpdfgroup /app.jar
EXPOSE 8080/tcp
# Set user and run command
ENTRYPOINT ["tini", "--", "/scripts/init.sh"]
CMD ["sh", "-c", "java -Dfile.encoding=UTF-8 -jar /app.jar & /opt/venv/bin/unoserver --port 2003 --interface 0.0.0.0"]
CMD ["java", "-Dfile.encoding=UTF-8", "-jar", "/app.jar"]

View File

@@ -1,5 +1,5 @@
# use alpine
FROM alpine:3.21.3@sha256:a8560b36e8b8210634f77d9f7f9efd7ffa463e380b75e2e74aff4511df3ef88c
FROM alpine:3.21.2@sha256:56fa17d2a7e7f168a043a2712e63aed1f8543aeafdcee47c58dcffe38ed51099
ARG VERSION_TAG

View File

@@ -11,12 +11,14 @@ Fork Stirling-PDF and create a new branch out of `main`.
Then add a reference to the language in the navbar by adding a new language entry to the dropdown:
- Edit the file: [languages.html](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/src/main/resources/templates/fragments/languages.html)
- Add a flag SVG file to: [flags directory](https://github.com/Stirling-Tools/Stirling-PDF/tree/main/src/main/resources/static/images/flags)
Any SVG flags are fine; most of the current ones were sourced from [here](https://flagicons.lipis.dev/). If your language isn't represented by a flag, choose a similar one, such as Saudi Arabia's flag for Arabic.
For example, to add Polish, you would add:
```html
<div th:replace="~{fragments/languageEntry :: languageEntry ('pl_PL', 'Polski')}" ></div>
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'pl_PL')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="pl_PL"> <img th:src="@{'/images/flags/pl.svg'}" alt="icon" width="20" height="15"> Polski</a>
```
The `data-bs-language-code` is the code used to reference the file in the next step.

View File

@@ -3,6 +3,7 @@
[![Docker Pulls](https://img.shields.io/docker/pulls/frooodle/s-pdf)](https://hub.docker.com/r/frooodle/s-pdf)
[![Discord](https://img.shields.io/discord/1068636748814483718?label=Discord)](https://discord.gg/HYmhKj45pU)
[![Docker Image Version (tag latest semver)](https://img.shields.io/docker/v/frooodle/s-pdf/latest)](https://github.com/Stirling-Tools/Stirling-PDF/)
[![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/Stirling-Tools/Stirling-PDF/badge)](https://scorecard.dev/viewer/?uri=github.com/Stirling-Tools/Stirling-PDF)
[![GitHub Repo stars](https://img.shields.io/github/stars/stirling-tools/stirling-pdf?style=social)](https://github.com/Stirling-Tools/stirling-pdf)
@@ -119,10 +120,10 @@ Stirling-PDF currently supports 39 languages!
| Arabic (العربية) (ar_AR) | ![89%](https://geps.dev/progress/89) |
| Azerbaijani (Azərbaycan Dili) (az_AZ) | ![88%](https://geps.dev/progress/88) |
| Basque (Euskara) (eu_ES) | ![51%](https://geps.dev/progress/51) |
| Bulgarian (Български) (bg_BG) | ![99%](https://geps.dev/progress/99) |
| Bulgarian (Български) (bg_BG) | ![85%](https://geps.dev/progress/85) |
| Catalan (Català) (ca_CA) | ![80%](https://geps.dev/progress/80) |
| Croatian (Hrvatski) (hr_HR) | ![86%](https://geps.dev/progress/86) |
| Czech (Česky) (cs_CZ) | ![97%](https://geps.dev/progress/97) |
| Croatian (Hrvatski) (hr_HR) | ![87%](https://geps.dev/progress/87) |
| Czech (Česky) (cs_CZ) | ![98%](https://geps.dev/progress/98) |
| Danish (Dansk) (da_DK) | ![85%](https://geps.dev/progress/85) |
| Dutch (Nederlands) (nl_NL) | ![85%](https://geps.dev/progress/85) |
| English (English) (en_GB) | ![100%](https://geps.dev/progress/100) |
@@ -135,24 +136,24 @@ Stirling-PDF currently supports 39 languages!
| Indonesian (Bahasa Indonesia) (id_ID) | ![86%](https://geps.dev/progress/86) |
| Irish (Gaeilge) (ga_IE) | ![98%](https://geps.dev/progress/98) |
| Italian (Italiano) (it_IT) | ![99%](https://geps.dev/progress/99) |
| Japanese (日本語) (ja_JP) | ![92%](https://geps.dev/progress/92) |
| Japanese (日本語) (ja_JP) | ![93%](https://geps.dev/progress/93) |
| Korean (한국어) (ko_KR) | ![98%](https://geps.dev/progress/98) |
| Norwegian (Norsk) (no_NB) | ![78%](https://geps.dev/progress/78) |
| Persian (فارسی) (fa_IR) | ![94%](https://geps.dev/progress/94) |
| Polish (Polski) (pl_PL) | ![85%](https://geps.dev/progress/85) |
| Polish (Polski) (pl_PL) | ![86%](https://geps.dev/progress/86) |
| Portuguese (Português) (pt_PT) | ![97%](https://geps.dev/progress/97) |
| Portuguese Brazilian (Português) (pt_BR) | ![98%](https://geps.dev/progress/98) |
| Romanian (Română) (ro_RO) | ![80%](https://geps.dev/progress/80) |
| Russian (Русский) (ru_RU) | ![97%](https://geps.dev/progress/97) |
| Romanian (Română) (ro_RO) | ![81%](https://geps.dev/progress/81) |
| Russian (Русский) (ru_RU) | ![98%](https://geps.dev/progress/98) |
| Serbian Latin alphabet (Srpski) (sr_LATN_RS) | ![63%](https://geps.dev/progress/63) |
| Simplified Chinese (简体中文) (zh_CN) | ![99%](https://geps.dev/progress/99) |
| Simplified Chinese (简体中文) (zh_CN) | ![90%](https://geps.dev/progress/90) |
| Slovakian (Slovensky) (sk_SK) | ![74%](https://geps.dev/progress/74) |
| Slovenian (Slovenščina) (sl_SI) | ![96%](https://geps.dev/progress/96) |
| Spanish (Español) (es_ES) | ![98%](https://geps.dev/progress/98) |
| Slovenian (Slovenščina) (sl_SI) | ![97%](https://geps.dev/progress/97) |
| Spanish (Español) (es_ES) | ![87%](https://geps.dev/progress/87) |
| Swedish (Svenska) (sv_SE) | ![92%](https://geps.dev/progress/92) |
| Thai (ไทย) (th_TH) | ![85%](https://geps.dev/progress/85) |
| Tibetan (བོད་ཡིག་) (zh_BO) | ![94%](https://geps.dev/progress/94) |
| Traditional Chinese (繁體中文) (zh_TW) | ![99%](https://geps.dev/progress/99) |
| Thai (ไทย) (th_TH) | ![86%](https://geps.dev/progress/86) |
| Tibetan (བོད་ཡིག་) (zh_BO) | ![95%](https://geps.dev/progress/95) |
| Traditional Chinese (繁體中文) (zh_TW) | ![98%](https://geps.dev/progress/98) |
| Turkish (Türkçe) (tr_TR) | ![82%](https://geps.dev/progress/82) |
| Ukrainian (Українська) (uk_UA) | ![72%](https://geps.dev/progress/72) |
| Vietnamese (Tiếng Việt) (vi_VN) | ![79%](https://geps.dev/progress/79) |

View File

@@ -1,6 +1,6 @@
plugins {
id "java"
id "org.springframework.boot" version "3.4.3"
id "org.springframework.boot" version "3.4.1"
id "io.spring.dependency-management" version "1.1.7"
id "org.springdoc.openapi-gradle-plugin" version "1.8.0"
id "io.swagger.swaggerhub" version "1.3.2"
@@ -15,17 +15,18 @@ plugins {
import com.github.jk1.license.render.*
ext {
springBootVersion = "3.4.3"
springBootVersion = "3.4.1"
pdfboxVersion = "3.0.4"
logbackVersion = "1.5.7"
imageioVersion = "3.12.0"
lombokVersion = "1.18.36"
bouncycastleVersion = "1.80"
springSecuritySamlVersion = "6.4.3"
springSecuritySamlVersion = "6.4.2"
openSamlVersion = "4.3.2"
}
group = "stirling.software"
version = "0.42.0"
version = "0.41.0"
java {
// 17 is lowest but we support and recommend 21
@@ -260,7 +261,7 @@ spotless {
googleJavaFormat("1.25.2").aosp().reorderImports(false)
importOrder("java", "javax", "org", "com", "net", "io", "jakarta", "lombok", "me", "stirling")
importOrder("java", "javax", "org", "com", "net", "io")
toggleOffOn()
trimTrailingWhitespace()
leadingTabsToSpaces()
@@ -272,7 +273,7 @@ sonar {
properties {
property "sonar.projectKey", "Stirling-Tools_Stirling-PDF"
property "sonar.organization", "stirling-tools"
property "sonar.exclusions", "**/build-wrapper-dump.json, src/main/java/org/apache/**, src/main/resources/static/pdfjs/**, src/main/resources/static/pdfjs-legacy/**, src/main/resources/static/js/thirdParty/**"
property "sonar.coverage.exclusions", "src/main/java/org/apache/**, src/main/resources/static/pdfjs/**, src/main/resources/static/pdfjs-legacy/**, src/main/resources/static/js/thirdParty/**"
property "sonar.cpd.exclusions", "src/main/java/org/apache/**, src/main/resources/static/pdfjs/**, src/main/resources/static/pdfjs-legacy/**, src/main/resources/static/js/thirdParty/**"
@@ -293,26 +294,14 @@ configurations.all {
}
dependencies {
//tmp for security bumps
implementation 'ch.qos.logback:logback-core:1.5.16'
implementation 'ch.qos.logback:logback-classic:1.5.16'
// Exclude vulnerable BouncyCastle version used in tableau
configurations.all {
exclude group: 'org.bouncycastle', module: 'bcpkix-jdk15on'
exclude group: 'org.bouncycastle', module: 'bcutil-jdk15on'
exclude group: 'org.bouncycastle', module: 'bcmail-jdk15on'
}
if (System.getenv("STIRLING_PDF_DESKTOP_UI") != "false") {
implementation "me.friwi:jcefmaven:132.3.1"
implementation "me.friwi:jcefmaven:127.3.1"
implementation "org.openjfx:javafx-controls:21"
implementation "org.openjfx:javafx-swing:21"
}
//security updates
implementation "org.springframework:spring-webmvc:6.2.3"
implementation "org.springframework:spring-webmvc:6.2.2"
implementation("io.github.pixee:java-security-toolkit:1.2.1")
@@ -331,8 +320,8 @@ dependencies {
implementation "org.springframework.boot:spring-boot-starter-data-jpa:$springBootVersion"
implementation "org.springframework.boot:spring-boot-starter-oauth2-client:$springBootVersion"
implementation "org.springframework.session:spring-session-core:3.4.2"
implementation "org.springframework:spring-jdbc:6.2.3"
implementation "org.springframework.session:spring-session-core:$springBootVersion"
implementation "org.springframework:spring-jdbc:6.2.2"
implementation 'com.unboundid.product.scim2:scim2-sdk-client:2.3.5'
// Don't upgrade h2database
@@ -407,7 +396,7 @@ dependencies {
implementation "org.bouncycastle:bcprov-jdk18on:$bouncycastleVersion"
implementation "org.bouncycastle:bcpkix-jdk18on:$bouncycastleVersion"
implementation "org.springframework.boot:spring-boot-starter-actuator:$springBootVersion"
implementation "io.micrometer:micrometer-core:1.14.4"
implementation "io.micrometer:micrometer-core:1.14.3"
implementation group: "com.google.zxing", name: "core", version: "3.5.3"
// https://mvnrepository.com/artifact/org.commonmark/commonmark
implementation "org.commonmark:commonmark:0.24.0"

View File

@@ -6,7 +6,6 @@ import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.model.ApplicationProperties;
@Configuration

View File

@@ -13,7 +13,6 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import com.posthog.java.shaded.org.json.JSONObject;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.model.ApplicationProperties;
import stirling.software.SPDF.utils.GeneralUtils;

View File

@@ -7,7 +7,6 @@ import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.model.ApplicationProperties;
import stirling.software.SPDF.utils.GeneralUtils;

View File

@@ -1,6 +1,7 @@
package stirling.software.SPDF;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -21,14 +22,11 @@ import io.github.pixee.security.SystemCommand;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.UI.WebBrowser;
import stirling.software.SPDF.config.ConfigInitializer;
import stirling.software.SPDF.config.InstallationPathConfig;
import stirling.software.SPDF.model.ApplicationProperties;
import stirling.software.SPDF.utils.UrlUtils;
@Slf4j
@EnableScheduling
@@ -64,12 +62,6 @@ public class SPDFApplication {
app.setHeadless(false);
props.put("java.awt.headless", "false");
props.put("spring.main.web-application-type", "servlet");
int desiredPort = 8080;
String port = UrlUtils.findAvailablePort(desiredPort);
props.put("server.port", port);
System.setProperty("server.port", port);
log.info("Desktop UI mode: Using port {}", port);
}
app.setAdditionalProfiles(getActiveProfile(args));
@@ -166,17 +158,7 @@ public class SPDFApplication {
}
@Value("${server.port:8080}")
public void setServerPort(String port) {
if ("auto".equalsIgnoreCase(port)) {
// Use Spring Boot's automatic port assignment (server.port=0)
SPDFApplication.serverPortStatic =
"0"; // This will let Spring Boot assign an available port
} else {
SPDFApplication.serverPortStatic = port;
}
}
public static void setServerPortStatic(String port) {
public void setServerPortStatic(String port) {
if ("auto".equalsIgnoreCase(port)) {
// Use Spring Boot's automatic port assignment (server.port=0)
SPDFApplication.serverPortStatic =
@@ -213,11 +195,36 @@ public class SPDFApplication {
return new String[] {"default"};
}
private static boolean isPortAvailable(int port) {
try (ServerSocket socket = new ServerSocket(port)) {
return true;
} catch (IOException e) {
return false;
}
}
// Optionally keep this method if you want to provide a manual port-incrementation fallback.
private static String findAvailablePort(int startPort) {
int port = startPort;
while (!isPortAvailable(port)) {
port++;
}
return String.valueOf(port);
}
public static String getStaticBaseUrl() {
return baseUrlStatic;
}
public String getNonStaticBaseUrl() {
return baseUrlStatic;
}
public static String getStaticPort() {
return serverPortStatic;
}
public String getNonStaticPort() {
return serverPortStatic;
}
}

View File

@@ -34,17 +34,13 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
import jakarta.annotation.PreDestroy;
import lombok.extern.slf4j.Slf4j;
import me.friwi.jcefmaven.CefAppBuilder;
import me.friwi.jcefmaven.EnumProgress;
import me.friwi.jcefmaven.MavenCefAppHandlerAdapter;
import me.friwi.jcefmaven.impl.progress.ConsoleProgressHandler;
import stirling.software.SPDF.UI.WebBrowser;
import stirling.software.SPDF.config.InstallationPathConfig;
import stirling.software.SPDF.utils.UIScaling;
@Component
@Slf4j
@@ -219,7 +215,7 @@ public class DesktopBrowser implements WebBrowser {
}
});
frame.setSize(UIScaling.scaleWidth(1280), UIScaling.scaleHeight(800));
frame.setSize(1280, 768);
frame.setLocationRelativeTo(null);
loadIcon();
@@ -268,9 +264,7 @@ public class DesktopBrowser implements WebBrowser {
frame.setOpacity(1.0f);
frame.setUndecorated(false);
frame.pack();
frame.setSize(
UIScaling.scaleWidth(1280),
UIScaling.scaleHeight(800));
frame.setSize(1280, 800);
frame.setLocationRelativeTo(null);
log.debug("Frame reconfigured");

View File

@@ -1,22 +1,13 @@
package stirling.software.SPDF.UI.impl;
import java.awt.*;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import javax.imageio.ImageIO;
import javax.swing.*;
import io.github.pixee.security.BoundedLineReader;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.utils.UIScaling;
@Slf4j
public class LoadingWindow extends JDialog {
private final JProgressBar progressBar;
@@ -25,13 +16,6 @@ public class LoadingWindow extends JDialog {
private final JLabel brandLabel;
private long startTime;
private Timer stuckTimer;
private long stuckThreshold = 4000;
private long timeAt90Percent = -1;
private volatile Process explorerProcess;
private static final boolean IS_WINDOWS =
System.getProperty("os.name").toLowerCase().contains("win");
public LoadingWindow(Frame parent, String initialUrl) {
super(parent, "Initializing Stirling-PDF", true);
startTime = System.currentTimeMillis();
@@ -57,12 +41,12 @@ public class LoadingWindow extends JDialog {
if (is != null) {
Image img = ImageIO.read(is);
if (img != null) {
Image scaledImg = UIScaling.scaleIcon(img, 48, 48);
Image scaledImg = img.getScaledInstance(48, 48, Image.SCALE_SMOOTH);
JLabel iconLabel = new JLabel(new ImageIcon(scaledImg));
iconLabel.setHorizontalAlignment(SwingConstants.CENTER);
gbc.gridy = 0;
mainPanel.add(iconLabel, gbc);
log.info("Icon loaded and scaled successfully");
log.debug("Icon loaded and scaled successfully");
}
}
}
@@ -99,8 +83,7 @@ public class LoadingWindow extends JDialog {
setUndecorated(false);
// Set size and position
setSize(UIScaling.scaleWidth(400), UIScaling.scaleHeight(200));
setSize(400, 200);
setLocationRelativeTo(parent);
setAlwaysOnTop(true);
setProgress(0);
@@ -111,163 +94,6 @@ public class LoadingWindow extends JDialog {
System.currentTimeMillis() - startTime);
}
private void checkAndRefreshExplorer() {
if (!IS_WINDOWS) {
return;
}
if (timeAt90Percent == -1) {
timeAt90Percent = System.currentTimeMillis();
stuckTimer =
new Timer(
1000,
e -> {
long currentTime = System.currentTimeMillis();
if (currentTime - timeAt90Percent > stuckThreshold) {
try {
log.debug(
"Attempting Windows explorer refresh due to 90% stuck state");
String currentDir = System.getProperty("user.dir");
// Store current explorer PIDs before we start new one
Set<String> existingPids = new HashSet<>();
ProcessBuilder listExplorer =
new ProcessBuilder(
"cmd",
"/c",
"wmic",
"process",
"where",
"name='explorer.exe'",
"get",
"ProcessId",
"/format:csv");
Process process = listExplorer.start();
BufferedReader reader =
new BufferedReader(
new InputStreamReader(
process.getInputStream()));
String line;
while ((line =
BoundedLineReader.readLine(
reader, 5_000_000))
!= null) {
if (line.matches(".*\\d+.*")) { // Contains numbers
String[] parts = line.trim().split(",");
if (parts.length >= 2) {
existingPids.add(
parts[parts.length - 1].trim());
}
}
}
process.waitFor(2, TimeUnit.SECONDS);
// Start new explorer
ProcessBuilder pb =
new ProcessBuilder(
"cmd",
"/c",
"start",
"/min",
"/b",
"explorer.exe",
currentDir);
pb.redirectErrorStream(true);
explorerProcess = pb.start();
// Schedule cleanup
Timer cleanupTimer =
new Timer(
2000,
cleanup -> {
try {
// Find new explorer processes
ProcessBuilder findNewExplorer =
new ProcessBuilder(
"cmd",
"/c",
"wmic",
"process",
"where",
"name='explorer.exe'",
"get",
"ProcessId",
"/format:csv");
Process newProcess =
findNewExplorer.start();
BufferedReader newReader =
new BufferedReader(
new InputStreamReader(
newProcess
.getInputStream()));
String newLine;
while ((newLine =
BoundedLineReader
.readLine(
newReader,
5_000_000))
!= null) {
if (newLine.matches(
".*\\d+.*")) {
String[] parts =
newLine.trim()
.split(",");
if (parts.length >= 2) {
String pid =
parts[
parts.length
- 1]
.trim();
if (!existingPids
.contains(
pid)) {
log.debug(
"Found new explorer.exe with PID: "
+ pid);
ProcessBuilder
killProcess =
new ProcessBuilder(
"taskkill",
"/PID",
pid,
"/F");
killProcess
.redirectErrorStream(
true);
Process killResult =
killProcess
.start();
killResult.waitFor(
2,
TimeUnit
.SECONDS);
log.debug(
"Explorer process terminated: "
+ pid);
}
}
}
}
newProcess.waitFor(
2, TimeUnit.SECONDS);
} catch (Exception ex) {
log.error(
"Error cleaning up Windows explorer process",
ex);
}
});
cleanupTimer.setRepeats(false);
cleanupTimer.start();
stuckTimer.stop();
} catch (Exception ex) {
log.error("Error refreshing Windows explorer", ex);
}
}
});
stuckTimer.setRepeats(true);
stuckTimer.start();
}
}
public void setProgress(final int progress) {
SwingUtilities.invokeLater(
() -> {
@@ -289,23 +115,11 @@ public class LoadingWindow extends JDialog {
// Add thread state logging
Thread currentThread = Thread.currentThread();
log.info(
log.debug(
"Current thread state - Name: {}, State: {}, Priority: {}",
currentThread.getName(),
currentThread.getState(),
currentThread.getPriority());
if (validProgress >= 90 && validProgress < 95) {
checkAndRefreshExplorer();
} else {
// Reset the timer if we move past 95%
if (validProgress >= 95) {
if (stuckTimer != null) {
stuckTimer.stop();
}
timeAt90Percent = -1;
}
}
}
progressBar.setValue(validProgress);
@@ -331,7 +145,7 @@ public class LoadingWindow extends JDialog {
statusLabel.setText(validStatus);
// Log UI state when status changes
log.info(
log.debug(
"UI State - Window visible: {}, Progress: {}%, Status: {}",
isVisible(), progressBar.getValue(), validStatus);

View File

@@ -20,7 +20,6 @@ import org.springframework.core.io.ResourceLoader;
import org.thymeleaf.spring6.SpringTemplateEngine;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.model.ApplicationProperties;
@Configuration
@@ -128,6 +127,15 @@ public class AppConfig {
}
}
@Bean(name = "bookAndHtmlFormatsInstalled")
public boolean bookAndHtmlFormatsInstalled() {
String installOps = System.getProperty("INSTALL_BOOK_AND_ADVANCED_HTML_OPS");
if (installOps == null) {
installOps = System.getenv("INSTALL_BOOK_AND_ADVANCED_HTML_OPS");
}
return "true".equalsIgnoreCase(installOps);
}
@ConditionalOnMissingClass("stirling.software.SPDF.config.security.SecurityConfiguration")
@Bean(name = "activSecurity")
public boolean missingActivSecurity() {

View File

@@ -2,13 +2,13 @@ package stirling.software.SPDF.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
import stirling.software.SPDF.config.interfaces.ShowAdminInterface;
import stirling.software.SPDF.model.ApplicationProperties;
@Configuration
@Service
class AppUpdateService {
private final ApplicationProperties applicationProperties;

View File

@@ -1,5 +1,6 @@
package stirling.software.SPDF.config;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
@@ -8,24 +9,30 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.DependsOn;
import org.springframework.stereotype.Service;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.model.ApplicationProperties;
@Service
@Slf4j
@DependsOn({"bookAndHtmlFormatsInstalled"})
public class EndpointConfiguration {
private static final String REMOVE_BLANKS = "remove-blanks";
private final ApplicationProperties applicationProperties;
private Map<String, Boolean> endpointStatuses = new ConcurrentHashMap<>();
private Map<String, Set<String>> endpointGroups = new ConcurrentHashMap<>();
private boolean bookAndHtmlFormatsInstalled;
@Autowired
public EndpointConfiguration(ApplicationProperties applicationProperties) {
public EndpointConfiguration(
ApplicationProperties applicationProperties,
@Qualifier("bookAndHtmlFormatsInstalled") boolean bookAndHtmlFormatsInstalled) {
this.applicationProperties = applicationProperties;
this.bookAndHtmlFormatsInstalled = bookAndHtmlFormatsInstalled;
init();
processEnvironmentConfigs();
}
@@ -190,8 +197,8 @@ public class EndpointConfiguration {
addEndpointToGroup("LibreOffice", "pdf-to-html");
addEndpointToGroup("LibreOffice", "pdf-to-xml");
// Unoconvert
addEndpointToGroup("Unoconvert", "file-to-pdf");
// Unoconv
addEndpointToGroup("Unoconv", "file-to-pdf");
// qpdf
addEndpointToGroup("qpdf", "compress-pdf");
@@ -265,6 +272,12 @@ public class EndpointConfiguration {
List<String> endpointsToRemove = applicationProperties.getEndpoints().getToRemove();
List<String> groupsToRemove = applicationProperties.getEndpoints().getGroupsToRemove();
if (!bookAndHtmlFormatsInstalled) {
if (groupsToRemove == null) {
groupsToRemove = new ArrayList<>();
}
groupsToRemove.add("Calibre");
}
if (endpointsToRemove != null) {
for (String endpoint : endpointsToRemove) {
disableEndpoint(endpoint.trim());

View File

@@ -9,7 +9,6 @@ import java.util.stream.Collectors;
import org.springframework.context.annotation.Configuration;
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
@Configuration
@@ -17,29 +16,21 @@ import lombok.extern.slf4j.Slf4j;
public class ExternalAppDepConfig {
private final EndpointConfiguration endpointConfiguration;
private final Map<String, List<String>> commandToGroupMapping =
new HashMap<>() {
private final String weasyprintPath;
private final String unoconvPath;
private final Map<String, List<String>> commandToGroupMapping;
{
put("soffice", List.of("LibreOffice"));
put("weasyprint", List.of("Weasyprint"));
put("pdftohtml", List.of("Pdftohtml"));
put("unoconv", List.of("Unoconv"));
put("qpdf", List.of("qpdf"));
put("tesseract", List.of("tesseract"));
}
};
public ExternalAppDepConfig(
EndpointConfiguration endpointConfiguration, RuntimePathConfig runtimePathConfig) {
public ExternalAppDepConfig(EndpointConfiguration endpointConfiguration) {
this.endpointConfiguration = endpointConfiguration;
weasyprintPath = runtimePathConfig.getWeasyPrintPath();
unoconvPath = runtimePathConfig.getUnoConvertPath();
commandToGroupMapping =
new HashMap<>() {
{
put("soffice", List.of("LibreOffice"));
put(weasyprintPath, List.of("Weasyprint"));
put("pdftohtml", List.of("Pdftohtml"));
put(unoconvPath, List.of("Unoconvert"));
put("qpdf", List.of("qpdf"));
put("tesseract", List.of("tesseract"));
}
};
}
private boolean isCommandAvailable(String command) {
@@ -110,9 +101,9 @@ public class ExternalAppDepConfig {
checkDependencyAndDisableGroup("tesseract");
checkDependencyAndDisableGroup("soffice");
checkDependencyAndDisableGroup("qpdf");
checkDependencyAndDisableGroup(weasyprintPath);
checkDependencyAndDisableGroup("weasyprint");
checkDependencyAndDisableGroup("pdftohtml");
checkDependencyAndDisableGroup(unoconvPath);
checkDependencyAndDisableGroup("unoconv");
// Special handling for Python/OpenCV dependencies
boolean pythonAvailable = isCommandAvailable("python3") || isCommandAvailable("python");
if (!pythonAvailable) {

View File

@@ -13,9 +13,7 @@ import org.springframework.stereotype.Component;
import io.micrometer.common.util.StringUtils;
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.model.ApplicationProperties;
import stirling.software.SPDF.utils.GeneralUtils;

View File

@@ -11,6 +11,7 @@ public class InstallationPathConfig {
// 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;
@@ -18,6 +19,11 @@ public class InstallationPathConfig {
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;
private static final String PIPELINE_DEFAULT_WEB_UI_CONFIGS;
// Custom file paths
private static final String STATIC_PATH;
private static final String TEMPLATES_PATH;
@@ -29,6 +35,7 @@ public class InstallationPathConfig {
// 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;
@@ -36,6 +43,11 @@ public class InstallationPathConfig {
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;
PIPELINE_DEFAULT_WEB_UI_CONFIGS = PIPELINE_PATH + "defaultWebUIConfigs" + File.separator;
// Initialize custom file paths
STATIC_PATH = CUSTOM_FILES_PATH + "static" + File.separator;
TEMPLATES_PATH = CUSTOM_FILES_PATH + "templates" + File.separator;
@@ -80,6 +92,10 @@ public class InstallationPathConfig {
return CONFIG_PATH;
}
public static String getPipelinePath() {
return PIPELINE_PATH;
}
public static String getCustomFilesPath() {
return CUSTOM_FILES_PATH;
}
@@ -96,6 +112,18 @@ public class InstallationPathConfig {
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 getPipelineDefaultWebUIConfigsDir() {
return PIPELINE_DEFAULT_WEB_UI_CONFIGS;
}
public static String getStaticPath() {
return STATIC_PATH;
}

View File

@@ -14,7 +14,6 @@ import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import stirling.software.SPDF.utils.RequestUriUtils;
@Component

View File

@@ -7,7 +7,6 @@ import org.springframework.context.annotation.Configuration;
import com.posthog.java.PostHog;
import jakarta.annotation.PreDestroy;
import lombok.extern.slf4j.Slf4j;
@Configuration

View File

@@ -1,84 +0,0 @@
package stirling.software.SPDF.config;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.annotation.Configuration;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.model.ApplicationProperties;
import stirling.software.SPDF.model.ApplicationProperties.CustomPaths.Operations;
import stirling.software.SPDF.model.ApplicationProperties.CustomPaths.Pipeline;
@Slf4j
@Configuration
@Getter
public class RuntimePathConfig {
private final ApplicationProperties properties;
private final String basePath;
private final String weasyPrintPath;
private final String unoConvertPath;
// Pipeline paths
private final String pipelineWatchedFoldersPath;
private final String pipelineFinishedFoldersPath;
private final String pipelineDefaultWebUiConfigs;
private final String pipelinePath;
public RuntimePathConfig(ApplicationProperties properties) {
this.properties = properties;
this.basePath = InstallationPathConfig.getPath();
String pipelinePath = basePath + "pipeline" + File.separator;
String watchedFoldersPath = pipelinePath + "watchedFolders" + File.separator;
String finishedFoldersPath = pipelinePath + "finishedFolders" + File.separator;
String webUiConfigsPath = pipelinePath + "defaultWebUIConfigs" + File.separator;
Pipeline pipeline = properties.getSystem().getCustomPaths().getPipeline();
if (pipeline != null) {
if (!StringUtils.isEmpty(pipeline.getWatchedFoldersDir())) {
watchedFoldersPath = pipeline.getWatchedFoldersDir();
}
if (!StringUtils.isEmpty(pipeline.getFinishedFoldersDir())) {
finishedFoldersPath = pipeline.getFinishedFoldersDir();
}
if (!StringUtils.isEmpty(pipeline.getWebUIConfigsDir())) {
webUiConfigsPath = pipeline.getWebUIConfigsDir();
}
}
this.pipelinePath = pipelinePath;
this.pipelineWatchedFoldersPath = watchedFoldersPath;
this.pipelineFinishedFoldersPath = finishedFoldersPath;
this.pipelineDefaultWebUiConfigs = webUiConfigsPath;
boolean isDocker = isRunningInDocker();
// Initialize Operation paths
String weasyPrintPath = isDocker ? "/opt/venv/bin/weasyprint" : "weasyprint";
String unoConvertPath = isDocker ? "/opt/venv/bin/unoconvert" : "unoconvert";
// Check for custom operation paths
Operations operations = properties.getSystem().getCustomPaths().getOperations();
if (operations != null) {
if (!StringUtils.isEmpty(operations.getWeasyprint())) {
weasyPrintPath = operations.getWeasyprint();
}
if (!StringUtils.isEmpty(operations.getUnoconvert())) {
unoConvertPath = operations.getUnoconvert();
}
}
// Assign operations final fields
this.weasyPrintPath = weasyPrintPath;
this.unoConvertPath = unoConvertPath;
}
private boolean isRunningInDocker() {
return Files.exists(Paths.get("/.dockerenv"));
}
}

View File

@@ -14,9 +14,7 @@ import org.springframework.security.web.authentication.SimpleUrlAuthenticationFa
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.model.User;
@Slf4j

View File

@@ -10,9 +10,7 @@ import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.utils.RequestUriUtils;
@Slf4j

View File

@@ -18,10 +18,8 @@ import com.coveo.saml.SamlClient;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.SPDFApplication;
import stirling.software.SPDF.config.security.saml2.CertificateUtils;
import stirling.software.SPDF.config.security.saml2.CustomSaml2AuthenticatedPrincipal;

View File

@@ -16,9 +16,7 @@ import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.model.User;
import stirling.software.SPDF.utils.RequestUriUtils;

View File

@@ -6,7 +6,6 @@ import java.util.concurrent.atomic.AtomicInteger;
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import stirling.software.SPDF.utils.RequestUriUtils;
public class IPRateLimitingFilter implements Filter {

View File

@@ -6,9 +6,7 @@ import java.util.UUID;
import org.springframework.stereotype.Component;
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.config.interfaces.DatabaseInterface;
import stirling.software.SPDF.model.ApplicationProperties;
import stirling.software.SPDF.model.Role;

View File

@@ -6,9 +6,7 @@ import java.util.concurrent.TimeUnit;
import org.springframework.stereotype.Service;
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.model.ApplicationProperties;
import stirling.software.SPDF.model.AttemptCounter;

View File

@@ -29,7 +29,6 @@ import org.springframework.security.web.savedrequest.NullRequestCache;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.config.security.oauth2.CustomOAuth2AuthenticationFailureHandler;
import stirling.software.SPDF.config.security.oauth2.CustomOAuth2AuthenticationSuccessHandler;
import stirling.software.SPDF.config.security.oauth2.CustomOAuth2UserService;

View File

@@ -22,9 +22,7 @@ import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.config.security.saml2.CustomSaml2AuthenticatedPrincipal;
import stirling.software.SPDF.config.security.session.SessionPersistentRegistry;
import stirling.software.SPDF.model.ApiKeyAuthenticationToken;

View File

@@ -23,7 +23,6 @@ import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import stirling.software.SPDF.model.Role;
@Component

View File

@@ -21,7 +21,6 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.config.interfaces.DatabaseInterface;
import stirling.software.SPDF.config.security.saml2.CustomSaml2AuthenticatedPrincipal;
import stirling.software.SPDF.config.security.session.SessionPersistentRegistry;
@@ -140,9 +139,6 @@ public class UserService implements UserServiceInterface {
User user =
findByUsernameIgnoreCase(username)
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
if (user.getApiKey() == null || user.getApiKey().length() == 0) {
user = addApiKeyToUser(username);
}
return user.getApiKey();
}

View File

@@ -11,7 +11,6 @@ import org.springframework.context.annotation.Configuration;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.config.InstallationPathConfig;
import stirling.software.SPDF.model.ApplicationProperties;
import stirling.software.SPDF.model.provider.UnsupportedProviderException;

View File

@@ -26,7 +26,6 @@ import org.springframework.jdbc.datasource.init.ScriptException;
import org.springframework.stereotype.Service;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.config.InstallationPathConfig;
import stirling.software.SPDF.config.interfaces.DatabaseInterface;
import stirling.software.SPDF.model.ApplicationProperties;

View File

@@ -13,7 +13,6 @@ import org.springframework.security.web.authentication.SimpleUrlAuthenticationFa
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
@Slf4j

View File

@@ -14,7 +14,6 @@ import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import stirling.software.SPDF.config.security.LoginAttemptService;
import stirling.software.SPDF.config.security.UserService;
import stirling.software.SPDF.model.ApplicationProperties;

View File

@@ -12,7 +12,6 @@ import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.config.security.LoginAttemptService;
import stirling.software.SPDF.config.security.UserService;
import stirling.software.SPDF.model.ApplicationProperties;

View File

@@ -20,7 +20,6 @@ import org.springframework.security.oauth2.client.registration.InMemoryClientReg
import org.springframework.security.oauth2.core.user.OAuth2UserAuthority;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.config.security.UserService;
import stirling.software.SPDF.model.ApplicationProperties;
import stirling.software.SPDF.model.ApplicationProperties.Security.OAUTH2;

View File

@@ -11,7 +11,6 @@ import org.springframework.security.web.authentication.SimpleUrlAuthenticationFa
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
@Slf4j

View File

@@ -12,10 +12,8 @@ import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.config.security.LoginAttemptService;
import stirling.software.SPDF.config.security.UserService;
import stirling.software.SPDF.model.ApplicationProperties;

View File

@@ -13,7 +13,6 @@ import org.springframework.security.saml2.provider.service.authentication.OpenSa
import org.springframework.security.saml2.provider.service.authentication.Saml2Authentication;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.config.security.UserService;
import stirling.software.SPDF.model.User;

View File

@@ -18,9 +18,7 @@ import org.springframework.security.saml2.provider.service.registration.Saml2Mes
import org.springframework.security.saml2.provider.service.web.authentication.OpenSaml4AuthenticationRequestResolver;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.model.ApplicationProperties;
import stirling.software.SPDF.model.ApplicationProperties.Security.SAML2;

View File

@@ -5,7 +5,6 @@ import org.springframework.stereotype.Component;
import jakarta.servlet.http.HttpSessionEvent;
import jakarta.servlet.http.HttpSessionListener;
import lombok.extern.slf4j.Slf4j;
@Component

View File

@@ -11,7 +11,6 @@ import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.stereotype.Component;
import jakarta.transaction.Transactional;
import stirling.software.SPDF.config.security.saml2.CustomSaml2AuthenticatedPrincipal;
import stirling.software.SPDF.model.SessionEntity;

View File

@@ -10,7 +10,6 @@ import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import jakarta.transaction.Transactional;
import stirling.software.SPDF.model.SessionEntity;
@Repository

View File

@@ -13,7 +13,6 @@ import org.springframework.web.bind.annotation.RestController;
import io.swagger.v3.oas.annotations.Hidden;
import jakarta.servlet.http.HttpServletResponse;
import stirling.software.SPDF.service.LanguageService;
@RestController

View File

@@ -25,7 +25,6 @@ import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.config.security.database.DatabaseService;
@Slf4j

View File

@@ -32,7 +32,6 @@ import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.model.api.general.MergePdfsRequest;
import stirling.software.SPDF.service.CustomPDDocumentFactory;
import stirling.software.SPDF.utils.GeneralUtils;

View File

@@ -21,7 +21,6 @@ import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.model.SortTypes;
import stirling.software.SPDF.model.api.PDFWithPageNums;
import stirling.software.SPDF.model.api.general.RearrangePagesRequest;
@@ -175,38 +174,7 @@ public class RearrangePagesPDFController {
return newPageOrderZeroBased;
}
private List<Integer> duplicate(int totalPages, String pageOrder) {
List<Integer> newPageOrder = new ArrayList<>();
int duplicateCount;
try {
// Parse the duplicate count from pageOrder
duplicateCount =
pageOrder != null && !pageOrder.isEmpty()
? Integer.parseInt(pageOrder.trim())
: 2; // Default to 2 if not specified
} catch (NumberFormatException e) {
log.error("Invalid duplicate count specified", e);
duplicateCount = 2; // Default to 2 if invalid input
}
// Validate duplicate count
if (duplicateCount < 1) {
duplicateCount = 2; // Default to 2 if invalid input
}
// For each page in the document
for (int pageNum = 0; pageNum < totalPages; pageNum++) {
// Add the current page index duplicateCount times
for (int dupCount = 0; dupCount < duplicateCount; dupCount++) {
newPageOrder.add(pageNum);
}
}
return newPageOrder;
}
private List<Integer> processSortTypes(String sortTypes, int totalPages, String pageOrder) {
private List<Integer> processSortTypes(String sortTypes, int totalPages) {
try {
SortTypes mode = SortTypes.valueOf(sortTypes.toUpperCase());
switch (mode) {
@@ -228,8 +196,6 @@ public class RearrangePagesPDFController {
return removeLast(totalPages);
case REMOVE_FIRST_AND_LAST:
return removeFirstAndLast(totalPages);
case DUPLICATE:
return duplicate(totalPages, pageOrder);
default:
throw new IllegalArgumentException("Unsupported custom mode");
}
@@ -257,10 +223,8 @@ public class RearrangePagesPDFController {
String[] pageOrderArr = pageOrder != null ? pageOrder.split(",") : new String[0];
int totalPages = document.getNumberOfPages();
List<Integer> newPageOrder;
if (sortType != null
&& sortType.length() > 0
&& !"custom".equals(sortType.toLowerCase())) {
newPageOrder = processSortTypes(sortType, totalPages, pageOrder);
if (sortType != null && sortType.length() > 0) {
newPageOrder = processSortTypes(sortType, totalPages);
} else {
newPageOrder = GeneralUtils.parsePageList(pageOrderArr, totalPages, false);
}

View File

@@ -27,7 +27,6 @@ import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.model.api.PDFWithPageNums;
import stirling.software.SPDF.service.CustomPDDocumentFactory;
import stirling.software.SPDF.utils.WebResponseUtils;

View File

@@ -31,7 +31,6 @@ import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.model.PdfMetadata;
import stirling.software.SPDF.model.api.SplitPdfByChaptersRequest;
import stirling.software.SPDF.service.PdfMetadataService;

View File

@@ -24,7 +24,6 @@ import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.model.api.general.SplitPdfBySizeOrCountRequest;
import stirling.software.SPDF.service.CustomPDDocumentFactory;
import stirling.software.SPDF.utils.GeneralUtils;

View File

@@ -26,9 +26,7 @@ import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.config.security.UserService;
import stirling.software.SPDF.config.security.saml2.CustomSaml2AuthenticatedPrincipal;
import stirling.software.SPDF.config.security.session.SessionPersistentRegistry;

View File

@@ -0,0 +1,77 @@
package stirling.software.SPDF.controller.api.converters;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.multipart.MultipartFile;
import io.github.pixee.security.Filenames;
import io.swagger.v3.oas.annotations.Operation;
import stirling.software.SPDF.model.api.GeneralFile;
import stirling.software.SPDF.service.CustomPDDocumentFactory;
import stirling.software.SPDF.utils.FileToPdf;
import stirling.software.SPDF.utils.WebResponseUtils;
// @RestController
// @Tag(name = "Convert", description = "Convert APIs")
// @RequestMapping("/api/v1/convert")
public class ConvertBookToPDFController {
private final boolean bookAndHtmlFormatsInstalled;
private final CustomPDDocumentFactory pdfDocumentFactory;
@Autowired
public ConvertBookToPDFController(
CustomPDDocumentFactory pdfDocumentFactory,
@Qualifier("bookAndHtmlFormatsInstalled") boolean bookAndHtmlFormatsInstalled) {
this.pdfDocumentFactory = pdfDocumentFactory;
this.bookAndHtmlFormatsInstalled = bookAndHtmlFormatsInstalled;
}
@PostMapping(consumes = "multipart/form-data", value = "/book/pdf")
@Operation(
summary =
"Convert a BOOK/comic (*.epub | *.mobi | *.azw3 | *.fb2 | *.txt | *.docx) to PDF",
description =
"(Requires bookAndHtmlFormatsInstalled flag and Calibre installed) This endpoint takes an BOOK/comic (*.epub | *.mobi | *.azw3 | *.fb2 | *.txt | *.docx) input and converts it to PDF format.")
public ResponseEntity<byte[]> HtmlToPdf(@ModelAttribute GeneralFile request) throws Exception {
MultipartFile fileInput = request.getFileInput();
if (!bookAndHtmlFormatsInstalled) {
throw new IllegalArgumentException(
"bookAndHtmlFormatsInstalled flag is False, this functionality is not available");
}
if (fileInput == null) {
throw new IllegalArgumentException("Please provide a file for conversion.");
}
String originalFilename = Filenames.toSimpleFileName(fileInput.getOriginalFilename());
if (originalFilename != null) {
String originalFilenameLower = originalFilename.toLowerCase();
if (!originalFilenameLower.endsWith(".epub")
&& !originalFilenameLower.endsWith(".mobi")
&& !originalFilenameLower.endsWith(".azw3")
&& !originalFilenameLower.endsWith(".fb2")
&& !originalFilenameLower.endsWith(".txt")
&& !originalFilenameLower.endsWith(".docx")) {
throw new IllegalArgumentException(
"File must be in .epub, .mobi, .azw3, .fb2, .txt, or .docx format.");
}
}
byte[] pdfBytes = FileToPdf.convertBookTypeToPdf(fileInput.getBytes(), originalFilename);
pdfBytes = pdfDocumentFactory.createNewBytesBasedOnOldDocument(pdfBytes);
String outputFilename =
originalFilename.replaceFirst("[.][^.]+$", "")
+ ".pdf"; // Remove file extension and append .pdf
return WebResponseUtils.bytesToWebResponse(pdfBytes, outputFilename);
}
}

View File

@@ -1,6 +1,7 @@
package stirling.software.SPDF.controller.api.converters;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
@@ -12,7 +13,6 @@ import io.github.pixee.security.Filenames;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import stirling.software.SPDF.config.RuntimePathConfig;
import stirling.software.SPDF.model.ApplicationProperties;
import stirling.software.SPDF.model.api.converters.HTMLToPdfRequest;
import stirling.software.SPDF.service.CustomPDDocumentFactory;
@@ -24,21 +24,20 @@ import stirling.software.SPDF.utils.WebResponseUtils;
@RequestMapping("/api/v1/convert")
public class ConvertHtmlToPDF {
private final boolean bookAndHtmlFormatsInstalled;
private final CustomPDDocumentFactory pdfDocumentFactory;
private final ApplicationProperties applicationProperties;
private final RuntimePathConfig runtimePathConfig;
@Autowired
public ConvertHtmlToPDF(
CustomPDDocumentFactory pdfDocumentFactory,
ApplicationProperties applicationProperties,
RuntimePathConfig runtimePathConfig) {
@Qualifier("bookAndHtmlFormatsInstalled") boolean bookAndHtmlFormatsInstalled,
ApplicationProperties applicationProperties) {
this.pdfDocumentFactory = pdfDocumentFactory;
this.bookAndHtmlFormatsInstalled = bookAndHtmlFormatsInstalled;
this.applicationProperties = applicationProperties;
this.runtimePathConfig = runtimePathConfig;
}
@PostMapping(consumes = "multipart/form-data", value = "/html/pdf")
@@ -66,10 +65,10 @@ public class ConvertHtmlToPDF {
byte[] pdfBytes =
FileToPdf.convertHtmlToPdf(
runtimePathConfig.getWeasyPrintPath(),
request,
fileInput.getBytes(),
originalFilename,
bookAndHtmlFormatsInstalled,
disableSanitize);
pdfBytes = pdfDocumentFactory.createNewBytesBasedOnOldDocument(pdfBytes);

View File

@@ -31,7 +31,6 @@ import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.model.api.converters.ConvertToImageRequest;
import stirling.software.SPDF.model.api.converters.ConvertToPdfRequest;
import stirling.software.SPDF.service.CustomPDDocumentFactory;

View File

@@ -11,6 +11,7 @@ import org.commonmark.parser.Parser;
import org.commonmark.renderer.html.AttributeProvider;
import org.commonmark.renderer.html.HtmlRenderer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
@@ -22,7 +23,6 @@ import io.github.pixee.security.Filenames;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import stirling.software.SPDF.config.RuntimePathConfig;
import stirling.software.SPDF.model.ApplicationProperties;
import stirling.software.SPDF.model.api.GeneralFile;
import stirling.software.SPDF.service.CustomPDDocumentFactory;
@@ -34,20 +34,20 @@ import stirling.software.SPDF.utils.WebResponseUtils;
@RequestMapping("/api/v1/convert")
public class ConvertMarkdownToPdf {
private final boolean bookAndHtmlFormatsInstalled;
private final CustomPDDocumentFactory pdfDocumentFactory;
private final ApplicationProperties applicationProperties;
private final RuntimePathConfig runtimePathConfig;
@Autowired
public ConvertMarkdownToPdf(
CustomPDDocumentFactory pdfDocumentFactory,
ApplicationProperties applicationProperties,
RuntimePathConfig runtimePathConfig) {
@Qualifier("bookAndHtmlFormatsInstalled") boolean bookAndHtmlFormatsInstalled,
ApplicationProperties applicationProperties) {
this.pdfDocumentFactory = pdfDocumentFactory;
this.bookAndHtmlFormatsInstalled = bookAndHtmlFormatsInstalled;
this.applicationProperties = applicationProperties;
this.runtimePathConfig = runtimePathConfig;
}
@PostMapping(consumes = "multipart/form-data", value = "/markdown/pdf")
@@ -86,10 +86,10 @@ public class ConvertMarkdownToPdf {
byte[] pdfBytes =
FileToPdf.convertHtmlToPdf(
runtimePathConfig.getWeasyPrintPath(),
null,
htmlContent.getBytes(),
"converted.html",
bookAndHtmlFormatsInstalled,
disableSanitize);
pdfBytes = pdfDocumentFactory.createNewBytesBasedOnOldDocument(pdfBytes);
String outputFilename =

View File

@@ -22,7 +22,6 @@ import io.github.pixee.security.Filenames;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import stirling.software.SPDF.config.RuntimePathConfig;
import stirling.software.SPDF.model.api.GeneralFile;
import stirling.software.SPDF.service.CustomPDDocumentFactory;
import stirling.software.SPDF.utils.ProcessExecutor;
@@ -35,13 +34,10 @@ import stirling.software.SPDF.utils.WebResponseUtils;
public class ConvertOfficeController {
private final CustomPDDocumentFactory pdfDocumentFactory;
private final RuntimePathConfig runtimePathConfig;
@Autowired
public ConvertOfficeController(
CustomPDDocumentFactory pdfDocumentFactory, RuntimePathConfig runtimePathConfig) {
public ConvertOfficeController(CustomPDDocumentFactory pdfDocumentFactory) {
this.pdfDocumentFactory = pdfDocumentFactory;
this.runtimePathConfig = runtimePathConfig;
}
public File convertToPdf(MultipartFile inputFile) throws IOException, InterruptedException {
@@ -65,13 +61,13 @@ public class ConvertOfficeController {
List<String> command =
new ArrayList<>(
Arrays.asList(
runtimePathConfig.getUnoConvertPath(),
"--port",
"2003",
"--convert-to",
"unoconv",
"-vvv",
"-f",
"pdf",
tempInputFile.toString(),
tempOutputFile.toString()));
"-o",
tempOutputFile.toString(),
tempInputFile.toString()));
ProcessExecutorResult returnCode =
ProcessExecutor.getInstance(ProcessExecutor.Processes.LIBRE_OFFICE)
.runCommandWithOutputHandling(command);

View File

@@ -0,0 +1,95 @@
package stirling.software.SPDF.controller.api.converters;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.multipart.MultipartFile;
import io.github.pixee.security.Filenames;
import io.swagger.v3.oas.annotations.Operation;
import stirling.software.SPDF.model.api.converters.PdfToBookRequest;
import stirling.software.SPDF.utils.ProcessExecutor;
import stirling.software.SPDF.utils.ProcessExecutor.ProcessExecutorResult;
import stirling.software.SPDF.utils.WebResponseUtils;
// @RestController
// @Tag(name = "Convert", description = "Convert APIs")
// @RequestMapping("/api/v1/convert")
public class ConvertPDFToBookController {
@Qualifier("bookAndHtmlFormatsInstalled")
private final boolean bookAndHtmlFormatsInstalled;
public ConvertPDFToBookController(
@Qualifier("bookAndHtmlFormatsInstalled") boolean bookAndHtmlFormatsInstalled) {
this.bookAndHtmlFormatsInstalled = bookAndHtmlFormatsInstalled;
}
@PostMapping(consumes = "multipart/form-data", value = "/pdf/book")
@Operation(
summary =
"Convert a PDF to a Book/comic (*.epub | *.mobi | *.azw3 | *.fb2 | *.txt | *.docx .. (others to include by chatgpt) to PDF",
description =
"(Requires bookAndHtmlFormatsInstalled flag and Calibre installed) This endpoint Convert a PDF to a Book/comic (*.epub | *.mobi | *.azw3 | *.fb2 | *.txt | *.docx .. (others to include by chatgpt) to PDF")
public ResponseEntity<byte[]> HtmlToPdf(@ModelAttribute PdfToBookRequest request)
throws Exception {
MultipartFile fileInput = request.getFileInput();
if (!bookAndHtmlFormatsInstalled) {
throw new IllegalArgumentException(
"bookAndHtmlFormatsInstalled flag is False, this functionality is not available");
}
if (fileInput == null) {
throw new IllegalArgumentException("Please provide a file for conversion.");
}
// Validate the output format
String outputFormat = request.getOutputFormat().toLowerCase();
List<String> allowedFormats =
Arrays.asList(
"epub", "mobi", "azw3", "docx", "rtf", "txt", "html", "lit", "fb2", "pdb",
"lrf");
if (!allowedFormats.contains(outputFormat)) {
throw new IllegalArgumentException("Invalid output format: " + outputFormat);
}
byte[] outputFileBytes;
List<String> command = new ArrayList<>();
Path tempOutputFile =
Files.createTempFile(
"output_", // Use the output format for the file extension
"." + outputFormat);
Path tempInputFile = null;
try {
// Create temp input file from the provided PDF
// Assuming input is always PDF
tempInputFile = Files.createTempFile("input_", ".pdf");
Files.write(tempInputFile, fileInput.getBytes());
command.add("ebook-convert");
command.add(tempInputFile.toString());
command.add(tempOutputFile.toString());
ProcessExecutorResult returnCode =
ProcessExecutor.getInstance(ProcessExecutor.Processes.CALIBRE)
.runCommandWithOutputHandling(command);
outputFileBytes = Files.readAllBytes(tempOutputFile);
} finally {
// Clean up temporary files
if (tempInputFile != null) {
Files.deleteIfExists(tempInputFile);
}
Files.deleteIfExists(tempOutputFile);
}
String outputFilename =
Filenames.toSimpleFileName(fileInput.getOriginalFilename())
.replaceFirst("[.][^.]+$", "")
+ "."
+ // Remove file extension and append .pdf
outputFormat;
return WebResponseUtils.bytesToWebResponse(outputFileBytes, outputFilename);
}
}

View File

@@ -21,7 +21,6 @@ import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.model.api.converters.PdfToPdfARequest;
import stirling.software.SPDF.utils.ProcessExecutor;
import stirling.software.SPDF.utils.ProcessExecutor.ProcessExecutorResult;

View File

@@ -18,8 +18,6 @@ import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.config.RuntimePathConfig;
import stirling.software.SPDF.model.api.converters.UrlToPdfRequest;
import stirling.software.SPDF.service.CustomPDDocumentFactory;
import stirling.software.SPDF.utils.GeneralUtils;
@@ -34,13 +32,10 @@ import stirling.software.SPDF.utils.WebResponseUtils;
public class ConvertWebsiteToPDF {
private final CustomPDDocumentFactory pdfDocumentFactory;
private final RuntimePathConfig runtimePathConfig;
@Autowired
public ConvertWebsiteToPDF(
CustomPDDocumentFactory pdfDocumentFactory, RuntimePathConfig runtimePathConfig) {
public ConvertWebsiteToPDF(CustomPDDocumentFactory pdfDocumentFactory) {
this.pdfDocumentFactory = pdfDocumentFactory;
this.runtimePathConfig = runtimePathConfig;
}
@PostMapping(consumes = "multipart/form-data", value = "/url/pdf")
@@ -70,7 +65,7 @@ public class ConvertWebsiteToPDF {
// Prepare the WeasyPrint command
List<String> command = new ArrayList<>();
command.add(runtimePathConfig.getWeasyPrintPath());
command.add("weasyprint");
command.add(URL);
command.add(tempOutputFile.toString());

View File

@@ -1,14 +1,7 @@
package stirling.software.SPDF.controller.api.converters;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.QuoteMode;
@@ -26,20 +19,17 @@ import org.springframework.web.bind.annotation.RestController;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.model.api.PDFWithPageNums;
import stirling.software.SPDF.model.api.extract.PDFFilePage;
import stirling.software.SPDF.pdf.FlexibleCSVWriter;
import technology.tabula.ObjectExtractor;
import technology.tabula.Page;
import technology.tabula.Table;
import technology.tabula.extractors.SpreadsheetExtractionAlgorithm;
import technology.tabula.writers.Writer;
@RestController
@RequestMapping("/api/v1/convert")
@Tag(name = "Convert", description = "Convert APIs")
@Slf4j
public class ExtractCSVController {
@PostMapping(value = "/pdf/csv", consumes = "multipart/form-data")
@@ -47,83 +37,31 @@ public class ExtractCSVController {
summary = "Extracts a CSV document from a PDF",
description =
"This operation takes an input PDF file and returns CSV file of whole page. Input:PDF Output:CSV Type:SISO")
public ResponseEntity<?> pdfToCsv(@ModelAttribute PDFWithPageNums form) throws Exception {
String baseName = getBaseName(form.getFileInput().getOriginalFilename());
List<CsvEntry> csvEntries = new ArrayList<>();
public ResponseEntity<String> PdfToCsv(@ModelAttribute PDFFilePage form) throws Exception {
StringWriter writer = new StringWriter();
try (PDDocument document = Loader.loadPDF(form.getFileInput().getBytes())) {
List<Integer> pages = form.getPageNumbersList(document, true);
SpreadsheetExtractionAlgorithm sea = new SpreadsheetExtractionAlgorithm();
CSVFormat format =
CSVFormat.EXCEL.builder().setEscape('"').setQuoteMode(QuoteMode.ALL).build();
for (int pageNum : pages) {
try (ObjectExtractor extractor = new ObjectExtractor(document)) {
log.info("{}", pageNum);
Page page = extractor.extract(pageNum);
List<Table> tables = sea.extract(page);
for (int i = 0; i < tables.size(); i++) {
StringWriter sw = new StringWriter();
FlexibleCSVWriter csvWriter = new FlexibleCSVWriter(format);
csvWriter.write(sw, Collections.singletonList(tables.get(i)));
String entryName = generateEntryName(baseName, pageNum, i + 1);
csvEntries.add(new CsvEntry(entryName, sw.toString()));
}
}
}
if (csvEntries.isEmpty()) {
return ResponseEntity.noContent().build();
} else if (csvEntries.size() == 1) {
return createCsvResponse(csvEntries.get(0), baseName);
} else {
return createZipResponse(csvEntries, baseName);
}
}
}
private ResponseEntity<byte[]> createZipResponse(List<CsvEntry> entries, String baseName)
throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (ZipOutputStream zipOut = new ZipOutputStream(baos)) {
for (CsvEntry entry : entries) {
ZipEntry zipEntry = new ZipEntry(entry.filename());
zipOut.putNextEntry(zipEntry);
zipOut.write(entry.content().getBytes(StandardCharsets.UTF_8));
zipOut.closeEntry();
Writer csvWriter = new FlexibleCSVWriter(format);
SpreadsheetExtractionAlgorithm sea = new SpreadsheetExtractionAlgorithm();
try (ObjectExtractor extractor = new ObjectExtractor(document)) {
Page page = extractor.extract(form.getPageId());
List<Table> tables = sea.extract(page);
csvWriter.write(writer, tables);
}
}
HttpHeaders headers = new HttpHeaders();
headers.setContentDisposition(
ContentDisposition.builder("attachment")
.filename(baseName + "_extracted.zip")
.build());
headers.setContentType(MediaType.parseMediaType("application/zip"));
return ResponseEntity.ok().headers(headers).body(baos.toByteArray());
}
private ResponseEntity<String> createCsvResponse(CsvEntry entry, String baseName) {
HttpHeaders headers = new HttpHeaders();
headers.setContentDisposition(
ContentDisposition.builder("attachment")
.filename(baseName + "_extracted.csv")
.filename(
form.getFileInput()
.getOriginalFilename()
.replaceFirst("[.][^.]+$", "")
+ "_extracted.csv")
.build());
headers.setContentType(MediaType.parseMediaType("text/csv"));
return ResponseEntity.ok().headers(headers).body(entry.content());
return ResponseEntity.ok().headers(headers).body(writer.toString());
}
private String generateEntryName(String baseName, int pageNum, int tableIndex) {
return String.format("%s_p%d_t%d.csv", baseName, pageNum, tableIndex);
}
private String getBaseName(String filename) {
return filename.replaceFirst("[.][^.]+$", "");
}
private record CsvEntry(String filename, String content) {}
}

View File

@@ -21,7 +21,6 @@ import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.model.api.misc.ExtractHeaderRequest;
import stirling.software.SPDF.utils.WebResponseUtils;

View File

@@ -33,7 +33,6 @@ import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.model.api.misc.AutoSplitPdfRequest;
import stirling.software.SPDF.service.CustomPDDocumentFactory;
import stirling.software.SPDF.utils.WebResponseUtils;

View File

@@ -29,7 +29,6 @@ import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.model.api.misc.RemoveBlankPagesRequest;
import stirling.software.SPDF.service.CustomPDDocumentFactory;
import stirling.software.SPDF.utils.PdfUtils;

View File

@@ -31,7 +31,6 @@ import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.model.api.misc.OptimizePdfRequest;
import stirling.software.SPDF.service.CustomPDDocumentFactory;
import stirling.software.SPDF.utils.GeneralUtils;
@@ -52,8 +51,7 @@ public class CompressController {
this.pdfDocumentFactory = pdfDocumentFactory;
}
private void compressImagesInPDF(Path pdfFile, double initialScaleFactor, boolean grayScale)
throws Exception {
private void compressImagesInPDF(Path pdfFile, double initialScaleFactor) throws Exception {
byte[] fileBytes = Files.readAllBytes(pdfFile);
try (PDDocument doc = Loader.loadPDF(fileBytes)) {
double scaleFactor = initialScaleFactor;
@@ -78,23 +76,11 @@ public class CompressController {
bufferedImage.getScaledInstance(
newWidth, newHeight, Image.SCALE_SMOOTH);
BufferedImage scaledBufferedImage;
if (grayScale
|| bufferedImage.getType() == BufferedImage.TYPE_BYTE_GRAY) {
scaledBufferedImage =
new BufferedImage(
newWidth, newHeight, BufferedImage.TYPE_BYTE_GRAY);
scaledBufferedImage
.getGraphics()
.drawImage(scaledImage, 0, 0, null);
} else {
scaledBufferedImage =
new BufferedImage(
newWidth, newHeight, BufferedImage.TYPE_INT_RGB);
scaledBufferedImage
.getGraphics()
.drawImage(scaledImage, 0, 0, null);
}
BufferedImage scaledBufferedImage =
new BufferedImage(
newWidth, newHeight, BufferedImage.TYPE_INT_RGB);
scaledBufferedImage.getGraphics().drawImage(scaledImage, 0, 0, null);
ByteArrayOutputStream compressedImageStream =
new ByteArrayOutputStream();
ImageIO.write(scaledBufferedImage, "jpeg", compressedImageStream);
@@ -153,7 +139,6 @@ public class CompressController {
}
boolean sizeMet = false;
boolean grayscaleEnabled = Boolean.TRUE.equals(request.getGrayscale());
while (!sizeMet && optimizeLevel <= 9) {
// Apply additional image compression for levels 6-9
@@ -167,7 +152,7 @@ public class CompressController {
case 9 -> 0.5; // 60% of original size
default -> 1.0;
};
compressImagesInPDF(tempInputFile, scaleFactor, grayscaleEnabled);
compressImagesInPDF(tempInputFile, scaleFactor);
}
// Run QPDF optimization
@@ -184,7 +169,6 @@ public class CompressController {
command.add("--compression-level=" + optimizeLevel);
command.add("--compress-streams=y");
command.add("--object-streams=generate");
command.add("--no-warn");
command.add(tempInputFile.toString());
command.add(tempOutputFile.toString());

View File

@@ -30,7 +30,6 @@ import io.swagger.v3.oas.annotations.parameters.RequestBody;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.model.api.misc.ExtractImageScansRequest;
import stirling.software.SPDF.utils.CheckProgramInstall;
import stirling.software.SPDF.utils.ProcessExecutor;

View File

@@ -38,7 +38,6 @@ import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.model.api.PDFExtractImagesRequest;
import stirling.software.SPDF.utils.ImageProcessingUtils;
import stirling.software.SPDF.utils.WebResponseUtils;

View File

@@ -25,7 +25,6 @@ import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.model.api.misc.FlattenRequest;
import stirling.software.SPDF.service.CustomPDDocumentFactory;
import stirling.software.SPDF.utils.WebResponseUtils;

View File

@@ -21,7 +21,6 @@ import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.model.api.misc.MetadataRequest;
import stirling.software.SPDF.utils.WebResponseUtils;
import stirling.software.SPDF.utils.propertyeditor.StringToMapPropertyEditor;

View File

@@ -30,7 +30,6 @@ import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.model.ApplicationProperties;
import stirling.software.SPDF.model.api.misc.ProcessPdfWithOcrRequest;
import stirling.software.SPDF.service.CustomPDDocumentFactory;

View File

@@ -16,7 +16,6 @@ import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.model.api.misc.OverlayImageRequest;
import stirling.software.SPDF.service.CustomPDDocumentFactory;
import stirling.software.SPDF.utils.PdfUtils;

View File

@@ -26,7 +26,6 @@ import org.springframework.web.multipart.MultipartFile;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.model.api.misc.PrintFileRequest;
@RestController

View File

@@ -19,9 +19,7 @@ import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.ServletContext;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.SPDFApplication;
import stirling.software.SPDF.model.ApiEndpoint;
import stirling.software.SPDF.model.Role;

View File

@@ -24,7 +24,6 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.model.PipelineConfig;
import stirling.software.SPDF.model.PipelineResult;
import stirling.software.SPDF.model.api.HandleDataRequest;

View File

@@ -5,14 +5,9 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.FileSystemException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.StandardCopyOption;
import java.nio.file.attribute.BasicFileAttributes;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
@@ -29,8 +24,7 @@ import org.springframework.stereotype.Service;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.config.RuntimePathConfig;
import stirling.software.SPDF.config.InstallationPathConfig;
import stirling.software.SPDF.model.PipelineConfig;
import stirling.software.SPDF.model.PipelineOperation;
import stirling.software.SPDF.model.PipelineResult;
@@ -56,19 +50,18 @@ public class PipelineDirectoryProcessor {
ObjectMapper objectMapper,
ApiDocService apiDocService,
PipelineProcessor processor,
FileMonitor fileMonitor,
RuntimePathConfig runtimePathConfig) {
FileMonitor fileMonitor) {
this.objectMapper = objectMapper;
this.apiDocService = apiDocService;
this.watchedFoldersDir = runtimePathConfig.getPipelineWatchedFoldersPath();
this.finishedFoldersDir = runtimePathConfig.getPipelineFinishedFoldersPath();
this.watchedFoldersDir = InstallationPathConfig.getPipelineWatchedFoldersDir();
this.finishedFoldersDir = InstallationPathConfig.getPipelineFinishedFoldersDir();
this.processor = processor;
this.fileMonitor = fileMonitor;
}
@Scheduled(fixedRate = 60000)
public void scanFolders() {
Path watchedFolderPath = Paths.get(watchedFoldersDir).toAbsolutePath();
Path watchedFolderPath = Paths.get(watchedFoldersDir);
if (!Files.exists(watchedFolderPath)) {
try {
Files.createDirectories(watchedFolderPath);
@@ -78,33 +71,19 @@ public class PipelineDirectoryProcessor {
return;
}
}
try {
Files.walkFileTree(
watchedFolderPath,
new SimpleFileVisitor<>() {
@Override
public FileVisitResult preVisitDirectory(
Path dir, BasicFileAttributes attrs) {
try {
// Skip root directory and "processing" subdirectories
if (!dir.equals(watchedFolderPath) && !dir.endsWith("processing")) {
handleDirectory(dir);
try (Stream<Path> paths = Files.walk(watchedFolderPath)) {
paths.filter(Files::isDirectory)
.forEach(
t -> {
try {
if (!t.equals(watchedFolderPath) && !t.endsWith("processing")) {
handleDirectory(t);
}
} catch (Exception e) {
log.error("Error handling directory: {}", t, e);
}
} catch (Exception e) {
log.error("Error handling directory: {}", dir, e);
}
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFileFailed(Path path, IOException exc) {
// Handle broken symlinks or inaccessible directories
log.error("Error accessing path: {}", path, exc);
return FileVisitResult.CONTINUE;
}
});
} catch (IOException e) {
});
} catch (Exception e) {
log.error("Error walking through directory: {}", watchedFolderPath, e);
}
}
@@ -208,7 +187,6 @@ public class PipelineDirectoryProcessor {
}
return isAllowed;
})
.map(Path::toAbsolutePath)
.filter(
path -> {
boolean isReady =
@@ -222,10 +200,7 @@ public class PipelineDirectoryProcessor {
})
.map(Path::toFile)
.toArray(File[]::new);
log.info(
"Collected {} files for processing for {}",
files.length,
dir.toAbsolutePath().toString());
log.info("Collected {} files for processing", files.length);
return files;
}
}
@@ -235,35 +210,8 @@ public class PipelineDirectoryProcessor {
List<File> filesToProcess = new ArrayList<>();
for (File file : files) {
Path targetPath = resolveUniqueFilePath(processingDir, file.getName());
// Retry with exponential backoff
int maxRetries = 3;
int retryDelayMs = 500;
boolean moved = false;
for (int attempt = 1; attempt <= maxRetries; attempt++) {
try {
Files.move(file.toPath(), targetPath, StandardCopyOption.REPLACE_EXISTING);
moved = true;
break;
} catch (FileSystemException e) {
if (attempt < maxRetries) {
log.info("File move failed (attempt {}), retrying...", attempt);
try {
Thread.sleep(retryDelayMs * (int) Math.pow(2, attempt - 1));
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
}
}
if (moved) {
filesToProcess.add(targetPath.toFile());
} else {
log.error("Failed to move file after {} attempts: {}", maxRetries, file.getName());
}
Files.move(file.toPath(), targetPath);
filesToProcess.add(targetPath.toFile());
}
return filesToProcess;
}

View File

@@ -29,9 +29,7 @@ import io.github.pixee.security.Filenames;
import io.github.pixee.security.ZipSecurity;
import jakarta.servlet.ServletContext;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.SPDFApplication;
import stirling.software.SPDF.model.PipelineConfig;
import stirling.software.SPDF.model.PipelineOperation;

View File

@@ -66,7 +66,6 @@ import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.model.api.security.SignPDFWithCertRequest;
import stirling.software.SPDF.service.CustomPDDocumentFactory;
import stirling.software.SPDF.utils.WebResponseUtils;

View File

@@ -60,7 +60,6 @@ import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.model.api.PDFFile;
import stirling.software.SPDF.utils.WebResponseUtils;

View File

@@ -26,7 +26,6 @@ import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.model.PDFText;
import stirling.software.SPDF.model.api.security.ManualRedactPdfRequest;
import stirling.software.SPDF.model.api.security.RedactPdfRequest;

Some files were not shown because too many files have changed in this diff Show More