Compare commits
217 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
08205ed32d | ||
|
|
9246b42057 | ||
|
|
67e4d6e3a2 | ||
|
|
cf4613d043 | ||
|
|
2f703796e9 | ||
|
|
731dc3f3dc | ||
|
|
97472310f2 | ||
|
|
ece1d071c0 | ||
|
|
20f532c872 | ||
|
|
bdcccfd937 | ||
|
|
146b8f0103 | ||
|
|
c8a37245fa | ||
|
|
af68c70239 | ||
|
|
5bd544dcd7 | ||
|
|
642b85069d | ||
|
|
6fef4ea82c | ||
|
|
8670afb96f | ||
|
|
33f8d60900 | ||
|
|
4e2156ad79 | ||
|
|
a07245224e | ||
|
|
f96a4cdb59 | ||
|
|
efea22aa6e | ||
|
|
ae9a7dc580 | ||
|
|
7135ace1aa | ||
|
|
625275124a | ||
|
|
c96ebccae4 | ||
|
|
20cb460a7e | ||
|
|
3a62d19979 | ||
|
|
51ad741744 | ||
|
|
673f005fe6 | ||
|
|
8d9f0361d0 | ||
|
|
56e3ec1219 | ||
|
|
a0acafcefc | ||
|
|
918f5954b7 | ||
|
|
148dcdaee7 | ||
|
|
a5f0777892 | ||
|
|
010426d488 | ||
|
|
6fc9c7be90 | ||
|
|
094fde9801 | ||
|
|
e4a76e96af | ||
|
|
68f582bcb9 | ||
|
|
639aed7120 | ||
|
|
994bb4d1d2 | ||
|
|
80b11a55fa | ||
|
|
3cfb554623 | ||
|
|
e84f9c5946 | ||
|
|
17cc31d6e7 | ||
|
|
0c6e10a6dd | ||
|
|
297c57631f | ||
|
|
bd4e252bb6 | ||
|
|
0ce34c70bc | ||
|
|
4df75cfba1 | ||
|
|
2aa435bcfb | ||
|
|
0a4a9e6947 | ||
|
|
d5860d0b55 | ||
|
|
6a487ce514 | ||
|
|
4f3b85e66b | ||
|
|
370cd97e05 | ||
|
|
55d4fda01b | ||
|
|
3dd0471e22 | ||
|
|
5a4efa81a7 | ||
|
|
ea59c12b27 | ||
|
|
9600f91dda | ||
|
|
a9f93b014a | ||
|
|
bf62e389f7 | ||
|
|
26af6b5636 | ||
|
|
0fabfea56d | ||
|
|
7a9417a62f | ||
|
|
eb45005baa | ||
|
|
a4f923eb3a | ||
|
|
e1c3561997 | ||
|
|
bf8b902100 | ||
|
|
8a5883501a | ||
|
|
fd8f3ce019 | ||
|
|
6f72096953 | ||
|
|
5a52e3d6dd | ||
|
|
23672cd18d | ||
|
|
68c0941666 | ||
|
|
96e399a617 | ||
|
|
22343e507d | ||
|
|
8a143d139c | ||
|
|
15ad46fe1c | ||
|
|
2473f0d034 | ||
|
|
f211eefc85 | ||
|
|
9da88b7652 | ||
|
|
729c8006d2 | ||
|
|
0d5b790443 | ||
|
|
aa16035137 | ||
|
|
41686883ee | ||
|
|
2e0790c893 | ||
|
|
4e937a6024 | ||
|
|
4af58118c9 | ||
|
|
aa2ad33c1d | ||
|
|
c7005bc07f | ||
|
|
3f932ebec9 | ||
|
|
296f265391 | ||
|
|
ca8519cb10 | ||
|
|
734d76a3b5 | ||
|
|
f5a39ed514 | ||
|
|
96f4e5eac7 | ||
|
|
48be772703 | ||
|
|
a9edb49723 | ||
|
|
95471a2fba | ||
|
|
36c277961f | ||
|
|
734fff5618 | ||
|
|
16136b2f6f | ||
|
|
c8dfe10a7c | ||
|
|
c8481fdbef | ||
|
|
8e0c02a151 | ||
|
|
271906097d | ||
|
|
91caa2a097 | ||
|
|
6105451e08 | ||
|
|
450e090252 | ||
|
|
86635f85b4 | ||
|
|
a7214a2171 | ||
|
|
61cd473e6c | ||
|
|
d67690d995 | ||
|
|
e20f4fe31a | ||
|
|
2deb40bb6d | ||
|
|
bfee745cca | ||
|
|
68d390e633 | ||
|
|
a884f1b3d4 | ||
|
|
d190ae0cf3 | ||
|
|
bb08a63296 | ||
|
|
3debc1b0df | ||
|
|
e560028097 | ||
|
|
5afcbdbc8b | ||
|
|
aaaf3ffe34 | ||
|
|
b043e666ae | ||
|
|
cda8f7b27d | ||
|
|
d524fcc157 | ||
|
|
e05e34f217 | ||
|
|
73bbb516d2 | ||
|
|
c2aaa65228 | ||
|
|
91722af8b0 | ||
|
|
71d33f6047 | ||
|
|
903faadff3 | ||
|
|
55020d45f8 | ||
|
|
2d37c707e2 | ||
|
|
b00f8c80ec | ||
|
|
53afb865c5 | ||
|
|
6f3e317484 | ||
|
|
e77d2847ea | ||
|
|
46abae9acc | ||
|
|
571320b9ba | ||
|
|
07e9fcb6cc | ||
|
|
7a8719743d | ||
|
|
746f2d0949 | ||
|
|
3986858adb | ||
|
|
589cb8d91f | ||
|
|
705c75e51d | ||
|
|
6acb593411 | ||
|
|
8060451713 | ||
|
|
6130f14d5a | ||
|
|
0fbc461877 | ||
|
|
89e461e4f6 | ||
|
|
ba4ad1aff9 | ||
|
|
be1904749b | ||
|
|
166fa0eb87 | ||
|
|
9a06e7a3ca | ||
|
|
cb12af2d95 | ||
|
|
46032b8ebb | ||
|
|
4a6bd60466 | ||
|
|
f85c8ea5ec | ||
|
|
06ef09035d | ||
|
|
13301e4606 | ||
|
|
b59651a0fb | ||
|
|
86b8d7f804 | ||
|
|
e4dded3faa | ||
|
|
75cf3ed0c1 | ||
|
|
2fa68be36b | ||
|
|
d09b252a4a | ||
|
|
a5165b04cd | ||
|
|
1bd17eded6 | ||
|
|
18d289d3b7 | ||
|
|
e43e6d18b9 | ||
|
|
ec5a3c5948 | ||
|
|
89c0e721b8 | ||
|
|
c807d20590 | ||
|
|
686af16cf5 | ||
|
|
219dd7834f | ||
|
|
1b83fda349 | ||
|
|
490acddc65 | ||
|
|
e69ed06b4f | ||
|
|
2fe9b5a24b | ||
|
|
3912f42128 | ||
|
|
801e307005 | ||
|
|
c8acddb251 | ||
|
|
d8cf7e81b9 | ||
|
|
c4ad442ec3 | ||
|
|
c8e5023ec1 | ||
|
|
5281d7a49a | ||
|
|
77dcf04cfe | ||
|
|
b6523e9989 | ||
|
|
d52a00185b | ||
|
|
00487275a7 | ||
|
|
823c8eb53e | ||
|
|
e9b8981a35 | ||
|
|
575e0b3e54 | ||
|
|
db931717a1 | ||
|
|
787c59efd3 | ||
|
|
45aead89e3 | ||
|
|
81d49b722b | ||
|
|
ab9e7bbb8c | ||
|
|
ee223d0405 | ||
|
|
99050ad73e | ||
|
|
9fc873e973 | ||
|
|
52fe4c6aa6 | ||
|
|
66df7053bb | ||
|
|
edde1a6436 | ||
|
|
50ee829e5f | ||
|
|
1924dfb4f1 | ||
|
|
873a4ecb7e | ||
|
|
32da14acbf | ||
|
|
139c793b5e | ||
|
|
e717d83f75 | ||
|
|
ef12c2f892 |
@@ -1,2 +1,5 @@
|
|||||||
# Formatting
|
# Formatting
|
||||||
5f771b785130154ed47952635b7acef371ffe0ec
|
5f771b785130154ed47952635b7acef371ffe0ec
|
||||||
|
|
||||||
|
# Normalize files
|
||||||
|
55d4fda01b2f39f5b7d7b4fda5214bd7ff0fd5dd
|
||||||
|
|||||||
18
.github/pull_request_template.md
vendored
18
.github/pull_request_template.md
vendored
@@ -1,4 +1,18 @@
|
|||||||
# License Agreement for Contributions
|
# Description
|
||||||
By submitting this pull request, I acknowledge and agree that my contributions will be included in Stirling-PDF and that they can be relicensed in the future under MPL 2.0 (Mozilla Public License Version 2.0) license.
|
|
||||||
|
Please provide a summary of the changes, including relevant motivation and context.
|
||||||
|
|
||||||
|
Closes #(issue_number)
|
||||||
|
|
||||||
|
## Checklist:
|
||||||
|
|
||||||
|
- [ ] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md)
|
||||||
|
- [ ] I have performed a self-review of my own code
|
||||||
|
- [ ] I have commented my code, particularly in hard-to-understand areas
|
||||||
|
- [ ] My changes generate no new warnings
|
||||||
|
|
||||||
|
## Contributor License Agreement
|
||||||
|
|
||||||
|
By submitting this pull request, I acknowledge and agree that my contributions will be included in Stirling-PDF and that they can be relicensed in the future under the MPL 2.0 (Mozilla Public License Version 2.0) license.
|
||||||
|
|
||||||
(This does not change the general open-source nature of Stirling-PDF, simply moving from one license to another license)
|
(This does not change the general open-source nature of Stirling-PDF, simply moving from one license to another license)
|
||||||
|
|||||||
3
.github/workflows/pull_request_template.md
vendored
3
.github/workflows/pull_request_template.md
vendored
@@ -1,3 +0,0 @@
|
|||||||
# License Agreement for Contributions
|
|
||||||
By submitting this pull request, I acknowledge and agree that my contributions will be included in Stirling-PDF and that they can be relicensed in the future under MPL 2.0 (Mozilla Public License Version 2.0) license.
|
|
||||||
(This does not change the open-source nature of Stirling-PDF, simply moving from one license to another license)
|
|
||||||
22
.github/workflows/push-docker.yml
vendored
22
.github/workflows/push-docker.yml
vendored
@@ -3,7 +3,7 @@ name: Push Docker Image with VersionNumber
|
|||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
- main
|
- main
|
||||||
permissions:
|
permissions:
|
||||||
@@ -15,13 +15,13 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
|
|
||||||
- uses: actions/checkout@v3.5.2
|
- uses: actions/checkout@v3.5.2
|
||||||
|
|
||||||
- name: Set up JDK 17
|
- name: Set up JDK 17
|
||||||
uses: actions/setup-java@v3.11.0
|
uses: actions/setup-java@v3.11.0
|
||||||
with:
|
with:
|
||||||
java-version: '17'
|
java-version: '17'
|
||||||
distribution: 'temurin'
|
distribution: 'temurin'
|
||||||
|
|
||||||
|
|
||||||
- uses: gradle/gradle-build-action@v2.4.2
|
- uses: gradle/gradle-build-action@v2.4.2
|
||||||
env:
|
env:
|
||||||
@@ -32,11 +32,11 @@ jobs:
|
|||||||
|
|
||||||
- name: Make Gradle wrapper executable
|
- name: Make Gradle wrapper executable
|
||||||
run: chmod +x gradlew
|
run: chmod +x gradlew
|
||||||
|
|
||||||
- name: Get version number
|
- name: Get version number
|
||||||
id: versionNumber
|
id: versionNumber
|
||||||
run: echo "::set-output name=versionNumber::$(./gradlew printVersion --quiet | tail -1)"
|
run: echo "::set-output name=versionNumber::$(./gradlew printVersion --quiet | tail -1)"
|
||||||
|
|
||||||
- name: Login to Docker Hub
|
- name: Login to Docker Hub
|
||||||
uses: docker/login-action@v2.1.0
|
uses: docker/login-action@v2.1.0
|
||||||
with:
|
with:
|
||||||
@@ -53,7 +53,7 @@ jobs:
|
|||||||
- name: Convert repository owner to lowercase
|
- name: Convert repository owner to lowercase
|
||||||
id: repoowner
|
id: repoowner
|
||||||
run: echo "::set-output name=lowercase::$(echo ${{ github.repository_owner }} | awk '{print tolower($0)}')"
|
run: echo "::set-output name=lowercase::$(echo ${{ github.repository_owner }} | awk '{print tolower($0)}')"
|
||||||
|
|
||||||
- name: Generate tags
|
- name: Generate tags
|
||||||
id: meta
|
id: meta
|
||||||
uses: docker/metadata-action@v4.4.0
|
uses: docker/metadata-action@v4.4.0
|
||||||
@@ -82,7 +82,7 @@ jobs:
|
|||||||
cache-to: type=gha,mode=max
|
cache-to: type=gha,mode=max
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
build-args:
|
build-args:
|
||||||
VERSION_TAG=${{ steps.versionNumber.outputs.versionNumber }}
|
VERSION_TAG=${{ steps.versionNumber.outputs.versionNumber }}
|
||||||
platforms: linux/amd64,linux/arm64/v8
|
platforms: linux/amd64,linux/arm64/v8
|
||||||
|
|
||||||
@@ -99,7 +99,7 @@ jobs:
|
|||||||
tags: |
|
tags: |
|
||||||
type=raw,value=${{ steps.versionNumber.outputs.versionNumber }}-ultra-lite,enable=${{ github.ref == 'refs/heads/master' }}
|
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' }}
|
type=raw,value=latest-ultra-lite,enable=${{ github.ref == 'refs/heads/master' }}
|
||||||
|
|
||||||
|
|
||||||
- name: Build and push Dockerfile-ultra-lite
|
- name: Build and push Dockerfile-ultra-lite
|
||||||
uses: docker/build-push-action@v4.0.0
|
uses: docker/build-push-action@v4.0.0
|
||||||
@@ -112,7 +112,7 @@ jobs:
|
|||||||
cache-to: type=gha,mode=max
|
cache-to: type=gha,mode=max
|
||||||
tags: ${{ steps.meta2.outputs.tags }}
|
tags: ${{ steps.meta2.outputs.tags }}
|
||||||
labels: ${{ steps.meta2.outputs.labels }}
|
labels: ${{ steps.meta2.outputs.labels }}
|
||||||
build-args:
|
build-args:
|
||||||
VERSION_TAG=${{ steps.versionNumber.outputs.versionNumber }}
|
VERSION_TAG=${{ steps.versionNumber.outputs.versionNumber }}
|
||||||
platforms: linux/amd64,linux/arm64/v8
|
platforms: linux/amd64,linux/arm64/v8
|
||||||
|
|
||||||
@@ -129,7 +129,7 @@ jobs:
|
|||||||
tags: |
|
tags: |
|
||||||
type=raw,value=${{ steps.versionNumber.outputs.versionNumber }}-lite,enable=${{ github.ref == 'refs/heads/master' }}
|
type=raw,value=${{ steps.versionNumber.outputs.versionNumber }}-lite,enable=${{ github.ref == 'refs/heads/master' }}
|
||||||
type=raw,value=latest-lite,enable=${{ github.ref == 'refs/heads/master' }}
|
type=raw,value=latest-lite,enable=${{ github.ref == 'refs/heads/master' }}
|
||||||
|
|
||||||
|
|
||||||
- name: Build and push Dockerfile-lite
|
- name: Build and push Dockerfile-lite
|
||||||
uses: docker/build-push-action@v4.0.0
|
uses: docker/build-push-action@v4.0.0
|
||||||
@@ -142,7 +142,7 @@ jobs:
|
|||||||
cache-to: type=gha,mode=max
|
cache-to: type=gha,mode=max
|
||||||
tags: ${{ steps.meta3.outputs.tags }}
|
tags: ${{ steps.meta3.outputs.tags }}
|
||||||
labels: ${{ steps.meta3.outputs.labels }}
|
labels: ${{ steps.meta3.outputs.labels }}
|
||||||
build-args:
|
build-args:
|
||||||
VERSION_TAG=${{ steps.versionNumber.outputs.versionNumber }}
|
VERSION_TAG=${{ steps.versionNumber.outputs.versionNumber }}
|
||||||
platforms: linux/amd64,linux/arm64/v8
|
platforms: linux/amd64,linux/arm64/v8
|
||||||
- name: Build and Push Helm Chart
|
- name: Build and Push Helm Chart
|
||||||
|
|||||||
12
.github/workflows/releaseArtifacts.yml
vendored
12
.github/workflows/releaseArtifacts.yml
vendored
@@ -1,7 +1,7 @@
|
|||||||
name: Release Artifacts
|
name: Release Artifacts
|
||||||
|
|
||||||
on:
|
on:
|
||||||
release:
|
release:
|
||||||
types: [created]
|
types: [created]
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
@@ -19,13 +19,13 @@ jobs:
|
|||||||
file_suffix: ''
|
file_suffix: ''
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3.5.2
|
- uses: actions/checkout@v3.5.2
|
||||||
|
|
||||||
- name: Set up JDK 17
|
- name: Set up JDK 17
|
||||||
uses: actions/setup-java@v3.11.0
|
uses: actions/setup-java@v3.11.0
|
||||||
with:
|
with:
|
||||||
java-version: '17'
|
java-version: '17'
|
||||||
distribution: 'temurin'
|
distribution: 'temurin'
|
||||||
|
|
||||||
- name: Grant execute permission for gradlew
|
- name: Grant execute permission for gradlew
|
||||||
run: chmod +x gradlew
|
run: chmod +x gradlew
|
||||||
|
|
||||||
@@ -42,11 +42,11 @@ jobs:
|
|||||||
asset_name: Stirling-PDF${{ matrix.file_suffix }}.exe
|
asset_name: Stirling-PDF${{ matrix.file_suffix }}.exe
|
||||||
tag: ${{ github.ref }}
|
tag: ${{ github.ref }}
|
||||||
overwrite: true
|
overwrite: true
|
||||||
|
|
||||||
- name: Get version number
|
- name: Get version number
|
||||||
id: versionNumber
|
id: versionNumber
|
||||||
run: echo "::set-output name=versionNumber::$(./gradlew printVersion --quiet | tail -1)"
|
run: echo "::set-output name=versionNumber::$(./gradlew printVersion --quiet | tail -1)"
|
||||||
|
|
||||||
- name: Upload jar binaries to release
|
- name: Upload jar binaries to release
|
||||||
uses: svenstaro/upload-release-action@v2
|
uses: svenstaro/upload-release-action@v2
|
||||||
with:
|
with:
|
||||||
|
|||||||
6
.github/workflows/swagger.yml
vendored
6
.github/workflows/swagger.yml
vendored
@@ -3,7 +3,7 @@ name: Update Swagger
|
|||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
jobs:
|
jobs:
|
||||||
push:
|
push:
|
||||||
@@ -12,13 +12,13 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
|
|
||||||
- uses: actions/checkout@v3.5.2
|
- uses: actions/checkout@v3.5.2
|
||||||
|
|
||||||
- name: Set up JDK 17
|
- name: Set up JDK 17
|
||||||
uses: actions/setup-java@v3.11.0
|
uses: actions/setup-java@v3.11.0
|
||||||
with:
|
with:
|
||||||
java-version: '17'
|
java-version: '17'
|
||||||
distribution: 'temurin'
|
distribution: 'temurin'
|
||||||
|
|
||||||
- name: Grant execute permission for gradlew
|
- name: Grant execute permission for gradlew
|
||||||
run: chmod +x gradlew
|
run: chmod +x gradlew
|
||||||
|
|
||||||
|
|||||||
2
.github/workflows/test.yml
vendored
2
.github/workflows/test.yml
vendored
@@ -30,7 +30,7 @@ jobs:
|
|||||||
- name: Run Docker Compose Tests
|
- name: Run Docker Compose Tests
|
||||||
run: |
|
run: |
|
||||||
chmod +x ./gradlew
|
chmod +x ./gradlew
|
||||||
|
|
||||||
- name: Get version number
|
- name: Get version number
|
||||||
id: versionNumber
|
id: versionNumber
|
||||||
run: echo "::set-output name=versionNumber::$(./gradlew printVersion --quiet | tail -1)"
|
run: echo "::set-output name=versionNumber::$(./gradlew printVersion --quiet | tail -1)"
|
||||||
|
|||||||
252
.gitignore
vendored
252
.gitignore
vendored
@@ -1,127 +1,127 @@
|
|||||||
|
|
||||||
|
|
||||||
### Eclipse ###
|
### Eclipse ###
|
||||||
.metadata
|
.metadata
|
||||||
bin/
|
bin/
|
||||||
tmp/
|
tmp/
|
||||||
*.tmp
|
*.tmp
|
||||||
*.bak
|
*.bak
|
||||||
*.swp
|
*.swp
|
||||||
*~.nib
|
*~.nib
|
||||||
local.properties
|
local.properties
|
||||||
.settings/
|
.settings/
|
||||||
.loadpath
|
.loadpath
|
||||||
.recommenders
|
.recommenders
|
||||||
.classpath
|
.classpath
|
||||||
.project
|
.project
|
||||||
version.properties
|
version.properties
|
||||||
pipeline/watchedFolders/
|
pipeline/watchedFolders/
|
||||||
pipeline/finishedFolders/
|
pipeline/finishedFolders/
|
||||||
#### Stirling-PDF Files ###
|
#### Stirling-PDF Files ###
|
||||||
customFiles/
|
customFiles/
|
||||||
configs/
|
configs/
|
||||||
watchedFolders/
|
watchedFolders/
|
||||||
|
|
||||||
|
|
||||||
# Gradle
|
# Gradle
|
||||||
.gradle
|
.gradle
|
||||||
.lock
|
.lock
|
||||||
|
|
||||||
# External tool builders
|
# External tool builders
|
||||||
.externalToolBuilders/
|
.externalToolBuilders/
|
||||||
|
|
||||||
# Locally stored "Eclipse launch configurations"
|
# Locally stored "Eclipse launch configurations"
|
||||||
*.launch
|
*.launch
|
||||||
|
|
||||||
# PyDev specific (Python IDE for Eclipse)
|
# PyDev specific (Python IDE for Eclipse)
|
||||||
*.pydevproject
|
*.pydevproject
|
||||||
|
|
||||||
# CDT-specific (C/C++ Development Tooling)
|
# CDT-specific (C/C++ Development Tooling)
|
||||||
.cproject
|
.cproject
|
||||||
|
|
||||||
# CDT- autotools
|
# CDT- autotools
|
||||||
.autotools
|
.autotools
|
||||||
|
|
||||||
# Java annotation processor (APT)
|
# Java annotation processor (APT)
|
||||||
.factorypath
|
.factorypath
|
||||||
|
|
||||||
# PDT-specific (PHP Development Tools)
|
# PDT-specific (PHP Development Tools)
|
||||||
.buildpath
|
.buildpath
|
||||||
|
|
||||||
# sbteclipse plugin
|
# sbteclipse plugin
|
||||||
.target
|
.target
|
||||||
|
|
||||||
# Tern plugin
|
# Tern plugin
|
||||||
.tern-project
|
.tern-project
|
||||||
|
|
||||||
# TeXlipse plugin
|
# TeXlipse plugin
|
||||||
.texlipse
|
.texlipse
|
||||||
|
|
||||||
# STS (Spring Tool Suite)
|
# STS (Spring Tool Suite)
|
||||||
.springBeans
|
.springBeans
|
||||||
|
|
||||||
# Code Recommenders
|
# Code Recommenders
|
||||||
.recommenders/
|
.recommenders/
|
||||||
|
|
||||||
# Annotation Processing
|
# Annotation Processing
|
||||||
.apt_generated/
|
.apt_generated/
|
||||||
.apt_generated_test/
|
.apt_generated_test/
|
||||||
|
|
||||||
# Scala IDE specific (Scala & Java development for Eclipse)
|
# Scala IDE specific (Scala & Java development for Eclipse)
|
||||||
.cache-main
|
.cache-main
|
||||||
.scala_dependencies
|
.scala_dependencies
|
||||||
.worksheet
|
.worksheet
|
||||||
|
|
||||||
# Uncomment this line if you wish to ignore the project description file.
|
# Uncomment this line if you wish to ignore the project description file.
|
||||||
# Typically, this file would be tracked if it contains build/dependency configurations:
|
# Typically, this file would be tracked if it contains build/dependency configurations:
|
||||||
#.project
|
#.project
|
||||||
|
|
||||||
### Eclipse Patch ###
|
### Eclipse Patch ###
|
||||||
# Spring Boot Tooling
|
# Spring Boot Tooling
|
||||||
.sts4-cache/
|
.sts4-cache/
|
||||||
|
|
||||||
### Git ###
|
### Git ###
|
||||||
# Created by git for backups. To disable backups in Git:
|
# Created by git for backups. To disable backups in Git:
|
||||||
# $ git config --global mergetool.keepBackup false
|
# $ git config --global mergetool.keepBackup false
|
||||||
*.orig
|
*.orig
|
||||||
|
|
||||||
# Created by git when using merge tools for conflicts
|
# Created by git when using merge tools for conflicts
|
||||||
*.BACKUP.*
|
*.BACKUP.*
|
||||||
*.BASE.*
|
*.BASE.*
|
||||||
*.LOCAL.*
|
*.LOCAL.*
|
||||||
*.REMOTE.*
|
*.REMOTE.*
|
||||||
*_BACKUP_*.txt
|
*_BACKUP_*.txt
|
||||||
*_BASE_*.txt
|
*_BASE_*.txt
|
||||||
*_LOCAL_*.txt
|
*_LOCAL_*.txt
|
||||||
*_REMOTE_*.txt
|
*_REMOTE_*.txt
|
||||||
|
|
||||||
### Java ###
|
### Java ###
|
||||||
# Compiled class file
|
# Compiled class file
|
||||||
*.class
|
*.class
|
||||||
|
|
||||||
# Log file
|
# Log file
|
||||||
*.log
|
*.log
|
||||||
|
|
||||||
# BlueJ files
|
# BlueJ files
|
||||||
*.ctxt
|
*.ctxt
|
||||||
|
|
||||||
# Mobile Tools for Java (J2ME)
|
# Mobile Tools for Java (J2ME)
|
||||||
.mtj.tmp/
|
.mtj.tmp/
|
||||||
|
|
||||||
# Package Files #
|
# Package Files #
|
||||||
*.jar
|
*.jar
|
||||||
*.war
|
*.war
|
||||||
*.nar
|
*.nar
|
||||||
*.ear
|
*.ear
|
||||||
*.zip
|
*.zip
|
||||||
*.tar.gz
|
*.tar.gz
|
||||||
*.rar
|
*.rar
|
||||||
*.db
|
*.db
|
||||||
/build
|
/build
|
||||||
|
|
||||||
/.vscode
|
/.vscode
|
||||||
/.idea
|
/.idea
|
||||||
|
|
||||||
# Ignore Mac DS_Store files
|
# Ignore Mac DS_Store files
|
||||||
.DS_Store
|
.DS_Store
|
||||||
**/.DS_Store
|
**/.DS_Store
|
||||||
40
CONTRIBUTING.md
Normal file
40
CONTRIBUTING.md
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
# Contributing to Stirling-PDF
|
||||||
|
|
||||||
|
Thank you for your interest in contributing to Stirling-PDF! There are many ways to contribute other than writing code. For example, reporting bugs, creating suggestions, and adding or modifying translations.
|
||||||
|
|
||||||
|
## Issue Guidelines
|
||||||
|
|
||||||
|
Issues can be used to report bugs, request features, or ask questions. If you have a question, you could also ask us in our [Discord](https://discord.gg/FJUSXUSYec).
|
||||||
|
|
||||||
|
Before opening an issue, please check to make sure someone hasn't already opened an issue about it.
|
||||||
|
|
||||||
|
## Pull Requests
|
||||||
|
|
||||||
|
Before you start working on an issue, please comment on (or create) the issue and wait for it to be assigned to you. If someone has already been assigned but didn't have the time to work on it lately, please communicate with them and ask if they're still working on it. This is to avoid multiple people working on the same issue.
|
||||||
|
|
||||||
|
Once you have been assigned an issue, you can start working on it. When you are ready to submit your changes, open a pull request.
|
||||||
|
For a detailed pull request tutorial, see [this guide](https://www.digitalocean.com/community/tutorials/how-to-create-a-pull-request-on-github).
|
||||||
|
|
||||||
|
Please make sure your Pull Request adheres to the following guidelines:
|
||||||
|
|
||||||
|
- Use the PR template provided.
|
||||||
|
- Keep your Pull Request title succinct, detailed and to the point.
|
||||||
|
- Keep commits atomic. One commit should contain one change. If you want to make multiple changes, submit multiple Pull Requests.
|
||||||
|
- Commits should be clear, concise and easy to understand.
|
||||||
|
- References to the Issue number in the Pull Request and/or Commit message.
|
||||||
|
|
||||||
|
## Translations
|
||||||
|
|
||||||
|
If you would like to add or modify a translation, please see [How to add new languages to Stirling-PDF](HowToAddNewLanguage.md). Also, please create a Pull Request so others can use it!
|
||||||
|
|
||||||
|
## Fixing Bugs or Adding a New Feature
|
||||||
|
|
||||||
|
First, make sure you've read the section [Pull Requests](#pull-requests).
|
||||||
|
|
||||||
|
To build from source, please follow this [Guide](LocalRunGuide.md).
|
||||||
|
|
||||||
|
If, at any point of time, you have a question, please feel free to ask in the same issue thread or in our [Discord](https://discord.gg/FJUSXUSYec).
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
By contributing to this project, you agree that your contributions will be licensed under the [GPL 3 License](LICENSE). You also acknowledge and agree that your contributions will be included in Stirling-PDF and that they can be relicensed in the future under the MPL 2.0 (Mozilla Public License Version 2.0) license.
|
||||||
109
Dockerfile
109
Dockerfile
@@ -1,47 +1,62 @@
|
|||||||
# Use the base image
|
# Main stage
|
||||||
FROM frooodle/stirling-pdf-base:version8
|
FROM alpine:3.19.1
|
||||||
|
|
||||||
ARG VERSION_TAG
|
# Copy necessary files
|
||||||
|
COPY scripts /scripts
|
||||||
# Set Environment Variables
|
COPY pipeline /pipeline
|
||||||
ENV DOCKER_ENABLE_SECURITY=false \
|
COPY src/main/resources/static/fonts/*.ttf /usr/share/fonts/opentype/noto
|
||||||
HOME=/home/stirlingpdfuser \
|
COPY src/main/resources/static/fonts/*.otf /usr/share/fonts/opentype/noto
|
||||||
VERSION_TAG=$VERSION_TAG \
|
COPY build/libs/*.jar app.jar
|
||||||
JAVA_TOOL_OPTIONS="$JAVA_TOOL_OPTIONS -XX:MaxRAMPercentage=75"
|
|
||||||
# PUID=1000 \
|
ARG VERSION_TAG
|
||||||
# PGID=1000 \
|
|
||||||
# UMASK=022 \
|
|
||||||
|
# Set Environment Variables
|
||||||
|
ENV DOCKER_ENABLE_SECURITY=false \
|
||||||
# Create user and group
|
VERSION_TAG=$VERSION_TAG \
|
||||||
##RUN groupadd -g $PGID stirlingpdfgroup && \
|
JAVA_TOOL_OPTIONS="$JAVA_TOOL_OPTIONS -XX:MaxRAMPercentage=75" \
|
||||||
## useradd -u $PUID -g stirlingpdfgroup -s /bin/sh stirlingpdfuser && \
|
HOME=/home/stirlingpdfuser \
|
||||||
## mkdir -p $HOME && chown stirlingpdfuser:stirlingpdfgroup $HOME
|
PUID=1000 \
|
||||||
|
PGID=1000 \
|
||||||
# Set up necessary directories and permissions
|
UMASK=022
|
||||||
RUN mkdir -p /scripts /usr/share/fonts/opentype/noto /usr/share/tesseract-ocr /configs /logs /customFiles /pipeline /pipeline/defaultWebUIConfigs /pipeline/watchedFolders /pipeline/finishedFolders
|
|
||||||
##&& \
|
|
||||||
## chown -R stirlingpdfuser:stirlingpdfgroup /scripts /usr/share/fonts/opentype/noto /usr/share/tesseract-ocr /configs /customFiles && \
|
# JDK for app
|
||||||
## chown -R stirlingpdfuser:stirlingpdfgroup /usr/share/tesseract-ocr-original
|
RUN echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/main" | tee -a /etc/apk/repositories && \
|
||||||
|
echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/community" | tee -a /etc/apk/repositories && \
|
||||||
# Copy necessary files
|
echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/testing" | tee -a /etc/apk/repositories && \
|
||||||
COPY ./scripts/* /scripts/
|
apk add --no-cache \
|
||||||
COPY ./pipeline/ /pipeline/
|
ca-certificates \
|
||||||
COPY src/main/resources/static/fonts/*.ttf /usr/share/fonts/opentype/noto/
|
tzdata \
|
||||||
COPY src/main/resources/static/fonts/*.otf /usr/share/fonts/opentype/noto/
|
tini \
|
||||||
COPY build/libs/*.jar app.jar
|
bash \
|
||||||
|
curl \
|
||||||
# Set font cache and permissions
|
openjdk17-jre \
|
||||||
RUN fc-cache -f -v && chmod +x /scripts/*
|
su-exec \
|
||||||
|
# Doc conversion
|
||||||
##&& \
|
libreoffice@testing \
|
||||||
## chown stirlingpdfuser:stirlingpdfgroup /app.jar && \
|
# OCR MY PDF (unpaper for descew and other advanced featues)
|
||||||
## chmod +x /scripts/init.sh
|
ocrmypdf \
|
||||||
|
tesseract-ocr-data-eng \
|
||||||
# Expose necessary ports
|
# CV
|
||||||
EXPOSE 8080
|
py3-opencv \
|
||||||
|
# python3/pip
|
||||||
# Set user and run command
|
python3 && \
|
||||||
##USER stirlingpdfuser
|
wget https://bootstrap.pypa.io/get-pip.py -qO - | python3 - --break-system-packages --no-cache-dir --upgrade && \
|
||||||
ENTRYPOINT ["/scripts/init.sh"]
|
# uno unoconv and HTML
|
||||||
CMD ["java", "-Dfile.encoding=UTF-8", "-jar", "/app.jar"]
|
pip install --break-system-packages --no-cache-dir --upgrade unoconv WeasyPrint && \
|
||||||
|
mv /usr/share/tessdata /usr/share/tessdata-original && \
|
||||||
|
mkdir -p $HOME /configs /logs /customFiles /pipeline/watchedFolders /pipeline/finishedFolders && \
|
||||||
|
fc-cache -f -v && \
|
||||||
|
chmod +x /scripts/* && \
|
||||||
|
chmod +x /scripts/init.sh && \
|
||||||
|
# User permissions
|
||||||
|
addgroup -S stirlingpdfgroup && adduser -S stirlingpdfuser -G stirlingpdfgroup && \
|
||||||
|
chown -R stirlingpdfuser:stirlingpdfgroup $HOME /scripts /usr/share/fonts/opentype/noto /configs /customFiles /pipeline && \
|
||||||
|
chown stirlingpdfuser:stirlingpdfgroup /app.jar
|
||||||
|
|
||||||
|
EXPOSE 8080
|
||||||
|
|
||||||
|
# Set user and run command
|
||||||
|
ENTRYPOINT ["tini", "--", "/scripts/init.sh"]
|
||||||
|
CMD ["java", "-Dfile.encoding=UTF-8", "-jar", "/app.jar"]
|
||||||
|
|||||||
@@ -1,65 +1,61 @@
|
|||||||
# Build jbig2enc in a separate stage
|
# use alpine
|
||||||
FROM bellsoft/liberica-openjdk-debian:17
|
FROM alpine:3.19.1
|
||||||
|
|
||||||
ARG VERSION_TAG
|
ARG VERSION_TAG
|
||||||
|
|
||||||
RUN apt-get update && \
|
|
||||||
apt-get install -y --no-install-recommends \
|
|
||||||
libreoffice-core \
|
|
||||||
libreoffice-common \
|
|
||||||
libreoffice-writer \
|
|
||||||
libreoffice-calc \
|
|
||||||
libreoffice-impress \
|
|
||||||
unoconv && \
|
|
||||||
rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
|
|
||||||
# Set Environment Variables
|
# Set Environment Variables
|
||||||
ENV DOCKER_ENABLE_SECURITY=false \
|
ENV DOCKER_ENABLE_SECURITY=false \
|
||||||
HOME=/home/stirlingpdfuser \
|
HOME=/home/stirlingpdfuser \
|
||||||
VERSION_TAG=$VERSION_TAG \
|
VERSION_TAG=$VERSION_TAG \
|
||||||
JAVA_TOOL_OPTIONS="$JAVA_TOOL_OPTIONS -XX:MaxRAMPercentage=75"
|
JAVA_TOOL_OPTIONS="$JAVA_TOOL_OPTIONS -XX:MaxRAMPercentage=75" \
|
||||||
# PUID=1000 \
|
PUID=1000 \
|
||||||
# PGID=1000 \
|
PGID=1000 \
|
||||||
# UMASK=022 \
|
UMASK=022
|
||||||
|
|
||||||
# Create user and group
|
|
||||||
#RUN groupadd -g $PGID stirlingpdfgroup && \
|
|
||||||
# useradd -u $PUID -g stirlingpdfgroup -s /bin/sh stirlingpdfuser && \
|
|
||||||
# mkdir -p $HOME && chown stirlingpdfuser:stirlingpdfgroup $HOME
|
|
||||||
|
|
||||||
# Set up necessary directories and permissions
|
|
||||||
RUN mkdir -p /scripts /usr/share/fonts/opentype/noto /configs /customFiles /logs /pipeline /pipeline/defaultWebUIConfigs /pipeline/watchedFolders /pipeline/finishedFolders
|
|
||||||
|
|
||||||
# chown -R stirlingpdfuser:stirlingpdfgroup /usr/share/fonts/opentype/noto /configs /customFiles
|
|
||||||
|
|
||||||
# Copy necessary files
|
# Copy necessary files
|
||||||
COPY ./scripts/download-security-jar.sh /scripts/download-security-jar.sh
|
COPY scripts/download-security-jar.sh /scripts/download-security-jar.sh
|
||||||
COPY ./scripts/init-without-ocr.sh /scripts/init-without-ocr.sh
|
COPY scripts/init-without-ocr.sh /scripts/init-without-ocr.sh
|
||||||
COPY ./pipeline/ /pipeline/
|
COPY pipeline /pipeline
|
||||||
COPY src/main/resources/static/fonts/*.ttf /usr/share/fonts/opentype/noto/
|
COPY src/main/resources/static/fonts/*.ttf /usr/share/fonts/opentype/noto
|
||||||
COPY src/main/resources/static/fonts/*.otf /usr/share/fonts/opentype/noto/
|
COPY src/main/resources/static/fonts/*.otf /usr/share/fonts/opentype/noto
|
||||||
COPY build/libs/*.jar app.jar
|
COPY build/libs/*.jar app.jar
|
||||||
|
|
||||||
|
RUN echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/main" | tee -a /etc/apk/repositories && \
|
||||||
|
echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/community" | tee -a /etc/apk/repositories && \
|
||||||
|
echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/testing" | tee -a /etc/apk/repositories && \
|
||||||
|
apk add --no-cache \
|
||||||
|
ca-certificates \
|
||||||
|
tzdata \
|
||||||
|
tini \
|
||||||
|
bash \
|
||||||
|
curl \
|
||||||
|
openjdk17-jre \
|
||||||
|
su-exec \
|
||||||
|
# Doc conversion
|
||||||
|
libreoffice@testing \
|
||||||
|
# python and pip
|
||||||
|
python3 && \
|
||||||
|
wget https://bootstrap.pypa.io/get-pip.py -qO - | python3 - --break-system-packages --no-cache-dir --upgrade && \
|
||||||
|
# uno unoconv and HTML
|
||||||
|
pip install --break-system-packages --no-cache-dir --upgrade unoconv WeasyPrint && \
|
||||||
|
# Set up necessary directories and permissions
|
||||||
|
mkdir -p /configs /logs /customFiles /pipeline/watchedFolders /pipeline/finishedFolders && \
|
||||||
# Set font cache and permissions
|
# Set font cache and permissions
|
||||||
RUN fc-cache -f -v && \
|
fc-cache -f -v && \
|
||||||
chmod +x /scripts/init-without-ocr.sh && \
|
chmod +x /scripts/*.sh && \
|
||||||
chmod +x /scripts/download-security-jar.sh
|
# User permissions
|
||||||
|
addgroup -S stirlingpdfgroup && adduser -S stirlingpdfuser -G stirlingpdfgroup && \
|
||||||
|
chown -R stirlingpdfuser:stirlingpdfgroup $HOME /scripts /usr/share/fonts/opentype/noto /configs /customFiles /pipeline && \
|
||||||
|
chown stirlingpdfuser:stirlingpdfgroup /app.jar
|
||||||
|
|
||||||
|
|
||||||
# chown stirlingpdfuser:stirlingpdfgroup /app.jar
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Expose the application port
|
|
||||||
EXPOSE 8080
|
|
||||||
|
|
||||||
# Set environment variables
|
# Set environment variables
|
||||||
ENV ENDPOINTS_GROUPS_TO_REMOVE=Python,OpenCV,OCRmyPDF
|
ENV ENDPOINTS_GROUPS_TO_REMOVE=OpenCV,OCRmyPDF
|
||||||
ENV DOCKER_ENABLE_SECURITY=false
|
ENV DOCKER_ENABLE_SECURITY=false
|
||||||
|
|
||||||
|
EXPOSE 8080
|
||||||
|
|
||||||
# Run the application
|
# Run the application
|
||||||
#USER stirlingpdfuser
|
|
||||||
ENTRYPOINT ["/scripts/init-without-ocr.sh"]
|
ENTRYPOINT ["tini", "--", "/scripts/init-without-ocr.sh"]
|
||||||
CMD ["java", "-Dfile.encoding=UTF-8", "-jar", "/app.jar"]
|
CMD ["java", "-Dfile.encoding=UTF-8", "-jar", "/app.jar"]
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Build jbig2enc in a separate stage
|
# use alpine
|
||||||
FROM bellsoft/liberica-openjdk-alpine:17
|
FROM alpine:3.19.1
|
||||||
|
|
||||||
ARG VERSION_TAG
|
ARG VERSION_TAG
|
||||||
|
|
||||||
@@ -7,40 +7,44 @@ ARG VERSION_TAG
|
|||||||
ENV DOCKER_ENABLE_SECURITY=false \
|
ENV DOCKER_ENABLE_SECURITY=false \
|
||||||
HOME=/home/stirlingpdfuser \
|
HOME=/home/stirlingpdfuser \
|
||||||
VERSION_TAG=$VERSION_TAG \
|
VERSION_TAG=$VERSION_TAG \
|
||||||
JAVA_TOOL_OPTIONS="$JAVA_TOOL_OPTIONS -XX:MaxRAMPercentage=75"
|
JAVA_TOOL_OPTIONS="$JAVA_TOOL_OPTIONS -XX:MaxRAMPercentage=75" \
|
||||||
# PUID=1000 \
|
PUID=1000 \
|
||||||
# PGID=1000 \
|
PGID=1000 \
|
||||||
# UMASK=022 \
|
UMASK=022
|
||||||
|
|
||||||
# Create user and group using Alpine's addgroup and adduser
|
# Copy necessary files
|
||||||
#RUN addgroup -g $PGID stirlingpdfgroup && \
|
COPY scripts/download-security-jar.sh /scripts/download-security-jar.sh
|
||||||
# adduser -u $PUID -G stirlingpdfgroup -s /bin/sh -D stirlingpdfuser && \
|
COPY scripts/init-without-ocr.sh /scripts/init-without-ocr.sh
|
||||||
# mkdir -p $HOME && chown stirlingpdfuser:stirlingpdfgroup $HOME
|
COPY pipeline /pipeline
|
||||||
|
|
||||||
# Set up necessary directories and permissions
|
|
||||||
#RUN mkdir -p /scripts /configs /customFiles && \
|
|
||||||
# chown -R stirlingpdfuser:stirlingpdfgroup /scripts /configs /customFiles /logs /pipeline /pipeline/defaultWebUIConfigs /pipeline/watchedFolders /pipeline/finishedFolders
|
|
||||||
|
|
||||||
RUN mkdir -p /scripts /usr/share/fonts/opentype/noto /configs /customFiles
|
|
||||||
COPY ./scripts/download-security-jar.sh /scripts/download-security-jar.sh
|
|
||||||
COPY ./scripts/init-without-ocr.sh /scripts/init-without-ocr.sh
|
|
||||||
COPY ./pipeline/ /pipeline/
|
|
||||||
COPY build/libs/*.jar app.jar
|
COPY build/libs/*.jar app.jar
|
||||||
|
|
||||||
# Set font cache and permissions
|
|
||||||
#RUN chown stirlingpdfuser:stirlingpdfgroup /app.jar
|
|
||||||
|
|
||||||
RUN chmod +x /scripts/init-without-ocr.sh && \
|
# Set up necessary directories and permissions
|
||||||
chmod +x /scripts/download-security-jar.sh && \
|
|
||||||
apk add --no-cache curl
|
|
||||||
|
|
||||||
# Expose the application port
|
RUN mkdir /configs /logs /customFiles && \
|
||||||
EXPOSE 8080
|
chmod +x /scripts/*.sh && \
|
||||||
|
apk add --no-cache \
|
||||||
|
ca-certificates \
|
||||||
|
tzdata \
|
||||||
|
tini \
|
||||||
|
bash \
|
||||||
|
curl \
|
||||||
|
su-exec \
|
||||||
|
openjdk17-jre && \
|
||||||
|
echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/main" | tee -a /etc/apk/repositories && \
|
||||||
|
echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/community" | tee -a /etc/apk/repositories && \
|
||||||
|
echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/testing" | tee -a /etc/apk/repositories && \
|
||||||
|
# User permissions
|
||||||
|
addgroup -S stirlingpdfgroup && adduser -S stirlingpdfuser -G stirlingpdfgroup && \
|
||||||
|
chown -R stirlingpdfuser:stirlingpdfgroup $HOME /scripts /configs /customFiles /pipeline && \
|
||||||
|
chown stirlingpdfuser:stirlingpdfgroup /app.jar
|
||||||
|
|
||||||
# Set environment variables
|
# Set environment variables
|
||||||
ENV ENDPOINTS_GROUPS_TO_REMOVE=CLI
|
ENV ENDPOINTS_GROUPS_TO_REMOVE=CLI
|
||||||
|
|
||||||
ENTRYPOINT ["/scripts/init-without-ocr.sh"]
|
EXPOSE 8080
|
||||||
|
|
||||||
|
ENTRYPOINT ["tini", "--", "/scripts/init-without-ocr.sh"]
|
||||||
|
|
||||||
# Run the application
|
# Run the application
|
||||||
CMD ["java", "-Dfile.encoding=UTF-8", "-jar", "/app.jar"]
|
CMD ["java", "-Dfile.encoding=UTF-8", "-jar", "/app.jar"]
|
||||||
|
|||||||
@@ -1,43 +0,0 @@
|
|||||||
# Main stage
|
|
||||||
FROM ubuntu:latest AS base
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# JDK for app
|
|
||||||
RUN apt-get update && \
|
|
||||||
apt-get install -y --no-install-recommends \
|
|
||||||
openjdk-17-jre && \
|
|
||||||
rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
# Doc conversion
|
|
||||||
RUN apt-get update && \
|
|
||||||
apt-get install -y --no-install-recommends \
|
|
||||||
libreoffice-core \
|
|
||||||
libreoffice-common \
|
|
||||||
libreoffice-writer \
|
|
||||||
libreoffice-calc \
|
|
||||||
libreoffice-impress \
|
|
||||||
python3-uno \
|
|
||||||
curl \
|
|
||||||
unoconv && \
|
|
||||||
rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
|
|
||||||
# OCR MY PDF (unpaper for descew and other advanced featues)
|
|
||||||
RUN apt-get update && apt-get install -y --no-install-recommends software-properties-common gnupg2 && \
|
|
||||||
add-apt-repository ppa:alex-p/tesseract-ocr5 && apt install -y --no-install-recommends tesseract-ocr && \
|
|
||||||
apt-get update && \
|
|
||||||
apt-get install -y --no-install-recommends \
|
|
||||||
ghostscript \
|
|
||||||
python3-pip \
|
|
||||||
ocrmypdf \
|
|
||||||
unpaper && \
|
|
||||||
rm -rf /var/lib/apt/lists/* && \
|
|
||||||
mv /usr/share/tesseract-ocr /usr/share/tesseract-ocr-original && \
|
|
||||||
pip install --no-cache-dir --upgrade pip && \
|
|
||||||
pip install --no-cache-dir --upgrade ocrmypdf && \
|
|
||||||
pip install --no-cache-dir --upgrade pillow==10.0.1 reportlab==3.6.13 wheel==0.38.1 setuptools==65.5.1 pyjwt==2.4.0 cryptography==39.0.1
|
|
||||||
|
|
||||||
|
|
||||||
#CV and HTML
|
|
||||||
RUN pip install --no-cache-dir opencv-python-headless WeasyPrint
|
|
||||||
@@ -1,46 +1,46 @@
|
|||||||
| Operation | PageOps | Convert | Security | Other | CLI | Python | OpenCV | LibreOffice | OCRmyPDF | Java | Javascript |
|
| Operation | PageOps | Convert | Security | Other | CLI | Python | OpenCV | LibreOffice | OCRmyPDF | Java | Javascript |
|
||||||
|---------------------|---------|---------|----------|-------|------|--------|--------|-------------|----------|----------|------------|
|
|---------------------|---------|---------|----------|-------|------|--------|--------|-------------|----------|----------|------------|
|
||||||
| adjust-contrast | ✔️ | | | | | | | | | | ✔️ |
|
| adjust-contrast | ✔️ | | | | | | | | | | ✔️ |
|
||||||
| auto-split-pdf | ✔️ | | | | | | | | | ✔️ | |
|
| auto-split-pdf | ✔️ | | | | | | | | | ✔️ | |
|
||||||
| crop | ✔️ | | | | | | | | | ✔️ | |
|
| crop | ✔️ | | | | | | | | | ✔️ | |
|
||||||
| extract-page | ✔️ | | | | | | | | | ✔️ | |
|
| extract-page | ✔️ | | | | | | | | | ✔️ | |
|
||||||
| merge-pdfs | ✔️ | | | | | | | | | ✔️ | |
|
| merge-pdfs | ✔️ | | | | | | | | | ✔️ | |
|
||||||
| multi-page-layout | ✔️ | | | | | | | | | ✔️ | |
|
| multi-page-layout | ✔️ | | | | | | | | | ✔️ | |
|
||||||
| pdf-organizer | ✔️ | | | | | | | | | ✔️ | ✔️ |
|
| pdf-organizer | ✔️ | | | | | | | | | ✔️ | ✔️ |
|
||||||
| pdf-to-single-page | ✔️ | | | | | | | | | ✔️ | |
|
| pdf-to-single-page | ✔️ | | | | | | | | | ✔️ | |
|
||||||
| remove-pages | ✔️ | | | | | | | | | ✔️ | |
|
| remove-pages | ✔️ | | | | | | | | | ✔️ | |
|
||||||
| rotate-pdf | ✔️ | | | | | | | | | ✔️ | |
|
| rotate-pdf | ✔️ | | | | | | | | | ✔️ | |
|
||||||
| scale-pages | ✔️ | | | | | | | | | ✔️ | |
|
| scale-pages | ✔️ | | | | | | | | | ✔️ | |
|
||||||
| split-pdfs | ✔️ | | | | | | | | | ✔️ | |
|
| split-pdfs | ✔️ | | | | | | | | | ✔️ | |
|
||||||
| file-to-pdf | | ✔️ | | | ✔️ | | | ✔️ | | | |
|
| file-to-pdf | | ✔️ | | | ✔️ | | | ✔️ | | | |
|
||||||
| img-to-pdf | | ✔️ | | | | | | | | ✔️ | |
|
| img-to-pdf | | ✔️ | | | | | | | | ✔️ | |
|
||||||
| pdf-to-html | | ✔️ | | | ✔️ | | | ✔️ | | | |
|
| pdf-to-html | | ✔️ | | | ✔️ | | | ✔️ | | | |
|
||||||
| pdf-to-img | | ✔️ | | | | | | | | ✔️ | |
|
| pdf-to-img | | ✔️ | | | | | | | | ✔️ | |
|
||||||
| pdf-to-pdfa | | ✔️ | | | ✔️ | | | | ✔️ | | |
|
| pdf-to-pdfa | | ✔️ | | | ✔️ | | | | ✔️ | | |
|
||||||
| pdf-to-markdown | | ✔️ | | | | | | | | ✔️ | |
|
| pdf-to-markdown | | ✔️ | | | | | | | | ✔️ | |
|
||||||
| pdf-to-presentation | | ✔️ | | | ✔️ | | | ✔️ | | | |
|
| pdf-to-presentation | | ✔️ | | | ✔️ | | | ✔️ | | | |
|
||||||
| pdf-to-text | | ✔️ | | | ✔️ | | | ✔️ | | | |
|
| pdf-to-text | | ✔️ | | | ✔️ | | | ✔️ | | | |
|
||||||
| pdf-to-word | | ✔️ | | | ✔️ | | | ✔️ | | | |
|
| pdf-to-word | | ✔️ | | | ✔️ | | | ✔️ | | | |
|
||||||
| pdf-to-xml | | ✔️ | | | ✔️ | | | ✔️ | | | |
|
| pdf-to-xml | | ✔️ | | | ✔️ | | | ✔️ | | | |
|
||||||
| xlsx-to-pdf | | ✔️ | | | ✔️ | | | ✔️ | | | |
|
| xlsx-to-pdf | | ✔️ | | | ✔️ | | | ✔️ | | | |
|
||||||
| add-password | | | ✔️ | | | | | | | ✔️ | |
|
| add-password | | | ✔️ | | | | | | | ✔️ | |
|
||||||
| add-watermark | | | ✔️ | | | | | | | ✔️ | |
|
| add-watermark | | | ✔️ | | | | | | | ✔️ | |
|
||||||
| cert-sign | | | ✔️ | | | | | | | ✔️ | |
|
| cert-sign | | | ✔️ | | | | | | | ✔️ | |
|
||||||
| change-permissions | | | ✔️ | | | | | | | ✔️ | |
|
| change-permissions | | | ✔️ | | | | | | | ✔️ | |
|
||||||
| remove-password | | | ✔️ | | | | | | | ✔️ | |
|
| remove-password | | | ✔️ | | | | | | | ✔️ | |
|
||||||
| sanitize-pdf | | | ✔️ | | | | | | | ✔️ | |
|
| sanitize-pdf | | | ✔️ | | | | | | | ✔️ | |
|
||||||
| add-image | | | | ✔️ | | | | | | ✔️ | |
|
| add-image | | | | ✔️ | | | | | | ✔️ | |
|
||||||
| add-page-numbers | | | | ✔️ | | | | | | ✔️ | |
|
| add-page-numbers | | | | ✔️ | | | | | | ✔️ | |
|
||||||
| auto-rename | | | | ✔️ | | | | | | ✔️ | |
|
| auto-rename | | | | ✔️ | | | | | | ✔️ | |
|
||||||
| change-metadata | | | | ✔️ | | | | | | ✔️ | |
|
| change-metadata | | | | ✔️ | | | | | | ✔️ | |
|
||||||
| compare | | | | ✔️ | | | | | | | ✔️ |
|
| compare | | | | ✔️ | | | | | | | ✔️ |
|
||||||
| compress-pdf | | | | ✔️ | ✔️ | | | | ✔️ | | |
|
| compress-pdf | | | | ✔️ | ✔️ | | | | ✔️ | | |
|
||||||
| extract-image-scans | | | | ✔️ | ✔️ | ✔️ | ✔️ | | | | |
|
| extract-image-scans | | | | ✔️ | ✔️ | ✔️ | ✔️ | | | | |
|
||||||
| extract-images | | | | ✔️ | | | | | | ✔️ | |
|
| extract-images | | | | ✔️ | | | | | | ✔️ | |
|
||||||
| flatten | | | | ✔️ | | | | | | | ✔️ |
|
| flatten | | | | ✔️ | | | | | | | ✔️ |
|
||||||
| get-info-on-pdf | | | | ✔️ | | | | | | ✔️ | |
|
| get-info-on-pdf | | | | ✔️ | | | | | | ✔️ | |
|
||||||
| ocr-pdf | | | | ✔️ | ✔️ | | | | ✔️ | | |
|
| ocr-pdf | | | | ✔️ | ✔️ | | | | ✔️ | | |
|
||||||
| remove-blanks | | | | ✔️ | ✔️ | ✔️ | ✔️ | | | | |
|
| remove-blanks | | | | ✔️ | ✔️ | ✔️ | ✔️ | | | | |
|
||||||
| repair | | | | ✔️ | ✔️ | | | ✔️ | | | |
|
| repair | | | | ✔️ | ✔️ | | | ✔️ | | | |
|
||||||
| show-javascript | | | | ✔️ | | | | | | | ✔️ |
|
| show-javascript | | | | ✔️ | | | | | | | ✔️ |
|
||||||
| sign | | | | ✔️ | | | | | | | ✔️ |
|
| sign | | | | ✔️ | | | | | | | ✔️ |
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
### Whilst Pipelines are in alpha...
|
### Whilst Pipelines are in alpha...
|
||||||
You must enable this alpha functionality by setting
|
You must enable this alpha functionality by setting
|
||||||
```
|
```yaml
|
||||||
system:
|
system:
|
||||||
enableAlphaFunctionality: true
|
enableAlphaFunctionality: true
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -9,21 +9,21 @@ Fork Stirling-PDF and make a new branch out of Main
|
|||||||
Then add reference to the language in the navbar by adding a new language entry to the dropdown
|
Then add reference to the language in the navbar by adding a new language entry to the dropdown
|
||||||
|
|
||||||
https://github.com/Stirling-Tools/Stirling-PDF/blob/main/src/main/resources/templates/fragments/languages.html
|
https://github.com/Stirling-Tools/Stirling-PDF/blob/main/src/main/resources/templates/fragments/languages.html
|
||||||
and add a flag svg file to
|
and add a flag svg file to
|
||||||
https://github.com/Stirling-Tools/Stirling-PDF/tree/main/src/main/resources/static/images/flags
|
https://github.com/Stirling-Tools/Stirling-PDF/tree/main/src/main/resources/static/images/flags
|
||||||
Any SVG flags are fine, i got most of mine from [here](https://flagicons.lipis.dev/)
|
Any SVG flags are fine, i got most of mine from [here](https://flagicons.lipis.dev/)
|
||||||
If your language isnt represented by a flag just find whichever closely matches it, such as for Arabic i chose Saudi Arabia
|
If your language isn't represented by a flag just find whichever closely matches it, such as for Arabic i chose Saudi Arabia
|
||||||
|
|
||||||
|
|
||||||
For example to add Polish you would add
|
For example to add Polish you would add
|
||||||
```
|
```html
|
||||||
<a class="dropdown-item lang_dropdown-item" href="" data-language-code="pl_PL">
|
<a class="dropdown-item lang_dropdown-item" href="" data-language-code="pl_PL">
|
||||||
<img src="images/flags/pl.svg" alt="icon" width="20" height="15"> Polski
|
<img src="images/flags/pl.svg" alt="icon" width="20" height="15"> Polski
|
||||||
</a>
|
</a>
|
||||||
```
|
```
|
||||||
The data-language-code is the code used to reference the file in the next step.
|
The data-language-code is the code used to reference the file in the next step.
|
||||||
|
|
||||||
Start by copying the existing english property file
|
Start by copying the existing english property file
|
||||||
|
|
||||||
[https://github.com/Stirling-Tools/Stirling-PDF/blob/main/src/main/resources/messages_en_GB.properties](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/src/main/resources/messages_en_GB.properties)
|
[https://github.com/Stirling-Tools/Stirling-PDF/blob/main/src/main/resources/messages_en_GB.properties](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/src/main/resources/messages_en_GB.properties)
|
||||||
|
|
||||||
@@ -32,7 +32,7 @@ Copy and rename it to messages_{your data-language-code here}.properties, in the
|
|||||||
|
|
||||||
Then simply translate all property entries within that file and make a PR into main for others to use!
|
Then simply translate all property entries within that file and make a PR into main for others to use!
|
||||||
|
|
||||||
If you do not have a java IDE i am happy to verify the changes worked once you raise PR (but wont be able to verify the translations themselves)
|
If you do not have a java IDE i am happy to verify the changes worked once you raise PR (but won't be able to verify the translations themselves)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -2,12 +2,12 @@
|
|||||||
|
|
||||||
This document provides instructions on how to add additional language packs for the OCR tab in Stirling-PDF, both inside and outside of Docker.
|
This document provides instructions on how to add additional language packs for the OCR tab in Stirling-PDF, both inside and outside of Docker.
|
||||||
|
|
||||||
## My OCR used to work and now doesnt!
|
## My OCR used to work and now doesn't!
|
||||||
Please update your tesseract docker volume path version from 4.00 to 5
|
The paths have changed for the tessadata locations on new docker images, please use ``/usr/share/tessdata`` (Others should still work for backwards compatability but might not)
|
||||||
|
|
||||||
## How does the OCR Work
|
## How does the OCR Work
|
||||||
Stirling-PDF uses [OCRmyPDF](https://github.com/ocrmypdf/OCRmyPDF) which in turn uses tesseract for its text recognition.
|
Stirling-PDF uses [OCRmyPDF](https://github.com/ocrmypdf/OCRmyPDF) which in turn uses tesseract for its text recognition.
|
||||||
All credit goes to them for this awesome work!
|
All credit goes to them for this awesome work!
|
||||||
|
|
||||||
## Language Packs
|
## Language Packs
|
||||||
|
|
||||||
@@ -21,13 +21,13 @@ Depending on your requirements, you can choose the appropriate language pack for
|
|||||||
### Installing Language Packs
|
### Installing Language Packs
|
||||||
|
|
||||||
1. Download the desired language pack(s) by selecting the `.traineddata` file(s) for the language(s) you need.
|
1. Download the desired language pack(s) by selecting the `.traineddata` file(s) for the language(s) you need.
|
||||||
2. Place the `.traineddata` files in the Tesseract tessdata directory: `/usr/share/tesseract-ocr/5/tessdata` (Debian) or `/usr/share/tesseract/tessdata` (Fedora)
|
2. Place the `.traineddata` files in the Tesseract tessdata directory: `/usr/share/tessdata`
|
||||||
|
|
||||||
# DO NOT REMOVE EXISTING ENG.TRAINEDDATA, IT'S REQUIRED.
|
# DO NOT REMOVE EXISTING ENG.TRAINEDDATA, IT'S REQUIRED.
|
||||||
|
|
||||||
#### Docker
|
#### Docker
|
||||||
|
|
||||||
If you are using Docker, you need to expose the Tesseract tessdata directory as a volume in order to use the additional language packs.
|
If you are using Docker, you need to expose the Tesseract tessdata directory as a volume in order to use the additional language packs.
|
||||||
#### Docker Compose
|
#### Docker Compose
|
||||||
Modify your `docker-compose.yml` file to include the following volume configuration:
|
Modify your `docker-compose.yml` file to include the following volume configuration:
|
||||||
|
|
||||||
@@ -37,14 +37,14 @@ services:
|
|||||||
your_service_name:
|
your_service_name:
|
||||||
image: your_docker_image_name
|
image: your_docker_image_name
|
||||||
volumes:
|
volumes:
|
||||||
- /location/of/trainingData:/usr/share/tesseract-ocr/5/tessdata
|
- /location/of/trainingData:/usr/share/tessdata
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
#### Docker run
|
#### Docker run
|
||||||
Add the following to your existing docker run command
|
Add the following to your existing docker run command
|
||||||
```bash
|
```bash
|
||||||
-v /location/of/trainingData:/usr/share/tesseract-ocr/5/tessdata
|
-v /location/of/trainingData:/usr/share/tessdata
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Non-Docker
|
#### Non-Docker
|
||||||
|
|||||||
88
Jenkinsfile
vendored
88
Jenkinsfile
vendored
@@ -1,45 +1,45 @@
|
|||||||
pipeline {
|
pipeline {
|
||||||
agent any
|
agent any
|
||||||
stages {
|
stages {
|
||||||
stage('Build') {
|
stage('Build') {
|
||||||
steps {
|
steps {
|
||||||
sh 'chmod 755 gradlew'
|
sh 'chmod 755 gradlew'
|
||||||
sh './gradlew build'
|
sh './gradlew build'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stage('Docker Build') {
|
stage('Docker Build') {
|
||||||
steps {
|
steps {
|
||||||
script {
|
script {
|
||||||
def appVersion = sh(returnStdout: true, script: './gradlew printVersion -q').trim()
|
def appVersion = sh(returnStdout: true, script: './gradlew printVersion -q').trim()
|
||||||
def image = "frooodle/s-pdf:$appVersion"
|
def image = "frooodle/s-pdf:$appVersion"
|
||||||
sh "docker build -t $image ."
|
sh "docker build -t $image ."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stage('Docker Push') {
|
stage('Docker Push') {
|
||||||
steps {
|
steps {
|
||||||
script {
|
script {
|
||||||
def appVersion = sh(returnStdout: true, script: './gradlew printVersion -q').trim()
|
def appVersion = sh(returnStdout: true, script: './gradlew printVersion -q').trim()
|
||||||
def image = "frooodle/s-pdf:$appVersion"
|
def image = "frooodle/s-pdf:$appVersion"
|
||||||
withCredentials([string(credentialsId: 'docker_hub_access_token', variable: 'DOCKER_HUB_ACCESS_TOKEN')]) {
|
withCredentials([string(credentialsId: 'docker_hub_access_token', variable: 'DOCKER_HUB_ACCESS_TOKEN')]) {
|
||||||
sh "docker login --username frooodle --password $DOCKER_HUB_ACCESS_TOKEN"
|
sh "docker login --username frooodle --password $DOCKER_HUB_ACCESS_TOKEN"
|
||||||
sh "docker push $image"
|
sh "docker push $image"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stage('Helm Push') {
|
stage('Helm Push') {
|
||||||
steps {
|
steps {
|
||||||
script {
|
script {
|
||||||
//TODO: Read chartVersion from Chart.yaml
|
//TODO: Read chartVersion from Chart.yaml
|
||||||
def chartVersion = '1.0.0'
|
def chartVersion = '1.0.0'
|
||||||
withCredentials([string(credentialsId: 'docker_hub_access_token', variable: 'DOCKER_HUB_ACCESS_TOKEN')]) {
|
withCredentials([string(credentialsId: 'docker_hub_access_token', variable: 'DOCKER_HUB_ACCESS_TOKEN')]) {
|
||||||
sh "docker login --username frooodle --password $DOCKER_HUB_ACCESS_TOKEN"
|
sh "docker login --username frooodle --password $DOCKER_HUB_ACCESS_TOKEN"
|
||||||
sh "helm package chart/stirling-pdf"
|
sh "helm package chart/stirling-pdf"
|
||||||
sh "helm push stirling-pdf-chart-1.0.0.tgz oci://registry-1.docker.io/frooodle"
|
sh "helm push stirling-pdf-chart-1.0.0.tgz oci://registry-1.docker.io/frooodle"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -45,7 +45,7 @@ sudo apt-get update
|
|||||||
sudo apt-get install -y git automake autoconf libtool libleptonica-dev pkg-config zlib1g-dev make g++ java-17-openjdk python3 python3-pip
|
sudo apt-get install -y git automake autoconf libtool libleptonica-dev pkg-config zlib1g-dev make g++ java-17-openjdk python3 python3-pip
|
||||||
```
|
```
|
||||||
|
|
||||||
For Fedora-based systems use this command:
|
For Fedora-based systems use this command:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo dnf install -y git automake autoconf libtool leptonica-devel pkg-config zlib-devel make gcc-c++ java-17-openjdk python3 python3-pip
|
sudo dnf install -y git automake autoconf libtool leptonica-devel pkg-config zlib-devel make gcc-c++ java-17-openjdk python3 python3-pip
|
||||||
@@ -65,7 +65,7 @@ sudo make install
|
|||||||
```
|
```
|
||||||
|
|
||||||
### Step 3: Install Additional Software
|
### Step 3: Install Additional Software
|
||||||
Next we need to install LibreOffice for conversions, ocrmypdf for OCR, and opencv for patern recognition functionality.
|
Next we need to install LibreOffice for conversions, ocrmypdf for OCR, and opencv for pattern recognition functionality.
|
||||||
|
|
||||||
Install the following software:
|
Install the following software:
|
||||||
|
|
||||||
@@ -95,14 +95,14 @@ For Debian-based systems, you can use the following command:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo apt-get install -y libreoffice-writer libreoffice-calc libreoffice-impress unpaper ocrmypdf
|
sudo apt-get install -y libreoffice-writer libreoffice-calc libreoffice-impress unpaper ocrmypdf
|
||||||
pip3 install uno opencv-python-headless unoconv pngquant WeasyPrint
|
pip3 install uno opencv-python-headless unoconv pngquant WeasyPrint
|
||||||
```
|
```
|
||||||
|
|
||||||
For Fedora:
|
For Fedora:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo dnf install -y libreoffice-writer libreoffice-calc libreoffice-impress unpaper ocrmypdf
|
sudo dnf install -y libreoffice-writer libreoffice-calc libreoffice-impress unpaper ocrmypdf
|
||||||
pip3 install uno opencv-python-headless unoconv pngquant WeasyPrint
|
pip3 install uno opencv-python-headless unoconv pngquant WeasyPrint
|
||||||
```
|
```
|
||||||
|
|
||||||
### Step 4: Clone and Build Stirling-PDF
|
### Step 4: Clone and Build Stirling-PDF
|
||||||
@@ -139,8 +139,8 @@ Easiest is to use the langpacks provided by your repositories. Skip the other st
|
|||||||
Manual:
|
Manual:
|
||||||
|
|
||||||
1. Download the desired language pack(s) by selecting the `.traineddata` file(s) for the language(s) you need.
|
1. Download the desired language pack(s) by selecting the `.traineddata` file(s) for the language(s) you need.
|
||||||
2. Place the `.traineddata` files in the Tesseract tessdata directory: `/usr/share/tesseract-ocr/5/tessdata`
|
2. Place the `.traineddata` files in the Tesseract tessdata directory: `/usr/share/tessdata`
|
||||||
3.
|
3.
|
||||||
Please view [OCRmyPDF install guide](https://ocrmypdf.readthedocs.io/en/latest/installation.html) for more info.
|
Please view [OCRmyPDF install guide](https://ocrmypdf.readthedocs.io/en/latest/installation.html) for more info.
|
||||||
**IMPORTANT:** DO NOT REMOVE EXISTING `eng.traineddata`, IT'S REQUIRED.
|
**IMPORTANT:** DO NOT REMOVE EXISTING `eng.traineddata`, IT'S REQUIRED.
|
||||||
|
|
||||||
@@ -264,10 +264,10 @@ sudo systemctl restart stirlingpdf.service
|
|||||||
|
|
||||||
Remember to set the necessary environment variables before running the project if you want to customize the application the list can be seen in the main readme.
|
Remember to set the necessary environment variables before running the project if you want to customize the application the list can be seen in the main readme.
|
||||||
|
|
||||||
You can do this in the terminal by using the `export` command or -D arguements to java -jar command:
|
You can do this in the terminal by using the `export` command or -D argument to java -jar command:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
export APP_HOME_NAME="Stirling PDF"
|
export APP_HOME_NAME="Stirling PDF"
|
||||||
or
|
or
|
||||||
-DAPP_HOME_NAME="Stirling PDF"
|
-DAPP_HOME_NAME="Stirling PDF"
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
## Whilst Pipelines are in alpha...
|
## Whilst Pipelines are in alpha...
|
||||||
You must enable this alpha functionality by setting
|
You must enable this alpha functionality by setting
|
||||||
```
|
```yaml
|
||||||
system:
|
system:
|
||||||
enableAlphaFunctionality: true
|
enableAlphaFunctionality: true
|
||||||
```
|
```
|
||||||
|
|||||||
134
README.md
134
README.md
@@ -6,7 +6,7 @@
|
|||||||
[](https://github.com/Stirling-Tools/Stirling-PDF/)
|
[](https://github.com/Stirling-Tools/Stirling-PDF/)
|
||||||
[](https://github.com/Stirling-Tools/stirling-pdf)
|
[](https://github.com/Stirling-Tools/stirling-pdf)
|
||||||
[](https://www.paypal.com/paypalme/froodleplex)
|
[](https://www.paypal.com/paypalme/froodleplex)
|
||||||
[](https://github.com/sponsors/Frooodle)
|
[](https://github.com/sponsors/Frooodle)
|
||||||
|
|
||||||
[](https://cloud.digitalocean.com/apps/new?repo=https://github.com/Stirling-Tools/Stirling-PDF/tree/digitalOcean&refcode=c3210994b1af)
|
[](https://cloud.digitalocean.com/apps/new?repo=https://github.com/Stirling-Tools/Stirling-PDF/tree/digitalOcean&refcode=c3210994b1af)
|
||||||
|
|
||||||
@@ -16,15 +16,13 @@ Stirling PDF makes no outbound calls for any record keeping or tracking.
|
|||||||
|
|
||||||
All files and PDFs exist either exclusively on the client side, reside in server memory only during task execution, or temporarily reside in a file solely for the execution of the task. Any file downloaded by the user will have been deleted from the server by that point.
|
All files and PDFs exist either exclusively on the client side, reside in server memory only during task execution, or temporarily reside in a file solely for the execution of the task. Any file downloaded by the user will have been deleted from the server by that point.
|
||||||
|
|
||||||
Please feel free to submit feature requests or report bugs either through GitHub issues or on our [Discord](https://discord.gg/Cn8pWhQRxZ)
|
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
- Dark mode support.
|
- Dark mode support.
|
||||||
- Custom download options (see [here](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/images/settings.png) for example)
|
- Custom download options (see [here](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/images/settings.png) for example)
|
||||||
- Parallel file processing and downloads
|
- Parallel file processing and downloads
|
||||||
- API for integration with external scripts
|
- API for integration with external scripts
|
||||||
- Optional Login and Authentication support (see [here](https://github.com/Stirling-Tools/Stirling-PDF/tree/main#login-authentication) for documentation)
|
- Optional Login and Authentication support (see [here](https://github.com/Stirling-Tools/Stirling-PDF/tree/main#login-authentication) for documentation)
|
||||||
|
|
||||||
|
|
||||||
@@ -32,56 +30,56 @@ Please feel free to submit feature requests or report bugs either through GitHub
|
|||||||
|
|
||||||
### **Page Operations**
|
### **Page Operations**
|
||||||
- View and modify PDFs - View multi page PDFs with custom viewing sorting and searching. Plus on page edit features like annotate, draw and adding text and images. (Using PDF.js with Joxit and Liberation.Liberation fonts)
|
- View and modify PDFs - View multi page PDFs with custom viewing sorting and searching. Plus on page edit features like annotate, draw and adding text and images. (Using PDF.js with Joxit and Liberation.Liberation fonts)
|
||||||
- Full interactive GUI for merging/splitting/rotating/moving PDFs and their pages.
|
- Full interactive GUI for merging/splitting/rotating/moving PDFs and their pages.
|
||||||
- Merge multiple PDFs together into a single resultant file.
|
- Merge multiple PDFs together into a single resultant file.
|
||||||
- Split PDFs into multiple files at specified page numbers or extract all pages as individual files.
|
- Split PDFs into multiple files at specified page numbers or extract all pages as individual files.
|
||||||
- Reorganize PDF pages into different orders.
|
- Reorganize PDF pages into different orders.
|
||||||
- Rotate PDFs in 90-degree increments.
|
- Rotate PDFs in 90-degree increments.
|
||||||
- Remove pages.
|
- Remove pages.
|
||||||
- Multi-page layout (Format PDFs into a multi-paged page).
|
- Multi-page layout (Format PDFs into a multi-paged page).
|
||||||
- Scale page contents size by set %.
|
- Scale page contents size by set %.
|
||||||
- Adjust Contrast.
|
- Adjust Contrast.
|
||||||
- Crop PDF.
|
- Crop PDF.
|
||||||
- Auto Split PDF (With physically scanned page dividers).
|
- Auto Split PDF (With physically scanned page dividers).
|
||||||
- Extract page(s).
|
- Extract page(s).
|
||||||
- Convert PDF to a single page.
|
- Convert PDF to a single page.
|
||||||
|
|
||||||
### **Conversion Operations**
|
### **Conversion Operations**
|
||||||
- Convert PDFs to and from images.
|
- Convert PDFs to and from images.
|
||||||
- Convert any common file to PDF (using LibreOffice).
|
- Convert any common file to PDF (using LibreOffice).
|
||||||
- Convert PDF to Word/Powerpoint/Others (using LibreOffice).
|
- Convert PDF to Word/Powerpoint/Others (using LibreOffice).
|
||||||
- Convert HTML to PDF.
|
- Convert HTML to PDF.
|
||||||
- URL to PDF.
|
- URL to PDF.
|
||||||
- Markdown to PDF.
|
- Markdown to PDF.
|
||||||
|
|
||||||
### **Security & Permissions**
|
### **Security & Permissions**
|
||||||
- Add and remove passwords.
|
- Add and remove passwords.
|
||||||
- Change/set PDF Permissions.
|
- Change/set PDF Permissions.
|
||||||
- Add watermark(s).
|
- Add watermark(s).
|
||||||
- Certify/sign PDFs.
|
- Certify/sign PDFs.
|
||||||
- Sanitize PDFs.
|
- Sanitize PDFs.
|
||||||
- Auto-redact text.
|
- Auto-redact text.
|
||||||
|
|
||||||
### **Other Operations**
|
### **Other Operations**
|
||||||
- Add/Generate/Write signatures.
|
- Add/Generate/Write signatures.
|
||||||
- Repair PDFs.
|
- Repair PDFs.
|
||||||
- Detect and remove blank pages.
|
- Detect and remove blank pages.
|
||||||
- Compare 2 PDFs and show differences in text.
|
- Compare 2 PDFs and show differences in text.
|
||||||
- Add images to PDFs.
|
- Add images to PDFs.
|
||||||
- Compress PDFs to decrease their filesize (Using OCRMyPDF).
|
- Compress PDFs to decrease their filesize (Using OCRMyPDF).
|
||||||
- Extract images from PDF.
|
- Extract images from PDF.
|
||||||
- Extract images from Scans.
|
- Extract images from Scans.
|
||||||
- Add page numbers.
|
- Add page numbers.
|
||||||
- Auto rename file by detecting PDF header text.
|
- Auto rename file by detecting PDF header text.
|
||||||
- OCR on PDF (Using OCRMyPDF).
|
- OCR on PDF (Using OCRMyPDF).
|
||||||
- PDF/A conversion (Using OCRMyPDF).
|
- PDF/A conversion (Using OCRMyPDF).
|
||||||
- Edit metadata.
|
- Edit metadata.
|
||||||
- Flatten PDFs.
|
- Flatten PDFs.
|
||||||
- Get all information on a PDF to view or export as JSON.
|
- Get all information on a PDF to view or export as JSON.
|
||||||
|
|
||||||
|
|
||||||
For a overview of the tasks and the technology each uses please view [Endpoint-groups.md](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/Endpoint-groups.md)
|
For a overview of the tasks and the technology each uses please view [Endpoint-groups.md](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/Endpoint-groups.md)
|
||||||
Hosted instance/demo of the app can be seen [here](https://pdf.adminforge.de/) hosted by the team at adminforge.de
|
Demo of the app is available [here](https://stirlingpdf.io). username: demo, password: demo
|
||||||
|
|
||||||
## Technologies used
|
## Technologies used
|
||||||
- Spring Boot + Thymeleaf
|
- Spring Boot + Thymeleaf
|
||||||
@@ -109,23 +107,24 @@ For people that don't mind about space optimization just use the latest tag.
|
|||||||

|

|
||||||
|
|
||||||
Docker Run
|
Docker Run
|
||||||
```
|
```bash
|
||||||
docker run -d \
|
docker run -d \
|
||||||
-p 8080:8080 \
|
-p 8080:8080 \
|
||||||
-v /location/of/trainingData:/usr/share/tesseract-ocr/5/tessdata \
|
-v /location/of/trainingData:/usr/share/tessdata \
|
||||||
-v /location/of/extraConfigs:/configs \
|
-v /location/of/extraConfigs:/configs \
|
||||||
-v /location/of/logs:/logs \
|
-v /location/of/logs:/logs \
|
||||||
-e DOCKER_ENABLE_SECURITY=false \
|
-e DOCKER_ENABLE_SECURITY=false \
|
||||||
|
-e INSTALL_BOOK_AND_ADVANCED_HTML_OPS=false \
|
||||||
--name stirling-pdf \
|
--name stirling-pdf \
|
||||||
frooodle/s-pdf:latest
|
frooodle/s-pdf:latest
|
||||||
|
|
||||||
|
|
||||||
Can also add these for customisation but are not required
|
Can also add these for customisation but are not required
|
||||||
|
|
||||||
-v /location/of/customFiles:/customFiles \
|
-v /location/of/customFiles:/customFiles \
|
||||||
```
|
```
|
||||||
Docker Compose
|
Docker Compose
|
||||||
```
|
```yaml
|
||||||
version: '3.3'
|
version: '3.3'
|
||||||
services:
|
services:
|
||||||
stirling-pdf:
|
stirling-pdf:
|
||||||
@@ -133,12 +132,13 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- '8080:8080'
|
- '8080:8080'
|
||||||
volumes:
|
volumes:
|
||||||
- /location/of/trainingData:/usr/share/tesseract-ocr/5/tessdata #Required for extra OCR languages
|
- /location/of/trainingData:/usr/share/tessdata #Required for extra OCR languages
|
||||||
- /location/of/extraConfigs:/configs
|
- /location/of/extraConfigs:/configs
|
||||||
# - /location/of/customFiles:/customFiles/
|
# - /location/of/customFiles:/customFiles/
|
||||||
# - /location/of/logs:/logs/
|
# - /location/of/logs:/logs/
|
||||||
environment:
|
environment:
|
||||||
- DOCKER_ENABLE_SECURITY=false
|
- DOCKER_ENABLE_SECURITY=false
|
||||||
|
- INSTALL_BOOK_AND_ADVANCED_HTML_OPS=false
|
||||||
```
|
```
|
||||||
|
|
||||||
Note: Podman is CLI-compatible with Docker, so simply replace "docker" with "podman".
|
Note: Podman is CLI-compatible with Docker, so simply replace "docker" with "podman".
|
||||||
@@ -146,7 +146,8 @@ Note: Podman is CLI-compatible with Docker, so simply replace "docker" with "pod
|
|||||||
## Enable OCR/Compression feature
|
## Enable OCR/Compression feature
|
||||||
Please view https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToUseOCR.md
|
Please view https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToUseOCR.md
|
||||||
|
|
||||||
## Want to add your own language?
|
## Supported Languages
|
||||||
|
|
||||||
Stirling PDF currently supports 26!
|
Stirling PDF currently supports 26!
|
||||||
- English (English) (en_GB)
|
- English (English) (en_GB)
|
||||||
- English (US) (en_US)
|
- English (US) (en_US)
|
||||||
@@ -173,17 +174,11 @@ Stirling PDF currently supports 26!
|
|||||||
- Hindi (हिंदी) (hi_IN)
|
- Hindi (हिंदी) (hi_IN)
|
||||||
- Hungarian (Magyar) (hu_HU)
|
- Hungarian (Magyar) (hu_HU)
|
||||||
- Bulgarian (Български) (bg_BG)
|
- Bulgarian (Български) (bg_BG)
|
||||||
- Sebian Latin alphabet (Srpski) (sr-Latn-RS)
|
- Sebian Latin alphabet (Srpski) (sr_LATN_RS)
|
||||||
|
|
||||||
If you want to add your own language to Stirling-PDF please refer
|
## Contributing (creating issues, translations, fixing bugs, etc.)
|
||||||
https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md
|
|
||||||
|
|
||||||
And please create a PR to merge it back in so others can use it!
|
|
||||||
|
|
||||||
## How to View
|
|
||||||
1. Open a web browser and navigate to `http://localhost:8080/`
|
|
||||||
2. Use the application by following the instructions on the website.
|
|
||||||
|
|
||||||
|
Please see our [Contributing Guide](CONTRIBUTING.md)!
|
||||||
|
|
||||||
## Customisation
|
## Customisation
|
||||||
Stirling PDF allows easy customization of the app.
|
Stirling PDF allows easy customization of the app.
|
||||||
@@ -197,7 +192,7 @@ This file is located in the ``/configs`` directory and follows standard YAML for
|
|||||||
|
|
||||||
Environment variables are also supported and would override the settings file
|
Environment variables are also supported and would override the settings file
|
||||||
For example in the settings.yml you have
|
For example in the settings.yml you have
|
||||||
```
|
```yaml
|
||||||
system:
|
system:
|
||||||
defaultLocale: 'en-US'
|
defaultLocale: 'en-US'
|
||||||
```
|
```
|
||||||
@@ -205,7 +200,7 @@ system:
|
|||||||
To have this via an environment variable you would have ``SYSTEM_DEFAULTLOCALE``
|
To have this via an environment variable you would have ``SYSTEM_DEFAULTLOCALE``
|
||||||
|
|
||||||
The Current list of settings is
|
The Current list of settings is
|
||||||
```
|
```yaml
|
||||||
security:
|
security:
|
||||||
enableLogin: false # set to 'true' to enable login
|
enableLogin: false # set to 'true' to enable login
|
||||||
csrfDisabled: true
|
csrfDisabled: true
|
||||||
@@ -235,6 +230,7 @@ metrics:
|
|||||||
- ``SYSTEM_ROOTURIPATH`` ie set to ``/pdf-app`` to Set the application's root URI to ``localhost:8080/pdf-app``
|
- ``SYSTEM_ROOTURIPATH`` ie set to ``/pdf-app`` to Set the application's root URI to ``localhost:8080/pdf-app``
|
||||||
- ``SYSTEM_CONNECTIONTIMEOUTMINUTES`` to set custom connection timeout values
|
- ``SYSTEM_CONNECTIONTIMEOUTMINUTES`` to set custom connection timeout values
|
||||||
- ``DOCKER_ENABLE_SECURITY`` to tell docker to download security jar (required as true for auth login)
|
- ``DOCKER_ENABLE_SECURITY`` to tell docker to download security jar (required as true for auth login)
|
||||||
|
- ``INSTALL_BOOK_AND_ADVANCED_HTML_OPS`` to download calibre onto stirling-pdf enabling pdf to/from book and advanced html conversion
|
||||||
|
|
||||||
## API
|
## API
|
||||||
For those wanting to use Stirling-PDFs backend API to link with their own custom scripting to edit PDFs you can view all existing API documentation
|
For those wanting to use Stirling-PDFs backend API to link with their own custom scripting to edit PDFs you can view all existing API documentation
|
||||||
@@ -243,9 +239,9 @@ For those wanting to use Stirling-PDFs backend API to link with their own custom
|
|||||||
|
|
||||||
## Login authentication
|
## Login authentication
|
||||||

|

|
||||||
### Prerequisites:
|
### Prerequisites:
|
||||||
- User must have the folder ./configs volumed within docker so that it is retained during updates.
|
- User must have the folder ./configs volumed within docker so that it is retained during updates.
|
||||||
- Docker uses must download the security jar version by setting ``DOCKER_ENABLE_SECURITY`` to ``true`` in environment variables.
|
- Docker uses must download the security jar version by setting ``DOCKER_ENABLE_SECURITY`` to ``true`` in environment variables.
|
||||||
- Then either enable login via the settings.yml file or via setting ``SECURITY_ENABLE_LOGIN`` to ``true``
|
- Then either enable login via the settings.yml file or via setting ``SECURITY_ENABLE_LOGIN`` to ``true``
|
||||||
- Now the initial user will be generated with username ``admin`` and password ``stirling``. On login you will be forced to change the password to a new one. You can also use the environment variables ``SECURITY_INITIALLOGIN_USERNAME`` and ``SECURITY_INITIALLOGIN_PASSWORD`` to set your own straight away (Recommended to remove them after user creation).
|
- Now the initial user will be generated with username ``admin`` and password ``stirling``. On login you will be forced to change the password to a new one. You can also use the environment variables ``SECURITY_INITIALLOGIN_USERNAME`` and ``SECURITY_INITIALLOGIN_PASSWORD`` to set your own straight away (Recommended to remove them after user creation).
|
||||||
|
|
||||||
@@ -266,10 +262,10 @@ For API usage you must provide a header with 'X-API-Key' and the associated API
|
|||||||
- Progress bar/Tracking
|
- Progress bar/Tracking
|
||||||
- Full custom logic pipelines to combine multiple operations together.
|
- Full custom logic pipelines to combine multiple operations together.
|
||||||
- Folder support with auto scanning to perform operations on
|
- Folder support with auto scanning to perform operations on
|
||||||
- Redact text (Via UI not just automated way)
|
- Redact text (Via UI not just automated way)
|
||||||
- Add Forms
|
- Add Forms
|
||||||
- Multi page layout (Stich PDF pages together) support x rows y columns and custom page sizing
|
- Multi page layout (Stich PDF pages together) support x rows y columns and custom page sizing
|
||||||
- Fill forms mannual and automatic
|
- Fill forms manually or automatically
|
||||||
|
|
||||||
### Q2: Why is my application downloading .htm files?
|
### Q2: Why is my application downloading .htm files?
|
||||||
This is an issue caused commonly by your NGINX configuration. The default file upload size for NGINX is 1MB, you need to add the following in your Nginx sites-available file. ``client_max_body_size SIZE;`` Where "SIZE" is 50M for example for 50MB files.
|
This is an issue caused commonly by your NGINX configuration. The default file upload size for NGINX is 1MB, you need to add the following in your Nginx sites-available file. ``client_max_body_size SIZE;`` Where "SIZE" is 50M for example for 50MB files.
|
||||||
|
|||||||
@@ -1,64 +1,64 @@
|
|||||||
|Technology | Ultra-Lite | Lite | Full |
|
|Technology | Ultra-Lite | Lite | Full |
|
||||||
|----------------|:----------:|:----:|:----:|
|
|----------------|:----------:|:----:|:----:|
|
||||||
| Java | ✔️ | ✔️ | ✔️ |
|
| Java | ✔️ | ✔️ | ✔️ |
|
||||||
| JavaScript | ✔️ | ✔️ | ✔️ |
|
| JavaScript | ✔️ | ✔️ | ✔️ |
|
||||||
| Libre | | ✔️ | ✔️ |
|
| Libre | | ✔️ | ✔️ |
|
||||||
| Python | | | ✔️ |
|
| Python | | | ✔️ |
|
||||||
| OpenCV | | | ✔️ |
|
| OpenCV | | | ✔️ |
|
||||||
| OCRmyPDF | | | ✔️ |
|
| OCRmyPDF | | | ✔️ |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Operation | Ultra-Lite | Lite | Full
|
Operation | Ultra-Lite | Lite | Full
|
||||||
--------------------|------------|------|-----
|
--------------------|------------|------|-----
|
||||||
add-page-numbers | ✔️ | ✔️ | ✔️
|
add-page-numbers | ✔️ | ✔️ | ✔️
|
||||||
add-password | ✔️ | ✔️ | ✔️
|
add-password | ✔️ | ✔️ | ✔️
|
||||||
add-image | ✔️ | ✔️ | ✔️
|
add-image | ✔️ | ✔️ | ✔️
|
||||||
add-watermark | ✔️ | ✔️ | ✔️
|
add-watermark | ✔️ | ✔️ | ✔️
|
||||||
adjust-contrast | ✔️ | ✔️ | ✔️
|
adjust-contrast | ✔️ | ✔️ | ✔️
|
||||||
auto-split-pdf | ✔️ | ✔️ | ✔️
|
auto-split-pdf | ✔️ | ✔️ | ✔️
|
||||||
auto-redact | ✔️ | ✔️ | ✔️
|
auto-redact | ✔️ | ✔️ | ✔️
|
||||||
auto-rename | ✔️ | ✔️ | ✔️
|
auto-rename | ✔️ | ✔️ | ✔️
|
||||||
cert-sign | ✔️ | ✔️ | ✔️
|
cert-sign | ✔️ | ✔️ | ✔️
|
||||||
crop | ✔️ | ✔️ | ✔️
|
crop | ✔️ | ✔️ | ✔️
|
||||||
change-metadata | ✔️ | ✔️ | ✔️
|
change-metadata | ✔️ | ✔️ | ✔️
|
||||||
change-permissions | ✔️ | ✔️ | ✔️
|
change-permissions | ✔️ | ✔️ | ✔️
|
||||||
compare | ✔️ | ✔️ | ✔️
|
compare | ✔️ | ✔️ | ✔️
|
||||||
extract-page | ✔️ | ✔️ | ✔️
|
extract-page | ✔️ | ✔️ | ✔️
|
||||||
extract-images | ✔️ | ✔️ | ✔️
|
extract-images | ✔️ | ✔️ | ✔️
|
||||||
flatten | ✔️ | ✔️ | ✔️
|
flatten | ✔️ | ✔️ | ✔️
|
||||||
get-info-on-pdf | ✔️ | ✔️ | ✔️
|
get-info-on-pdf | ✔️ | ✔️ | ✔️
|
||||||
img-to-pdf | ✔️ | ✔️ | ✔️
|
img-to-pdf | ✔️ | ✔️ | ✔️
|
||||||
markdown-to-pdf | ✔️ | ✔️ | ✔️
|
markdown-to-pdf | ✔️ | ✔️ | ✔️
|
||||||
merge-pdfs | ✔️ | ✔️ | ✔️
|
merge-pdfs | ✔️ | ✔️ | ✔️
|
||||||
multi-page-layout | ✔️ | ✔️ | ✔️
|
multi-page-layout | ✔️ | ✔️ | ✔️
|
||||||
overlay-pdf | ✔️ | ✔️ | ✔️
|
overlay-pdf | ✔️ | ✔️ | ✔️
|
||||||
pdf-organizer | ✔️ | ✔️ | ✔️
|
pdf-organizer | ✔️ | ✔️ | ✔️
|
||||||
pdf-to-csv | ✔️ | ✔️ | ✔️
|
pdf-to-csv | ✔️ | ✔️ | ✔️
|
||||||
pdf-to-img | ✔️ | ✔️ | ✔️
|
pdf-to-img | ✔️ | ✔️ | ✔️
|
||||||
pdf-to-single-page | ✔️ | ✔️ | ✔️
|
pdf-to-single-page | ✔️ | ✔️ | ✔️
|
||||||
remove-pages | ✔️ | ✔️ | ✔️
|
remove-pages | ✔️ | ✔️ | ✔️
|
||||||
remove-password | ✔️ | ✔️ | ✔️
|
remove-password | ✔️ | ✔️ | ✔️
|
||||||
rotate-pdf | ✔️ | ✔️ | ✔️
|
rotate-pdf | ✔️ | ✔️ | ✔️
|
||||||
sanitize-pdf | ✔️ | ✔️ | ✔️
|
sanitize-pdf | ✔️ | ✔️ | ✔️
|
||||||
scale-pages | ✔️ | ✔️ | ✔️
|
scale-pages | ✔️ | ✔️ | ✔️
|
||||||
sign | ✔️ | ✔️ | ✔️
|
sign | ✔️ | ✔️ | ✔️
|
||||||
show-javascript | ✔️ | ✔️ | ✔️
|
show-javascript | ✔️ | ✔️ | ✔️
|
||||||
split-by-size-or-count | ✔️ | ✔️ | ✔️
|
split-by-size-or-count | ✔️ | ✔️ | ✔️
|
||||||
split-pdf-by-sections | ✔️ | ✔️ | ✔️
|
split-pdf-by-sections | ✔️ | ✔️ | ✔️
|
||||||
split-pdfs | ✔️ | ✔️ | ✔️
|
split-pdfs | ✔️ | ✔️ | ✔️
|
||||||
file-to-pdf | | ✔️ | ✔️
|
file-to-pdf | | ✔️ | ✔️
|
||||||
pdf-to-html | | ✔️ | ✔️
|
pdf-to-html | | ✔️ | ✔️
|
||||||
pdf-to-presentation | | ✔️ | ✔️
|
pdf-to-presentation | | ✔️ | ✔️
|
||||||
pdf-to-text | | ✔️ | ✔️
|
pdf-to-text | | ✔️ | ✔️
|
||||||
pdf-to-word | | ✔️ | ✔️
|
pdf-to-word | | ✔️ | ✔️
|
||||||
pdf-to-xml | | ✔️ | ✔️
|
pdf-to-xml | | ✔️ | ✔️
|
||||||
repair | | ✔️ | ✔️
|
repair | | ✔️ | ✔️
|
||||||
xlsx-to-pdf | | ✔️ | ✔️
|
xlsx-to-pdf | | ✔️ | ✔️
|
||||||
compress-pdf | | | ✔️
|
compress-pdf | | | ✔️
|
||||||
extract-image-scans | | | ✔️
|
extract-image-scans | | | ✔️
|
||||||
ocr-pdf | | | ✔️
|
ocr-pdf | | | ✔️
|
||||||
pdf-to-pdfa | | | ✔️
|
pdf-to-pdfa | | | ✔️
|
||||||
remove-blanks | | | ✔️
|
remove-blanks | | | ✔️
|
||||||
|
|||||||
42
build.gradle
42
build.gradle
@@ -1,18 +1,18 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id 'java'
|
id 'java'
|
||||||
id 'org.springframework.boot' version '3.2.1'
|
id 'org.springframework.boot' version '3.2.2'
|
||||||
id 'io.spring.dependency-management' version '1.1.3'
|
id 'io.spring.dependency-management' version '1.1.3'
|
||||||
id 'org.springdoc.openapi-gradle-plugin' version '1.8.0'
|
id 'org.springdoc.openapi-gradle-plugin' version '1.8.0'
|
||||||
id "io.swagger.swaggerhub" version "1.3.2"
|
id "io.swagger.swaggerhub" version "1.3.2"
|
||||||
id 'edu.sc.seis.launch4j' version '3.0.5'
|
id 'edu.sc.seis.launch4j' version '3.0.5'
|
||||||
id 'com.diffplug.spotless' version '6.23.3'
|
id 'com.diffplug.spotless' version '6.25.0'
|
||||||
id 'com.github.jk1.dependency-license-report' version '2.5'
|
id 'com.github.jk1.dependency-license-report' version '2.5'
|
||||||
}
|
}
|
||||||
|
|
||||||
import com.github.jk1.license.render.*
|
import com.github.jk1.license.render.*
|
||||||
|
|
||||||
group = 'stirling.software'
|
group = 'stirling.software'
|
||||||
version = '0.19.0'
|
version = '0.22.0'
|
||||||
sourceCompatibility = '17'
|
sourceCompatibility = '17'
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
@@ -89,22 +89,24 @@ dependencies {
|
|||||||
//security updates
|
//security updates
|
||||||
implementation 'ch.qos.logback:logback-classic:1.4.14'
|
implementation 'ch.qos.logback:logback-classic:1.4.14'
|
||||||
implementation 'ch.qos.logback:logback-core:1.4.14'
|
implementation 'ch.qos.logback:logback-core:1.4.14'
|
||||||
implementation 'org.springframework:spring-webmvc:6.1.2'
|
implementation 'org.springframework:spring-webmvc:6.1.3'
|
||||||
|
|
||||||
|
implementation("io.github.pixee:java-security-toolkit:1.1.2")
|
||||||
|
|
||||||
implementation 'org.yaml:snakeyaml:2.2'
|
implementation 'org.yaml:snakeyaml:2.2'
|
||||||
implementation 'org.springframework.boot:spring-boot-starter-web:3.2.1'
|
implementation 'org.springframework.boot:spring-boot-starter-web:3.2.2'
|
||||||
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf:3.2.1'
|
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf:3.2.2'
|
||||||
|
|
||||||
if (System.getenv('DOCKER_ENABLE_SECURITY') != 'false') {
|
if (System.getenv('DOCKER_ENABLE_SECURITY') != 'false') {
|
||||||
implementation 'org.springframework.boot:spring-boot-starter-security:3.2.1'
|
implementation 'org.springframework.boot:spring-boot-starter-security:3.2.2'
|
||||||
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5:3.1.2.RELEASE'
|
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5:3.1.2.RELEASE'
|
||||||
implementation "org.springframework.boot:spring-boot-starter-data-jpa:3.2.1"
|
implementation "org.springframework.boot:spring-boot-starter-data-jpa:3.2.2"
|
||||||
|
|
||||||
//2.2.x requires rebuild of DB file.. need migration path
|
//2.2.x requires rebuild of DB file.. need migration path
|
||||||
implementation "com.h2database:h2:2.1.214"
|
implementation "com.h2database:h2:2.1.214"
|
||||||
}
|
}
|
||||||
|
|
||||||
testImplementation 'org.springframework.boot:spring-boot-starter-test:3.2.1'
|
testImplementation 'org.springframework.boot:spring-boot-starter-test:3.2.2'
|
||||||
|
|
||||||
// Batik
|
// Batik
|
||||||
implementation 'org.apache.xmlgraphics:batik-all:1.17'
|
implementation 'org.apache.xmlgraphics:batik-all:1.17'
|
||||||
@@ -136,26 +138,27 @@ dependencies {
|
|||||||
implementation ('com.opencsv:opencsv:5.9') {
|
implementation ('com.opencsv:opencsv:5.9') {
|
||||||
exclude group: 'commons-logging', module: 'commons-logging'
|
exclude group: 'commons-logging', module: 'commons-logging'
|
||||||
}
|
}
|
||||||
|
|
||||||
implementation ('org.apache.pdfbox:pdfbox:2.0.30'){
|
implementation ('org.apache.pdfbox:pdfbox:3.0.1'){
|
||||||
exclude group: 'commons-logging', module: 'commons-logging'
|
exclude group: 'commons-logging', module: 'commons-logging'
|
||||||
}
|
}
|
||||||
|
|
||||||
implementation ('org.apache.pdfbox:xmpbox:2.0.30'){
|
implementation ('org.apache.pdfbox:xmpbox:3.0.1'){
|
||||||
exclude group: 'commons-logging', module: 'commons-logging'
|
exclude group: 'commons-logging', module: 'commons-logging'
|
||||||
}
|
}
|
||||||
|
|
||||||
implementation 'org.bouncycastle:bcprov-jdk18on:1.77'
|
implementation 'org.bouncycastle:bcprov-jdk18on:1.77'
|
||||||
implementation 'org.bouncycastle:bcpkix-jdk18on:1.77'
|
implementation 'org.bouncycastle:bcpkix-jdk18on:1.77'
|
||||||
implementation 'org.springframework.boot:spring-boot-starter-actuator'
|
implementation 'org.springframework.boot:spring-boot-starter-actuator:3.2.2'
|
||||||
implementation 'io.micrometer:micrometer-core'
|
implementation 'io.micrometer:micrometer-core:1.12.3'
|
||||||
implementation group: 'com.google.zxing', name: 'core', version: '3.5.2'
|
implementation group: 'com.google.zxing', name: 'core', version: '3.5.2'
|
||||||
// https://mvnrepository.com/artifact/org.commonmark/commonmark
|
// https://mvnrepository.com/artifact/org.commonmark/commonmark
|
||||||
implementation 'org.commonmark:commonmark:0.21.0'
|
implementation 'org.commonmark:commonmark:0.21.0'
|
||||||
|
implementation 'org.commonmark:commonmark-ext-gfm-tables:0.21.0'
|
||||||
// https://mvnrepository.com/artifact/com.github.vladimir-bukhtoyarov/bucket4j-core
|
// https://mvnrepository.com/artifact/com.github.vladimir-bukhtoyarov/bucket4j-core
|
||||||
implementation 'com.github.vladimir-bukhtoyarov:bucket4j-core:7.6.0'
|
implementation 'com.github.vladimir-bukhtoyarov:bucket4j-core:7.6.0'
|
||||||
|
|
||||||
developmentOnly("org.springframework.boot:spring-boot-devtools")
|
developmentOnly("org.springframework.boot:spring-boot-devtools:3.2.2")
|
||||||
compileOnly 'org.projectlombok:lombok:1.18.30'
|
compileOnly 'org.projectlombok:lombok:1.18.30'
|
||||||
annotationProcessor 'org.projectlombok:lombok:1.18.28'
|
annotationProcessor 'org.projectlombok:lombok:1.18.28'
|
||||||
}
|
}
|
||||||
@@ -163,6 +166,9 @@ dependencies {
|
|||||||
tasks.withType(JavaCompile) {
|
tasks.withType(JavaCompile) {
|
||||||
dependsOn 'spotlessApply'
|
dependsOn 'spotlessApply'
|
||||||
}
|
}
|
||||||
|
compileJava {
|
||||||
|
options.compilerArgs << '-parameters'
|
||||||
|
}
|
||||||
|
|
||||||
task writeVersion {
|
task writeVersion {
|
||||||
def propsFile = file('src/main/resources/version.properties')
|
def propsFile = file('src/main/resources/version.properties')
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
apiVersion: v2
|
apiVersion: v2
|
||||||
appVersion: 0.14.2
|
appVersion: 0.20.2
|
||||||
description: locally hosted web application that allows you to perform various operations on PDF files
|
description: locally hosted web application that allows you to perform various operations on PDF files
|
||||||
home: https://github.com/Stirling-Tools/Stirling-PDF
|
home: https://github.com/Stirling-Tools/Stirling-PDF
|
||||||
keywords:
|
keywords:
|
||||||
|
|||||||
@@ -43,6 +43,6 @@ spec:
|
|||||||
name: http
|
name: http
|
||||||
{{- end }}
|
{{- end }}
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
|
|
||||||
selector:
|
selector:
|
||||||
{{- include "stirlingpdf.selectorLabels" . | nindent 4 }}
|
{{- include "stirlingpdf.selectorLabels" . | nindent 4 }}
|
||||||
|
|||||||
@@ -16,11 +16,11 @@ commonLabels: {}
|
|||||||
# team_name: dev
|
# team_name: dev
|
||||||
|
|
||||||
envs: []
|
envs: []
|
||||||
# - name: PP_HOME_NAME
|
# - name: UI_APP_NAME
|
||||||
# value: "Stirling PDF"
|
# value: "Stirling PDF"
|
||||||
# - name: APP_HOME_DESCRIPTION
|
# - name: UI_HOME_DESCRIPTION
|
||||||
# value: "Your locally hosted one-stop-shop for all your PDF needs."
|
# value: "Your locally hosted one-stop-shop for all your PDF needs."
|
||||||
# - name: APP_NAVBAR_NAME
|
# - name: UI_APP_NAVBAR_NAME
|
||||||
# value: "Stirling PDF"
|
# value: "Stirling PDF"
|
||||||
# - name: ALLOW_GOOGLE_VISIBILITY
|
# - name: ALLOW_GOOGLE_VISIBILITY
|
||||||
# value: "true"
|
# value: "true"
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 8.7 KiB |
@@ -1,310 +1,110 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
|
||||||
<svg
|
<svg
|
||||||
width="99.537987mm"
|
|
||||||
height="95.209366mm"
|
|
||||||
viewBox="0 0 99.537987 95.209366"
|
|
||||||
version="1.1"
|
version="1.1"
|
||||||
id="svg745"
|
id="Layer_1"
|
||||||
|
x="0px"
|
||||||
|
y="0px"
|
||||||
|
viewBox="0 0 512 512"
|
||||||
|
style="enable-background:new 0 0 512 512;"
|
||||||
xml:space="preserve"
|
xml:space="preserve"
|
||||||
inkscape:version="1.2.1 (9c6d41e4, 2022-07-14)"
|
sodipodi:docname="favicon.svg"
|
||||||
sodipodi:docname="stirling.svg"
|
inkscape:version="1.2.2 (732a01da63, 2022-12-09)"
|
||||||
inkscape:export-filename="stirling.png"
|
inkscape:export-filename="favicon.png"
|
||||||
inkscape:export-xdpi="80"
|
inkscape:export-xdpi="96"
|
||||||
inkscape:export-ydpi="80"
|
inkscape:export-ydpi="96"
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
|
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
||||||
id="namedview747"
|
id="defs173">
|
||||||
pagecolor="#ffffff"
|
|
||||||
bordercolor="#666666"
|
|
||||||
borderopacity="1.0"
|
<linearGradient
|
||||||
inkscape:showpageshadow="2"
|
id="XMLID_5_"
|
||||||
inkscape:pageopacity="0.0"
|
gradientUnits="userSpaceOnUse"
|
||||||
inkscape:pagecheckerboard="0"
|
x1="304.496"
|
||||||
inkscape:deskcolor="#d1d1d1"
|
y1="422.9102"
|
||||||
inkscape:document-units="mm"
|
x2="316.036"
|
||||||
showgrid="false"
|
y2="326.2626">
|
||||||
inkscape:zoom="0.914906"
|
<stop
|
||||||
inkscape:cx="184.17193"
|
offset="0"
|
||||||
inkscape:cy="509.88845"
|
style="stop-color:#DCF1F3"
|
||||||
inkscape:window-width="2293"
|
id="stop156" />
|
||||||
inkscape:window-height="1387"
|
<stop
|
||||||
inkscape:window-x="122"
|
offset="1"
|
||||||
inkscape:window-y="25"
|
style="stop-color:#C2C2C9"
|
||||||
inkscape:window-maximized="0"
|
id="stop158" />
|
||||||
inkscape:current-layer="svg745" /><defs
|
</linearGradient>
|
||||||
id="defs742"><linearGradient
|
|
||||||
inkscape:collect="always"
|
</defs><sodipodi:namedview
|
||||||
id="linearGradient72198"><stop
|
id="namedview171"
|
||||||
style="stop-color:#ccd6d7;stop-opacity:1;"
|
pagecolor="#ffffff"
|
||||||
offset="0"
|
bordercolor="#000000"
|
||||||
id="stop72194" /><stop
|
borderopacity="0.25"
|
||||||
style="stop-color:#0f3a3f;stop-opacity:1;"
|
inkscape:showpageshadow="2"
|
||||||
offset="1"
|
inkscape:pageopacity="0.0"
|
||||||
id="stop72196" /></linearGradient><linearGradient
|
inkscape:pagecheckerboard="0"
|
||||||
inkscape:collect="always"
|
inkscape:deskcolor="#d1d1d1"
|
||||||
id="linearGradient71928"><stop
|
showgrid="false"
|
||||||
style="stop-color:#4b0005;stop-opacity:1;"
|
inkscape:zoom="1.4142136"
|
||||||
offset="0"
|
inkscape:cx="219.91021"
|
||||||
id="stop71924" /><stop
|
inkscape:cy="232.63813"
|
||||||
style="stop-color:#8f000c;stop-opacity:1;"
|
inkscape:window-width="3840"
|
||||||
offset="1"
|
inkscape:window-height="2054"
|
||||||
id="stop71926" /></linearGradient><linearGradient
|
inkscape:window-x="2869"
|
||||||
inkscape:collect="always"
|
inkscape:window-y="-11"
|
||||||
id="linearGradient71920"><stop
|
inkscape:window-maximized="1"
|
||||||
style="stop-color:#4b0005;stop-opacity:1;"
|
inkscape:current-layer="XMLID_4_" />
|
||||||
offset="0"
|
<style
|
||||||
id="stop71916" /><stop
|
type="text/css"
|
||||||
style="stop-color:#8f000c;stop-opacity:1;"
|
id="style150">
|
||||||
offset="1"
|
.st0{fill:#FFFFFF;}
|
||||||
id="stop71918" /></linearGradient><linearGradient
|
.st1{fill:#C02223;}
|
||||||
inkscape:collect="always"
|
.st2{fill:#882425;}
|
||||||
id="linearGradient69598"><stop
|
.st3{fill:url(#XMLID_5_);}
|
||||||
style="stop-color:#6a0007;stop-opacity:1;"
|
.st4{fill:url(#XMLID_7_);}
|
||||||
offset="0"
|
</style>
|
||||||
id="stop69594" /><stop
|
|
||||||
style="stop-color:#b8000f;stop-opacity:1;"
|
<g
|
||||||
offset="1"
|
id="XMLID_4_">
|
||||||
id="stop69596" /></linearGradient><linearGradient
|
<path
|
||||||
inkscape:collect="always"
|
id="XMLID_131_"
|
||||||
id="linearGradient46361"><stop
|
class="st1"
|
||||||
style="stop-color:#f7f6f8;stop-opacity:1;"
|
d="M 347.01402,14.355825 98.978019,69.02261 C 73.825483,74.547445 55.942464,96.792175 55.942464,122.52628 v 315.06096 c 0,22.39012 16.719895,41.14548 38.819234,43.76251 L 224.8861,498.36042 339.48636,384.26465 455.76603,265.15425 453.73057,84.870162 C 453.43979,62.916214 433.08513,46.632491 411.71274,51.284984 l -28.78729,6.251786 0.14539,-13.666697 C 383.36162,24.678542 365.62399,10.284894 347.01402,14.355825 Z"
|
||||||
offset="0"
|
sodipodi:nodetypes="ccssccccccccc"
|
||||||
id="stop46359" /><stop
|
style="stroke-width:1.45391" /><path
|
||||||
style="stop-color:#b7b7b5;stop-opacity:1;"
|
id="XMLID_117_"
|
||||||
offset="1"
|
class="st2"
|
||||||
id="stop46357" /></linearGradient><linearGradient
|
d="m 383.21622,57.53677 v 285.8375 L 456.05681,265.00885 454.02135,78.763767 C 453.87595,59.863016 436.28372,45.905539 417.81914,49.97647 Z"
|
||||||
inkscape:collect="always"
|
style="stroke-width:1.45391" /><polygon
|
||||||
id="linearGradient40554"><stop
|
id="XMLID_18_"
|
||||||
style="stop-color:#f4f2f4;stop-opacity:1;"
|
class="st3"
|
||||||
offset="0"
|
points="234.7,422.6 368.5,387.7 393.5,262.2 "
|
||||||
id="stop40550" /><stop
|
style="fill:url(#XMLID_5_)"
|
||||||
style="stop-color:#57767b;stop-opacity:1;"
|
transform="matrix(1.4556308,0,0,1.4548265,-116.73161,-116.45231)" />
|
||||||
offset="1"
|
<linearGradient
|
||||||
id="stop40552" /></linearGradient><linearGradient
|
id="XMLID_7_"
|
||||||
inkscape:collect="always"
|
gradientUnits="userSpaceOnUse"
|
||||||
id="linearGradient39095"><stop
|
x1="223.0838"
|
||||||
style="stop-color:#285459;stop-opacity:1;"
|
y1="372.7559"
|
||||||
offset="0"
|
x2="241.4174"
|
||||||
id="stop39093" /><stop
|
y2="114.557"
|
||||||
style="stop-color:#a6b6af;stop-opacity:1;"
|
gradientTransform="matrix(1.4539039,0,0,1.4539039,-116.19976,-116.20474)">
|
||||||
offset="1"
|
<stop
|
||||||
id="stop39091" /></linearGradient><linearGradient
|
offset="0"
|
||||||
inkscape:collect="always"
|
style="stop-color:#DCF1F3"
|
||||||
id="linearGradient36672"><stop
|
id="stop163" />
|
||||||
style="stop-color:#da453f;stop-opacity:1;"
|
<stop
|
||||||
offset="0"
|
offset="1"
|
||||||
id="stop36668" /><stop
|
style="stop-color:#C2C2C9"
|
||||||
style="stop-color:#a60008;stop-opacity:1;"
|
id="stop165" />
|
||||||
offset="1"
|
</linearGradient>
|
||||||
id="stop36670" /></linearGradient><linearGradient
|
<path
|
||||||
inkscape:collect="always"
|
id="XMLID_6_"
|
||||||
id="linearGradient19524"><stop
|
class="st4"
|
||||||
style="stop-color:#3a4b4f;stop-opacity:1;"
|
d="m 282.89686,214.84917 c 0,0 -22.24473,-28.93269 -38.67384,-36.78377 -10.46811,-4.94327 -26.02489,-6.83335 -38.23768,-0.72695 -18.02841,9.0142 -19.91848,34.31213 -3.34397,44.34406 3.92553,2.47165 9.15959,4.50711 15.99294,6.10641 36.63838,8.43264 97.12077,25.87949 89.70587,96.10304 0,0 -4.21633,65.86185 -73.56753,73.42215 -12.2128,1.30851 -24.57098,0.43617 -36.493,-2.32625 -16.42911,-3.63476 -45.50719,-11.04967 -59.75545,-19.91849 l -2.61703,-75.16682 h 6.97875 c 0,0 13.81208,33.43978 53.06749,49.57812 7.26952,2.90781 15.26599,4.07093 22.97168,2.90781 9.74116,-1.45391 21.22699,-6.68796 25.87949,-22.53551 0,0 7.85108,-23.11707 -32.85823,-35.76604 -32.56744,-10.17733 -63.24481,-20.64543 -75.89378,-54.95757 -5.961,-16.28371 -6.97874,-34.31212 -2.90781,-51.61358 5.37944,-22.53551 20.79082,-54.23062 64.40794,-67.89732 0,0 57.28381,-15.55677 96.53922,5.52484 l -1.74468,89.70587 z"
|
||||||
offset="0"
|
style="fill:url(#XMLID_7_);stroke-width:1.45391" />
|
||||||
id="stop19522" /><stop
|
</g>
|
||||||
style="stop-color:#617979;stop-opacity:0.97461927;"
|
</svg>
|
||||||
offset="1"
|
|
||||||
id="stop19520" /></linearGradient><linearGradient
|
|
||||||
inkscape:collect="always"
|
|
||||||
id="linearGradient17996"><stop
|
|
||||||
style="stop-color:#401016;stop-opacity:1;"
|
|
||||||
offset="0"
|
|
||||||
id="stop17994" /><stop
|
|
||||||
style="stop-color:#761f19;stop-opacity:1;"
|
|
||||||
offset="1"
|
|
||||||
id="stop17992" /></linearGradient><linearGradient
|
|
||||||
inkscape:collect="always"
|
|
||||||
id="linearGradient15569"><stop
|
|
||||||
style="stop-color:#8ea8ad;stop-opacity:1;"
|
|
||||||
offset="0"
|
|
||||||
id="stop15565" /><stop
|
|
||||||
style="stop-color:#e9e7eb;stop-opacity:1;"
|
|
||||||
offset="1"
|
|
||||||
id="stop15567" /></linearGradient><linearGradient
|
|
||||||
inkscape:collect="always"
|
|
||||||
id="linearGradient15557"><stop
|
|
||||||
style="stop-color:#9b0e11;stop-opacity:1;"
|
|
||||||
offset="0"
|
|
||||||
id="stop15553" /><stop
|
|
||||||
style="stop-color:#370707;stop-opacity:1;"
|
|
||||||
offset="1"
|
|
||||||
id="stop15555" /></linearGradient><linearGradient
|
|
||||||
inkscape:collect="always"
|
|
||||||
xlink:href="#linearGradient15557"
|
|
||||||
id="linearGradient15559"
|
|
||||||
x1="120.06672"
|
|
||||||
y1="63.25761"
|
|
||||||
x2="135.16347"
|
|
||||||
y2="78.078682"
|
|
||||||
gradientUnits="userSpaceOnUse" /><linearGradient
|
|
||||||
inkscape:collect="always"
|
|
||||||
xlink:href="#linearGradient15569"
|
|
||||||
id="linearGradient15571"
|
|
||||||
x1="127.97037"
|
|
||||||
y1="101.66144"
|
|
||||||
x2="133.88971"
|
|
||||||
y2="104.77026"
|
|
||||||
gradientUnits="userSpaceOnUse" /><linearGradient
|
|
||||||
inkscape:collect="always"
|
|
||||||
xlink:href="#linearGradient17996"
|
|
||||||
id="linearGradient17998"
|
|
||||||
x1="117.9284"
|
|
||||||
y1="86.055084"
|
|
||||||
x2="130.67392"
|
|
||||||
y2="76.945541"
|
|
||||||
gradientUnits="userSpaceOnUse" /><linearGradient
|
|
||||||
inkscape:collect="always"
|
|
||||||
xlink:href="#linearGradient19524"
|
|
||||||
id="linearGradient19528"
|
|
||||||
x1="130.98172"
|
|
||||||
y1="82.402977"
|
|
||||||
x2="135.72115"
|
|
||||||
y2="86.45166"
|
|
||||||
gradientUnits="userSpaceOnUse" /><linearGradient
|
|
||||||
inkscape:collect="always"
|
|
||||||
xlink:href="#linearGradient36672"
|
|
||||||
id="linearGradient36674"
|
|
||||||
x1="63.797714"
|
|
||||||
y1="74.81752"
|
|
||||||
x2="96.636673"
|
|
||||||
y2="120.29293"
|
|
||||||
gradientUnits="userSpaceOnUse" /><linearGradient
|
|
||||||
inkscape:collect="always"
|
|
||||||
xlink:href="#linearGradient39095"
|
|
||||||
id="linearGradient39097"
|
|
||||||
x1="120.54506"
|
|
||||||
y1="124.76902"
|
|
||||||
x2="128.04152"
|
|
||||||
y2="126.0704"
|
|
||||||
gradientUnits="userSpaceOnUse" /><linearGradient
|
|
||||||
inkscape:collect="always"
|
|
||||||
xlink:href="#linearGradient40554"
|
|
||||||
id="linearGradient40556"
|
|
||||||
x1="113.84585"
|
|
||||||
y1="114.04285"
|
|
||||||
x2="119.65858"
|
|
||||||
y2="128.50244"
|
|
||||||
gradientUnits="userSpaceOnUse" /><linearGradient
|
|
||||||
inkscape:collect="always"
|
|
||||||
xlink:href="#linearGradient46361"
|
|
||||||
id="linearGradient46363"
|
|
||||||
x1="73.993439"
|
|
||||||
y1="114.13906"
|
|
||||||
x2="94.845322"
|
|
||||||
y2="71.247383"
|
|
||||||
gradientUnits="userSpaceOnUse" /><linearGradient
|
|
||||||
inkscape:collect="always"
|
|
||||||
xlink:href="#linearGradient69598"
|
|
||||||
id="linearGradient69600"
|
|
||||||
x1="95.854446"
|
|
||||||
y1="114.66749"
|
|
||||||
x2="103.77842"
|
|
||||||
y2="120.1887"
|
|
||||||
gradientUnits="userSpaceOnUse" /><linearGradient
|
|
||||||
inkscape:collect="always"
|
|
||||||
xlink:href="#linearGradient71920"
|
|
||||||
id="linearGradient71922"
|
|
||||||
x1="98.580376"
|
|
||||||
y1="87.186958"
|
|
||||||
x2="118.09738"
|
|
||||||
y2="101.19449"
|
|
||||||
gradientUnits="userSpaceOnUse" /><linearGradient
|
|
||||||
inkscape:collect="always"
|
|
||||||
xlink:href="#linearGradient71928"
|
|
||||||
id="linearGradient71930"
|
|
||||||
x1="78.278267"
|
|
||||||
y1="97.433273"
|
|
||||||
x2="92.313202"
|
|
||||||
y2="104.33479"
|
|
||||||
gradientUnits="userSpaceOnUse" /><linearGradient
|
|
||||||
inkscape:collect="always"
|
|
||||||
xlink:href="#linearGradient72198"
|
|
||||||
id="linearGradient72200"
|
|
||||||
x1="125.76636"
|
|
||||||
y1="138.46817"
|
|
||||||
x2="123.3327"
|
|
||||||
y2="126.03291"
|
|
||||||
gradientUnits="userSpaceOnUse" /></defs><g
|
|
||||||
inkscape:groupmode="layer"
|
|
||||||
id="layer4"
|
|
||||||
inkscape:label="background"
|
|
||||||
style="display:inline"
|
|
||||||
sodipodi:insensitive="true"
|
|
||||||
transform="translate(-51.420144,-44.470286)"><rect
|
|
||||||
style="display:inline;fill:#ccd6d7;fill-opacity:1;stroke:none;stroke-width:4.13755;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:3.2"
|
|
||||||
id="rect72067"
|
|
||||||
width="99.481979"
|
|
||||||
height="94.999512"
|
|
||||||
x="51.476147"
|
|
||||||
y="44.680138" /></g><g
|
|
||||||
inkscape:groupmode="layer"
|
|
||||||
id="layer5"
|
|
||||||
inkscape:label="shadow"
|
|
||||||
style="display:inline"
|
|
||||||
sodipodi:insensitive="true"
|
|
||||||
transform="translate(-51.420144,-44.470286)"><path
|
|
||||||
style="display:inline;fill:url(#linearGradient72200);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
|
||||||
d="m 84.146049,134.73858 c 0,0 11.721038,2.48294 17.938661,2.91673 6.21763,0.43378 14.75251,0.59994 22.41237,-0.43379 8.01008,-1.081 13.19907,-2.22733 14.50043,-2.66112 1.30136,-0.43379 4.00784,-1.24297 4.15244,-2.25514 0.1446,-1.01217 -3.4703,-2.74733 -6.21763,-3.32571 -2.74732,-0.57838 -12.72444,-1.44596 -14.89337,-1.44596 -2.16894,0 -37.892901,7.20499 -37.892901,7.20499 z"
|
|
||||||
id="path72192"
|
|
||||||
sodipodi:nodetypes="cssssssc" /></g><g
|
|
||||||
inkscape:groupmode="layer"
|
|
||||||
id="layer2"
|
|
||||||
inkscape:label="Origami"
|
|
||||||
style="display:inline"
|
|
||||||
sodipodi:insensitive="true"
|
|
||||||
transform="translate(-51.420144,-44.470286)"><path
|
|
||||||
style="display:inline;fill:url(#linearGradient40556);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
|
||||||
d="m 84.460552,134.86721 35.165798,-6.85679 16.15467,-42.7383 z"
|
|
||||||
id="path960"
|
|
||||||
sodipodi:nodetypes="cccc" /><path
|
|
||||||
style="fill:url(#linearGradient15571);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
|
||||||
d="m 135.71493,85.428056 0.3984,45.049024 -9.66213,-20.46173 z"
|
|
||||||
id="path964"
|
|
||||||
sodipodi:nodetypes="cccc" /><path
|
|
||||||
style="display:inline;fill:url(#linearGradient39097);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
|
||||||
d="m 119.60518,128.00293 16.5337,2.48693 -9.68769,-20.5512 z"
|
|
||||||
id="path966"
|
|
||||||
sodipodi:nodetypes="cccc" /><path
|
|
||||||
style="display:inline;fill:url(#linearGradient15559);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
|
||||||
d="m 118.42209,57.022622 12.70423,-2.766809 -0.0785,25.087175 -12.43878,-13.376518 z"
|
|
||||||
id="path968"
|
|
||||||
sodipodi:nodetypes="ccccc" /><path
|
|
||||||
style="display:inline;fill:url(#linearGradient19528);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
|
||||||
d="m 135.72114,85.386768 -4.84219,-6.459493 0.0305,11.126604 z"
|
|
||||||
id="path970"
|
|
||||||
sodipodi:nodetypes="cccc" /><path
|
|
||||||
style="display:inline;fill:url(#linearGradient17998);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
|
||||||
d="m 119.10273,65.682415 11.96883,13.44935 -0.0899,10.819868 -11.88577,11.430427 z"
|
|
||||||
id="path972"
|
|
||||||
sodipodi:nodetypes="ccccc" /><path
|
|
||||||
style="display:inline;fill:url(#linearGradient36674);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
|
||||||
d="M 62.145635,130.15618 62.043392,65.435258 c 0,0 0.179321,-2.778132 1.89516,-4.036097 1.874923,-1.374597 4.341768,-1.894096 4.341768,-1.894096 l 50.91788,-11.349167 -0.0113,53.144272 -34.733274,33.56547 z"
|
|
||||||
id="path958"
|
|
||||||
sodipodi:nodetypes="ccsccccc" /></g><g
|
|
||||||
inkscape:groupmode="layer"
|
|
||||||
id="layer3"
|
|
||||||
inkscape:label="Letter"
|
|
||||||
style="display:inline"
|
|
||||||
sodipodi:insensitive="true"
|
|
||||||
transform="translate(-51.420144,-44.470286)"><path
|
|
||||||
style="display:inline;fill:url(#linearGradient69600);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
|
||||||
d="m 94.780881,91.406803 16.870379,17.074877 -23.723345,23.00249 -18.122131,-17.99816 5.497473,-2.80607 18.404054,-0.0511 2.35163,-8.23071 z"
|
|
||||||
id="path54894"
|
|
||||||
sodipodi:nodetypes="cccccccc" /><path
|
|
||||||
style="display:inline;fill:url(#linearGradient71930);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
|
||||||
d="m 72.440405,92.224764 16.15467,15.745686 4.089788,-6.79927 z"
|
|
||||||
id="path54892" /><path
|
|
||||||
style="display:inline;fill:url(#linearGradient71922);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
|
||||||
d="m 95.138739,84.965385 1.124691,-14.109776 22.92453,22.286787 0.008,8.164604 -3.28863,3.16649 z"
|
|
||||||
id="path54890"
|
|
||||||
sodipodi:nodetypes="cccccc"
|
|
||||||
inkscape:label="path54890" /><path
|
|
||||||
style="display:inline;fill:url(#linearGradient46363);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
|
||||||
d="m 95.138739,84.965385 h 1.226936 l -0.05112,-14.109776 c 0,0 -5.776827,-3.220709 -12.167126,-2.40275 -6.390296,0.817957 -8.151582,2.1248 -10.58233,4.396523 -1.90229,1.777838 -2.913974,3.527446 -3.987546,7.157132 -0.512646,1.733226 -0.281963,5.988892 0.613471,8.537436 0.664591,1.891528 2.453873,4.294281 4.958868,6.134686 2.662335,1.956002 8.281825,3.527443 8.281825,3.527443 0,0 5.134614,1.887351 5.572338,4.294281 0.308137,1.69437 -0.102243,3.22071 -1.635914,4.95887 -1.258314,1.42609 -3.62969,1.99377 -6.288054,1.07357 -2.658364,-0.92021 -6.139514,-3.85065 -7.106009,-4.90775 -0.817958,-0.89464 -2.820665,-3.06173 -2.890231,-4.294021 -0.01209,-0.214205 -1.229505,-0.0963 -1.229505,-0.0963 l -0.0723,14.256941 5.879073,2.24938 c 0,0 5.214483,1.78929 8.946415,1.43143 3.731934,-0.35786 7.617235,-0.51122 11.604778,-5.16336 3.987542,-4.65213 3.680812,-12.831715 2.147141,-15.899056 -1.533673,-3.067344 -3.561212,-6.138812 -10.480087,-8.281826 -5.776829,-1.789283 -7.872846,-3.01622 -8.128458,-4.396524 -0.255611,-1.380305 0.0091,-4.253646 2.760607,-5.214481 3.220711,-1.124693 5.623462,-0.05112 7.05489,1.12469 1.431425,1.175817 5.572339,5.623462 5.572339,5.623462 z"
|
|
||||||
id="path46355"
|
|
||||||
sodipodi:nodetypes="cccssssscssssscccssssssscc" /></g></svg>
|
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 4.0 KiB |
@@ -15,13 +15,13 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- 8080:8080
|
- 8080:8080
|
||||||
volumes:
|
volumes:
|
||||||
- /stirling/latest/data:/usr/share/tesseract-ocr/5/tessdata:rw
|
- /stirling/latest/data:/usr/share/tessdata:rw
|
||||||
- /stirling/latest/config:/configs:rw
|
- /stirling/latest/config:/configs:rw
|
||||||
- /stirling/latest/logs:/logs:rw
|
- /stirling/latest/logs:/logs:rw
|
||||||
environment:
|
environment:
|
||||||
DOCKER_ENABLE_SECURITY: "true"
|
DOCKER_ENABLE_SECURITY: "true"
|
||||||
SECURITY_ENABLELOGIN: "true"
|
SECURITY_ENABLELOGIN: "true"
|
||||||
SYSTEM_DEFAULTLOCALE: en_US
|
SYSTEM_DEFAULTLOCALE: en-US
|
||||||
UI_APPNAME: Stirling-PDF-Lite
|
UI_APPNAME: Stirling-PDF-Lite
|
||||||
UI_HOMEDESCRIPTION: Demo site for Stirling-PDF-Lite Latest with Security
|
UI_HOMEDESCRIPTION: Demo site for Stirling-PDF-Lite Latest with Security
|
||||||
UI_APPNAMENAVBAR: Stirling-PDF-Lite Latest
|
UI_APPNAMENAVBAR: Stirling-PDF-Lite Latest
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
DOCKER_ENABLE_SECURITY: "false"
|
DOCKER_ENABLE_SECURITY: "false"
|
||||||
SECURITY_ENABLELOGIN: "false"
|
SECURITY_ENABLELOGIN: "false"
|
||||||
SYSTEM_DEFAULTLOCALE: en_US
|
SYSTEM_DEFAULTLOCALE: en-US
|
||||||
UI_APPNAME: Stirling-PDF-Lite
|
UI_APPNAME: Stirling-PDF-Lite
|
||||||
UI_HOMEDESCRIPTION: Demo site for Stirling-PDF-Lite Latest
|
UI_HOMEDESCRIPTION: Demo site for Stirling-PDF-Lite Latest
|
||||||
UI_APPNAMENAVBAR: Stirling-PDF-Lite Latest
|
UI_APPNAMENAVBAR: Stirling-PDF-Lite Latest
|
||||||
|
|||||||
@@ -15,13 +15,13 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- 8080:8080
|
- 8080:8080
|
||||||
volumes:
|
volumes:
|
||||||
- /stirling/latest/data:/usr/share/tesseract-ocr/5/tessdata:rw
|
- /stirling/latest/data:/usr/share/tessdata:rw
|
||||||
- /stirling/latest/config:/configs:rw
|
- /stirling/latest/config:/configs:rw
|
||||||
- /stirling/latest/logs:/logs:rw
|
- /stirling/latest/logs:/logs:rw
|
||||||
environment:
|
environment:
|
||||||
DOCKER_ENABLE_SECURITY: "true"
|
DOCKER_ENABLE_SECURITY: "true"
|
||||||
SECURITY_ENABLELOGIN: "true"
|
SECURITY_ENABLELOGIN: "true"
|
||||||
SYSTEM_DEFAULTLOCALE: en_US
|
SYSTEM_DEFAULTLOCALE: en-US
|
||||||
UI_APPNAME: Stirling-PDF
|
UI_APPNAME: Stirling-PDF
|
||||||
UI_HOMEDESCRIPTION: Demo site for Stirling-PDF Latest with Security
|
UI_HOMEDESCRIPTION: Demo site for Stirling-PDF Latest with Security
|
||||||
UI_APPNAMENAVBAR: Stirling-PDF Latest
|
UI_APPNAMENAVBAR: Stirling-PDF Latest
|
||||||
|
|||||||
@@ -15,13 +15,13 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- 8080:8080
|
- 8080:8080
|
||||||
volumes:
|
volumes:
|
||||||
- /stirling/latest/data:/usr/share/tesseract-ocr/5/tessdata:rw
|
- /stirling/latest/data:/usr/share/tessdata:rw
|
||||||
- /stirling/latest/config:/configs:rw
|
- /stirling/latest/config:/configs:rw
|
||||||
- /stirling/latest/logs:/logs:rw
|
- /stirling/latest/logs:/logs:rw
|
||||||
environment:
|
environment:
|
||||||
DOCKER_ENABLE_SECURITY: "true"
|
DOCKER_ENABLE_SECURITY: "true"
|
||||||
SECURITY_ENABLELOGIN: "true"
|
SECURITY_ENABLELOGIN: "true"
|
||||||
SYSTEM_DEFAULTLOCALE: en_US
|
SYSTEM_DEFAULTLOCALE: en-US
|
||||||
UI_APPNAME: Stirling-PDF-Lite
|
UI_APPNAME: Stirling-PDF-Lite
|
||||||
UI_HOMEDESCRIPTION: Demo site for Stirling-PDF-Lite Latest with Security
|
UI_HOMEDESCRIPTION: Demo site for Stirling-PDF-Lite Latest with Security
|
||||||
UI_APPNAMENAVBAR: Stirling-PDF-Lite Latest
|
UI_APPNAMENAVBAR: Stirling-PDF-Lite Latest
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
DOCKER_ENABLE_SECURITY: "false"
|
DOCKER_ENABLE_SECURITY: "false"
|
||||||
SECURITY_ENABLELOGIN: "false"
|
SECURITY_ENABLELOGIN: "false"
|
||||||
SYSTEM_DEFAULTLOCALE: en_US
|
SYSTEM_DEFAULTLOCALE: en-US
|
||||||
UI_APPNAME: Stirling-PDF-Ultra-lite
|
UI_APPNAME: Stirling-PDF-Ultra-lite
|
||||||
UI_HOMEDESCRIPTION: Demo site for Stirling-PDF-Ultra-lite Latest
|
UI_HOMEDESCRIPTION: Demo site for Stirling-PDF-Ultra-lite Latest
|
||||||
UI_APPNAMENAVBAR: Stirling-PDF-Ultra-lite Latest
|
UI_APPNAMENAVBAR: Stirling-PDF-Ultra-lite Latest
|
||||||
|
|||||||
@@ -15,13 +15,13 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- 8080:8080
|
- 8080:8080
|
||||||
volumes:
|
volumes:
|
||||||
- /stirling/latest/data:/usr/share/tesseract-ocr/5/tessdata:rw
|
- /stirling/latest/data:/usr/share/tessdata:rw
|
||||||
- /stirling/latest/config:/configs:rw
|
- /stirling/latest/config:/configs:rw
|
||||||
- /stirling/latest/logs:/logs:rw
|
- /stirling/latest/logs:/logs:rw
|
||||||
environment:
|
environment:
|
||||||
DOCKER_ENABLE_SECURITY: "false"
|
DOCKER_ENABLE_SECURITY: "false"
|
||||||
SECURITY_ENABLELOGIN: "false"
|
SECURITY_ENABLELOGIN: "false"
|
||||||
SYSTEM_DEFAULTLOCALE: en_US
|
SYSTEM_DEFAULTLOCALE: en-US
|
||||||
UI_APPNAME: Stirling-PDF
|
UI_APPNAME: Stirling-PDF
|
||||||
UI_HOMEDESCRIPTION: Demo site for Stirling-PDF Latest
|
UI_HOMEDESCRIPTION: Demo site for Stirling-PDF Latest
|
||||||
UI_APPNAMENAVBAR: Stirling-PDF Latest
|
UI_APPNAMENAVBAR: Stirling-PDF Latest
|
||||||
|
|||||||
182
gradlew.bat
vendored
182
gradlew.bat
vendored
@@ -1,91 +1,91 @@
|
|||||||
@rem
|
@rem
|
||||||
@rem Copyright 2015 the original author or authors.
|
@rem Copyright 2015 the original author or authors.
|
||||||
@rem
|
@rem
|
||||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@rem you may not use this file except in compliance with the License.
|
@rem you may not use this file except in compliance with the License.
|
||||||
@rem You may obtain a copy of the License at
|
@rem You may obtain a copy of the License at
|
||||||
@rem
|
@rem
|
||||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||||
@rem
|
@rem
|
||||||
@rem Unless required by applicable law or agreed to in writing, software
|
@rem Unless required by applicable law or agreed to in writing, software
|
||||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
@rem See the License for the specific language governing permissions and
|
@rem See the License for the specific language governing permissions and
|
||||||
@rem limitations under the License.
|
@rem limitations under the License.
|
||||||
@rem
|
@rem
|
||||||
|
|
||||||
@if "%DEBUG%"=="" @echo off
|
@if "%DEBUG%"=="" @echo off
|
||||||
@rem ##########################################################################
|
@rem ##########################################################################
|
||||||
@rem
|
@rem
|
||||||
@rem Gradle startup script for Windows
|
@rem Gradle startup script for Windows
|
||||||
@rem
|
@rem
|
||||||
@rem ##########################################################################
|
@rem ##########################################################################
|
||||||
|
|
||||||
@rem Set local scope for the variables with windows NT shell
|
@rem Set local scope for the variables with windows NT shell
|
||||||
if "%OS%"=="Windows_NT" setlocal
|
if "%OS%"=="Windows_NT" setlocal
|
||||||
|
|
||||||
set DIRNAME=%~dp0
|
set DIRNAME=%~dp0
|
||||||
if "%DIRNAME%"=="" set DIRNAME=.
|
if "%DIRNAME%"=="" set DIRNAME=.
|
||||||
set APP_BASE_NAME=%~n0
|
set APP_BASE_NAME=%~n0
|
||||||
set APP_HOME=%DIRNAME%
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||||
|
|
||||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||||
|
|
||||||
@rem Find java.exe
|
@rem Find java.exe
|
||||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||||
|
|
||||||
set JAVA_EXE=java.exe
|
set JAVA_EXE=java.exe
|
||||||
%JAVA_EXE% -version >NUL 2>&1
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
if %ERRORLEVEL% equ 0 goto execute
|
if %ERRORLEVEL% equ 0 goto execute
|
||||||
|
|
||||||
echo.
|
echo.
|
||||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
echo.
|
echo.
|
||||||
echo Please set the JAVA_HOME variable in your environment to match the
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
echo location of your Java installation.
|
echo location of your Java installation.
|
||||||
|
|
||||||
goto fail
|
goto fail
|
||||||
|
|
||||||
:findJavaFromJavaHome
|
:findJavaFromJavaHome
|
||||||
set JAVA_HOME=%JAVA_HOME:"=%
|
set JAVA_HOME=%JAVA_HOME:"=%
|
||||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||||
|
|
||||||
if exist "%JAVA_EXE%" goto execute
|
if exist "%JAVA_EXE%" goto execute
|
||||||
|
|
||||||
echo.
|
echo.
|
||||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||||
echo.
|
echo.
|
||||||
echo Please set the JAVA_HOME variable in your environment to match the
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
echo location of your Java installation.
|
echo location of your Java installation.
|
||||||
|
|
||||||
goto fail
|
goto fail
|
||||||
|
|
||||||
:execute
|
:execute
|
||||||
@rem Setup the command line
|
@rem Setup the command line
|
||||||
|
|
||||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||||
|
|
||||||
|
|
||||||
@rem Execute Gradle
|
@rem Execute Gradle
|
||||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||||
|
|
||||||
:end
|
:end
|
||||||
@rem End local scope for the variables with windows NT shell
|
@rem End local scope for the variables with windows NT shell
|
||||||
if %ERRORLEVEL% equ 0 goto mainEnd
|
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||||
|
|
||||||
:fail
|
:fail
|
||||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||||
rem the _cmd.exe /c_ return code!
|
rem the _cmd.exe /c_ return code!
|
||||||
set EXIT_CODE=%ERRORLEVEL%
|
set EXIT_CODE=%ERRORLEVEL%
|
||||||
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||||
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||||
exit /b %EXIT_CODE%
|
exit /b %EXIT_CODE%
|
||||||
|
|
||||||
:mainEnd
|
:mainEnd
|
||||||
if "%OS%"=="Windows_NT" endlocal
|
if "%OS%"=="Windows_NT" endlocal
|
||||||
|
|
||||||
:omega
|
:omega
|
||||||
|
|||||||
@@ -6,7 +6,8 @@
|
|||||||
"parameters": {
|
"parameters": {
|
||||||
"horizontalDivisions": 2,
|
"horizontalDivisions": 2,
|
||||||
"verticalDivisions": 2,
|
"verticalDivisions": 2,
|
||||||
"fileInput": "automated"
|
"fileInput": "automated",
|
||||||
|
"merge": false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -30,4 +31,4 @@
|
|||||||
},
|
},
|
||||||
"outputDir": "{outputFolder}",
|
"outputDir": "{outputFolder}",
|
||||||
"outputFileName": "{filename}"
|
"outputFileName": "{filename}"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ public class PropSync {
|
|||||||
Map<String, String> enProps = linesToProps(enLines);
|
Map<String, String> enProps = linesToProps(enLines);
|
||||||
|
|
||||||
for (File file : files) {
|
for (File file : files) {
|
||||||
if (!file.getName().equals("messages_en_GB.properties")) {
|
if (!"messages_en_GB.properties".equals(file.getName())) {
|
||||||
System.out.println("Processing file: " + file.getName());
|
System.out.println("Processing file: " + file.getName());
|
||||||
List<String> lines;
|
List<String> lines;
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -1,37 +0,0 @@
|
|||||||
import cv2
|
|
||||||
import sys
|
|
||||||
import argparse
|
|
||||||
import numpy as np
|
|
||||||
|
|
||||||
def is_blank_image(image_path, threshold=10, white_percent=99, white_value=255, blur_size=5):
|
|
||||||
image = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
|
|
||||||
|
|
||||||
if image is None:
|
|
||||||
print(f"Error: Unable to read the image file: {image_path}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
# Apply Gaussian blur to reduce noise
|
|
||||||
blurred_image = cv2.GaussianBlur(image, (blur_size, blur_size), 0)
|
|
||||||
|
|
||||||
_, thresholded_image = cv2.threshold(blurred_image, white_value - threshold, white_value, cv2.THRESH_BINARY)
|
|
||||||
|
|
||||||
# Calculate the percentage of white pixels in the thresholded image
|
|
||||||
white_pixels = np.sum(thresholded_image == white_value)
|
|
||||||
white_pixel_percentage = (white_pixels / thresholded_image.size) * 100
|
|
||||||
|
|
||||||
print(f"Page has white pixel percent of {white_pixel_percentage}")
|
|
||||||
return white_pixel_percentage >= white_percent
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
parser = argparse.ArgumentParser(description='Detect if an image is considered blank or not.')
|
|
||||||
parser.add_argument('image_path', help='The path to the image file.')
|
|
||||||
parser.add_argument('-t', '--threshold', type=int, default=10, help='Threshold for determining white pixels. The default value is 10.')
|
|
||||||
parser.add_argument('-w', '--white_percent', type=float, default=99, help='The percentage of white pixels for an image to be considered blank. The default value is 99.')
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
blank = is_blank_image(args.image_path, args.threshold, args.white_percent)
|
|
||||||
|
|
||||||
# Return code 1: The image is considered blank.
|
|
||||||
# Return code 0: The image is not considered blank.
|
|
||||||
sys.exit(int(blank))
|
|
||||||
@@ -4,7 +4,7 @@ if [ "$DOCKER_ENABLE_SECURITY" = "true" ] && [ "$VERSION_TAG" != "alpha" ]; then
|
|||||||
if [ ! -f app-security.jar ]; then
|
if [ ! -f app-security.jar ]; then
|
||||||
echo "Trying to download from: https://github.com/Stirling-Tools/Stirling-PDF/releases/download/v$VERSION_TAG/Stirling-PDF-with-login.jar"
|
echo "Trying to download from: https://github.com/Stirling-Tools/Stirling-PDF/releases/download/v$VERSION_TAG/Stirling-PDF-with-login.jar"
|
||||||
curl -L -o app-security.jar https://github.com/Stirling-Tools/Stirling-PDF/releases/download/v$VERSION_TAG/Stirling-PDF-with-login.jar
|
curl -L -o app-security.jar https://github.com/Stirling-Tools/Stirling-PDF/releases/download/v$VERSION_TAG/Stirling-PDF-with-login.jar
|
||||||
|
|
||||||
# If the first download attempt failed, try with the 'v' prefix
|
# If the first download attempt failed, try with the 'v' prefix
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
echo "Trying to download from: https://github.com/Stirling-Tools/Stirling-PDF/releases/download/$VERSION_TAG/Stirling-PDF-with-login.jar"
|
echo "Trying to download from: https://github.com/Stirling-Tools/Stirling-PDF/releases/download/$VERSION_TAG/Stirling-PDF-with-login.jar"
|
||||||
@@ -14,6 +14,8 @@ if [ "$DOCKER_ENABLE_SECURITY" = "true" ] && [ "$VERSION_TAG" != "alpha" ]; then
|
|||||||
if [ $? -eq 0 ]; then # checks if curl was successful
|
if [ $? -eq 0 ]; then # checks if curl was successful
|
||||||
rm -f app.jar
|
rm -f app.jar
|
||||||
ln -s app-security.jar app.jar
|
ln -s app-security.jar app.jar
|
||||||
|
chown stirlingpdfuser:stirlingpdfgroup app.jar
|
||||||
|
chmod 755 app.jar
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -1,6 +1,24 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Update the user and group IDs as per environment variables
|
||||||
|
if [ ! -z "$PUID" ] && [ "$PUID" != "$(id -u stirlingpdfuser)" ]; then
|
||||||
|
usermod -o -u "$PUID" stirlingpdfuser
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -z "$PGID" ] && [ "$PGID" != "$(id -g stirlingpdfgroup)" ]; then
|
||||||
|
groupmod -o -g "$PGID" stirlingpdfgroup
|
||||||
|
fi
|
||||||
|
umask "$UMASK"
|
||||||
|
|
||||||
|
echo "Setting permissions and ownership for necessary directories..."
|
||||||
|
chown -R stirlingpdfuser:stirlingpdfgroup /logs /scripts /usr/share/fonts/opentype/noto /usr/share/tessdata /configs /customFiles
|
||||||
|
chmod -R 755 /logs /scripts /usr/share/fonts/opentype/noto /usr/share/tessdata /configs /customFiles
|
||||||
|
if [[ "$INSTALL_BOOK_AND_ADVANCED_HTML_OPS" == "true" ]]; then
|
||||||
|
apk add --no-cache calibre@testing
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
/scripts/download-security-jar.sh
|
/scripts/download-security-jar.sh
|
||||||
|
|
||||||
# Run the main command
|
# Run the main command
|
||||||
exec "$@"
|
exec su-exec stirlingpdfuser "$@"
|
||||||
@@ -2,25 +2,57 @@
|
|||||||
|
|
||||||
# Copy the original tesseract-ocr files to the volume directory without overwriting existing files
|
# Copy the original tesseract-ocr files to the volume directory without overwriting existing files
|
||||||
echo "Copying original files without overwriting existing files"
|
echo "Copying original files without overwriting existing files"
|
||||||
mkdir -p /usr/share/tesseract-ocr
|
mkdir -p /usr/share/tessdata
|
||||||
cp -rn /usr/share/tesseract-ocr-original/* /usr/share/tesseract-ocr
|
cp -rn /usr/share/tessdata-original/* /usr/share/tessdata
|
||||||
|
|
||||||
if [ -d /usr/share/tesseract-ocr/4.00/tessdata ]; then
|
if [ -d /usr/share/tesseract-ocr/4.00/tessdata ]; then
|
||||||
cp -r /usr/share/tesseract-ocr/4.00/tessdata/* /usr/share/tesseract-ocr/5/tessdata/ || true;
|
cp -r /usr/share/tesseract-ocr/4.00/tessdata/* /usr/share/tessdata || true;
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [ -d /usr/share/tesseract-ocr/5/tessdata ]; then
|
||||||
|
cp -r /usr/share/tesseract-ocr/5/tessdata/* /usr/share/tessdata || true;
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
# Update the user and group IDs as per environment variables
|
||||||
|
if [ ! -z "$PUID" ] && [ "$PUID" != "$(id -u stirlingpdfuser)" ]; then
|
||||||
|
usermod -o -u "$PUID" stirlingpdfuser
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -z "$PGID" ] && [ "$PGID" != "$(id -g stirlingpdfgroup)" ]; then
|
||||||
|
groupmod -o -g "$PGID" stirlingpdfgroup
|
||||||
|
fi
|
||||||
|
umask "$UMASK"
|
||||||
|
|
||||||
|
echo "Setting permissions and ownership for necessary directories..."
|
||||||
|
chown -R stirlingpdfuser:stirlingpdfgroup /logs /scripts /usr/share/fonts/opentype/noto /usr/share/tessdata /configs /customFiles
|
||||||
|
chmod -R 755 /logs /scripts /usr/share/fonts/opentype/noto /usr/share/tessdata /configs /customFiles
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Check if TESSERACT_LANGS environment variable is set and is not empty
|
# Check if TESSERACT_LANGS environment variable is set and is not empty
|
||||||
if [[ -n "$TESSERACT_LANGS" ]]; then
|
if [[ -n "$TESSERACT_LANGS" ]]; then
|
||||||
# Convert comma-separated values to a space-separated list
|
# Convert comma-separated values to a space-separated list
|
||||||
LANGS=$(echo $TESSERACT_LANGS | tr ',' ' ')
|
LANGS=$(echo $TESSERACT_LANGS | tr ',' ' ')
|
||||||
|
pattern='^[a-zA-Z]{2,4}(_[a-zA-Z]{2,4})?$'
|
||||||
# Install each language pack
|
# Install each language pack
|
||||||
for LANG in $LANGS; do
|
for LANG in $LANGS; do
|
||||||
apt-get install -y "tesseract-ocr-$LANG"
|
if [[ $LANG =~ $pattern ]]; then
|
||||||
|
apk add --no-cache "tesseract-ocr-data-$LANG"
|
||||||
|
else
|
||||||
|
echo "Skipping invalid language code"
|
||||||
|
fi
|
||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [[ "$INSTALL_BOOK_AND_ADVANCED_HTML_OPS" == "true" ]]; then
|
||||||
|
apk add --no-cache calibre@testing
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/scripts/download-security-jar.sh
|
/scripts/download-security-jar.sh
|
||||||
|
|
||||||
# Run the main command
|
# Run the main command and switch to stirling user for rest of run
|
||||||
exec "$@"
|
exec su-exec stirlingpdfuser "$@"
|
||||||
@@ -2,7 +2,7 @@ import argparse
|
|||||||
import sys
|
import sys
|
||||||
import cv2
|
import cv2
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import os
|
import os
|
||||||
|
|
||||||
def find_photo_boundaries(image, background_color, tolerance=30, min_area=10000, min_contour_area=500):
|
def find_photo_boundaries(image, background_color, tolerance=30, min_area=10000, min_contour_area=500):
|
||||||
mask = cv2.inRange(image, background_color - tolerance, background_color + tolerance)
|
mask = cv2.inRange(image, background_color - tolerance, background_color + tolerance)
|
||||||
@@ -49,9 +49,9 @@ def auto_rotate(image, angle_threshold=1):
|
|||||||
angles = []
|
angles = []
|
||||||
for rho, theta in lines[:, 0]:
|
for rho, theta in lines[:, 0]:
|
||||||
angles.append((theta * 180) / np.pi - 90)
|
angles.append((theta * 180) / np.pi - 90)
|
||||||
|
|
||||||
angle = np.median(angles)
|
angle = np.median(angles)
|
||||||
|
|
||||||
if abs(angle) < angle_threshold:
|
if abs(angle) < angle_threshold:
|
||||||
return image
|
return image
|
||||||
|
|
||||||
@@ -65,16 +65,16 @@ def auto_rotate(image, angle_threshold=1):
|
|||||||
|
|
||||||
def crop_borders(image, border_color, tolerance=30):
|
def crop_borders(image, border_color, tolerance=30):
|
||||||
mask = cv2.inRange(image, border_color - tolerance, border_color + tolerance)
|
mask = cv2.inRange(image, border_color - tolerance, border_color + tolerance)
|
||||||
|
|
||||||
contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
|
contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
|
||||||
if len(contours) == 0:
|
if len(contours) == 0:
|
||||||
return image
|
return image
|
||||||
|
|
||||||
largest_contour = max(contours, key=cv2.contourArea)
|
largest_contour = max(contours, key=cv2.contourArea)
|
||||||
x, y, w, h = cv2.boundingRect(largest_contour)
|
x, y, w, h = cv2.boundingRect(largest_contour)
|
||||||
|
|
||||||
return image[y:y+h, x:x+w]
|
return image[y:y+h, x:x+w]
|
||||||
|
|
||||||
def split_photos(input_file, output_directory, tolerance=30, min_area=10000, min_contour_area=500, angle_threshold=10, border_size=0):
|
def split_photos(input_file, output_directory, tolerance=30, min_area=10000, min_contour_area=500, angle_threshold=10, border_size=0):
|
||||||
image = cv2.imread(input_file)
|
image = cv2.imread(input_file)
|
||||||
background_color = estimate_background_color(image)
|
background_color = estimate_background_color(image)
|
||||||
@@ -110,7 +110,7 @@ if __name__ == "__main__":
|
|||||||
parser.add_argument("--min_contour_area", type=int, default=500, help="Sets the minimum contour area threshold for a photo (default: 500).")
|
parser.add_argument("--min_contour_area", type=int, default=500, help="Sets the minimum contour area threshold for a photo (default: 500).")
|
||||||
parser.add_argument("--angle_threshold", type=int, default=10, help="Sets the minimum absolute angle required for the image to be rotated (default: 10).")
|
parser.add_argument("--angle_threshold", type=int, default=10, help="Sets the minimum absolute angle required for the image to be rotated (default: 10).")
|
||||||
parser.add_argument("--border_size", type=int, default=0, help="Sets the size of the border added and removed to prevent white borders in the output (default: 0).")
|
parser.add_argument("--border_size", type=int, default=0, help="Sets the size of the border added and removed to prevent white borders in the output (default: 0).")
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
split_photos(args.input_file, args.output_directory, tolerance=args.tolerance, min_area=args.min_area, min_contour_area=args.min_contour_area, angle_threshold=args.angle_threshold, border_size=args.border_size)
|
split_photos(args.input_file, args.output_directory, tolerance=args.tolerance, min_area=args.min_area, min_contour_area=args.min_contour_area, angle_threshold=args.angle_threshold, border_size=args.border_size)
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import java.net.Socket;
|
|||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
|
import io.github.pixee.security.SystemCommand;
|
||||||
|
|
||||||
public class LibreOfficeListener {
|
public class LibreOfficeListener {
|
||||||
|
|
||||||
private static final long ACTIVITY_TIMEOUT = 20 * 60 * 1000; // 20 minutes
|
private static final long ACTIVITY_TIMEOUT = 20 * 60 * 1000; // 20 minutes
|
||||||
@@ -44,7 +46,7 @@ public class LibreOfficeListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Start the listener process
|
// Start the listener process
|
||||||
process = Runtime.getRuntime().exec("unoconv --listener");
|
process = SystemCommand.runCommand(Runtime.getRuntime(), "unoconv --listener");
|
||||||
lastActivityTime = System.currentTimeMillis();
|
lastActivityTime = System.currentTimeMillis();
|
||||||
|
|
||||||
// Start a background thread to monitor the activity timeout
|
// Start a background thread to monitor the activity timeout
|
||||||
|
|||||||
@@ -1,48 +1,67 @@
|
|||||||
package stirling.software.SPDF;
|
package stirling.software.SPDF;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
import org.springframework.core.env.Environment;
|
import org.springframework.core.env.Environment;
|
||||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||||
|
|
||||||
|
import io.github.pixee.security.SystemCommand;
|
||||||
|
|
||||||
import jakarta.annotation.PostConstruct;
|
import jakarta.annotation.PostConstruct;
|
||||||
import stirling.software.SPDF.config.ConfigInitializer;
|
import stirling.software.SPDF.config.ConfigInitializer;
|
||||||
import stirling.software.SPDF.utils.GeneralUtils;
|
import stirling.software.SPDF.model.ApplicationProperties;
|
||||||
|
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
@EnableScheduling
|
@EnableScheduling
|
||||||
public class SPdfApplication {
|
public class SPdfApplication {
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(SPdfApplication.class);
|
||||||
|
|
||||||
@Autowired private Environment env;
|
@Autowired private Environment env;
|
||||||
|
|
||||||
|
@Autowired ApplicationProperties applicationProperties;
|
||||||
|
|
||||||
|
private static String serverPortStatic;
|
||||||
|
|
||||||
|
@Value("${server.port:8080}")
|
||||||
|
public void setServerPortStatic(String port) {
|
||||||
|
SPdfApplication.serverPortStatic = port;
|
||||||
|
}
|
||||||
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
public void init() {
|
public void init() {
|
||||||
// Check if the BROWSER_OPEN environment variable is set to true
|
// Check if the BROWSER_OPEN environment variable is set to true
|
||||||
String browserOpenEnv = env.getProperty("BROWSER_OPEN");
|
String browserOpenEnv = env.getProperty("BROWSER_OPEN");
|
||||||
boolean browserOpen = browserOpenEnv != null && browserOpenEnv.equalsIgnoreCase("true");
|
boolean browserOpen = browserOpenEnv != null && "true".equalsIgnoreCase(browserOpenEnv);
|
||||||
|
|
||||||
if (browserOpen) {
|
if (browserOpen) {
|
||||||
try {
|
try {
|
||||||
String url = "http://localhost:" + getPort();
|
String url = "http://localhost:" + getNonStaticPort();
|
||||||
|
|
||||||
String os = System.getProperty("os.name").toLowerCase();
|
String os = System.getProperty("os.name").toLowerCase();
|
||||||
Runtime rt = Runtime.getRuntime();
|
Runtime rt = Runtime.getRuntime();
|
||||||
if (os.contains("win")) {
|
if (os.contains("win")) {
|
||||||
// For Windows
|
// For Windows
|
||||||
rt.exec("rundll32 url.dll,FileProtocolHandler " + url);
|
SystemCommand.runCommand(rt, "rundll32 url.dll,FileProtocolHandler " + url);
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
logger.error("Error opening browser: {}", e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
logger.info("Running configs {}", applicationProperties.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) throws IOException, InterruptedException {
|
||||||
SpringApplication app = new SpringApplication(SPdfApplication.class);
|
SpringApplication app = new SpringApplication(SPdfApplication.class);
|
||||||
app.addInitializers(new ConfigInitializer());
|
app.addInitializers(new ConfigInitializer());
|
||||||
if (Files.exists(Paths.get("configs/settings.yml"))) {
|
if (Files.exists(Paths.get("configs/settings.yml"))) {
|
||||||
@@ -50,7 +69,7 @@ public class SPdfApplication {
|
|||||||
Collections.singletonMap(
|
Collections.singletonMap(
|
||||||
"spring.config.additional-location", "file:configs/settings.yml"));
|
"spring.config.additional-location", "file:configs/settings.yml"));
|
||||||
} else {
|
} else {
|
||||||
System.out.println(
|
logger.warn(
|
||||||
"External configuration file 'configs/settings.yml' does not exist. Using default configuration and environment configuration instead.");
|
"External configuration file 'configs/settings.yml' does not exist. Using default configuration and environment configuration instead.");
|
||||||
}
|
}
|
||||||
app.run(args);
|
app.run(args);
|
||||||
@@ -58,24 +77,30 @@ public class SPdfApplication {
|
|||||||
try {
|
try {
|
||||||
Thread.sleep(1000);
|
Thread.sleep(1000);
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
// TODO Auto-generated catch block
|
Thread.currentThread().interrupt();
|
||||||
e.printStackTrace();
|
throw new RuntimeException("Thread interrupted while sleeping", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
GeneralUtils.createDir("customFiles/static/");
|
try {
|
||||||
GeneralUtils.createDir("customFiles/templates/");
|
Files.createDirectories(Path.of("customFiles/static/"));
|
||||||
|
Files.createDirectories(Path.of("customFiles/templates/"));
|
||||||
System.out.println("Stirling-PDF Started.");
|
} catch (Exception e) {
|
||||||
|
logger.error("Error creating directories: {}", e.getMessage());
|
||||||
String url = "http://localhost:" + getPort();
|
}
|
||||||
System.out.println("Navigate to " + url);
|
printStartupLogs();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getPort() {
|
private static void printStartupLogs() {
|
||||||
String port = System.getProperty("local.server.port");
|
logger.info("Stirling-PDF Started.");
|
||||||
if (port == null || port.isEmpty()) {
|
String url = "http://localhost:" + getStaticPort();
|
||||||
port = "8080";
|
logger.info("Navigate to {}", url);
|
||||||
}
|
}
|
||||||
return port;
|
|
||||||
|
public static String getStaticPort() {
|
||||||
|
return serverPortStatic;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getNonStaticPort() {
|
||||||
|
return serverPortStatic;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,15 @@
|
|||||||
package stirling.software.SPDF.config;
|
package stirling.software.SPDF.config;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.core.io.ClassPathResource;
|
||||||
|
import org.springframework.core.io.Resource;
|
||||||
|
|
||||||
import stirling.software.SPDF.model.ApplicationProperties;
|
import stirling.software.SPDF.model.ApplicationProperties;
|
||||||
|
|
||||||
@@ -24,8 +31,15 @@ public class AppConfig {
|
|||||||
|
|
||||||
@Bean(name = "appVersion")
|
@Bean(name = "appVersion")
|
||||||
public String appVersion() {
|
public String appVersion() {
|
||||||
String version = getClass().getPackage().getImplementationVersion();
|
Resource resource = new ClassPathResource("version.properties");
|
||||||
return (version != null) ? version : "0.0.0";
|
Properties props = new Properties();
|
||||||
|
try {
|
||||||
|
props.load(resource.getInputStream());
|
||||||
|
return props.getProperty("version");
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return "0.0.0";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean(name = "homeText")
|
@Bean(name = "homeText")
|
||||||
@@ -57,4 +71,18 @@ public class AppConfig {
|
|||||||
if (appName == null) appName = System.getenv("rateLimit");
|
if (appName == null) appName = System.getenv("rateLimit");
|
||||||
return (appName != null) ? Boolean.valueOf(appName) : false;
|
return (appName != null) ? Boolean.valueOf(appName) : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean(name = "RunningInDocker")
|
||||||
|
public boolean runningInDocker() {
|
||||||
|
return Files.exists(Paths.get("/.dockerenv"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,12 +79,22 @@ public class ConfigInitializer
|
|||||||
return parts.length > 0 ? parts[0].trim().replace("#", "").trim() : "";
|
return parts.length > 0 ? parts[0].trim().replace("#", "").trim() : "";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Function<String, Integer> getIndentationLevel =
|
||||||
|
line -> {
|
||||||
|
int count = 0;
|
||||||
|
for (char ch : line.toCharArray()) {
|
||||||
|
if (ch == ' ') count++;
|
||||||
|
else break;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
};
|
||||||
|
|
||||||
Set<String> userKeys = userLines.stream().map(extractKey).collect(Collectors.toSet());
|
Set<String> userKeys = userLines.stream().map(extractKey).collect(Collectors.toSet());
|
||||||
|
|
||||||
for (String line : templateLines) {
|
for (String line : templateLines) {
|
||||||
String key = extractKey.apply(line);
|
String key = extractKey.apply(line);
|
||||||
|
|
||||||
if (line.trim().equalsIgnoreCase("AutomaticallyGenerated:")) {
|
if ("AutomaticallyGenerated:".equalsIgnoreCase(line.trim())) {
|
||||||
insideAutoGenerated = true;
|
insideAutoGenerated = true;
|
||||||
mergedLines.add(line);
|
mergedLines.add(line);
|
||||||
continue;
|
continue;
|
||||||
@@ -134,10 +144,77 @@ public class ConfigInitializer
|
|||||||
.map(extractKey)
|
.map(extractKey)
|
||||||
.anyMatch(templateKey -> templateKey.equalsIgnoreCase(userKey));
|
.anyMatch(templateKey -> templateKey.equalsIgnoreCase(userKey));
|
||||||
if (!isPresentInTemplate && !isCommented.apply(userLine)) {
|
if (!isPresentInTemplate && !isCommented.apply(userLine)) {
|
||||||
mergedLines.add(userLine);
|
if (!childOfTemplateEntry(
|
||||||
|
isCommented,
|
||||||
|
extractKey,
|
||||||
|
getIndentationLevel,
|
||||||
|
userLines,
|
||||||
|
userLine,
|
||||||
|
templateLines)) {
|
||||||
|
// check if userLine is a child of a entry within templateLines or not, if child
|
||||||
|
// of parent in templateLines then dont add to mergedLines, if anything else
|
||||||
|
// then add
|
||||||
|
mergedLines.add(userLine);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Files.write(outputPath, mergedLines, StandardCharsets.UTF_8);
|
Files.write(outputPath, mergedLines, StandardCharsets.UTF_8);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// New method to check if a userLine is a child of an entry in templateLines
|
||||||
|
boolean childOfTemplateEntry(
|
||||||
|
Function<String, Boolean> isCommented,
|
||||||
|
Function<String, String> extractKey,
|
||||||
|
Function<String, Integer> getIndentationLevel,
|
||||||
|
List<String> userLines,
|
||||||
|
String userLine,
|
||||||
|
List<String> templateLines) {
|
||||||
|
String userKey = extractKey.apply(userLine).trim();
|
||||||
|
int userIndentation = getIndentationLevel.apply(userLine);
|
||||||
|
|
||||||
|
// Start by assuming the line is not a child of an entry in templateLines
|
||||||
|
boolean isChild = false;
|
||||||
|
|
||||||
|
// Iterate backwards through userLines from the current line to find any parent
|
||||||
|
for (int i = userLines.indexOf(userLine) - 1; i >= 0; i--) {
|
||||||
|
String potentialParentLine = userLines.get(i);
|
||||||
|
int parentIndentation = getIndentationLevel.apply(potentialParentLine);
|
||||||
|
|
||||||
|
// Check if we've reached a potential parent based on indentation
|
||||||
|
if (parentIndentation < userIndentation) {
|
||||||
|
String parentKey = extractKey.apply(potentialParentLine).trim();
|
||||||
|
|
||||||
|
// Now, check if this potential parent or any of its parents exist in templateLines
|
||||||
|
boolean parentExistsInTemplate =
|
||||||
|
templateLines.stream()
|
||||||
|
.filter(line -> !isCommented.apply(line)) // Skip commented lines
|
||||||
|
.anyMatch(
|
||||||
|
templateLine -> {
|
||||||
|
String templateKey =
|
||||||
|
extractKey.apply(templateLine).trim();
|
||||||
|
return parentKey.equalsIgnoreCase(templateKey);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!parentExistsInTemplate) {
|
||||||
|
// If the parent does not exist in template, check the next level parent
|
||||||
|
userIndentation =
|
||||||
|
parentIndentation; // Update userIndentation to the parent's indentation
|
||||||
|
// for next iteration
|
||||||
|
if (parentIndentation == 0) {
|
||||||
|
// If we've reached the top-level parent and it's not in template, the
|
||||||
|
// original line is considered not a child
|
||||||
|
isChild = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// If any parent exists in template, the original line is considered a child
|
||||||
|
isChild = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return isChild; // Return true if the line is not a child of any entry in templateLines
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,11 +9,14 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
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 org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import stirling.software.SPDF.model.ApplicationProperties;
|
import stirling.software.SPDF.model.ApplicationProperties;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
|
@DependsOn({"bookAndHtmlFormatsInstalled"})
|
||||||
public class EndpointConfiguration {
|
public class EndpointConfiguration {
|
||||||
private static final Logger logger = LoggerFactory.getLogger(EndpointConfiguration.class);
|
private static final Logger logger = LoggerFactory.getLogger(EndpointConfiguration.class);
|
||||||
private Map<String, Boolean> endpointStatuses = new ConcurrentHashMap<>();
|
private Map<String, Boolean> endpointStatuses = new ConcurrentHashMap<>();
|
||||||
@@ -21,9 +24,14 @@ public class EndpointConfiguration {
|
|||||||
|
|
||||||
private final ApplicationProperties applicationProperties;
|
private final ApplicationProperties applicationProperties;
|
||||||
|
|
||||||
|
private boolean bookAndHtmlFormatsInstalled;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public EndpointConfiguration(ApplicationProperties applicationProperties) {
|
public EndpointConfiguration(
|
||||||
|
ApplicationProperties applicationProperties,
|
||||||
|
@Qualifier("bookAndHtmlFormatsInstalled") boolean bookAndHtmlFormatsInstalled) {
|
||||||
this.applicationProperties = applicationProperties;
|
this.applicationProperties = applicationProperties;
|
||||||
|
this.bookAndHtmlFormatsInstalled = bookAndHtmlFormatsInstalled;
|
||||||
init();
|
init();
|
||||||
processEnvironmentConfigs();
|
processEnvironmentConfigs();
|
||||||
}
|
}
|
||||||
@@ -132,7 +140,6 @@ public class EndpointConfiguration {
|
|||||||
// CLI
|
// CLI
|
||||||
addEndpointToGroup("CLI", "compress-pdf");
|
addEndpointToGroup("CLI", "compress-pdf");
|
||||||
addEndpointToGroup("CLI", "extract-image-scans");
|
addEndpointToGroup("CLI", "extract-image-scans");
|
||||||
addEndpointToGroup("CLI", "remove-blanks");
|
|
||||||
addEndpointToGroup("CLI", "repair");
|
addEndpointToGroup("CLI", "repair");
|
||||||
addEndpointToGroup("CLI", "pdf-to-pdfa");
|
addEndpointToGroup("CLI", "pdf-to-pdfa");
|
||||||
addEndpointToGroup("CLI", "file-to-pdf");
|
addEndpointToGroup("CLI", "file-to-pdf");
|
||||||
@@ -145,6 +152,12 @@ public class EndpointConfiguration {
|
|||||||
addEndpointToGroup("CLI", "ocr-pdf");
|
addEndpointToGroup("CLI", "ocr-pdf");
|
||||||
addEndpointToGroup("CLI", "html-to-pdf");
|
addEndpointToGroup("CLI", "html-to-pdf");
|
||||||
addEndpointToGroup("CLI", "url-to-pdf");
|
addEndpointToGroup("CLI", "url-to-pdf");
|
||||||
|
addEndpointToGroup("CLI", "book-to-pdf");
|
||||||
|
addEndpointToGroup("CLI", "pdf-to-book");
|
||||||
|
|
||||||
|
// Calibre
|
||||||
|
addEndpointToGroup("Calibre", "book-to-pdf");
|
||||||
|
addEndpointToGroup("Calibre", "pdf-to-book");
|
||||||
|
|
||||||
// python
|
// python
|
||||||
addEndpointToGroup("Python", "extract-image-scans");
|
addEndpointToGroup("Python", "extract-image-scans");
|
||||||
@@ -204,6 +217,7 @@ public class EndpointConfiguration {
|
|||||||
addEndpointToGroup("Java", "split-by-size-or-count");
|
addEndpointToGroup("Java", "split-by-size-or-count");
|
||||||
addEndpointToGroup("Java", "overlay-pdf");
|
addEndpointToGroup("Java", "overlay-pdf");
|
||||||
addEndpointToGroup("Java", "split-pdf-by-sections");
|
addEndpointToGroup("Java", "split-pdf-by-sections");
|
||||||
|
addEndpointToGroup("Java", "remove-blanks");
|
||||||
|
|
||||||
// Javascript
|
// Javascript
|
||||||
addEndpointToGroup("Javascript", "pdf-organizer");
|
addEndpointToGroup("Javascript", "pdf-organizer");
|
||||||
@@ -215,7 +229,9 @@ public class EndpointConfiguration {
|
|||||||
private void processEnvironmentConfigs() {
|
private void processEnvironmentConfigs() {
|
||||||
List<String> endpointsToRemove = applicationProperties.getEndpoints().getToRemove();
|
List<String> endpointsToRemove = applicationProperties.getEndpoints().getToRemove();
|
||||||
List<String> groupsToRemove = applicationProperties.getEndpoints().getGroupsToRemove();
|
List<String> groupsToRemove = applicationProperties.getEndpoints().getGroupsToRemove();
|
||||||
|
if (!bookAndHtmlFormatsInstalled) {
|
||||||
|
groupsToRemove.add("Calibre");
|
||||||
|
}
|
||||||
if (endpointsToRemove != null) {
|
if (endpointsToRemove != null) {
|
||||||
for (String endpoint : endpointsToRemove) {
|
for (String endpoint : endpointsToRemove) {
|
||||||
disableEndpoint(endpoint.trim());
|
disableEndpoint(endpoint.trim());
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package stirling.software.SPDF.config.security;
|
package stirling.software.SPDF.config.security;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.security.authentication.BadCredentialsException;
|
import org.springframework.security.authentication.BadCredentialsException;
|
||||||
@@ -12,15 +13,19 @@ import org.springframework.stereotype.Component;
|
|||||||
import jakarta.servlet.ServletException;
|
import jakarta.servlet.ServletException;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import stirling.software.SPDF.model.User;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
public class CustomAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
|
public class CustomAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
|
||||||
|
|
||||||
@Autowired private final LoginAttemptService loginAttemptService;
|
@Autowired private final LoginAttemptService loginAttemptService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired private final UserService userService; // Inject the UserService
|
||||||
public CustomAuthenticationFailureHandler(LoginAttemptService loginAttemptService) {
|
|
||||||
|
public CustomAuthenticationFailureHandler(
|
||||||
|
LoginAttemptService loginAttemptService, UserService userService) {
|
||||||
this.loginAttemptService = loginAttemptService;
|
this.loginAttemptService = loginAttemptService;
|
||||||
|
this.userService = userService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -33,17 +38,28 @@ public class CustomAuthenticationFailureHandler extends SimpleUrlAuthenticationF
|
|||||||
logger.error("Failed login attempt from IP: " + ip);
|
logger.error("Failed login attempt from IP: " + ip);
|
||||||
|
|
||||||
String username = request.getParameter("username");
|
String username = request.getParameter("username");
|
||||||
if (loginAttemptService.loginAttemptCheck(username)) {
|
if (!isDemoUser(username)) {
|
||||||
setDefaultFailureUrl("/login?error=locked");
|
if (loginAttemptService.loginAttemptCheck(username)) {
|
||||||
|
|
||||||
} else {
|
|
||||||
if (exception.getClass().isAssignableFrom(BadCredentialsException.class)) {
|
|
||||||
setDefaultFailureUrl("/login?error=badcredentials");
|
|
||||||
} else if (exception.getClass().isAssignableFrom(LockedException.class)) {
|
|
||||||
setDefaultFailureUrl("/login?error=locked");
|
setDefaultFailureUrl("/login?error=locked");
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if (exception.getClass().isAssignableFrom(LockedException.class)) {
|
||||||
|
setDefaultFailureUrl("/login?error=locked");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (exception.getClass().isAssignableFrom(BadCredentialsException.class)) {
|
||||||
|
setDefaultFailureUrl("/login?error=badcredentials");
|
||||||
|
}
|
||||||
|
|
||||||
super.onAuthenticationFailure(request, response, exception);
|
super.onAuthenticationFailure(request, response, exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isDemoUser(String username) {
|
||||||
|
Optional<User> user = userService.findByUsername(username);
|
||||||
|
return user.isPresent()
|
||||||
|
&& user.get().getAuthorities().stream()
|
||||||
|
.anyMatch(authority -> "ROLE_DEMO_USER".equals(authority.getAuthority()));
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,9 @@ import org.springframework.security.authentication.dao.DaoAuthenticationProvider
|
|||||||
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
|
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
|
||||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||||
|
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||||
|
import org.springframework.security.core.session.SessionRegistry;
|
||||||
|
import org.springframework.security.core.session.SessionRegistryImpl;
|
||||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
@@ -18,6 +21,7 @@ import org.springframework.security.web.authentication.rememberme.PersistentToke
|
|||||||
import org.springframework.security.web.savedrequest.NullRequestCache;
|
import org.springframework.security.web.savedrequest.NullRequestCache;
|
||||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||||
|
|
||||||
|
import jakarta.servlet.http.HttpSession;
|
||||||
import stirling.software.SPDF.repository.JPATokenRepositoryImpl;
|
import stirling.software.SPDF.repository.JPATokenRepositoryImpl;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@@ -44,6 +48,11 @@ public class SecurityConfiguration {
|
|||||||
|
|
||||||
@Autowired private FirstLoginFilter firstLoginFilter;
|
@Autowired private FirstLoginFilter firstLoginFilter;
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public SessionRegistry sessionRegistry() {
|
||||||
|
return new SessionRegistryImpl();
|
||||||
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||||
http.addFilterBefore(userAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
|
http.addFilterBefore(userAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
|
||||||
@@ -53,6 +62,14 @@ public class SecurityConfiguration {
|
|||||||
http.csrf(csrf -> csrf.disable());
|
http.csrf(csrf -> csrf.disable());
|
||||||
http.addFilterBefore(rateLimitingFilter(), UsernamePasswordAuthenticationFilter.class);
|
http.addFilterBefore(rateLimitingFilter(), UsernamePasswordAuthenticationFilter.class);
|
||||||
http.addFilterAfter(firstLoginFilter, UsernamePasswordAuthenticationFilter.class);
|
http.addFilterAfter(firstLoginFilter, UsernamePasswordAuthenticationFilter.class);
|
||||||
|
http.sessionManagement(
|
||||||
|
sessionManagement ->
|
||||||
|
sessionManagement
|
||||||
|
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
|
||||||
|
.maximumSessions(3)
|
||||||
|
.maxSessionsPreventsLogin(true)
|
||||||
|
.sessionRegistry(sessionRegistry())
|
||||||
|
.expiredUrl("/login?logout=true"));
|
||||||
http.formLogin(
|
http.formLogin(
|
||||||
formLogin ->
|
formLogin ->
|
||||||
formLogin
|
formLogin
|
||||||
@@ -62,7 +79,7 @@ public class SecurityConfiguration {
|
|||||||
.defaultSuccessUrl("/")
|
.defaultSuccessUrl("/")
|
||||||
.failureHandler(
|
.failureHandler(
|
||||||
new CustomAuthenticationFailureHandler(
|
new CustomAuthenticationFailureHandler(
|
||||||
loginAttemptService))
|
loginAttemptService, userService))
|
||||||
.permitAll())
|
.permitAll())
|
||||||
.requestCache(requestCache -> requestCache.requestCache(new NullRequestCache()))
|
.requestCache(requestCache -> requestCache.requestCache(new NullRequestCache()))
|
||||||
.logout(
|
.logout(
|
||||||
@@ -71,7 +88,19 @@ public class SecurityConfiguration {
|
|||||||
new AntPathRequestMatcher("/logout"))
|
new AntPathRequestMatcher("/logout"))
|
||||||
.logoutSuccessUrl("/login?logout=true")
|
.logoutSuccessUrl("/login?logout=true")
|
||||||
.invalidateHttpSession(true) // Invalidate session
|
.invalidateHttpSession(true) // Invalidate session
|
||||||
.deleteCookies("JSESSIONID", "remember-me"))
|
.deleteCookies("JSESSIONID", "remember-me")
|
||||||
|
.addLogoutHandler(
|
||||||
|
(request, response, authentication) -> {
|
||||||
|
HttpSession session =
|
||||||
|
request.getSession(
|
||||||
|
false);
|
||||||
|
if (session != null) {
|
||||||
|
String sessionId = session.getId();
|
||||||
|
sessionRegistry()
|
||||||
|
.removeSessionInformation(
|
||||||
|
sessionId);
|
||||||
|
}
|
||||||
|
}))
|
||||||
.rememberMe(
|
.rememberMe(
|
||||||
rememberMeConfigurer ->
|
rememberMeConfigurer ->
|
||||||
rememberMeConfigurer // Use the configurator directly
|
rememberMeConfigurer // Use the configurator directly
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
package stirling.software.SPDF.controller.api;
|
package stirling.software.SPDF.controller.api;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.apache.pdfbox.Loader;
|
||||||
import org.apache.pdfbox.multipdf.LayerUtility;
|
import org.apache.pdfbox.multipdf.LayerUtility;
|
||||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
import org.apache.pdfbox.pdmodel.PDPage;
|
import org.apache.pdfbox.pdmodel.PDPage;
|
||||||
import org.apache.pdfbox.pdmodel.PDPageContentStream;
|
import org.apache.pdfbox.pdmodel.PDPageContentStream;
|
||||||
|
import org.apache.pdfbox.pdmodel.PDPageContentStream.AppendMode;
|
||||||
import org.apache.pdfbox.pdmodel.common.PDRectangle;
|
import org.apache.pdfbox.pdmodel.common.PDRectangle;
|
||||||
import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject;
|
import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@@ -37,9 +38,7 @@ public class CropController {
|
|||||||
description =
|
description =
|
||||||
"This operation takes an input PDF file and crops it according to the given coordinates. Input:PDF Output:PDF Type:SISO")
|
"This operation takes an input PDF file and crops it according to the given coordinates. Input:PDF Output:PDF Type:SISO")
|
||||||
public ResponseEntity<byte[]> cropPdf(@ModelAttribute CropPdfForm form) throws IOException {
|
public ResponseEntity<byte[]> cropPdf(@ModelAttribute CropPdfForm form) throws IOException {
|
||||||
|
PDDocument sourceDocument = Loader.loadPDF(form.getFileInput().getBytes());
|
||||||
PDDocument sourceDocument =
|
|
||||||
PDDocument.load(new ByteArrayInputStream(form.getFileInput().getBytes()));
|
|
||||||
|
|
||||||
PDDocument newDocument = new PDDocument();
|
PDDocument newDocument = new PDDocument();
|
||||||
|
|
||||||
@@ -53,7 +52,8 @@ public class CropController {
|
|||||||
// Create a new page with the size of the source page
|
// Create a new page with the size of the source page
|
||||||
PDPage newPage = new PDPage(sourcePage.getMediaBox());
|
PDPage newPage = new PDPage(sourcePage.getMediaBox());
|
||||||
newDocument.addPage(newPage);
|
newDocument.addPage(newPage);
|
||||||
PDPageContentStream contentStream = new PDPageContentStream(newDocument, newPage);
|
PDPageContentStream contentStream =
|
||||||
|
new PDPageContentStream(newDocument, newPage, AppendMode.OVERWRITE, true, true);
|
||||||
|
|
||||||
// Import the source page as a form XObject
|
// Import the source page as a form XObject
|
||||||
PDFormXObject formXObject = layerUtility.importPageAsForm(sourceDocument, i);
|
PDFormXObject formXObject = layerUtility.importPageAsForm(sourceDocument, i);
|
||||||
|
|||||||
@@ -1,16 +1,17 @@
|
|||||||
package stirling.software.SPDF.controller.api;
|
package stirling.software.SPDF.controller.api;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.nio.file.attribute.BasicFileAttributes;
|
import java.nio.file.attribute.BasicFileAttributes;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.apache.pdfbox.io.MemoryUsageSetting;
|
import org.apache.pdfbox.Loader;
|
||||||
import org.apache.pdfbox.multipdf.PDFMergerUtility;
|
import org.apache.pdfbox.multipdf.PDFMergerUtility;
|
||||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
import org.apache.pdfbox.pdmodel.PDPage;
|
import org.apache.pdfbox.pdmodel.PDPage;
|
||||||
@@ -27,6 +28,7 @@ import io.swagger.v3.oas.annotations.Operation;
|
|||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
|
||||||
import stirling.software.SPDF.model.api.general.MergePdfsRequest;
|
import stirling.software.SPDF.model.api.general.MergePdfsRequest;
|
||||||
|
import stirling.software.SPDF.utils.GeneralUtils;
|
||||||
import stirling.software.SPDF.utils.WebResponseUtils;
|
import stirling.software.SPDF.utils.WebResponseUtils;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@@ -36,7 +38,7 @@ public class MergeController {
|
|||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(MergeController.class);
|
private static final Logger logger = LoggerFactory.getLogger(MergeController.class);
|
||||||
|
|
||||||
private PDDocument mergeDocuments(List<PDDocument> documents) throws IOException {
|
public PDDocument mergeDocuments(List<PDDocument> documents) throws IOException {
|
||||||
PDDocument mergedDoc = new PDDocument();
|
PDDocument mergedDoc = new PDDocument();
|
||||||
for (PDDocument doc : documents) {
|
for (PDDocument doc : documents) {
|
||||||
for (PDPage page : doc.getPages()) {
|
for (PDPage page : doc.getPages()) {
|
||||||
@@ -84,8 +86,8 @@ public class MergeController {
|
|||||||
};
|
};
|
||||||
case "byPDFTitle":
|
case "byPDFTitle":
|
||||||
return (file1, file2) -> {
|
return (file1, file2) -> {
|
||||||
try (PDDocument doc1 = PDDocument.load(file1.getInputStream());
|
try (PDDocument doc1 = Loader.loadPDF(file1.getBytes());
|
||||||
PDDocument doc2 = PDDocument.load(file2.getInputStream())) {
|
PDDocument doc2 = Loader.loadPDF(file2.getBytes())) {
|
||||||
String title1 = doc1.getDocumentInformation().getTitle();
|
String title1 = doc1.getDocumentInformation().getTitle();
|
||||||
String title2 = doc2.getDocumentInformation().getTitle();
|
String title2 = doc2.getDocumentInformation().getTitle();
|
||||||
return title1.compareTo(title2);
|
return title1.compareTo(title2);
|
||||||
@@ -106,6 +108,7 @@ public class MergeController {
|
|||||||
"This endpoint merges multiple PDF files into a single PDF file. The merged file will contain all pages from the input files in the order they were provided. Input:PDF Output:PDF Type:MISO")
|
"This endpoint merges multiple PDF files into a single PDF file. The merged file will contain all pages from the input files in the order they were provided. Input:PDF Output:PDF Type:MISO")
|
||||||
public ResponseEntity<byte[]> mergePdfs(@ModelAttribute MergePdfsRequest form)
|
public ResponseEntity<byte[]> mergePdfs(@ModelAttribute MergePdfsRequest form)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
|
List<File> filesToDelete = new ArrayList<File>();
|
||||||
try {
|
try {
|
||||||
MultipartFile[] files = form.getFileInput();
|
MultipartFile[] files = form.getFileInput();
|
||||||
Arrays.sort(files, getSortComparator(form.getSortType()));
|
Arrays.sort(files, getSortComparator(form.getSortType()));
|
||||||
@@ -113,20 +116,27 @@ public class MergeController {
|
|||||||
PDFMergerUtility mergedDoc = new PDFMergerUtility();
|
PDFMergerUtility mergedDoc = new PDFMergerUtility();
|
||||||
ByteArrayOutputStream docOutputstream = new ByteArrayOutputStream();
|
ByteArrayOutputStream docOutputstream = new ByteArrayOutputStream();
|
||||||
|
|
||||||
for (MultipartFile file : files) {
|
for (MultipartFile multipartFile : files) {
|
||||||
mergedDoc.addSource(new ByteArrayInputStream(file.getBytes()));
|
File tempFile = GeneralUtils.convertMultipartFileToFile(multipartFile);
|
||||||
|
filesToDelete.add(tempFile);
|
||||||
|
mergedDoc.addSource(tempFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
mergedDoc.setDestinationFileName(
|
mergedDoc.setDestinationFileName(
|
||||||
files[0].getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_merged.pdf");
|
files[0].getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_merged.pdf");
|
||||||
mergedDoc.setDestinationStream(docOutputstream);
|
mergedDoc.setDestinationStream(docOutputstream);
|
||||||
mergedDoc.mergeDocuments(MemoryUsageSetting.setupMainMemoryOnly());
|
|
||||||
|
mergedDoc.mergeDocuments(null);
|
||||||
|
|
||||||
return WebResponseUtils.bytesToWebResponse(
|
return WebResponseUtils.bytesToWebResponse(
|
||||||
docOutputstream.toByteArray(), mergedDoc.getDestinationFileName());
|
docOutputstream.toByteArray(), mergedDoc.getDestinationFileName());
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
logger.error("Error in merge pdf process", ex);
|
logger.error("Error in merge pdf process", ex);
|
||||||
throw ex;
|
throw ex;
|
||||||
|
} finally {
|
||||||
|
for (File file : filesToDelete) {
|
||||||
|
file.delete();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import java.awt.Color;
|
|||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.apache.pdfbox.Loader;
|
||||||
import org.apache.pdfbox.multipdf.LayerUtility;
|
import org.apache.pdfbox.multipdf.LayerUtility;
|
||||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
import org.apache.pdfbox.pdmodel.PDPage;
|
import org.apache.pdfbox.pdmodel.PDPage;
|
||||||
@@ -20,6 +21,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import io.github.pixee.security.Filenames;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
|
||||||
@@ -57,7 +59,7 @@ public class MultiPageLayoutController {
|
|||||||
: (int) Math.sqrt(pagesPerSheet);
|
: (int) Math.sqrt(pagesPerSheet);
|
||||||
int rows = pagesPerSheet == 2 || pagesPerSheet == 3 ? 1 : (int) Math.sqrt(pagesPerSheet);
|
int rows = pagesPerSheet == 2 || pagesPerSheet == 3 ? 1 : (int) Math.sqrt(pagesPerSheet);
|
||||||
|
|
||||||
PDDocument sourceDocument = PDDocument.load(file.getInputStream());
|
PDDocument sourceDocument = Loader.loadPDF(file.getBytes());
|
||||||
PDDocument newDocument = new PDDocument();
|
PDDocument newDocument = new PDDocument();
|
||||||
PDPage newPage = new PDPage(PDRectangle.A4);
|
PDPage newPage = new PDPage(PDRectangle.A4);
|
||||||
newDocument.addPage(newPage);
|
newDocument.addPage(newPage);
|
||||||
@@ -135,6 +137,7 @@ public class MultiPageLayoutController {
|
|||||||
byte[] result = baos.toByteArray();
|
byte[] result = baos.toByteArray();
|
||||||
return WebResponseUtils.bytesToWebResponse(
|
return WebResponseUtils.bytesToWebResponse(
|
||||||
result,
|
result,
|
||||||
file.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_layoutChanged.pdf");
|
Filenames.toSimpleFileName(file.getOriginalFilename()).replaceFirst("[.][^.]+$", "")
|
||||||
|
+ "_layoutChanged.pdf");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,11 +3,13 @@ package stirling.software.SPDF.controller.api;
|
|||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.apache.pdfbox.Loader;
|
||||||
import org.apache.pdfbox.multipdf.Overlay;
|
import org.apache.pdfbox.multipdf.Overlay;
|
||||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
@@ -18,6 +20,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import io.github.pixee.security.Filenames;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
|
||||||
@@ -53,7 +56,7 @@ public class PdfOverlayController {
|
|||||||
// "FixedRepeatOverlay"
|
// "FixedRepeatOverlay"
|
||||||
int[] counts = request.getCounts(); // Used for FixedRepeatOverlay mode
|
int[] counts = request.getCounts(); // Used for FixedRepeatOverlay mode
|
||||||
|
|
||||||
try (PDDocument basePdf = PDDocument.load(baseFile.getInputStream());
|
try (PDDocument basePdf = Loader.loadPDF(baseFile.getBytes());
|
||||||
Overlay overlay = new Overlay()) {
|
Overlay overlay = new Overlay()) {
|
||||||
Map<Integer, String> overlayGuide =
|
Map<Integer, String> overlayGuide =
|
||||||
prepareOverlayGuide(
|
prepareOverlayGuide(
|
||||||
@@ -74,7 +77,8 @@ public class PdfOverlayController {
|
|||||||
overlay.overlay(overlayGuide).save(outputStream);
|
overlay.overlay(overlayGuide).save(outputStream);
|
||||||
byte[] data = outputStream.toByteArray();
|
byte[] data = outputStream.toByteArray();
|
||||||
String outputFilename =
|
String outputFilename =
|
||||||
baseFile.getOriginalFilename().replaceFirst("[.][^.]+$", "")
|
Filenames.toSimpleFileName(baseFile.getOriginalFilename())
|
||||||
|
.replaceFirst("[.][^.]+$", "")
|
||||||
+ "_overlayed.pdf"; // Remove file extension and append .pdf
|
+ "_overlayed.pdf"; // Remove file extension and append .pdf
|
||||||
|
|
||||||
return WebResponseUtils.bytesToWebResponse(
|
return WebResponseUtils.bytesToWebResponse(
|
||||||
@@ -131,10 +135,10 @@ public class PdfOverlayController {
|
|||||||
overlayFileIndex = (overlayFileIndex + 1) % overlayFiles.length;
|
overlayFileIndex = (overlayFileIndex + 1) % overlayFiles.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
try (PDDocument overlayPdf = PDDocument.load(overlayFiles[overlayFileIndex])) {
|
try (PDDocument overlayPdf = Loader.loadPDF(overlayFiles[overlayFileIndex])) {
|
||||||
PDDocument singlePageDocument = new PDDocument();
|
PDDocument singlePageDocument = new PDDocument();
|
||||||
singlePageDocument.addPage(overlayPdf.getPage(pageCountInCurrentOverlay));
|
singlePageDocument.addPage(overlayPdf.getPage(pageCountInCurrentOverlay));
|
||||||
File tempFile = File.createTempFile("overlay-page-", ".pdf");
|
File tempFile = Files.createTempFile("overlay-page-", ".pdf").toFile();
|
||||||
singlePageDocument.save(tempFile);
|
singlePageDocument.save(tempFile);
|
||||||
singlePageDocument.close();
|
singlePageDocument.close();
|
||||||
|
|
||||||
@@ -147,7 +151,7 @@ public class PdfOverlayController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private int getNumberOfPages(File file) throws IOException {
|
private int getNumberOfPages(File file) throws IOException {
|
||||||
try (PDDocument doc = PDDocument.load(file)) {
|
try (PDDocument doc = Loader.loadPDF(file)) {
|
||||||
return doc.getNumberOfPages();
|
return doc.getNumberOfPages();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -159,7 +163,7 @@ public class PdfOverlayController {
|
|||||||
File overlayFile = overlayFiles[(basePageIndex - 1) % overlayFiles.length];
|
File overlayFile = overlayFiles[(basePageIndex - 1) % overlayFiles.length];
|
||||||
|
|
||||||
// Load the overlay document to check its page count
|
// Load the overlay document to check its page count
|
||||||
try (PDDocument overlayPdf = PDDocument.load(overlayFile)) {
|
try (PDDocument overlayPdf = Loader.loadPDF(overlayFile)) {
|
||||||
int overlayPageCount = overlayPdf.getNumberOfPages();
|
int overlayPageCount = overlayPdf.getNumberOfPages();
|
||||||
if ((basePageIndex - 1) % overlayPageCount < overlayPageCount) {
|
if ((basePageIndex - 1) % overlayPageCount < overlayPageCount) {
|
||||||
overlayGuide.put(basePageIndex, overlayFile.getAbsolutePath());
|
overlayGuide.put(basePageIndex, overlayFile.getAbsolutePath());
|
||||||
@@ -181,7 +185,7 @@ public class PdfOverlayController {
|
|||||||
int repeatCount = counts[i];
|
int repeatCount = counts[i];
|
||||||
|
|
||||||
// Load the overlay document to check its page count
|
// Load the overlay document to check its page count
|
||||||
try (PDDocument overlayPdf = PDDocument.load(overlayFile)) {
|
try (PDDocument overlayPdf = Loader.loadPDF(overlayFile)) {
|
||||||
int overlayPageCount = overlayPdf.getNumberOfPages();
|
int overlayPageCount = overlayPdf.getNumberOfPages();
|
||||||
for (int j = 0; j < repeatCount; j++) {
|
for (int j = 0; j < repeatCount; j++) {
|
||||||
for (int page = 0; page < overlayPageCount; page++) {
|
for (int page = 0; page < overlayPageCount; page++) {
|
||||||
|
|||||||
@@ -2,8 +2,10 @@ package stirling.software.SPDF.controller.api;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.pdfbox.Loader;
|
||||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
import org.apache.pdfbox.pdmodel.PDPage;
|
import org.apache.pdfbox.pdmodel.PDPage;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@@ -15,6 +17,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import io.github.pixee.security.Filenames;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
|
||||||
@@ -42,13 +45,15 @@ public class RearrangePagesPDFController {
|
|||||||
MultipartFile pdfFile = request.getFileInput();
|
MultipartFile pdfFile = request.getFileInput();
|
||||||
String pagesToDelete = request.getPageNumbers();
|
String pagesToDelete = request.getPageNumbers();
|
||||||
|
|
||||||
PDDocument document = PDDocument.load(pdfFile.getBytes());
|
PDDocument document = Loader.loadPDF(pdfFile.getBytes());
|
||||||
|
|
||||||
// Split the page order string into an array of page numbers or range of numbers
|
// Split the page order string into an array of page numbers or range of numbers
|
||||||
String[] pageOrderArr = pagesToDelete.split(",");
|
String[] pageOrderArr = pagesToDelete.split(",");
|
||||||
|
|
||||||
List<Integer> pagesToRemove =
|
List<Integer> pagesToRemove =
|
||||||
GeneralUtils.parsePageList(pageOrderArr, document.getNumberOfPages());
|
GeneralUtils.parsePageList(pageOrderArr, document.getNumberOfPages(), true);
|
||||||
|
|
||||||
|
Collections.sort(pagesToRemove);
|
||||||
|
|
||||||
for (int i = pagesToRemove.size() - 1; i >= 0; i--) {
|
for (int i = pagesToRemove.size() - 1; i >= 0; i--) {
|
||||||
int pageIndex = pagesToRemove.get(i);
|
int pageIndex = pagesToRemove.get(i);
|
||||||
@@ -56,7 +61,9 @@ public class RearrangePagesPDFController {
|
|||||||
}
|
}
|
||||||
return WebResponseUtils.pdfDocToWebResponse(
|
return WebResponseUtils.pdfDocToWebResponse(
|
||||||
document,
|
document,
|
||||||
pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_removed_pages.pdf");
|
Filenames.toSimpleFileName(pdfFile.getOriginalFilename())
|
||||||
|
.replaceFirst("[.][^.]+$", "")
|
||||||
|
+ "_removed_pages.pdf");
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Integer> removeFirst(int totalPages) {
|
private List<Integer> removeFirst(int totalPages) {
|
||||||
@@ -179,7 +186,7 @@ public class RearrangePagesPDFController {
|
|||||||
String sortType = request.getCustomMode();
|
String sortType = request.getCustomMode();
|
||||||
try {
|
try {
|
||||||
// Load the input PDF
|
// Load the input PDF
|
||||||
PDDocument document = PDDocument.load(pdfFile.getInputStream());
|
PDDocument document = Loader.loadPDF(pdfFile.getBytes());
|
||||||
|
|
||||||
// Split the page order string into an array of page numbers or range of numbers
|
// Split the page order string into an array of page numbers or range of numbers
|
||||||
String[] pageOrderArr = pageOrder != null ? pageOrder.split(",") : new String[0];
|
String[] pageOrderArr = pageOrder != null ? pageOrder.split(",") : new String[0];
|
||||||
@@ -188,7 +195,7 @@ public class RearrangePagesPDFController {
|
|||||||
if (sortType != null && sortType.length() > 0) {
|
if (sortType != null && sortType.length() > 0) {
|
||||||
newPageOrder = processSortTypes(sortType, totalPages);
|
newPageOrder = processSortTypes(sortType, totalPages);
|
||||||
} else {
|
} else {
|
||||||
newPageOrder = GeneralUtils.parsePageList(pageOrderArr, totalPages);
|
newPageOrder = GeneralUtils.parsePageList(pageOrderArr, totalPages, true);
|
||||||
}
|
}
|
||||||
logger.info("newPageOrder = " + newPageOrder);
|
logger.info("newPageOrder = " + newPageOrder);
|
||||||
logger.info("totalPages = " + totalPages);
|
logger.info("totalPages = " + totalPages);
|
||||||
@@ -210,7 +217,8 @@ public class RearrangePagesPDFController {
|
|||||||
|
|
||||||
return WebResponseUtils.pdfDocToWebResponse(
|
return WebResponseUtils.pdfDocToWebResponse(
|
||||||
document,
|
document,
|
||||||
pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "")
|
Filenames.toSimpleFileName(pdfFile.getOriginalFilename())
|
||||||
|
.replaceFirst("[.][^.]+$", "")
|
||||||
+ "_rearranged.pdf");
|
+ "_rearranged.pdf");
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
logger.error("Failed rearranging documents", e);
|
logger.error("Failed rearranging documents", e);
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package stirling.software.SPDF.controller.api;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.apache.pdfbox.Loader;
|
||||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
import org.apache.pdfbox.pdmodel.PDPage;
|
import org.apache.pdfbox.pdmodel.PDPage;
|
||||||
import org.apache.pdfbox.pdmodel.PDPageTree;
|
import org.apache.pdfbox.pdmodel.PDPageTree;
|
||||||
@@ -14,6 +15,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import io.github.pixee.security.Filenames;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
|
||||||
@@ -37,7 +39,7 @@ public class RotationController {
|
|||||||
MultipartFile pdfFile = request.getFileInput();
|
MultipartFile pdfFile = request.getFileInput();
|
||||||
Integer angle = request.getAngle();
|
Integer angle = request.getAngle();
|
||||||
// Load the PDF document
|
// Load the PDF document
|
||||||
PDDocument document = PDDocument.load(pdfFile.getBytes());
|
PDDocument document = Loader.loadPDF(pdfFile.getBytes());
|
||||||
|
|
||||||
// Get the list of pages in the document
|
// Get the list of pages in the document
|
||||||
PDPageTree pages = document.getPages();
|
PDPageTree pages = document.getPages();
|
||||||
@@ -48,6 +50,8 @@ public class RotationController {
|
|||||||
|
|
||||||
return WebResponseUtils.pdfDocToWebResponse(
|
return WebResponseUtils.pdfDocToWebResponse(
|
||||||
document,
|
document,
|
||||||
pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_rotated.pdf");
|
Filenames.toSimpleFileName(pdfFile.getOriginalFilename())
|
||||||
|
.replaceFirst("[.][^.]+$", "")
|
||||||
|
+ "_rotated.pdf");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import java.io.IOException;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.apache.pdfbox.Loader;
|
||||||
import org.apache.pdfbox.multipdf.LayerUtility;
|
import org.apache.pdfbox.multipdf.LayerUtility;
|
||||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
import org.apache.pdfbox.pdmodel.PDPage;
|
import org.apache.pdfbox.pdmodel.PDPage;
|
||||||
@@ -21,6 +22,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import io.github.pixee.security.Filenames;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
|
||||||
@@ -66,7 +68,7 @@ public class ScalePagesController {
|
|||||||
|
|
||||||
PDRectangle targetSize = sizeMap.get(targetPDRectangle);
|
PDRectangle targetSize = sizeMap.get(targetPDRectangle);
|
||||||
|
|
||||||
PDDocument sourceDocument = PDDocument.load(file.getBytes());
|
PDDocument sourceDocument = Loader.loadPDF(file.getBytes());
|
||||||
PDDocument outputDocument = new PDDocument();
|
PDDocument outputDocument = new PDDocument();
|
||||||
|
|
||||||
int totalPages = sourceDocument.getNumberOfPages();
|
int totalPages = sourceDocument.getNumberOfPages();
|
||||||
@@ -83,7 +85,11 @@ public class ScalePagesController {
|
|||||||
|
|
||||||
PDPageContentStream contentStream =
|
PDPageContentStream contentStream =
|
||||||
new PDPageContentStream(
|
new PDPageContentStream(
|
||||||
outputDocument, newPage, PDPageContentStream.AppendMode.APPEND, true);
|
outputDocument,
|
||||||
|
newPage,
|
||||||
|
PDPageContentStream.AppendMode.APPEND,
|
||||||
|
true,
|
||||||
|
true);
|
||||||
|
|
||||||
float x = (targetSize.getWidth() - sourceSize.getWidth() * scale) / 2;
|
float x = (targetSize.getWidth() - sourceSize.getWidth() * scale) / 2;
|
||||||
float y = (targetSize.getHeight() - sourceSize.getHeight() * scale) / 2;
|
float y = (targetSize.getHeight() - sourceSize.getHeight() * scale) / 2;
|
||||||
@@ -107,6 +113,7 @@ public class ScalePagesController {
|
|||||||
|
|
||||||
return WebResponseUtils.bytesToWebResponse(
|
return WebResponseUtils.bytesToWebResponse(
|
||||||
baos.toByteArray(),
|
baos.toByteArray(),
|
||||||
file.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_scaled.pdf");
|
Filenames.toSimpleFileName(file.getOriginalFilename()).replaceFirst("[.][^.]+$", "")
|
||||||
|
+ "_scaled.pdf");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package stirling.software.SPDF.controller.api;
|
|||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -11,6 +10,7 @@ import java.util.stream.Collectors;
|
|||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
import java.util.zip.ZipOutputStream;
|
import java.util.zip.ZipOutputStream;
|
||||||
|
|
||||||
|
import org.apache.pdfbox.Loader;
|
||||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
import org.apache.pdfbox.pdmodel.PDPage;
|
import org.apache.pdfbox.pdmodel.PDPage;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@@ -23,6 +23,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import io.github.pixee.security.Filenames;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
|
||||||
@@ -46,12 +47,16 @@ public class SplitPDFController {
|
|||||||
MultipartFile file = request.getFileInput();
|
MultipartFile file = request.getFileInput();
|
||||||
String pages = request.getPageNumbers();
|
String pages = request.getPageNumbers();
|
||||||
// open the pdf document
|
// open the pdf document
|
||||||
InputStream inputStream = file.getInputStream();
|
|
||||||
PDDocument document = PDDocument.load(inputStream);
|
|
||||||
|
|
||||||
List<Integer> pageNumbers = request.getPageNumbersList(document);
|
PDDocument document = Loader.loadPDF(file.getBytes());
|
||||||
if (!pageNumbers.contains(document.getNumberOfPages() - 1))
|
|
||||||
|
List<Integer> pageNumbers = request.getPageNumbersList(document, true);
|
||||||
|
if (!pageNumbers.contains(document.getNumberOfPages() - 1)) {
|
||||||
|
// Create a mutable ArrayList so we can add to it
|
||||||
|
pageNumbers = new ArrayList<>(pageNumbers);
|
||||||
pageNumbers.add(document.getNumberOfPages() - 1);
|
pageNumbers.add(document.getNumberOfPages() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
"Splitting PDF into pages: {}",
|
"Splitting PDF into pages: {}",
|
||||||
pageNumbers.stream().map(String::valueOf).collect(Collectors.joining(",")));
|
pageNumbers.stream().map(String::valueOf).collect(Collectors.joining(",")));
|
||||||
@@ -83,7 +88,9 @@ public class SplitPDFController {
|
|||||||
|
|
||||||
Path zipFile = Files.createTempFile("split_documents", ".zip");
|
Path zipFile = Files.createTempFile("split_documents", ".zip");
|
||||||
|
|
||||||
String filename = file.getOriginalFilename().replaceFirst("[.][^.]+$", "");
|
String filename =
|
||||||
|
Filenames.toSimpleFileName(file.getOriginalFilename())
|
||||||
|
.replaceFirst("[.][^.]+$", "");
|
||||||
try (ZipOutputStream zipOut = new ZipOutputStream(Files.newOutputStream(zipFile))) {
|
try (ZipOutputStream zipOut = new ZipOutputStream(Files.newOutputStream(zipFile))) {
|
||||||
// loop through the split documents and write them to the zip file
|
// loop through the split documents and write them to the zip file
|
||||||
for (int i = 0; i < splitDocumentsBoas.size(); i++) {
|
for (int i = 0; i < splitDocumentsBoas.size(); i++) {
|
||||||
|
|||||||
@@ -9,10 +9,12 @@ import java.util.List;
|
|||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
import java.util.zip.ZipOutputStream;
|
import java.util.zip.ZipOutputStream;
|
||||||
|
|
||||||
|
import org.apache.pdfbox.Loader;
|
||||||
import org.apache.pdfbox.multipdf.LayerUtility;
|
import org.apache.pdfbox.multipdf.LayerUtility;
|
||||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
import org.apache.pdfbox.pdmodel.PDPage;
|
import org.apache.pdfbox.pdmodel.PDPage;
|
||||||
import org.apache.pdfbox.pdmodel.PDPageContentStream;
|
import org.apache.pdfbox.pdmodel.PDPageContentStream;
|
||||||
|
import org.apache.pdfbox.pdmodel.PDPageContentStream.AppendMode;
|
||||||
import org.apache.pdfbox.pdmodel.common.PDRectangle;
|
import org.apache.pdfbox.pdmodel.common.PDRectangle;
|
||||||
import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject;
|
import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject;
|
||||||
import org.apache.pdfbox.util.Matrix;
|
import org.apache.pdfbox.util.Matrix;
|
||||||
@@ -24,6 +26,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import io.github.pixee.security.Filenames;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
|
||||||
@@ -45,13 +48,26 @@ public class SplitPdfBySectionsController {
|
|||||||
List<ByteArrayOutputStream> splitDocumentsBoas = new ArrayList<>();
|
List<ByteArrayOutputStream> splitDocumentsBoas = new ArrayList<>();
|
||||||
|
|
||||||
MultipartFile file = request.getFileInput();
|
MultipartFile file = request.getFileInput();
|
||||||
PDDocument sourceDocument = PDDocument.load(file.getInputStream());
|
PDDocument sourceDocument = Loader.loadPDF(file.getBytes());
|
||||||
|
|
||||||
// Process the PDF based on split parameters
|
// Process the PDF based on split parameters
|
||||||
int horiz = request.getHorizontalDivisions() + 1;
|
int horiz = request.getHorizontalDivisions() + 1;
|
||||||
int verti = request.getVerticalDivisions() + 1;
|
int verti = request.getVerticalDivisions() + 1;
|
||||||
|
boolean merge = request.isMerge();
|
||||||
List<PDDocument> splitDocuments = splitPdfPages(sourceDocument, verti, horiz);
|
List<PDDocument> splitDocuments = splitPdfPages(sourceDocument, verti, horiz);
|
||||||
|
|
||||||
|
String filename =
|
||||||
|
Filenames.toSimpleFileName(file.getOriginalFilename())
|
||||||
|
.replaceFirst("[.][^.]+$", "");
|
||||||
|
if (merge) {
|
||||||
|
MergeController mergeController = new MergeController();
|
||||||
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
mergeController.mergeDocuments(splitDocuments).save(baos);
|
||||||
|
return WebResponseUtils.bytesToWebResponse(
|
||||||
|
baos.toByteArray(),
|
||||||
|
filename + "_split.pdf",
|
||||||
|
MediaType.APPLICATION_OCTET_STREAM);
|
||||||
|
}
|
||||||
for (PDDocument doc : splitDocuments) {
|
for (PDDocument doc : splitDocuments) {
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
doc.save(baos);
|
doc.save(baos);
|
||||||
@@ -62,7 +78,6 @@ public class SplitPdfBySectionsController {
|
|||||||
sourceDocument.close();
|
sourceDocument.close();
|
||||||
|
|
||||||
Path zipFile = Files.createTempFile("split_documents", ".zip");
|
Path zipFile = Files.createTempFile("split_documents", ".zip");
|
||||||
String filename = file.getOriginalFilename().replaceFirst("[.][^.]+$", "");
|
|
||||||
byte[] data;
|
byte[] data;
|
||||||
|
|
||||||
try (ZipOutputStream zipOut = new ZipOutputStream(Files.newOutputStream(zipFile))) {
|
try (ZipOutputStream zipOut = new ZipOutputStream(Files.newOutputStream(zipFile))) {
|
||||||
@@ -115,13 +130,13 @@ public class SplitPdfBySectionsController {
|
|||||||
document, document.getPages().indexOf(originalPage));
|
document, document.getPages().indexOf(originalPage));
|
||||||
|
|
||||||
try (PDPageContentStream contentStream =
|
try (PDPageContentStream contentStream =
|
||||||
new PDPageContentStream(subDoc, subPage)) {
|
new PDPageContentStream(
|
||||||
|
subDoc, subPage, AppendMode.APPEND, true, true)) {
|
||||||
// Set clipping area and position
|
// Set clipping area and position
|
||||||
float translateX = -subPageWidth * i;
|
float translateX = -subPageWidth * i;
|
||||||
float translateY = height - subPageHeight * (verticalDivisions - j);
|
|
||||||
|
|
||||||
// Code for google Docs pdfs..
|
// float translateY = height - subPageHeight * (verticalDivisions - j);
|
||||||
// float translateY = -subPageHeight * (verticalDivisions - 1 - j);
|
float translateY = -subPageHeight * (verticalDivisions - 1 - j);
|
||||||
|
|
||||||
contentStream.saveGraphicsState();
|
contentStream.saveGraphicsState();
|
||||||
contentStream.addRect(0, 0, subPageWidth, subPageHeight);
|
contentStream.addRect(0, 0, subPageWidth, subPageHeight);
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import java.util.List;
|
|||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
import java.util.zip.ZipOutputStream;
|
import java.util.zip.ZipOutputStream;
|
||||||
|
|
||||||
|
import org.apache.pdfbox.Loader;
|
||||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
import org.apache.pdfbox.pdmodel.PDPage;
|
import org.apache.pdfbox.pdmodel.PDPage;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
@@ -19,6 +20,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import io.github.pixee.security.Filenames;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
|
||||||
@@ -42,7 +44,7 @@ public class SplitPdfBySizeController {
|
|||||||
List<ByteArrayOutputStream> splitDocumentsBoas = new ArrayList<ByteArrayOutputStream>();
|
List<ByteArrayOutputStream> splitDocumentsBoas = new ArrayList<ByteArrayOutputStream>();
|
||||||
|
|
||||||
MultipartFile file = request.getFileInput();
|
MultipartFile file = request.getFileInput();
|
||||||
PDDocument sourceDocument = PDDocument.load(file.getInputStream());
|
PDDocument sourceDocument = Loader.loadPDF(file.getBytes());
|
||||||
|
|
||||||
// 0 = size, 1 = page count, 2 = doc count
|
// 0 = size, 1 = page count, 2 = doc count
|
||||||
int type = request.getSplitType();
|
int type = request.getSplitType();
|
||||||
@@ -119,7 +121,9 @@ public class SplitPdfBySizeController {
|
|||||||
sourceDocument.close();
|
sourceDocument.close();
|
||||||
|
|
||||||
Path zipFile = Files.createTempFile("split_documents", ".zip");
|
Path zipFile = Files.createTempFile("split_documents", ".zip");
|
||||||
String filename = file.getOriginalFilename().replaceFirst("[.][^.]+$", "");
|
String filename =
|
||||||
|
Filenames.toSimpleFileName(file.getOriginalFilename())
|
||||||
|
.replaceFirst("[.][^.]+$", "");
|
||||||
byte[] data;
|
byte[] data;
|
||||||
|
|
||||||
try (ZipOutputStream zipOut = new ZipOutputStream(Files.newOutputStream(zipFile))) {
|
try (ZipOutputStream zipOut = new ZipOutputStream(Files.newOutputStream(zipFile))) {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import java.awt.geom.AffineTransform;
|
|||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.apache.pdfbox.Loader;
|
||||||
import org.apache.pdfbox.multipdf.LayerUtility;
|
import org.apache.pdfbox.multipdf.LayerUtility;
|
||||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
import org.apache.pdfbox.pdmodel.PDPage;
|
import org.apache.pdfbox.pdmodel.PDPage;
|
||||||
@@ -40,7 +41,7 @@ public class ToSinglePageController {
|
|||||||
throws IOException {
|
throws IOException {
|
||||||
|
|
||||||
// Load the source document
|
// Load the source document
|
||||||
PDDocument sourceDocument = PDDocument.load(request.getFileInput().getInputStream());
|
PDDocument sourceDocument = Loader.loadPDF(request.getFileInput().getBytes());
|
||||||
|
|
||||||
// Calculate total height and max width
|
// Calculate total height and max width
|
||||||
float totalHeight = 0;
|
float totalHeight = 0;
|
||||||
|
|||||||
@@ -10,9 +10,13 @@ import org.springframework.http.HttpStatus;
|
|||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.core.session.SessionInformation;
|
||||||
|
import org.springframework.security.core.session.SessionRegistry;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
|
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
import org.springframework.ui.Model;
|
import org.springframework.ui.Model;
|
||||||
|
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||||
import org.springframework.web.bind.annotation.PathVariable;
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
@@ -20,13 +24,17 @@ import org.springframework.web.bind.annotation.RequestParam;
|
|||||||
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
|
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
|
||||||
import org.springframework.web.servlet.view.RedirectView;
|
import org.springframework.web.servlet.view.RedirectView;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import stirling.software.SPDF.config.security.UserService;
|
import stirling.software.SPDF.config.security.UserService;
|
||||||
import stirling.software.SPDF.model.Role;
|
import stirling.software.SPDF.model.Role;
|
||||||
import stirling.software.SPDF.model.User;
|
import stirling.software.SPDF.model.User;
|
||||||
|
import stirling.software.SPDF.model.api.user.UsernameAndPass;
|
||||||
|
|
||||||
@Controller
|
@Controller
|
||||||
|
@Tag(name = "User", description = "User APIs")
|
||||||
@RequestMapping("/api/v1/user")
|
@RequestMapping("/api/v1/user")
|
||||||
public class UserController {
|
public class UserController {
|
||||||
|
|
||||||
@@ -34,61 +42,16 @@ public class UserController {
|
|||||||
|
|
||||||
@PreAuthorize("!hasAuthority('ROLE_DEMO_USER')")
|
@PreAuthorize("!hasAuthority('ROLE_DEMO_USER')")
|
||||||
@PostMapping("/register")
|
@PostMapping("/register")
|
||||||
public String register(
|
public String register(@ModelAttribute UsernameAndPass requestModel, Model model) {
|
||||||
@RequestParam String username, @RequestParam String password, Model model) {
|
if (userService.usernameExists(requestModel.getUsername())) {
|
||||||
if (userService.usernameExists(username)) {
|
|
||||||
model.addAttribute("error", "Username already exists");
|
model.addAttribute("error", "Username already exists");
|
||||||
return "register";
|
return "register";
|
||||||
}
|
}
|
||||||
|
|
||||||
userService.saveUser(username, password);
|
userService.saveUser(requestModel.getUsername(), requestModel.getPassword());
|
||||||
return "redirect:/login?registered=true";
|
return "redirect:/login?registered=true";
|
||||||
}
|
}
|
||||||
|
|
||||||
@PreAuthorize("!hasAuthority('ROLE_DEMO_USER')")
|
|
||||||
@PostMapping("/change-username-and-password")
|
|
||||||
public RedirectView changeUsernameAndPassword(
|
|
||||||
Principal principal,
|
|
||||||
@RequestParam String currentPassword,
|
|
||||||
@RequestParam String newUsername,
|
|
||||||
@RequestParam String newPassword,
|
|
||||||
HttpServletRequest request,
|
|
||||||
HttpServletResponse response,
|
|
||||||
RedirectAttributes redirectAttributes) {
|
|
||||||
if (principal == null) {
|
|
||||||
return new RedirectView("/change-creds?messageType=notAuthenticated");
|
|
||||||
}
|
|
||||||
|
|
||||||
Optional<User> userOpt = userService.findByUsername(principal.getName());
|
|
||||||
|
|
||||||
if (userOpt == null || userOpt.isEmpty()) {
|
|
||||||
return new RedirectView("/change-creds?messageType=userNotFound");
|
|
||||||
}
|
|
||||||
|
|
||||||
User user = userOpt.get();
|
|
||||||
|
|
||||||
if (!userService.isPasswordCorrect(user, currentPassword)) {
|
|
||||||
return new RedirectView("/change-creds?messageType=incorrectPassword");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!user.getUsername().equals(newUsername) && userService.usernameExists(newUsername)) {
|
|
||||||
return new RedirectView("/change-creds?messageType=usernameExists");
|
|
||||||
}
|
|
||||||
|
|
||||||
userService.changePassword(user, newPassword);
|
|
||||||
if (newUsername != null
|
|
||||||
&& newUsername.length() > 0
|
|
||||||
&& !user.getUsername().equals(newUsername)) {
|
|
||||||
userService.changeUsername(user, newUsername);
|
|
||||||
}
|
|
||||||
userService.changeFirstUse(user, false);
|
|
||||||
|
|
||||||
// Logout using Spring's utility
|
|
||||||
new SecurityContextLogoutHandler().logout(request, response, null);
|
|
||||||
|
|
||||||
return new RedirectView("/login?messageType=credsUpdated");
|
|
||||||
}
|
|
||||||
|
|
||||||
@PreAuthorize("!hasAuthority('ROLE_DEMO_USER')")
|
@PreAuthorize("!hasAuthority('ROLE_DEMO_USER')")
|
||||||
@PostMapping("/change-username")
|
@PostMapping("/change-username")
|
||||||
public RedirectView changeUsername(
|
public RedirectView changeUsername(
|
||||||
@@ -128,6 +91,39 @@ public class UserController {
|
|||||||
return new RedirectView("/login?messageType=credsUpdated");
|
return new RedirectView("/login?messageType=credsUpdated");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PreAuthorize("!hasAuthority('ROLE_DEMO_USER')")
|
||||||
|
@PostMapping("/change-password-on-login")
|
||||||
|
public RedirectView changePasswordOnLogin(
|
||||||
|
Principal principal,
|
||||||
|
@RequestParam String currentPassword,
|
||||||
|
@RequestParam String newPassword,
|
||||||
|
HttpServletRequest request,
|
||||||
|
HttpServletResponse response,
|
||||||
|
RedirectAttributes redirectAttributes) {
|
||||||
|
if (principal == null) {
|
||||||
|
return new RedirectView("/change-creds?messageType=notAuthenticated");
|
||||||
|
}
|
||||||
|
|
||||||
|
Optional<User> userOpt = userService.findByUsername(principal.getName());
|
||||||
|
|
||||||
|
if (userOpt == null || userOpt.isEmpty()) {
|
||||||
|
return new RedirectView("/change-creds?messageType=userNotFound");
|
||||||
|
}
|
||||||
|
|
||||||
|
User user = userOpt.get();
|
||||||
|
|
||||||
|
if (!userService.isPasswordCorrect(user, currentPassword)) {
|
||||||
|
return new RedirectView("/change-creds?messageType=incorrectPassword");
|
||||||
|
}
|
||||||
|
|
||||||
|
userService.changePassword(user, newPassword);
|
||||||
|
userService.changeFirstUse(user, false);
|
||||||
|
// Logout using Spring's utility
|
||||||
|
new SecurityContextLogoutHandler().logout(request, response, null);
|
||||||
|
|
||||||
|
return new RedirectView("/login?messageType=credsUpdated");
|
||||||
|
}
|
||||||
|
|
||||||
@PreAuthorize("!hasAuthority('ROLE_DEMO_USER')")
|
@PreAuthorize("!hasAuthority('ROLE_DEMO_USER')")
|
||||||
@PostMapping("/change-password")
|
@PostMapping("/change-password")
|
||||||
public RedirectView changePassword(
|
public RedirectView changePassword(
|
||||||
@@ -211,18 +207,38 @@ public class UserController {
|
|||||||
|
|
||||||
@PreAuthorize("hasRole('ROLE_ADMIN')")
|
@PreAuthorize("hasRole('ROLE_ADMIN')")
|
||||||
@PostMapping("/admin/deleteUser/{username}")
|
@PostMapping("/admin/deleteUser/{username}")
|
||||||
public String deleteUser(@PathVariable String username, Authentication authentication) {
|
public RedirectView deleteUser(@PathVariable String username, Authentication authentication) {
|
||||||
|
|
||||||
|
if (!userService.usernameExists(username)) {
|
||||||
|
return new RedirectView("/addUsers?messageType=deleteUsernameExists");
|
||||||
|
}
|
||||||
|
|
||||||
// Get the currently authenticated username
|
// Get the currently authenticated username
|
||||||
String currentUsername = authentication.getName();
|
String currentUsername = authentication.getName();
|
||||||
|
|
||||||
// Check if the provided username matches the current session's username
|
// Check if the provided username matches the current session's username
|
||||||
if (currentUsername.equals(username)) {
|
if (currentUsername.equals(username)) {
|
||||||
throw new IllegalArgumentException("Cannot delete currently logined in user.");
|
return new RedirectView("/addUsers?messageType=deleteCurrentUser");
|
||||||
}
|
}
|
||||||
|
invalidateUserSessions(username);
|
||||||
userService.deleteUser(username);
|
userService.deleteUser(username);
|
||||||
return "redirect:/addUsers";
|
return new RedirectView("/addUsers");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Autowired private SessionRegistry sessionRegistry;
|
||||||
|
|
||||||
|
private void invalidateUserSessions(String username) {
|
||||||
|
for (Object principal : sessionRegistry.getAllPrincipals()) {
|
||||||
|
if (principal instanceof UserDetails) {
|
||||||
|
UserDetails userDetails = (UserDetails) principal;
|
||||||
|
if (userDetails.getUsername().equals(username)) {
|
||||||
|
for (SessionInformation session :
|
||||||
|
sessionRegistry.getAllSessions(principal, false)) {
|
||||||
|
session.expireNow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@PreAuthorize("!hasAuthority('ROLE_DEMO_USER')")
|
@PreAuthorize("!hasAuthority('ROLE_DEMO_USER')")
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
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.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import io.github.pixee.security.Filenames;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
|
||||||
|
import stirling.software.SPDF.model.api.GeneralFile;
|
||||||
|
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 {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
@Qualifier("bookAndHtmlFormatsInstalled")
|
||||||
|
private boolean 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 avaiable");
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
String outputFilename =
|
||||||
|
originalFilename.replaceFirst("[.][^.]+$", "")
|
||||||
|
+ ".pdf"; // Remove file extension and append .pdf
|
||||||
|
|
||||||
|
return WebResponseUtils.bytesToWebResponse(pdfBytes, outputFilename);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
package stirling.software.SPDF.controller.api.converters;
|
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.http.ResponseEntity;
|
||||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
@@ -7,10 +9,11 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import io.github.pixee.security.Filenames;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
|
||||||
import stirling.software.SPDF.model.api.GeneralFile;
|
import stirling.software.SPDF.model.api.converters.HTMLToPdfRequest;
|
||||||
import stirling.software.SPDF.utils.FileToPdf;
|
import stirling.software.SPDF.utils.FileToPdf;
|
||||||
import stirling.software.SPDF.utils.WebResponseUtils;
|
import stirling.software.SPDF.utils.WebResponseUtils;
|
||||||
|
|
||||||
@@ -19,12 +22,17 @@ import stirling.software.SPDF.utils.WebResponseUtils;
|
|||||||
@RequestMapping("/api/v1/convert")
|
@RequestMapping("/api/v1/convert")
|
||||||
public class ConvertHtmlToPDF {
|
public class ConvertHtmlToPDF {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
@Qualifier("bookAndHtmlFormatsInstalled")
|
||||||
|
private boolean bookAndHtmlFormatsInstalled;
|
||||||
|
|
||||||
@PostMapping(consumes = "multipart/form-data", value = "/html/pdf")
|
@PostMapping(consumes = "multipart/form-data", value = "/html/pdf")
|
||||||
@Operation(
|
@Operation(
|
||||||
summary = "Convert an HTML or ZIP (containing HTML and CSS) to PDF",
|
summary = "Convert an HTML or ZIP (containing HTML and CSS) to PDF",
|
||||||
description =
|
description =
|
||||||
"This endpoint takes an HTML or ZIP file input and converts it to a PDF format.")
|
"This endpoint takes an HTML or ZIP file input and converts it to a PDF format.")
|
||||||
public ResponseEntity<byte[]> HtmlToPdf(@ModelAttribute GeneralFile request) throws Exception {
|
public ResponseEntity<byte[]> HtmlToPdf(@ModelAttribute HTMLToPdfRequest request)
|
||||||
|
throws Exception {
|
||||||
MultipartFile fileInput = request.getFileInput();
|
MultipartFile fileInput = request.getFileInput();
|
||||||
|
|
||||||
if (fileInput == null) {
|
if (fileInput == null) {
|
||||||
@@ -32,12 +40,17 @@ public class ConvertHtmlToPDF {
|
|||||||
"Please provide an HTML or ZIP file for conversion.");
|
"Please provide an HTML or ZIP file for conversion.");
|
||||||
}
|
}
|
||||||
|
|
||||||
String originalFilename = fileInput.getOriginalFilename();
|
String originalFilename = Filenames.toSimpleFileName(fileInput.getOriginalFilename());
|
||||||
if (originalFilename == null
|
if (originalFilename == null
|
||||||
|| (!originalFilename.endsWith(".html") && !originalFilename.endsWith(".zip"))) {
|
|| (!originalFilename.endsWith(".html") && !originalFilename.endsWith(".zip"))) {
|
||||||
throw new IllegalArgumentException("File must be either .html or .zip format.");
|
throw new IllegalArgumentException("File must be either .html or .zip format.");
|
||||||
}
|
}
|
||||||
byte[] pdfBytes = FileToPdf.convertHtmlToPdf(fileInput.getBytes(), originalFilename);
|
byte[] pdfBytes =
|
||||||
|
FileToPdf.convertHtmlToPdf(
|
||||||
|
request,
|
||||||
|
fileInput.getBytes(),
|
||||||
|
originalFilename,
|
||||||
|
bookAndHtmlFormatsInstalled);
|
||||||
|
|
||||||
String outputFilename =
|
String outputFilename =
|
||||||
originalFilename.replaceFirst("[.][^.]+$", "")
|
originalFilename.replaceFirst("[.][^.]+$", "")
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import io.github.pixee.security.Filenames;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
|
||||||
@@ -54,9 +55,11 @@ public class ConvertImgPDFController {
|
|||||||
colorTypeResult = ImageType.BINARY;
|
colorTypeResult = ImageType.BINARY;
|
||||||
}
|
}
|
||||||
// returns bytes for image
|
// returns bytes for image
|
||||||
boolean singleImage = singleOrMultiple.equals("single");
|
boolean singleImage = "single".equals(singleOrMultiple);
|
||||||
byte[] result = null;
|
byte[] result = null;
|
||||||
String filename = file.getOriginalFilename().replaceFirst("[.][^.]+$", "");
|
String filename =
|
||||||
|
Filenames.toSimpleFileName(file.getOriginalFilename())
|
||||||
|
.replaceFirst("[.][^.]+$", "");
|
||||||
try {
|
try {
|
||||||
result =
|
result =
|
||||||
PdfUtils.convertFromPdf(
|
PdfUtils.convertFromPdf(
|
||||||
@@ -96,7 +99,7 @@ public class ConvertImgPDFController {
|
|||||||
@Operation(
|
@Operation(
|
||||||
summary = "Convert images to a PDF file",
|
summary = "Convert images to a PDF file",
|
||||||
description =
|
description =
|
||||||
"This endpoint converts one or more images to a PDF file. Users can specify whether to stretch the images to fit the PDF page, and whether to automatically rotate the images. Input:Image Output:PDF Type:SISO?")
|
"This endpoint converts one or more images to a PDF file. Users can specify whether to stretch the images to fit the PDF page, and whether to automatically rotate the images. Input:Image Output:PDF Type:MISO")
|
||||||
public ResponseEntity<byte[]> convertToPdf(@ModelAttribute ConvertToPdfRequest request)
|
public ResponseEntity<byte[]> convertToPdf(@ModelAttribute ConvertToPdfRequest request)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
MultipartFile[] file = request.getFileInput();
|
MultipartFile[] file = request.getFileInput();
|
||||||
@@ -113,6 +116,6 @@ public class ConvertImgPDFController {
|
|||||||
|
|
||||||
private String getMediaType(String imageFormat) {
|
private String getMediaType(String imageFormat) {
|
||||||
String mimeType = URLConnection.guessContentTypeFromName("." + imageFormat);
|
String mimeType = URLConnection.guessContentTypeFromName("." + imageFormat);
|
||||||
return mimeType.equals("null") ? "application/octet-stream" : mimeType;
|
return "null".equals(mimeType) ? "application/octet-stream" : mimeType;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,17 @@
|
|||||||
package stirling.software.SPDF.controller.api.converters;
|
package stirling.software.SPDF.controller.api.converters;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.commonmark.Extension;
|
||||||
|
import org.commonmark.ext.gfm.tables.TableBlock;
|
||||||
|
import org.commonmark.ext.gfm.tables.TablesExtension;
|
||||||
import org.commonmark.node.Node;
|
import org.commonmark.node.Node;
|
||||||
import org.commonmark.parser.Parser;
|
import org.commonmark.parser.Parser;
|
||||||
|
import org.commonmark.renderer.html.AttributeProvider;
|
||||||
import org.commonmark.renderer.html.HtmlRenderer;
|
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.http.ResponseEntity;
|
||||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
@@ -10,6 +19,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import io.github.pixee.security.Filenames;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
|
||||||
@@ -22,11 +32,15 @@ import stirling.software.SPDF.utils.WebResponseUtils;
|
|||||||
@RequestMapping("/api/v1/convert")
|
@RequestMapping("/api/v1/convert")
|
||||||
public class ConvertMarkdownToPdf {
|
public class ConvertMarkdownToPdf {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
@Qualifier("bookAndHtmlFormatsInstalled")
|
||||||
|
private boolean bookAndHtmlFormatsInstalled;
|
||||||
|
|
||||||
@PostMapping(consumes = "multipart/form-data", value = "/markdown/pdf")
|
@PostMapping(consumes = "multipart/form-data", value = "/markdown/pdf")
|
||||||
@Operation(
|
@Operation(
|
||||||
summary = "Convert a Markdown file to PDF",
|
summary = "Convert a Markdown file to PDF",
|
||||||
description =
|
description =
|
||||||
"This endpoint takes a Markdown file input, converts it to HTML, and then to PDF format.")
|
"This endpoint takes a Markdown file input, converts it to HTML, and then to PDF format. Input:MARKDOWN Output:PDF Type:SISO")
|
||||||
public ResponseEntity<byte[]> markdownToPdf(@ModelAttribute GeneralFile request)
|
public ResponseEntity<byte[]> markdownToPdf(@ModelAttribute GeneralFile request)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
MultipartFile fileInput = request.getFileInput();
|
MultipartFile fileInput = request.getFileInput();
|
||||||
@@ -35,18 +49,30 @@ public class ConvertMarkdownToPdf {
|
|||||||
throw new IllegalArgumentException("Please provide a Markdown file for conversion.");
|
throw new IllegalArgumentException("Please provide a Markdown file for conversion.");
|
||||||
}
|
}
|
||||||
|
|
||||||
String originalFilename = fileInput.getOriginalFilename();
|
String originalFilename = Filenames.toSimpleFileName(fileInput.getOriginalFilename());
|
||||||
if (originalFilename == null || !originalFilename.endsWith(".md")) {
|
if (originalFilename == null || !originalFilename.endsWith(".md")) {
|
||||||
throw new IllegalArgumentException("File must be in .md format.");
|
throw new IllegalArgumentException("File must be in .md format.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert Markdown to HTML using CommonMark
|
// Convert Markdown to HTML using CommonMark
|
||||||
Parser parser = Parser.builder().build();
|
List<Extension> extensions = List.of(TablesExtension.create());
|
||||||
|
Parser parser = Parser.builder().extensions(extensions).build();
|
||||||
|
|
||||||
Node document = parser.parse(new String(fileInput.getBytes()));
|
Node document = parser.parse(new String(fileInput.getBytes()));
|
||||||
HtmlRenderer renderer = HtmlRenderer.builder().build();
|
HtmlRenderer renderer =
|
||||||
|
HtmlRenderer.builder()
|
||||||
|
.attributeProviderFactory(context -> new TableAttributeProvider())
|
||||||
|
.extensions(extensions)
|
||||||
|
.build();
|
||||||
|
|
||||||
String htmlContent = renderer.render(document);
|
String htmlContent = renderer.render(document);
|
||||||
|
|
||||||
byte[] pdfBytes = FileToPdf.convertHtmlToPdf(htmlContent.getBytes(), "converted.html");
|
byte[] pdfBytes =
|
||||||
|
FileToPdf.convertHtmlToPdf(
|
||||||
|
null,
|
||||||
|
htmlContent.getBytes(),
|
||||||
|
"converted.html",
|
||||||
|
bookAndHtmlFormatsInstalled);
|
||||||
|
|
||||||
String outputFilename =
|
String outputFilename =
|
||||||
originalFilename.replaceFirst("[.][^.]+$", "")
|
originalFilename.replaceFirst("[.][^.]+$", "")
|
||||||
@@ -54,3 +80,12 @@ public class ConvertMarkdownToPdf {
|
|||||||
return WebResponseUtils.bytesToWebResponse(pdfBytes, outputFilename);
|
return WebResponseUtils.bytesToWebResponse(pdfBytes, outputFilename);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class TableAttributeProvider implements AttributeProvider {
|
||||||
|
@Override
|
||||||
|
public void setAttributes(Node node, String tagName, Map<String, String> attributes) {
|
||||||
|
if (node instanceof TableBlock) {
|
||||||
|
attributes.put("class", "table table-striped");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import io.github.pixee.security.Filenames;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
|
||||||
@@ -31,7 +32,7 @@ public class ConvertOfficeController {
|
|||||||
|
|
||||||
public byte[] convertToPdf(MultipartFile inputFile) throws IOException, InterruptedException {
|
public byte[] convertToPdf(MultipartFile inputFile) throws IOException, InterruptedException {
|
||||||
// Check for valid file extension
|
// Check for valid file extension
|
||||||
String originalFilename = inputFile.getOriginalFilename();
|
String originalFilename = Filenames.toSimpleFileName(inputFile.getOriginalFilename());
|
||||||
if (originalFilename == null
|
if (originalFilename == null
|
||||||
|| !isValidFileExtension(FilenameUtils.getExtension(originalFilename))) {
|
|| !isValidFileExtension(FilenameUtils.getExtension(originalFilename))) {
|
||||||
throw new IllegalArgumentException("Invalid file extension");
|
throw new IllegalArgumentException("Invalid file extension");
|
||||||
@@ -89,7 +90,8 @@ public class ConvertOfficeController {
|
|||||||
byte[] pdfByteArray = convertToPdf(inputFile);
|
byte[] pdfByteArray = convertToPdf(inputFile);
|
||||||
return WebResponseUtils.bytesToWebResponse(
|
return WebResponseUtils.bytesToWebResponse(
|
||||||
pdfByteArray,
|
pdfByteArray,
|
||||||
inputFile.getOriginalFilename().replaceFirst("[.][^.]+$", "")
|
Filenames.toSimpleFileName(inputFile.getOriginalFilename())
|
||||||
|
.replaceFirst("[.][^.]+$", "")
|
||||||
+ "_convertedToPDF.pdf");
|
+ "_convertedToPDF.pdf");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,103 @@
|
|||||||
|
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.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.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import io.github.pixee.security.Filenames;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
|
||||||
|
import stirling.software.SPDF.model.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 {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
@Qualifier("bookAndHtmlFormatsInstalled")
|
||||||
|
private boolean 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 avaiable");
|
||||||
|
}
|
||||||
|
|
||||||
|
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_",
|
||||||
|
"." + outputFormat); // Use the output format for the file extension
|
||||||
|
Path tempInputFile = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Create temp input file from the provided PDF
|
||||||
|
tempInputFile = Files.createTempFile("input_", ".pdf"); // Assuming input is always 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("[.][^.]+$", "")
|
||||||
|
+ "."
|
||||||
|
+ outputFormat; // Remove file extension and append .pdf
|
||||||
|
|
||||||
|
return WebResponseUtils.bytesToWebResponse(outputFileBytes, outputFilename);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,10 @@ package stirling.software.SPDF.controller.api.converters;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.apache.pdfbox.Loader;
|
||||||
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
|
import org.apache.pdfbox.text.PDFTextStripper;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
@@ -9,6 +13,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import io.github.pixee.security.Filenames;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
|
||||||
@@ -17,6 +22,7 @@ import stirling.software.SPDF.model.api.converters.PdfToPresentationRequest;
|
|||||||
import stirling.software.SPDF.model.api.converters.PdfToTextOrRTFRequest;
|
import stirling.software.SPDF.model.api.converters.PdfToTextOrRTFRequest;
|
||||||
import stirling.software.SPDF.model.api.converters.PdfToWordRequest;
|
import stirling.software.SPDF.model.api.converters.PdfToWordRequest;
|
||||||
import stirling.software.SPDF.utils.PDFToFile;
|
import stirling.software.SPDF.utils.PDFToFile;
|
||||||
|
import stirling.software.SPDF.utils.WebResponseUtils;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/api/v1/convert")
|
@RequestMapping("/api/v1/convert")
|
||||||
@@ -59,9 +65,21 @@ public class ConvertPDFToOffice {
|
|||||||
throws IOException, InterruptedException {
|
throws IOException, InterruptedException {
|
||||||
MultipartFile inputFile = request.getFileInput();
|
MultipartFile inputFile = request.getFileInput();
|
||||||
String outputFormat = request.getOutputFormat();
|
String outputFormat = request.getOutputFormat();
|
||||||
|
if ("txt".equals(request.getOutputFormat())) {
|
||||||
PDFToFile pdfToFile = new PDFToFile();
|
try (PDDocument document = Loader.loadPDF(inputFile.getBytes())) {
|
||||||
return pdfToFile.processPdfToOfficeFormat(inputFile, outputFormat, "writer_pdf_import");
|
PDFTextStripper stripper = new PDFTextStripper();
|
||||||
|
String text = stripper.getText(document);
|
||||||
|
return WebResponseUtils.bytesToWebResponse(
|
||||||
|
text.getBytes(),
|
||||||
|
Filenames.toSimpleFileName(inputFile.getOriginalFilename())
|
||||||
|
.replaceFirst("[.][^.]+$", "")
|
||||||
|
+ ".txt",
|
||||||
|
MediaType.TEXT_PLAIN);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
PDFToFile pdfToFile = new PDFToFile();
|
||||||
|
return pdfToFile.processPdfToOfficeFormat(inputFile, outputFormat, "writer_pdf_import");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping(consumes = "multipart/form-data", value = "/pdf/word")
|
@PostMapping(consumes = "multipart/form-data", value = "/pdf/word")
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import io.github.pixee.security.Filenames;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
|
||||||
@@ -63,7 +64,9 @@ public class ConvertPDFToPDFA {
|
|||||||
|
|
||||||
// Return the optimized PDF as a response
|
// Return the optimized PDF as a response
|
||||||
String outputFilename =
|
String outputFilename =
|
||||||
inputFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_PDFA.pdf";
|
Filenames.toSimpleFileName(inputFile.getOriginalFilename())
|
||||||
|
.replaceFirst("[.][^.]+$", "")
|
||||||
|
+ "_PDFA.pdf";
|
||||||
return WebResponseUtils.bytesToWebResponse(pdfBytes, outputFilename);
|
return WebResponseUtils.bytesToWebResponse(pdfBytes, outputFilename);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import java.nio.file.Path;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
@@ -26,6 +28,10 @@ import stirling.software.SPDF.utils.WebResponseUtils;
|
|||||||
@RequestMapping("/api/v1/convert")
|
@RequestMapping("/api/v1/convert")
|
||||||
public class ConvertWebsiteToPDF {
|
public class ConvertWebsiteToPDF {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
@Qualifier("bookAndHtmlFormatsInstalled")
|
||||||
|
private boolean bookAndHtmlFormatsInstalled;
|
||||||
|
|
||||||
@PostMapping(consumes = "multipart/form-data", value = "/url/pdf")
|
@PostMapping(consumes = "multipart/form-data", value = "/url/pdf")
|
||||||
@Operation(
|
@Operation(
|
||||||
summary = "Convert a URL to a PDF",
|
summary = "Convert a URL to a PDF",
|
||||||
@@ -47,7 +53,11 @@ public class ConvertWebsiteToPDF {
|
|||||||
|
|
||||||
// Prepare the OCRmyPDF command
|
// Prepare the OCRmyPDF command
|
||||||
List<String> command = new ArrayList<>();
|
List<String> command = new ArrayList<>();
|
||||||
command.add("weasyprint");
|
if (!bookAndHtmlFormatsInstalled) {
|
||||||
|
command.add("weasyprint");
|
||||||
|
} else {
|
||||||
|
command.add("wkhtmltopdf");
|
||||||
|
}
|
||||||
command.add(URL);
|
command.add(URL);
|
||||||
command.add(tempOutputFile.toString());
|
command.add(tempOutputFile.toString());
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
package stirling.software.SPDF.controller.api.converters;
|
package stirling.software.SPDF.controller.api.converters;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.StringWriter;
|
import java.io.StringWriter;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.pdfbox.Loader;
|
||||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
import org.apache.pdfbox.pdmodel.PDPage;
|
import org.apache.pdfbox.pdmodel.PDPage;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@@ -44,8 +44,7 @@ public class ExtractController {
|
|||||||
ArrayList<String> tableData = new ArrayList<>();
|
ArrayList<String> tableData = new ArrayList<>();
|
||||||
int columnsCount = 0;
|
int columnsCount = 0;
|
||||||
|
|
||||||
try (PDDocument document =
|
try (PDDocument document = Loader.loadPDF(form.getFileInput().getBytes())) {
|
||||||
PDDocument.load(new ByteArrayInputStream(form.getFileInput().getBytes()))) {
|
|
||||||
final double res = 72; // PDF units are at 72 DPI
|
final double res = 72; // PDF units are at 72 DPI
|
||||||
PDFTableStripper stripper = new PDFTableStripper();
|
PDFTableStripper stripper = new PDFTableStripper();
|
||||||
PDPage pdPage = document.getPage(form.getPageId() - 1);
|
PDPage pdPage = document.getPage(form.getPageId() - 1);
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package stirling.software.SPDF.controller.api.filters;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.apache.pdfbox.Loader;
|
||||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
import org.apache.pdfbox.pdmodel.PDPage;
|
import org.apache.pdfbox.pdmodel.PDPage;
|
||||||
import org.apache.pdfbox.pdmodel.common.PDRectangle;
|
import org.apache.pdfbox.pdmodel.common.PDRectangle;
|
||||||
@@ -12,6 +13,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import io.github.pixee.security.Filenames;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
|
||||||
@@ -39,10 +41,10 @@ public class FilterController {
|
|||||||
String text = request.getText();
|
String text = request.getText();
|
||||||
String pageNumber = request.getPageNumbers();
|
String pageNumber = request.getPageNumbers();
|
||||||
|
|
||||||
PDDocument pdfDocument = PDDocument.load(inputFile.getInputStream());
|
PDDocument pdfDocument = Loader.loadPDF(inputFile.getBytes());
|
||||||
if (PdfUtils.hasText(pdfDocument, pageNumber, text))
|
if (PdfUtils.hasText(pdfDocument, pageNumber, text))
|
||||||
return WebResponseUtils.pdfDocToWebResponse(
|
return WebResponseUtils.pdfDocToWebResponse(
|
||||||
pdfDocument, inputFile.getOriginalFilename());
|
pdfDocument, Filenames.toSimpleFileName(inputFile.getOriginalFilename()));
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,10 +58,10 @@ public class FilterController {
|
|||||||
MultipartFile inputFile = request.getFileInput();
|
MultipartFile inputFile = request.getFileInput();
|
||||||
String pageNumber = request.getPageNumbers();
|
String pageNumber = request.getPageNumbers();
|
||||||
|
|
||||||
PDDocument pdfDocument = PDDocument.load(inputFile.getInputStream());
|
PDDocument pdfDocument = Loader.loadPDF(inputFile.getBytes());
|
||||||
if (PdfUtils.hasImages(pdfDocument, pageNumber))
|
if (PdfUtils.hasImages(pdfDocument, pageNumber))
|
||||||
return WebResponseUtils.pdfDocToWebResponse(
|
return WebResponseUtils.pdfDocToWebResponse(
|
||||||
pdfDocument, inputFile.getOriginalFilename());
|
pdfDocument, Filenames.toSimpleFileName(inputFile.getOriginalFilename()));
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,7 +75,7 @@ public class FilterController {
|
|||||||
String pageCount = request.getPageCount();
|
String pageCount = request.getPageCount();
|
||||||
String comparator = request.getComparator();
|
String comparator = request.getComparator();
|
||||||
// Load the PDF
|
// Load the PDF
|
||||||
PDDocument document = PDDocument.load(inputFile.getInputStream());
|
PDDocument document = Loader.loadPDF(inputFile.getBytes());
|
||||||
int actualPageCount = document.getNumberOfPages();
|
int actualPageCount = document.getNumberOfPages();
|
||||||
|
|
||||||
boolean valid = false;
|
boolean valid = false;
|
||||||
@@ -107,7 +109,7 @@ public class FilterController {
|
|||||||
String comparator = request.getComparator();
|
String comparator = request.getComparator();
|
||||||
|
|
||||||
// Load the PDF
|
// Load the PDF
|
||||||
PDDocument document = PDDocument.load(inputFile.getInputStream());
|
PDDocument document = Loader.loadPDF(inputFile.getBytes());
|
||||||
|
|
||||||
PDPage firstPage = document.getPage(0);
|
PDPage firstPage = document.getPage(0);
|
||||||
PDRectangle actualPageSize = firstPage.getMediaBox();
|
PDRectangle actualPageSize = firstPage.getMediaBox();
|
||||||
@@ -183,7 +185,7 @@ public class FilterController {
|
|||||||
String comparator = request.getComparator();
|
String comparator = request.getComparator();
|
||||||
|
|
||||||
// Load the PDF
|
// Load the PDF
|
||||||
PDDocument document = PDDocument.load(inputFile.getInputStream());
|
PDDocument document = Loader.loadPDF(inputFile.getBytes());
|
||||||
|
|
||||||
// Get the rotation of the first page
|
// Get the rotation of the first page
|
||||||
PDPage firstPage = document.getPage(0);
|
PDPage firstPage = document.getPage(0);
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.pdfbox.Loader;
|
||||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
import org.apache.pdfbox.text.PDFTextStripper;
|
import org.apache.pdfbox.text.PDFTextStripper;
|
||||||
import org.apache.pdfbox.text.TextPosition;
|
import org.apache.pdfbox.text.TextPosition;
|
||||||
@@ -17,6 +18,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import io.github.pixee.security.Filenames;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
|
||||||
@@ -43,7 +45,7 @@ public class AutoRenameController {
|
|||||||
MultipartFile file = request.getFileInput();
|
MultipartFile file = request.getFileInput();
|
||||||
Boolean useFirstTextAsFallback = request.isUseFirstTextAsFallback();
|
Boolean useFirstTextAsFallback = request.isUseFirstTextAsFallback();
|
||||||
|
|
||||||
PDDocument document = PDDocument.load(file.getInputStream());
|
PDDocument document = Loader.loadPDF(file.getBytes());
|
||||||
PDFTextStripper reader =
|
PDFTextStripper reader =
|
||||||
new PDFTextStripper() {
|
new PDFTextStripper() {
|
||||||
class LineInfo {
|
class LineInfo {
|
||||||
@@ -132,7 +134,8 @@ public class AutoRenameController {
|
|||||||
return WebResponseUtils.pdfDocToWebResponse(document, header + ".pdf");
|
return WebResponseUtils.pdfDocToWebResponse(document, header + ".pdf");
|
||||||
} else {
|
} else {
|
||||||
logger.info("File has no good title to be found");
|
logger.info("File has no good title to be found");
|
||||||
return WebResponseUtils.pdfDocToWebResponse(document, file.getOriginalFilename());
|
return WebResponseUtils.pdfDocToWebResponse(
|
||||||
|
document, Filenames.toSimpleFileName(file.getOriginalFilename()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import java.awt.image.DataBufferByte;
|
|||||||
import java.awt.image.DataBufferInt;
|
import java.awt.image.DataBufferInt;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -13,6 +12,7 @@ import java.util.List;
|
|||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
import java.util.zip.ZipOutputStream;
|
import java.util.zip.ZipOutputStream;
|
||||||
|
|
||||||
|
import org.apache.pdfbox.Loader;
|
||||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
import org.apache.pdfbox.rendering.PDFRenderer;
|
import org.apache.pdfbox.rendering.PDFRenderer;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
@@ -31,6 +31,7 @@ import com.google.zxing.PlanarYUVLuminanceSource;
|
|||||||
import com.google.zxing.Result;
|
import com.google.zxing.Result;
|
||||||
import com.google.zxing.common.HybridBinarizer;
|
import com.google.zxing.common.HybridBinarizer;
|
||||||
|
|
||||||
|
import io.github.pixee.security.Filenames;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
|
||||||
@@ -43,6 +44,7 @@ import stirling.software.SPDF.utils.WebResponseUtils;
|
|||||||
public class AutoSplitPdfController {
|
public class AutoSplitPdfController {
|
||||||
|
|
||||||
private static final String QR_CONTENT = "https://github.com/Stirling-Tools/Stirling-PDF";
|
private static final String QR_CONTENT = "https://github.com/Stirling-Tools/Stirling-PDF";
|
||||||
|
private static final String QR_CONTENT_OLD = "https://github.com/Frooodle/Stirling-PDF";
|
||||||
|
|
||||||
@PostMapping(value = "/auto-split-pdf", consumes = "multipart/form-data")
|
@PostMapping(value = "/auto-split-pdf", consumes = "multipart/form-data")
|
||||||
@Operation(
|
@Operation(
|
||||||
@@ -54,8 +56,7 @@ public class AutoSplitPdfController {
|
|||||||
MultipartFile file = request.getFileInput();
|
MultipartFile file = request.getFileInput();
|
||||||
boolean duplexMode = request.isDuplexMode();
|
boolean duplexMode = request.isDuplexMode();
|
||||||
|
|
||||||
InputStream inputStream = file.getInputStream();
|
PDDocument document = Loader.loadPDF(file.getBytes());
|
||||||
PDDocument document = PDDocument.load(inputStream);
|
|
||||||
PDFRenderer pdfRenderer = new PDFRenderer(document);
|
PDFRenderer pdfRenderer = new PDFRenderer(document);
|
||||||
|
|
||||||
List<PDDocument> splitDocuments = new ArrayList<>();
|
List<PDDocument> splitDocuments = new ArrayList<>();
|
||||||
@@ -64,12 +65,13 @@ public class AutoSplitPdfController {
|
|||||||
for (int page = 0; page < document.getNumberOfPages(); ++page) {
|
for (int page = 0; page < document.getNumberOfPages(); ++page) {
|
||||||
BufferedImage bim = pdfRenderer.renderImageWithDPI(page, 150);
|
BufferedImage bim = pdfRenderer.renderImageWithDPI(page, 150);
|
||||||
String result = decodeQRCode(bim);
|
String result = decodeQRCode(bim);
|
||||||
|
if ((QR_CONTENT.equals(result) || QR_CONTENT_OLD.equals(result)) && page != 0) {
|
||||||
if (QR_CONTENT.equals(result) && page != 0) {
|
|
||||||
splitDocuments.add(new PDDocument());
|
splitDocuments.add(new PDDocument());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!splitDocuments.isEmpty() && !QR_CONTENT.equals(result)) {
|
if (!splitDocuments.isEmpty()
|
||||||
|
&& !QR_CONTENT.equals(result)
|
||||||
|
&& !QR_CONTENT_OLD.equals(result)) {
|
||||||
splitDocuments.get(splitDocuments.size() - 1).addPage(document.getPage(page));
|
splitDocuments.get(splitDocuments.size() - 1).addPage(document.getPage(page));
|
||||||
} else if (page == 0) {
|
} else if (page == 0) {
|
||||||
PDDocument firstDocument = new PDDocument();
|
PDDocument firstDocument = new PDDocument();
|
||||||
@@ -78,7 +80,7 @@ public class AutoSplitPdfController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If duplexMode is true and current page is a divider, then skip next page
|
// If duplexMode is true and current page is a divider, then skip next page
|
||||||
if (duplexMode && QR_CONTENT.equals(result)) {
|
if (duplexMode && (QR_CONTENT.equals(result) || QR_CONTENT_OLD.equals(result))) {
|
||||||
page++;
|
page++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -96,7 +98,9 @@ public class AutoSplitPdfController {
|
|||||||
document.close();
|
document.close();
|
||||||
|
|
||||||
Path zipFile = Files.createTempFile("split_documents", ".zip");
|
Path zipFile = Files.createTempFile("split_documents", ".zip");
|
||||||
String filename = file.getOriginalFilename().replaceFirst("[.][^.]+$", "");
|
String filename =
|
||||||
|
Filenames.toSimpleFileName(file.getOriginalFilename())
|
||||||
|
.replaceFirst("[.][^.]+$", "");
|
||||||
byte[] data;
|
byte[] data;
|
||||||
|
|
||||||
try (ZipOutputStream zipOut = new ZipOutputStream(Files.newOutputStream(zipFile))) {
|
try (ZipOutputStream zipOut = new ZipOutputStream(Files.newOutputStream(zipFile))) {
|
||||||
|
|||||||
@@ -2,22 +2,20 @@ package stirling.software.SPDF.controller.api.misc;
|
|||||||
|
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.IntStream;
|
import java.util.stream.IntStream;
|
||||||
|
|
||||||
import javax.imageio.ImageIO;
|
import org.apache.pdfbox.Loader;
|
||||||
|
|
||||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
import org.apache.pdfbox.pdmodel.PDPage;
|
import org.apache.pdfbox.pdmodel.PDPage;
|
||||||
import org.apache.pdfbox.pdmodel.PDPageTree;
|
import org.apache.pdfbox.pdmodel.PDPageTree;
|
||||||
import org.apache.pdfbox.rendering.PDFRenderer;
|
import org.apache.pdfbox.rendering.PDFRenderer;
|
||||||
import org.apache.pdfbox.text.PDFTextStripper;
|
import org.apache.pdfbox.text.PDFTextStripper;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||||
@@ -26,13 +24,12 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import io.github.pixee.security.Filenames;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
|
||||||
import stirling.software.SPDF.model.api.misc.RemoveBlankPagesRequest;
|
import stirling.software.SPDF.model.api.misc.RemoveBlankPagesRequest;
|
||||||
import stirling.software.SPDF.utils.PdfUtils;
|
import stirling.software.SPDF.utils.PdfUtils;
|
||||||
import stirling.software.SPDF.utils.ProcessExecutor;
|
|
||||||
import stirling.software.SPDF.utils.ProcessExecutor.ProcessExecutorResult;
|
|
||||||
import stirling.software.SPDF.utils.WebResponseUtils;
|
import stirling.software.SPDF.utils.WebResponseUtils;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@@ -40,6 +37,8 @@ import stirling.software.SPDF.utils.WebResponseUtils;
|
|||||||
@Tag(name = "Misc", description = "Miscellaneous APIs")
|
@Tag(name = "Misc", description = "Miscellaneous APIs")
|
||||||
public class BlankPageController {
|
public class BlankPageController {
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(BlankPageController.class);
|
||||||
|
|
||||||
@PostMapping(consumes = "multipart/form-data", value = "/remove-blanks")
|
@PostMapping(consumes = "multipart/form-data", value = "/remove-blanks")
|
||||||
@Operation(
|
@Operation(
|
||||||
summary = "Remove blank pages from a PDF file",
|
summary = "Remove blank pages from a PDF file",
|
||||||
@@ -53,7 +52,7 @@ public class BlankPageController {
|
|||||||
|
|
||||||
PDDocument document = null;
|
PDDocument document = null;
|
||||||
try {
|
try {
|
||||||
document = PDDocument.load(inputFile.getInputStream());
|
document = Loader.loadPDF(inputFile.getBytes());
|
||||||
PDPageTree pages = document.getDocumentCatalog().getPages();
|
PDPageTree pages = document.getDocumentCatalog().getPages();
|
||||||
PDFTextStripper textStripper = new PDFTextStripper();
|
PDFTextStripper textStripper = new PDFTextStripper();
|
||||||
|
|
||||||
@@ -62,56 +61,35 @@ public class BlankPageController {
|
|||||||
PDFRenderer pdfRenderer = new PDFRenderer(document);
|
PDFRenderer pdfRenderer = new PDFRenderer(document);
|
||||||
|
|
||||||
for (PDPage page : pages) {
|
for (PDPage page : pages) {
|
||||||
System.out.println("checking page " + pageIndex);
|
logger.info("checking page " + pageIndex);
|
||||||
textStripper.setStartPage(pageIndex + 1);
|
textStripper.setStartPage(pageIndex + 1);
|
||||||
textStripper.setEndPage(pageIndex + 1);
|
textStripper.setEndPage(pageIndex + 1);
|
||||||
String pageText = textStripper.getText(document);
|
String pageText = textStripper.getText(document);
|
||||||
boolean hasText = !pageText.trim().isEmpty();
|
boolean hasText = !pageText.trim().isEmpty();
|
||||||
|
|
||||||
|
Boolean blank = false;
|
||||||
if (hasText) {
|
if (hasText) {
|
||||||
pagesToKeepIndex.add(pageIndex);
|
logger.info("page " + pageIndex + " has text, not blank");
|
||||||
System.out.println("page " + pageIndex + " has text");
|
blank = false;
|
||||||
} else {
|
} else {
|
||||||
boolean hasImages = PdfUtils.hasImagesOnPage(page);
|
boolean hasImages = PdfUtils.hasImagesOnPage(page);
|
||||||
if (hasImages) {
|
if (hasImages) {
|
||||||
System.out.println("page " + pageIndex + " has image");
|
logger.info("page " + pageIndex + " has image, running blank detection");
|
||||||
|
|
||||||
Path tempFile = Files.createTempFile("image_", ".png");
|
|
||||||
|
|
||||||
// Render image and save as temp file
|
// Render image and save as temp file
|
||||||
BufferedImage image = pdfRenderer.renderImageWithDPI(pageIndex, 300);
|
BufferedImage image = pdfRenderer.renderImageWithDPI(pageIndex, 30);
|
||||||
ImageIO.write(image, "png", tempFile.toFile());
|
blank = isBlankImage(image, threshold, whitePercent, threshold);
|
||||||
|
|
||||||
List<String> command =
|
|
||||||
new ArrayList<>(
|
|
||||||
Arrays.asList(
|
|
||||||
"python3",
|
|
||||||
System.getProperty("user.dir")
|
|
||||||
+ "/scripts/detect-blank-pages.py",
|
|
||||||
tempFile.toString(),
|
|
||||||
"--threshold",
|
|
||||||
String.valueOf(threshold),
|
|
||||||
"--white_percent",
|
|
||||||
String.valueOf(whitePercent)));
|
|
||||||
|
|
||||||
// Run CLI command
|
|
||||||
ProcessExecutorResult returnCode =
|
|
||||||
ProcessExecutor.getInstance(ProcessExecutor.Processes.PYTHON_OPENCV)
|
|
||||||
.runCommandWithOutputHandling(command);
|
|
||||||
|
|
||||||
// does contain data
|
|
||||||
if (returnCode.getRc() == 0) {
|
|
||||||
System.out.println(
|
|
||||||
"page " + pageIndex + " has image which is not blank");
|
|
||||||
pagesToKeepIndex.add(pageIndex);
|
|
||||||
} else {
|
|
||||||
System.out.println("Skipping, Image was blank for page #" + pageIndex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (blank) {
|
||||||
|
logger.info("Skipping, Image was blank for page #" + pageIndex);
|
||||||
|
} else {
|
||||||
|
logger.info("page " + pageIndex + " has image which is not blank");
|
||||||
|
pagesToKeepIndex.add(pageIndex);
|
||||||
|
}
|
||||||
|
|
||||||
pageIndex++;
|
pageIndex++;
|
||||||
}
|
}
|
||||||
System.out.print("pagesToKeep=" + pagesToKeepIndex.size());
|
|
||||||
|
|
||||||
// Remove pages not present in pagesToKeepIndex
|
// Remove pages not present in pagesToKeepIndex
|
||||||
List<Integer> pageIndices =
|
List<Integer> pageIndices =
|
||||||
IntStream.range(0, pages.getCount()).boxed().collect(Collectors.toList());
|
IntStream.range(0, pages.getCount()).boxed().collect(Collectors.toList());
|
||||||
@@ -124,7 +102,8 @@ public class BlankPageController {
|
|||||||
|
|
||||||
return WebResponseUtils.pdfDocToWebResponse(
|
return WebResponseUtils.pdfDocToWebResponse(
|
||||||
document,
|
document,
|
||||||
inputFile.getOriginalFilename().replaceFirst("[.][^.]+$", "")
|
Filenames.toSimpleFileName(inputFile.getOriginalFilename())
|
||||||
|
.replaceFirst("[.][^.]+$", "")
|
||||||
+ "_blanksRemoved.pdf");
|
+ "_blanksRemoved.pdf");
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
@@ -133,4 +112,30 @@ public class BlankPageController {
|
|||||||
if (document != null) document.close();
|
if (document != null) document.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isBlankImage(
|
||||||
|
BufferedImage image, int threshold, double whitePercent, int blurSize) {
|
||||||
|
if (image == null) {
|
||||||
|
logger.info("Error: Image is null");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert to binary image based on the threshold
|
||||||
|
int whitePixels = 0;
|
||||||
|
int totalPixels = image.getWidth() * image.getHeight();
|
||||||
|
|
||||||
|
for (int i = 0; i < image.getHeight(); i++) {
|
||||||
|
for (int j = 0; j < image.getWidth(); j++) {
|
||||||
|
int color = image.getRGB(j, i) & 0xFF;
|
||||||
|
if (color >= 255 - threshold) {
|
||||||
|
whitePixels++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
double whitePixelPercentage = (whitePixels / (double) totalPixels) * 100;
|
||||||
|
logger.info(String.format("Page has white pixel percent of %.2f%%", whitePixelPercentage));
|
||||||
|
|
||||||
|
return whitePixelPercentage >= whitePercent;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import java.util.List;
|
|||||||
import javax.imageio.ImageIO;
|
import javax.imageio.ImageIO;
|
||||||
|
|
||||||
import org.apache.commons.io.FileUtils;
|
import org.apache.commons.io.FileUtils;
|
||||||
|
import org.apache.pdfbox.Loader;
|
||||||
import org.apache.pdfbox.cos.COSName;
|
import org.apache.pdfbox.cos.COSName;
|
||||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
import org.apache.pdfbox.pdmodel.PDPage;
|
import org.apache.pdfbox.pdmodel.PDPage;
|
||||||
@@ -28,6 +29,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import io.github.pixee.security.Filenames;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
|
||||||
@@ -147,7 +149,7 @@ public class CompressController {
|
|||||||
if (expectedOutputSize != null && autoMode) {
|
if (expectedOutputSize != null && autoMode) {
|
||||||
long outputFileSize = Files.size(tempOutputFile);
|
long outputFileSize = Files.size(tempOutputFile);
|
||||||
if (outputFileSize > expectedOutputSize) {
|
if (outputFileSize > expectedOutputSize) {
|
||||||
try (PDDocument doc = PDDocument.load(new File(tempOutputFile.toString()))) {
|
try (PDDocument doc = Loader.loadPDF(new File(tempOutputFile.toString()))) {
|
||||||
long previousFileSize = 0;
|
long previousFileSize = 0;
|
||||||
double scaleFactor = 1.0;
|
double scaleFactor = 1.0;
|
||||||
while (true) {
|
while (true) {
|
||||||
@@ -263,7 +265,9 @@ public class CompressController {
|
|||||||
|
|
||||||
// Return the optimized PDF as a response
|
// Return the optimized PDF as a response
|
||||||
String outputFilename =
|
String outputFilename =
|
||||||
inputFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_Optimized.pdf";
|
Filenames.toSimpleFileName(inputFile.getOriginalFilename())
|
||||||
|
.replaceFirst("[.][^.]+$", "")
|
||||||
|
+ "_Optimized.pdf";
|
||||||
return WebResponseUtils.bytesToWebResponse(pdfBytes, outputFilename);
|
return WebResponseUtils.bytesToWebResponse(pdfBytes, outputFilename);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package stirling.software.SPDF.controller.api.misc;
|
package stirling.software.SPDF.controller.api.misc;
|
||||||
|
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
@@ -17,6 +16,7 @@ import java.util.zip.ZipOutputStream;
|
|||||||
import javax.imageio.ImageIO;
|
import javax.imageio.ImageIO;
|
||||||
|
|
||||||
import org.apache.commons.io.FileUtils;
|
import org.apache.commons.io.FileUtils;
|
||||||
|
import org.apache.pdfbox.Loader;
|
||||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
import org.apache.pdfbox.rendering.PDFRenderer;
|
import org.apache.pdfbox.rendering.PDFRenderer;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@@ -72,113 +72,146 @@ public class ExtractImageScansController {
|
|||||||
String extension = fileName.substring(fileName.lastIndexOf(".") + 1);
|
String extension = fileName.substring(fileName.lastIndexOf(".") + 1);
|
||||||
|
|
||||||
List<String> images = new ArrayList<>();
|
List<String> images = new ArrayList<>();
|
||||||
|
|
||||||
// Check if input file is a PDF
|
List<Path> tempImageFiles = new ArrayList<>();
|
||||||
if (extension.equalsIgnoreCase("pdf")) {
|
Path tempInputFile = null;
|
||||||
// Load PDF document
|
Path tempZipFile = null;
|
||||||
try (PDDocument document =
|
List<Path> tempDirs = new ArrayList<>();
|
||||||
PDDocument.load(new ByteArrayInputStream(form.getFileInput().getBytes()))) {
|
|
||||||
PDFRenderer pdfRenderer = new PDFRenderer(document);
|
try {
|
||||||
int pageCount = document.getNumberOfPages();
|
// Check if input file is a PDF
|
||||||
images = new ArrayList<>();
|
if ("pdf".equalsIgnoreCase(extension)) {
|
||||||
|
// Load PDF document
|
||||||
// Create images of all pages
|
try (PDDocument document = Loader.loadPDF(form.getFileInput().getBytes())) {
|
||||||
for (int i = 0; i < pageCount; i++) {
|
PDFRenderer pdfRenderer = new PDFRenderer(document);
|
||||||
// Create temp file to save the image
|
int pageCount = document.getNumberOfPages();
|
||||||
Path tempFile = Files.createTempFile("image_", ".png");
|
images = new ArrayList<>();
|
||||||
|
|
||||||
// Render image and save as temp file
|
// Create images of all pages
|
||||||
BufferedImage image = pdfRenderer.renderImageWithDPI(i, 300);
|
for (int i = 0; i < pageCount; i++) {
|
||||||
ImageIO.write(image, "png", tempFile.toFile());
|
// Create temp file to save the image
|
||||||
|
Path tempFile = Files.createTempFile("image_", ".png");
|
||||||
// Add temp file path to images list
|
|
||||||
images.add(tempFile.toString());
|
// Render image and save as temp file
|
||||||
|
BufferedImage image = pdfRenderer.renderImageWithDPI(i, 300);
|
||||||
|
ImageIO.write(image, "png", tempFile.toFile());
|
||||||
|
|
||||||
|
// Add temp file path to images list
|
||||||
|
images.add(tempFile.toString());
|
||||||
|
tempImageFiles.add(tempFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
tempInputFile = Files.createTempFile("input_", "." + extension);
|
||||||
|
Files.copy(
|
||||||
|
form.getFileInput().getInputStream(),
|
||||||
|
tempInputFile,
|
||||||
|
StandardCopyOption.REPLACE_EXISTING);
|
||||||
|
// Add input file path to images list
|
||||||
|
images.add(tempInputFile.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
List<byte[]> processedImageBytes = new ArrayList<>();
|
||||||
|
|
||||||
|
// Process each image
|
||||||
|
for (int i = 0; i < images.size(); i++) {
|
||||||
|
|
||||||
|
Path tempDir = Files.createTempDirectory("openCV_output");
|
||||||
|
tempDirs.add(tempDir);
|
||||||
|
List<String> command =
|
||||||
|
new ArrayList<>(
|
||||||
|
Arrays.asList(
|
||||||
|
"python3",
|
||||||
|
"./scripts/split_photos.py",
|
||||||
|
images.get(i),
|
||||||
|
tempDir.toString(),
|
||||||
|
"--angle_threshold",
|
||||||
|
String.valueOf(form.getAngleThreshold()),
|
||||||
|
"--tolerance",
|
||||||
|
String.valueOf(form.getTolerance()),
|
||||||
|
"--min_area",
|
||||||
|
String.valueOf(form.getMinArea()),
|
||||||
|
"--min_contour_area",
|
||||||
|
String.valueOf(form.getMinContourArea()),
|
||||||
|
"--border_size",
|
||||||
|
String.valueOf(form.getBorderSize())));
|
||||||
|
|
||||||
|
// Run CLI command
|
||||||
|
ProcessExecutorResult returnCode =
|
||||||
|
ProcessExecutor.getInstance(ProcessExecutor.Processes.PYTHON_OPENCV)
|
||||||
|
.runCommandWithOutputHandling(command);
|
||||||
|
|
||||||
|
// Read the output photos in temp directory
|
||||||
|
List<Path> tempOutputFiles = Files.list(tempDir).sorted().collect(Collectors.toList());
|
||||||
|
for (Path tempOutputFile : tempOutputFiles) {
|
||||||
|
byte[] imageBytes = Files.readAllBytes(tempOutputFile);
|
||||||
|
processedImageBytes.add(imageBytes);
|
||||||
|
}
|
||||||
|
// Clean up the temporary directory
|
||||||
|
FileUtils.deleteDirectory(tempDir.toFile());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create zip file if multiple images
|
||||||
|
if (processedImageBytes.size() > 1) {
|
||||||
|
String outputZipFilename = fileName.replaceFirst("[.][^.]+$", "") + "_processed.zip";
|
||||||
|
tempZipFile = Files.createTempFile("output_", ".zip");
|
||||||
|
|
||||||
|
try (ZipOutputStream zipOut =
|
||||||
|
new ZipOutputStream(new FileOutputStream(tempZipFile.toFile()))) {
|
||||||
|
// Add processed images to the zip
|
||||||
|
for (int i = 0; i < processedImageBytes.size(); i++) {
|
||||||
|
ZipEntry entry =
|
||||||
|
new ZipEntry(
|
||||||
|
fileName.replaceFirst("[.][^.]+$", "")
|
||||||
|
+ "_"
|
||||||
|
+ (i + 1)
|
||||||
|
+ ".png");
|
||||||
|
zipOut.putNextEntry(entry);
|
||||||
|
zipOut.write(processedImageBytes.get(i));
|
||||||
|
zipOut.closeEntry();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] zipBytes = Files.readAllBytes(tempZipFile);
|
||||||
|
|
||||||
|
// Clean up the temporary zip file
|
||||||
|
Files.delete(tempZipFile);
|
||||||
|
|
||||||
|
return WebResponseUtils.bytesToWebResponse(
|
||||||
|
zipBytes, outputZipFilename, MediaType.APPLICATION_OCTET_STREAM);
|
||||||
|
} else {
|
||||||
|
// Return the processed image as a response
|
||||||
|
byte[] imageBytes = processedImageBytes.get(0);
|
||||||
|
return WebResponseUtils.bytesToWebResponse(
|
||||||
|
imageBytes,
|
||||||
|
fileName.replaceFirst("[.][^.]+$", "") + ".png",
|
||||||
|
MediaType.IMAGE_PNG);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
// Cleanup logic for all temporary files and directories
|
||||||
|
tempImageFiles.forEach(path -> {
|
||||||
|
try {
|
||||||
|
Files.deleteIfExists(path);
|
||||||
|
} catch (IOException e) {
|
||||||
|
logger.error("Failed to delete temporary image file: " + path, e);
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
} else {
|
|
||||||
Path tempInputFile = Files.createTempFile("input_", "." + extension);
|
|
||||||
Files.copy(
|
|
||||||
form.getFileInput().getInputStream(),
|
|
||||||
tempInputFile,
|
|
||||||
StandardCopyOption.REPLACE_EXISTING);
|
|
||||||
// Add input file path to images list
|
|
||||||
images.add(tempInputFile.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
List<byte[]> processedImageBytes = new ArrayList<>();
|
if (tempZipFile != null && Files.exists(tempZipFile)) {
|
||||||
|
try {
|
||||||
// Process each image
|
Files.delete(tempZipFile);
|
||||||
for (int i = 0; i < images.size(); i++) {
|
} catch (IOException e) {
|
||||||
|
logger.error("Failed to delete temporary zip file: " + tempZipFile, e);
|
||||||
Path tempDir = Files.createTempDirectory("openCV_output");
|
|
||||||
List<String> command =
|
|
||||||
new ArrayList<>(
|
|
||||||
Arrays.asList(
|
|
||||||
"python3",
|
|
||||||
"./scripts/split_photos.py",
|
|
||||||
images.get(i),
|
|
||||||
tempDir.toString(),
|
|
||||||
"--angle_threshold",
|
|
||||||
String.valueOf(form.getAngleThreshold()),
|
|
||||||
"--tolerance",
|
|
||||||
String.valueOf(form.getTolerance()),
|
|
||||||
"--min_area",
|
|
||||||
String.valueOf(form.getMinArea()),
|
|
||||||
"--min_contour_area",
|
|
||||||
String.valueOf(form.getMinContourArea()),
|
|
||||||
"--border_size",
|
|
||||||
String.valueOf(form.getBorderSize())));
|
|
||||||
|
|
||||||
// Run CLI command
|
|
||||||
ProcessExecutorResult returnCode =
|
|
||||||
ProcessExecutor.getInstance(ProcessExecutor.Processes.PYTHON_OPENCV)
|
|
||||||
.runCommandWithOutputHandling(command);
|
|
||||||
|
|
||||||
// Read the output photos in temp directory
|
|
||||||
List<Path> tempOutputFiles = Files.list(tempDir).sorted().collect(Collectors.toList());
|
|
||||||
for (Path tempOutputFile : tempOutputFiles) {
|
|
||||||
byte[] imageBytes = Files.readAllBytes(tempOutputFile);
|
|
||||||
processedImageBytes.add(imageBytes);
|
|
||||||
}
|
|
||||||
// Clean up the temporary directory
|
|
||||||
FileUtils.deleteDirectory(tempDir.toFile());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create zip file if multiple images
|
|
||||||
if (processedImageBytes.size() > 1) {
|
|
||||||
String outputZipFilename = fileName.replaceFirst("[.][^.]+$", "") + "_processed.zip";
|
|
||||||
Path tempZipFile = Files.createTempFile("output_", ".zip");
|
|
||||||
|
|
||||||
try (ZipOutputStream zipOut =
|
|
||||||
new ZipOutputStream(new FileOutputStream(tempZipFile.toFile()))) {
|
|
||||||
// Add processed images to the zip
|
|
||||||
for (int i = 0; i < processedImageBytes.size(); i++) {
|
|
||||||
ZipEntry entry =
|
|
||||||
new ZipEntry(
|
|
||||||
fileName.replaceFirst("[.][^.]+$", "")
|
|
||||||
+ "_"
|
|
||||||
+ (i + 1)
|
|
||||||
+ ".png");
|
|
||||||
zipOut.putNextEntry(entry);
|
|
||||||
zipOut.write(processedImageBytes.get(i));
|
|
||||||
zipOut.closeEntry();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] zipBytes = Files.readAllBytes(tempZipFile);
|
tempDirs.forEach(dir -> {
|
||||||
|
try {
|
||||||
// Clean up the temporary zip file
|
FileUtils.deleteDirectory(dir.toFile());
|
||||||
Files.delete(tempZipFile);
|
} catch (IOException e) {
|
||||||
|
logger.error("Failed to delete temporary directory: " + dir, e);
|
||||||
return WebResponseUtils.bytesToWebResponse(
|
}
|
||||||
zipBytes, outputZipFilename, MediaType.APPLICATION_OCTET_STREAM);
|
});
|
||||||
} else {
|
|
||||||
// Return the processed image as a response
|
|
||||||
byte[] imageBytes = processedImageBytes.get(0);
|
|
||||||
return WebResponseUtils.bytesToWebResponse(
|
|
||||||
imageBytes,
|
|
||||||
fileName.replaceFirst("[.][^.]+$", "") + ".png",
|
|
||||||
MediaType.IMAGE_PNG);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import java.util.zip.ZipOutputStream;
|
|||||||
|
|
||||||
import javax.imageio.ImageIO;
|
import javax.imageio.ImageIO;
|
||||||
|
|
||||||
|
import org.apache.pdfbox.Loader;
|
||||||
import org.apache.pdfbox.cos.COSName;
|
import org.apache.pdfbox.cos.COSName;
|
||||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
import org.apache.pdfbox.pdmodel.PDPage;
|
import org.apache.pdfbox.pdmodel.PDPage;
|
||||||
@@ -28,6 +29,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import io.github.pixee.security.Filenames;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
|
||||||
@@ -53,7 +55,7 @@ public class ExtractImagesController {
|
|||||||
|
|
||||||
System.out.println(
|
System.out.println(
|
||||||
System.currentTimeMillis() + "file=" + file.getName() + ", format=" + format);
|
System.currentTimeMillis() + "file=" + file.getName() + ", format=" + format);
|
||||||
PDDocument document = PDDocument.load(file.getBytes());
|
PDDocument document = Loader.loadPDF(file.getBytes());
|
||||||
|
|
||||||
// Create ByteArrayOutputStream to write zip file to byte array
|
// Create ByteArrayOutputStream to write zip file to byte array
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
@@ -65,7 +67,9 @@ public class ExtractImagesController {
|
|||||||
zos.setLevel(Deflater.BEST_COMPRESSION);
|
zos.setLevel(Deflater.BEST_COMPRESSION);
|
||||||
|
|
||||||
int imageIndex = 1;
|
int imageIndex = 1;
|
||||||
String filename = file.getOriginalFilename().replaceFirst("[.][^.]+$", "");
|
String filename =
|
||||||
|
Filenames.toSimpleFileName(file.getOriginalFilename())
|
||||||
|
.replaceFirst("[.][^.]+$", "");
|
||||||
int pageNum = 0;
|
int pageNum = 0;
|
||||||
Set<Integer> processedImages = new HashSet<>();
|
Set<Integer> processedImages = new HashSet<>();
|
||||||
// Iterate over each page
|
// Iterate over each page
|
||||||
@@ -84,19 +88,19 @@ public class ExtractImagesController {
|
|||||||
// Convert image to desired format
|
// Convert image to desired format
|
||||||
RenderedImage renderedImage = image.getImage();
|
RenderedImage renderedImage = image.getImage();
|
||||||
BufferedImage bufferedImage = null;
|
BufferedImage bufferedImage = null;
|
||||||
if (format.equalsIgnoreCase("png")) {
|
if ("png".equalsIgnoreCase(format)) {
|
||||||
bufferedImage =
|
bufferedImage =
|
||||||
new BufferedImage(
|
new BufferedImage(
|
||||||
renderedImage.getWidth(),
|
renderedImage.getWidth(),
|
||||||
renderedImage.getHeight(),
|
renderedImage.getHeight(),
|
||||||
BufferedImage.TYPE_INT_ARGB);
|
BufferedImage.TYPE_INT_ARGB);
|
||||||
} else if (format.equalsIgnoreCase("jpeg") || format.equalsIgnoreCase("jpg")) {
|
} else if ("jpeg".equalsIgnoreCase(format) || "jpg".equalsIgnoreCase(format)) {
|
||||||
bufferedImage =
|
bufferedImage =
|
||||||
new BufferedImage(
|
new BufferedImage(
|
||||||
renderedImage.getWidth(),
|
renderedImage.getWidth(),
|
||||||
renderedImage.getHeight(),
|
renderedImage.getHeight(),
|
||||||
BufferedImage.TYPE_INT_RGB);
|
BufferedImage.TYPE_INT_RGB);
|
||||||
} else if (format.equalsIgnoreCase("gif")) {
|
} else if ("gif".equalsIgnoreCase(format)) {
|
||||||
bufferedImage =
|
bufferedImage =
|
||||||
new BufferedImage(
|
new BufferedImage(
|
||||||
renderedImage.getWidth(),
|
renderedImage.getWidth(),
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import java.util.Random;
|
|||||||
|
|
||||||
import javax.imageio.ImageIO;
|
import javax.imageio.ImageIO;
|
||||||
|
|
||||||
|
import org.apache.pdfbox.Loader;
|
||||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
import org.apache.pdfbox.pdmodel.PDPage;
|
import org.apache.pdfbox.pdmodel.PDPage;
|
||||||
import org.apache.pdfbox.pdmodel.PDPageContentStream;
|
import org.apache.pdfbox.pdmodel.PDPageContentStream;
|
||||||
@@ -28,11 +29,11 @@ import org.slf4j.Logger;
|
|||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import io.github.pixee.security.Filenames;
|
||||||
import io.swagger.v3.oas.annotations.Hidden;
|
import io.swagger.v3.oas.annotations.Hidden;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
@@ -49,7 +50,7 @@ public class FakeScanControllerWIP {
|
|||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
@Hidden
|
@Hidden
|
||||||
@PostMapping(consumes = "multipart/form-data", value = "/fakeScan")
|
// @PostMapping(consumes = "multipart/form-data", value = "/fakeScan")
|
||||||
@Operation(
|
@Operation(
|
||||||
summary = "Repair a PDF file",
|
summary = "Repair a PDF file",
|
||||||
description =
|
description =
|
||||||
@@ -57,7 +58,7 @@ public class FakeScanControllerWIP {
|
|||||||
public ResponseEntity<byte[]> repairPdf(@ModelAttribute PDFFile request) throws IOException {
|
public ResponseEntity<byte[]> repairPdf(@ModelAttribute PDFFile request) throws IOException {
|
||||||
MultipartFile inputFile = request.getFileInput();
|
MultipartFile inputFile = request.getFileInput();
|
||||||
|
|
||||||
PDDocument document = PDDocument.load(inputFile.getBytes());
|
PDDocument document = Loader.loadPDF(inputFile.getBytes());
|
||||||
PDFRenderer pdfRenderer = new PDFRenderer(document);
|
PDFRenderer pdfRenderer = new PDFRenderer(document);
|
||||||
for (int page = 0; page < document.getNumberOfPages(); ++page) {
|
for (int page = 0; page < document.getNumberOfPages(); ++page) {
|
||||||
BufferedImage image = pdfRenderer.renderImageWithDPI(page, 300, ImageType.RGB);
|
BufferedImage image = pdfRenderer.renderImageWithDPI(page, 300, ImageType.RGB);
|
||||||
@@ -141,7 +142,9 @@ public class FakeScanControllerWIP {
|
|||||||
|
|
||||||
// Return the optimized PDF as a response
|
// Return the optimized PDF as a response
|
||||||
String outputFilename =
|
String outputFilename =
|
||||||
inputFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_scanned.pdf";
|
Filenames.toSimpleFileName(inputFile.getOriginalFilename())
|
||||||
|
.replaceFirst("[.][^.]+$", "")
|
||||||
|
+ "_scanned.pdf";
|
||||||
return WebResponseUtils.boasToWebResponse(baos, outputFilename);
|
return WebResponseUtils.boasToWebResponse(baos, outputFilename);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import java.util.Calendar;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
|
import org.apache.pdfbox.Loader;
|
||||||
import org.apache.pdfbox.cos.COSName;
|
import org.apache.pdfbox.cos.COSName;
|
||||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
import org.apache.pdfbox.pdmodel.PDDocumentInformation;
|
import org.apache.pdfbox.pdmodel.PDDocumentInformation;
|
||||||
@@ -17,6 +18,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import io.github.pixee.security.Filenames;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
|
||||||
@@ -67,7 +69,7 @@ public class MetadataController {
|
|||||||
allRequestParams = new java.util.HashMap<String, String>();
|
allRequestParams = new java.util.HashMap<String, String>();
|
||||||
}
|
}
|
||||||
// Load the PDF file into a PDDocument
|
// Load the PDF file into a PDDocument
|
||||||
PDDocument document = PDDocument.load(pdfFile.getBytes());
|
PDDocument document = Loader.loadPDF(pdfFile.getBytes());
|
||||||
|
|
||||||
// Get the document information from the PDF
|
// Get the document information from the PDF
|
||||||
PDDocumentInformation info = document.getDocumentInformation();
|
PDDocumentInformation info = document.getDocumentInformation();
|
||||||
@@ -108,15 +110,15 @@ public class MetadataController {
|
|||||||
for (Entry<String, String> entry : allRequestParams.entrySet()) {
|
for (Entry<String, String> entry : allRequestParams.entrySet()) {
|
||||||
String key = entry.getKey();
|
String key = entry.getKey();
|
||||||
// Check if the key is a standard metadata key
|
// Check if the key is a standard metadata key
|
||||||
if (!key.equalsIgnoreCase("Author")
|
if (!"Author".equalsIgnoreCase(key)
|
||||||
&& !key.equalsIgnoreCase("CreationDate")
|
&& !"CreationDate".equalsIgnoreCase(key)
|
||||||
&& !key.equalsIgnoreCase("Creator")
|
&& !"Creator".equalsIgnoreCase(key)
|
||||||
&& !key.equalsIgnoreCase("Keywords")
|
&& !"Keywords".equalsIgnoreCase(key)
|
||||||
&& !key.equalsIgnoreCase("modificationDate")
|
&& !"modificationDate".equalsIgnoreCase(key)
|
||||||
&& !key.equalsIgnoreCase("Producer")
|
&& !"Producer".equalsIgnoreCase(key)
|
||||||
&& !key.equalsIgnoreCase("Subject")
|
&& !"Subject".equalsIgnoreCase(key)
|
||||||
&& !key.equalsIgnoreCase("Title")
|
&& !"Title".equalsIgnoreCase(key)
|
||||||
&& !key.equalsIgnoreCase("Trapped")
|
&& !"Trapped".equalsIgnoreCase(key)
|
||||||
&& !key.contains("customKey")
|
&& !key.contains("customKey")
|
||||||
&& !key.contains("customValue")) {
|
&& !key.contains("customValue")) {
|
||||||
info.setCustomMetadataValue(key, entry.getValue());
|
info.setCustomMetadataValue(key, entry.getValue());
|
||||||
@@ -163,6 +165,8 @@ public class MetadataController {
|
|||||||
document.setDocumentInformation(info);
|
document.setDocumentInformation(info);
|
||||||
return WebResponseUtils.pdfDocToWebResponse(
|
return WebResponseUtils.pdfDocToWebResponse(
|
||||||
document,
|
document,
|
||||||
pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_metadata.pdf");
|
Filenames.toSimpleFileName(pdfFile.getOriginalFilename())
|
||||||
|
.replaceFirst("[.][^.]+$", "")
|
||||||
|
+ "_metadata.pdf");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import io.github.pixee.security.Filenames;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
|
||||||
@@ -40,7 +41,7 @@ public class OCRController {
|
|||||||
private static final Logger logger = LoggerFactory.getLogger(OCRController.class);
|
private static final Logger logger = LoggerFactory.getLogger(OCRController.class);
|
||||||
|
|
||||||
public List<String> getAvailableTesseractLanguages() {
|
public List<String> getAvailableTesseractLanguages() {
|
||||||
String tessdataDir = "/usr/share/tesseract-ocr/5/tessdata";
|
String tessdataDir = "/usr/share/tessdata";
|
||||||
File[] files = new File(tessdataDir).listFiles();
|
File[] files = new File(tessdataDir).listFiles();
|
||||||
if (files == null) {
|
if (files == null) {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
@@ -74,7 +75,7 @@ public class OCRController {
|
|||||||
throw new IOException("Please select at least one language.");
|
throw new IOException("Please select at least one language.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ocrRenderType.equals("hocr") && !ocrRenderType.equals("sandwich")) {
|
if (!"hocr".equals(ocrRenderType) && !"sandwich".equals(ocrRenderType)) {
|
||||||
throw new IOException("ocrRenderType wrong");
|
throw new IOException("ocrRenderType wrong");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,7 +128,7 @@ public class OCRController {
|
|||||||
if (cleanFinal != null && cleanFinal) {
|
if (cleanFinal != null && cleanFinal) {
|
||||||
command.add("--clean-final");
|
command.add("--clean-final");
|
||||||
}
|
}
|
||||||
if (ocrType != null && !ocrType.equals("")) {
|
if (ocrType != null && !"".equals(ocrType)) {
|
||||||
if ("skip-text".equals(ocrType)) {
|
if ("skip-text".equals(ocrType)) {
|
||||||
command.add("--skip-text");
|
command.add("--skip-text");
|
||||||
} else if ("force-ocr".equals(ocrType)) {
|
} else if ("force-ocr".equals(ocrType)) {
|
||||||
@@ -182,12 +183,16 @@ public class OCRController {
|
|||||||
|
|
||||||
// Return the OCR processed PDF as a response
|
// Return the OCR processed PDF as a response
|
||||||
String outputFilename =
|
String outputFilename =
|
||||||
inputFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_OCR.pdf";
|
Filenames.toSimpleFileName(inputFile.getOriginalFilename())
|
||||||
|
.replaceFirst("[.][^.]+$", "")
|
||||||
|
+ "_OCR.pdf";
|
||||||
|
|
||||||
if (sidecar != null && sidecar) {
|
if (sidecar != null && sidecar) {
|
||||||
// Create a zip file containing both the PDF and the text file
|
// Create a zip file containing both the PDF and the text file
|
||||||
String outputZipFilename =
|
String outputZipFilename =
|
||||||
inputFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_OCR.zip";
|
Filenames.toSimpleFileName(inputFile.getOriginalFilename())
|
||||||
|
.replaceFirst("[.][^.]+$", "")
|
||||||
|
+ "_OCR.zip";
|
||||||
Path tempZipFile = Files.createTempFile("output_", ".zip");
|
Path tempZipFile = Files.createTempFile("output_", ".zip");
|
||||||
|
|
||||||
try (ZipOutputStream zipOut =
|
try (ZipOutputStream zipOut =
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import io.github.pixee.security.Filenames;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
|
||||||
@@ -30,7 +31,7 @@ public class OverlayImageController {
|
|||||||
@Operation(
|
@Operation(
|
||||||
summary = "Overlay image onto a PDF file",
|
summary = "Overlay image onto a PDF file",
|
||||||
description =
|
description =
|
||||||
"This endpoint overlays an image onto a PDF file at the specified coordinates. The image can be overlaid on every page of the PDF if specified. Input:PDF/IMAGE Output:PDF Type:MF-SISO")
|
"This endpoint overlays an image onto a PDF file at the specified coordinates. The image can be overlaid on every page of the PDF if specified. Input:PDF/IMAGE Output:PDF Type:SISO")
|
||||||
public ResponseEntity<byte[]> overlayImage(@ModelAttribute OverlayImageRequest request) {
|
public ResponseEntity<byte[]> overlayImage(@ModelAttribute OverlayImageRequest request) {
|
||||||
MultipartFile pdfFile = request.getFileInput();
|
MultipartFile pdfFile = request.getFileInput();
|
||||||
MultipartFile imageFile = request.getImageFile();
|
MultipartFile imageFile = request.getImageFile();
|
||||||
@@ -44,7 +45,9 @@ public class OverlayImageController {
|
|||||||
|
|
||||||
return WebResponseUtils.bytesToWebResponse(
|
return WebResponseUtils.bytesToWebResponse(
|
||||||
result,
|
result,
|
||||||
pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_overlayed.pdf");
|
Filenames.toSimpleFileName(pdfFile.getOriginalFilename())
|
||||||
|
.replaceFirst("[.][^.]+$", "")
|
||||||
|
+ "_overlayed.pdf");
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
logger.error("Failed to add image to PDF", e);
|
logger.error("Failed to add image to PDF", e);
|
||||||
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
|
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
|
||||||
|
|||||||
@@ -4,11 +4,13 @@ import java.io.ByteArrayOutputStream;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.pdfbox.Loader;
|
||||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
import org.apache.pdfbox.pdmodel.PDPage;
|
import org.apache.pdfbox.pdmodel.PDPage;
|
||||||
import org.apache.pdfbox.pdmodel.PDPageContentStream;
|
import org.apache.pdfbox.pdmodel.PDPageContentStream;
|
||||||
import org.apache.pdfbox.pdmodel.common.PDRectangle;
|
import org.apache.pdfbox.pdmodel.common.PDRectangle;
|
||||||
import org.apache.pdfbox.pdmodel.font.PDType1Font;
|
import org.apache.pdfbox.pdmodel.font.PDType1Font;
|
||||||
|
import org.apache.pdfbox.pdmodel.font.Standard14Fonts;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
@@ -19,6 +21,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import io.github.pixee.security.Filenames;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
|
||||||
@@ -48,7 +51,7 @@ public class PageNumbersController {
|
|||||||
String customText = request.getCustomText();
|
String customText = request.getCustomText();
|
||||||
int pageNumber = startingNumber;
|
int pageNumber = startingNumber;
|
||||||
byte[] fileBytes = file.getBytes();
|
byte[] fileBytes = file.getBytes();
|
||||||
PDDocument document = PDDocument.load(fileBytes);
|
PDDocument document = Loader.loadPDF(fileBytes);
|
||||||
|
|
||||||
float marginFactor;
|
float marginFactor;
|
||||||
switch (customMargin.toLowerCase()) {
|
switch (customMargin.toLowerCase()) {
|
||||||
@@ -71,7 +74,6 @@ public class PageNumbersController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
float fontSize = 12.0f;
|
float fontSize = 12.0f;
|
||||||
PDType1Font font = PDType1Font.HELVETICA;
|
|
||||||
if (pagesToNumber == null || pagesToNumber.length() == 0) {
|
if (pagesToNumber == null || pagesToNumber.length() == 0) {
|
||||||
pagesToNumber = "all";
|
pagesToNumber = "all";
|
||||||
}
|
}
|
||||||
@@ -92,7 +94,7 @@ public class PageNumbersController {
|
|||||||
.replace("{total}", String.valueOf(document.getNumberOfPages()))
|
.replace("{total}", String.valueOf(document.getNumberOfPages()))
|
||||||
.replace(
|
.replace(
|
||||||
"{filename}",
|
"{filename}",
|
||||||
file.getOriginalFilename()
|
Filenames.toSimpleFileName(file.getOriginalFilename())
|
||||||
.replaceFirst("[.][^.]+$", ""))
|
.replaceFirst("[.][^.]+$", ""))
|
||||||
: String.valueOf(pageNumber);
|
: String.valueOf(pageNumber);
|
||||||
|
|
||||||
@@ -127,9 +129,9 @@ public class PageNumbersController {
|
|||||||
|
|
||||||
PDPageContentStream contentStream =
|
PDPageContentStream contentStream =
|
||||||
new PDPageContentStream(
|
new PDPageContentStream(
|
||||||
document, page, PDPageContentStream.AppendMode.APPEND, true);
|
document, page, PDPageContentStream.AppendMode.APPEND, true, true);
|
||||||
contentStream.beginText();
|
contentStream.beginText();
|
||||||
contentStream.setFont(font, fontSize);
|
contentStream.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA), fontSize);
|
||||||
contentStream.newLineAtOffset(x, y);
|
contentStream.newLineAtOffset(x, y);
|
||||||
contentStream.showText(text);
|
contentStream.showText(text);
|
||||||
contentStream.endText();
|
contentStream.endText();
|
||||||
@@ -144,7 +146,8 @@ public class PageNumbersController {
|
|||||||
|
|
||||||
return WebResponseUtils.bytesToWebResponse(
|
return WebResponseUtils.bytesToWebResponse(
|
||||||
baos.toByteArray(),
|
baos.toByteArray(),
|
||||||
file.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_numbersAdded.pdf",
|
Filenames.toSimpleFileName(file.getOriginalFilename()).replaceFirst("[.][^.]+$", "")
|
||||||
|
+ "_numbersAdded.pdf",
|
||||||
MediaType.APPLICATION_PDF);
|
MediaType.APPLICATION_PDF);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import io.github.pixee.security.Filenames;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
|
||||||
@@ -65,7 +66,9 @@ public class RepairController {
|
|||||||
|
|
||||||
// Return the optimized PDF as a response
|
// Return the optimized PDF as a response
|
||||||
String outputFilename =
|
String outputFilename =
|
||||||
inputFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_repaired.pdf";
|
Filenames.toSimpleFileName(inputFile.getOriginalFilename())
|
||||||
|
.replaceFirst("[.][^.]+$", "")
|
||||||
|
+ "_repaired.pdf";
|
||||||
return WebResponseUtils.bytesToWebResponse(pdfBytes, outputFilename);
|
return WebResponseUtils.bytesToWebResponse(pdfBytes, outputFilename);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package stirling.software.SPDF.controller.api.misc;
|
|||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.apache.pdfbox.Loader;
|
||||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
import org.apache.pdfbox.pdmodel.common.PDNameTreeNode;
|
import org.apache.pdfbox.pdmodel.common.PDNameTreeNode;
|
||||||
import org.apache.pdfbox.pdmodel.interactive.action.PDActionJavaScript;
|
import org.apache.pdfbox.pdmodel.interactive.action.PDActionJavaScript;
|
||||||
@@ -15,6 +16,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import io.github.pixee.security.Filenames;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
|
||||||
@@ -36,7 +38,7 @@ public class ShowJavascript {
|
|||||||
MultipartFile inputFile = request.getFileInput();
|
MultipartFile inputFile = request.getFileInput();
|
||||||
String script = "";
|
String script = "";
|
||||||
|
|
||||||
try (PDDocument document = PDDocument.load(inputFile.getInputStream())) {
|
try (PDDocument document = Loader.loadPDF(inputFile.getBytes())) {
|
||||||
|
|
||||||
if (document.getDocumentCatalog() != null
|
if (document.getDocumentCatalog() != null
|
||||||
&& document.getDocumentCatalog().getNames() != null) {
|
&& document.getDocumentCatalog().getNames() != null) {
|
||||||
@@ -53,7 +55,8 @@ public class ShowJavascript {
|
|||||||
|
|
||||||
script +=
|
script +=
|
||||||
"// File: "
|
"// File: "
|
||||||
+ inputFile.getOriginalFilename()
|
+ Filenames.toSimpleFileName(
|
||||||
|
inputFile.getOriginalFilename())
|
||||||
+ ", Script: "
|
+ ", Script: "
|
||||||
+ name
|
+ name
|
||||||
+ "\n"
|
+ "\n"
|
||||||
@@ -65,12 +68,14 @@ public class ShowJavascript {
|
|||||||
|
|
||||||
if (script.isEmpty()) {
|
if (script.isEmpty()) {
|
||||||
script =
|
script =
|
||||||
"PDF '" + inputFile.getOriginalFilename() + "' does not contain Javascript";
|
"PDF '"
|
||||||
|
+ Filenames.toSimpleFileName(inputFile.getOriginalFilename())
|
||||||
|
+ "' does not contain Javascript";
|
||||||
}
|
}
|
||||||
|
|
||||||
return WebResponseUtils.bytesToWebResponse(
|
return WebResponseUtils.bytesToWebResponse(
|
||||||
script.getBytes(StandardCharsets.UTF_8),
|
script.getBytes(StandardCharsets.UTF_8),
|
||||||
inputFile.getOriginalFilename() + ".js");
|
Filenames.toSimpleFileName(inputFile.getOriginalFilename()) + ".js");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,320 @@
|
|||||||
|
package stirling.software.SPDF.controller.api.misc;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.apache.pdfbox.Loader;
|
||||||
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
|
import org.apache.pdfbox.pdmodel.PDPage;
|
||||||
|
import org.apache.pdfbox.pdmodel.PDPageContentStream;
|
||||||
|
import org.apache.pdfbox.pdmodel.common.PDRectangle;
|
||||||
|
import org.apache.pdfbox.pdmodel.font.PDFont;
|
||||||
|
import org.apache.pdfbox.pdmodel.font.PDType0Font;
|
||||||
|
import org.apache.pdfbox.pdmodel.font.PDType1Font;
|
||||||
|
import org.apache.pdfbox.pdmodel.font.Standard14Fonts;
|
||||||
|
import org.apache.pdfbox.pdmodel.graphics.image.LosslessFactory;
|
||||||
|
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
|
||||||
|
import org.apache.pdfbox.pdmodel.graphics.state.PDExtendedGraphicsState;
|
||||||
|
import org.apache.pdfbox.util.Matrix;
|
||||||
|
import org.springframework.core.io.ClassPathResource;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import io.github.pixee.security.Filenames;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
|
||||||
|
import stirling.software.SPDF.model.api.misc.AddStampRequest;
|
||||||
|
import stirling.software.SPDF.utils.WebResponseUtils;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/v1/misc")
|
||||||
|
@Tag(name = "Misc", description = "Miscellaneous APIs")
|
||||||
|
public class StampController {
|
||||||
|
|
||||||
|
@PostMapping(consumes = "multipart/form-data", value = "/add-stamp")
|
||||||
|
@Operation(
|
||||||
|
summary = "Add stamp to a PDF file",
|
||||||
|
description =
|
||||||
|
"This endpoint adds a stamp to a given PDF file. Users can specify the stamp type (text or image), rotation, opacity, width spacer, and height spacer. Input:PDF Output:PDF Type:SISO")
|
||||||
|
public ResponseEntity<byte[]> addStamp(@ModelAttribute AddStampRequest request)
|
||||||
|
throws IOException, Exception {
|
||||||
|
MultipartFile pdfFile = request.getFileInput();
|
||||||
|
String stampType = request.getStampType();
|
||||||
|
String stampText = request.getStampText();
|
||||||
|
MultipartFile stampImage = request.getStampImage();
|
||||||
|
String alphabet = request.getAlphabet();
|
||||||
|
float fontSize = request.getFontSize();
|
||||||
|
float rotation = request.getRotation();
|
||||||
|
float opacity = request.getOpacity();
|
||||||
|
int position = request.getPosition(); // Updated to use 1-9 positioning logic
|
||||||
|
float overrideX = request.getOverrideX(); // New field for X override
|
||||||
|
float overrideY = request.getOverrideY(); // New field for Y override
|
||||||
|
|
||||||
|
String customColor = request.getCustomColor();
|
||||||
|
float marginFactor;
|
||||||
|
|
||||||
|
switch (request.getCustomMargin().toLowerCase()) {
|
||||||
|
case "small":
|
||||||
|
marginFactor = 0.02f;
|
||||||
|
break;
|
||||||
|
case "medium":
|
||||||
|
marginFactor = 0.035f;
|
||||||
|
break;
|
||||||
|
case "large":
|
||||||
|
marginFactor = 0.05f;
|
||||||
|
break;
|
||||||
|
case "x-large":
|
||||||
|
marginFactor = 0.075f;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
marginFactor = 0.035f;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the input PDF
|
||||||
|
PDDocument document = Loader.loadPDF(pdfFile.getBytes());
|
||||||
|
|
||||||
|
List<Integer> pageNumbers = request.getPageNumbersList(document, false);
|
||||||
|
|
||||||
|
for (int pageIndex : pageNumbers) {
|
||||||
|
int zeroBasedIndex = pageIndex - 1;
|
||||||
|
if (zeroBasedIndex >= 0 && zeroBasedIndex < document.getNumberOfPages()) {
|
||||||
|
PDPage page = document.getPage(zeroBasedIndex);
|
||||||
|
PDRectangle pageSize = page.getMediaBox();
|
||||||
|
float margin = marginFactor * (pageSize.getWidth() + pageSize.getHeight()) / 2;
|
||||||
|
|
||||||
|
PDPageContentStream contentStream =
|
||||||
|
new PDPageContentStream(
|
||||||
|
document, page, PDPageContentStream.AppendMode.APPEND, true, true);
|
||||||
|
|
||||||
|
PDExtendedGraphicsState graphicsState = new PDExtendedGraphicsState();
|
||||||
|
graphicsState.setNonStrokingAlphaConstant(opacity);
|
||||||
|
contentStream.setGraphicsStateParameters(graphicsState);
|
||||||
|
|
||||||
|
if ("text".equalsIgnoreCase(stampType)) {
|
||||||
|
addTextStamp(
|
||||||
|
contentStream,
|
||||||
|
stampText,
|
||||||
|
document,
|
||||||
|
page,
|
||||||
|
rotation,
|
||||||
|
position,
|
||||||
|
fontSize,
|
||||||
|
alphabet,
|
||||||
|
overrideX,
|
||||||
|
overrideY,
|
||||||
|
margin,
|
||||||
|
customColor);
|
||||||
|
} else if ("image".equalsIgnoreCase(stampType)) {
|
||||||
|
addImageStamp(
|
||||||
|
contentStream,
|
||||||
|
stampImage,
|
||||||
|
document,
|
||||||
|
page,
|
||||||
|
rotation,
|
||||||
|
position,
|
||||||
|
fontSize,
|
||||||
|
overrideX,
|
||||||
|
overrideY,
|
||||||
|
margin);
|
||||||
|
}
|
||||||
|
|
||||||
|
contentStream.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return WebResponseUtils.pdfDocToWebResponse(
|
||||||
|
document,
|
||||||
|
Filenames.toSimpleFileName(pdfFile.getOriginalFilename())
|
||||||
|
.replaceFirst("[.][^.]+$", "")
|
||||||
|
+ "_stamped.pdf");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addTextStamp(
|
||||||
|
PDPageContentStream contentStream,
|
||||||
|
String stampText,
|
||||||
|
PDDocument document,
|
||||||
|
PDPage page,
|
||||||
|
float rotation,
|
||||||
|
int position, // 1-9 positioning logic
|
||||||
|
float fontSize,
|
||||||
|
String alphabet,
|
||||||
|
float overrideX, // X override
|
||||||
|
float overrideY,
|
||||||
|
float margin,
|
||||||
|
String colorString) // Y override
|
||||||
|
throws IOException {
|
||||||
|
String resourceDir = "";
|
||||||
|
PDFont font = new PDType1Font(Standard14Fonts.FontName.HELVETICA);
|
||||||
|
switch (alphabet) {
|
||||||
|
case "arabic":
|
||||||
|
resourceDir = "static/fonts/NotoSansArabic-Regular.ttf";
|
||||||
|
break;
|
||||||
|
case "japanese":
|
||||||
|
resourceDir = "static/fonts/Meiryo.ttf";
|
||||||
|
break;
|
||||||
|
case "korean":
|
||||||
|
resourceDir = "static/fonts/malgun.ttf";
|
||||||
|
break;
|
||||||
|
case "chinese":
|
||||||
|
resourceDir = "static/fonts/SimSun.ttf";
|
||||||
|
break;
|
||||||
|
case "roman":
|
||||||
|
default:
|
||||||
|
resourceDir = "static/fonts/NotoSans-Regular.ttf";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!"".equals(resourceDir)) {
|
||||||
|
ClassPathResource classPathResource = new ClassPathResource(resourceDir);
|
||||||
|
String fileExtension = resourceDir.substring(resourceDir.lastIndexOf("."));
|
||||||
|
File tempFile = Files.createTempFile("NotoSansFont", fileExtension).toFile();
|
||||||
|
try (InputStream is = classPathResource.getInputStream();
|
||||||
|
FileOutputStream os = new FileOutputStream(tempFile)) {
|
||||||
|
IOUtils.copy(is, os);
|
||||||
|
}
|
||||||
|
|
||||||
|
font = PDType0Font.load(document, tempFile);
|
||||||
|
tempFile.deleteOnExit();
|
||||||
|
}
|
||||||
|
|
||||||
|
contentStream.setFont(font, fontSize);
|
||||||
|
|
||||||
|
Color redactColor;
|
||||||
|
try {
|
||||||
|
if (!colorString.startsWith("#")) {
|
||||||
|
colorString = "#" + colorString;
|
||||||
|
}
|
||||||
|
redactColor = Color.decode(colorString);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
|
||||||
|
redactColor = Color.LIGHT_GRAY;
|
||||||
|
}
|
||||||
|
|
||||||
|
contentStream.setNonStrokingColor(redactColor);
|
||||||
|
|
||||||
|
PDRectangle pageSize = page.getMediaBox();
|
||||||
|
float x, y;
|
||||||
|
|
||||||
|
if (overrideX >= 0 && overrideY >= 0) {
|
||||||
|
// Use override values if provided
|
||||||
|
x = overrideX;
|
||||||
|
y = overrideY;
|
||||||
|
} else {
|
||||||
|
x = calculatePositionX(pageSize, position, fontSize, font, fontSize, stampText, margin);
|
||||||
|
y =
|
||||||
|
calculatePositionY(
|
||||||
|
pageSize, position, calculateTextCapHeight(font, fontSize), margin);
|
||||||
|
}
|
||||||
|
|
||||||
|
contentStream.beginText();
|
||||||
|
contentStream.setTextMatrix(Matrix.getRotateInstance(Math.toRadians(rotation), x, y));
|
||||||
|
contentStream.showText(stampText);
|
||||||
|
contentStream.endText();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addImageStamp(
|
||||||
|
PDPageContentStream contentStream,
|
||||||
|
MultipartFile stampImage,
|
||||||
|
PDDocument document,
|
||||||
|
PDPage page,
|
||||||
|
float rotation,
|
||||||
|
int position, // 1-9 positioning logic
|
||||||
|
float fontSize,
|
||||||
|
float overrideX,
|
||||||
|
float overrideY,
|
||||||
|
float margin)
|
||||||
|
throws IOException {
|
||||||
|
|
||||||
|
// Load the stamp image
|
||||||
|
BufferedImage image = ImageIO.read(stampImage.getInputStream());
|
||||||
|
|
||||||
|
// Compute width based on original aspect ratio
|
||||||
|
float aspectRatio = (float) image.getWidth() / (float) image.getHeight();
|
||||||
|
|
||||||
|
// Desired physical height (in PDF points)
|
||||||
|
float desiredPhysicalHeight = fontSize;
|
||||||
|
|
||||||
|
// Desired physical width based on the aspect ratio
|
||||||
|
float desiredPhysicalWidth = desiredPhysicalHeight * aspectRatio;
|
||||||
|
|
||||||
|
// Convert the BufferedImage to PDImageXObject
|
||||||
|
PDImageXObject xobject = LosslessFactory.createFromImage(document, image);
|
||||||
|
|
||||||
|
PDRectangle pageSize = page.getMediaBox();
|
||||||
|
float x, y;
|
||||||
|
|
||||||
|
if (overrideX >= 0 && overrideY >= 0) {
|
||||||
|
// Use override values if provided
|
||||||
|
x = overrideX;
|
||||||
|
y = overrideY;
|
||||||
|
} else {
|
||||||
|
x = calculatePositionX(pageSize, position, desiredPhysicalWidth, null, 0, null, margin);
|
||||||
|
y = calculatePositionY(pageSize, position, fontSize, margin);
|
||||||
|
}
|
||||||
|
|
||||||
|
contentStream.saveGraphicsState();
|
||||||
|
contentStream.transform(Matrix.getTranslateInstance(x, y));
|
||||||
|
contentStream.transform(Matrix.getRotateInstance(Math.toRadians(rotation), 0, 0));
|
||||||
|
contentStream.drawImage(xobject, 0, 0, desiredPhysicalWidth, desiredPhysicalHeight);
|
||||||
|
contentStream.restoreGraphicsState();
|
||||||
|
}
|
||||||
|
|
||||||
|
private float calculatePositionX(
|
||||||
|
PDRectangle pageSize,
|
||||||
|
int position,
|
||||||
|
float contentWidth,
|
||||||
|
PDFont font,
|
||||||
|
float fontSize,
|
||||||
|
String text,
|
||||||
|
float margin)
|
||||||
|
throws IOException {
|
||||||
|
float actualWidth =
|
||||||
|
(text != null) ? calculateTextWidth(text, font, fontSize) : contentWidth;
|
||||||
|
switch (position % 3) {
|
||||||
|
case 1: // Left
|
||||||
|
return pageSize.getLowerLeftX() + margin;
|
||||||
|
case 2: // Center
|
||||||
|
return (pageSize.getWidth() - actualWidth) / 2;
|
||||||
|
case 0: // Right
|
||||||
|
return pageSize.getUpperRightX() - actualWidth - margin;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private float calculatePositionY(
|
||||||
|
PDRectangle pageSize, int position, float height, float margin) {
|
||||||
|
switch ((position - 1) / 3) {
|
||||||
|
case 0: // Top
|
||||||
|
return pageSize.getUpperRightY() - height - margin;
|
||||||
|
case 1: // Middle
|
||||||
|
return (pageSize.getHeight() - height) / 2;
|
||||||
|
case 2: // Bottom
|
||||||
|
return pageSize.getLowerLeftY() + margin;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private float calculateTextWidth(String text, PDFont font, float fontSize) throws IOException {
|
||||||
|
return font.getStringWidth(text) / 1000 * fontSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
private float calculateTextCapHeight(PDFont font, float fontSize) {
|
||||||
|
return font.getFontDescriptor().getCapHeight() / 1000 * fontSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
package stirling.software.SPDF.controller.api.pipeline;
|
package stirling.software.SPDF.controller.api.pipeline;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
@@ -34,11 +36,62 @@ public class ApiDocService {
|
|||||||
|
|
||||||
private String getApiDocsUrl() {
|
private String getApiDocsUrl() {
|
||||||
String contextPath = servletContext.getContextPath();
|
String contextPath = servletContext.getContextPath();
|
||||||
String port = SPdfApplication.getPort();
|
String port = SPdfApplication.getStaticPort();
|
||||||
|
|
||||||
return "http://localhost:" + port + contextPath + "/v1/api-docs";
|
return "http://localhost:" + port + contextPath + "/v1/api-docs";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Map<String, List<String>> outputToFileTypes = new HashMap<>();
|
||||||
|
|
||||||
|
public List getExtensionTypes(boolean output, String operationName) {
|
||||||
|
if (outputToFileTypes.size() == 0) {
|
||||||
|
outputToFileTypes.put("PDF", Arrays.asList("pdf"));
|
||||||
|
outputToFileTypes.put(
|
||||||
|
"IMAGE",
|
||||||
|
Arrays.asList(
|
||||||
|
"png", "jpg", "jpeg", "gif", "webp", "bmp", "tif", "tiff", "svg", "psd",
|
||||||
|
"ai", "eps"));
|
||||||
|
outputToFileTypes.put(
|
||||||
|
"ZIP",
|
||||||
|
Arrays.asList("zip", "rar", "7z", "tar", "gz", "bz2", "xz", "lz", "lzma", "z"));
|
||||||
|
outputToFileTypes.put("WORD", Arrays.asList("doc", "docx", "odt", "rtf"));
|
||||||
|
outputToFileTypes.put("CSV", Arrays.asList("csv"));
|
||||||
|
outputToFileTypes.put("JS", Arrays.asList("js", "jsx"));
|
||||||
|
outputToFileTypes.put("HTML", Arrays.asList("html", "htm", "xhtml"));
|
||||||
|
outputToFileTypes.put("JSON", Arrays.asList("json"));
|
||||||
|
outputToFileTypes.put("TXT", Arrays.asList("txt", "text", "md", "markdown"));
|
||||||
|
outputToFileTypes.put("PPT", Arrays.asList("ppt", "pptx", "odp"));
|
||||||
|
outputToFileTypes.put("XML", Arrays.asList("xml", "xsd", "xsl"));
|
||||||
|
outputToFileTypes.put(
|
||||||
|
"BOOK", Arrays.asList("epub", "mobi", "azw3", "fb2", "txt", "docx"));
|
||||||
|
// type.
|
||||||
|
}
|
||||||
|
|
||||||
|
if (apiDocsJsonRootNode == null || apiDocumentation.size() == 0) {
|
||||||
|
loadApiDocumentation();
|
||||||
|
}
|
||||||
|
if (!apiDocumentation.containsKey(operationName)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
ApiEndpoint endpoint = apiDocumentation.get(operationName);
|
||||||
|
String description = endpoint.getDescription();
|
||||||
|
Pattern pattern = null;
|
||||||
|
if (output) {
|
||||||
|
pattern = Pattern.compile("Output:(\\w+)");
|
||||||
|
} else {
|
||||||
|
pattern = Pattern.compile("Input:(\\w+)");
|
||||||
|
}
|
||||||
|
Matcher matcher = pattern.matcher(description);
|
||||||
|
while (matcher.find()) {
|
||||||
|
String type = matcher.group(1).toUpperCase();
|
||||||
|
if (outputToFileTypes.containsKey(type)) {
|
||||||
|
return outputToFileTypes.get(type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@Autowired(required = false)
|
@Autowired(required = false)
|
||||||
private UserServiceInterface userService;
|
private UserServiceInterface userService;
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import java.nio.file.Files;
|
|||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
@@ -35,6 +36,9 @@ import org.springframework.util.MultiValueMap;
|
|||||||
import org.springframework.web.client.RestTemplate;
|
import org.springframework.web.client.RestTemplate;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import io.github.pixee.security.Filenames;
|
||||||
|
import io.github.pixee.security.ZipSecurity;
|
||||||
|
|
||||||
import jakarta.servlet.ServletContext;
|
import jakarta.servlet.ServletContext;
|
||||||
import stirling.software.SPDF.SPdfApplication;
|
import stirling.software.SPDF.SPdfApplication;
|
||||||
import stirling.software.SPDF.model.PipelineConfig;
|
import stirling.software.SPDF.model.PipelineConfig;
|
||||||
@@ -60,7 +64,7 @@ public class PipelineProcessor {
|
|||||||
|
|
||||||
private String getBaseUrl() {
|
private String getBaseUrl() {
|
||||||
String contextPath = servletContext.getContextPath();
|
String contextPath = servletContext.getContextPath();
|
||||||
String port = SPdfApplication.getPort();
|
String port = SPdfApplication.getStaticPort();
|
||||||
|
|
||||||
return "http://localhost:" + port + contextPath + "/";
|
return "http://localhost:" + port + contextPath + "/";
|
||||||
}
|
}
|
||||||
@@ -82,15 +86,11 @@ public class PipelineProcessor {
|
|||||||
operation,
|
operation,
|
||||||
isMultiInputOperation);
|
isMultiInputOperation);
|
||||||
Map<String, Object> parameters = pipelineOperation.getParameters();
|
Map<String, Object> parameters = pipelineOperation.getParameters();
|
||||||
String inputFileExtension = "";
|
List<String> inputFileTypes = apiDocService.getExtensionTypes(false, operation);
|
||||||
|
if (inputFileTypes == null) {
|
||||||
// TODO
|
inputFileTypes = new ArrayList<String>(Arrays.asList("ALL"));
|
||||||
// if (operationNode.has("inputFileType")) {
|
}
|
||||||
// inputFileExtension = operationNode.get("inputFileType").asText();
|
// List outputFileTypes = apiDocService.getExtensionTypes(true, operation);
|
||||||
// } else {
|
|
||||||
inputFileExtension = ".pdf";
|
|
||||||
// }
|
|
||||||
final String finalInputFileExtension = inputFileExtension;
|
|
||||||
|
|
||||||
String url = getBaseUrl() + operation;
|
String url = getBaseUrl() + operation;
|
||||||
|
|
||||||
@@ -98,38 +98,42 @@ public class PipelineProcessor {
|
|||||||
if (!isMultiInputOperation) {
|
if (!isMultiInputOperation) {
|
||||||
for (Resource file : outputFiles) {
|
for (Resource file : outputFiles) {
|
||||||
boolean hasInputFileType = false;
|
boolean hasInputFileType = false;
|
||||||
if (file.getFilename().endsWith(inputFileExtension)) {
|
for (String extension : inputFileTypes) {
|
||||||
hasInputFileType = true;
|
if ("ALL".equals(extension) || file.getFilename().endsWith(extension)) {
|
||||||
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
|
hasInputFileType = true;
|
||||||
body.add("fileInput", file);
|
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
|
||||||
|
body.add("fileInput", file);
|
||||||
|
|
||||||
for (Entry<String, Object> entry : parameters.entrySet()) {
|
for (Entry<String, Object> entry : parameters.entrySet()) {
|
||||||
body.add(entry.getKey(), entry.getValue());
|
body.add(entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
ResponseEntity<byte[]> response = sendWebRequest(url, body);
|
||||||
|
|
||||||
|
// If the operation is filter and the response body is null or empty,
|
||||||
|
// skip
|
||||||
|
// this
|
||||||
|
// file
|
||||||
|
if (operation.startsWith("filter-")
|
||||||
|
&& (response.getBody() == null
|
||||||
|
|| response.getBody().length == 0)) {
|
||||||
|
logger.info("Skipping file due to failing {}", operation);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!response.getStatusCode().equals(HttpStatus.OK)) {
|
||||||
|
logPrintStream.println("Error: " + response.getBody());
|
||||||
|
hasErrors = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
processOutputFiles(operation, response, newOutputFiles);
|
||||||
}
|
}
|
||||||
|
|
||||||
ResponseEntity<byte[]> response = sendWebRequest(url, body);
|
|
||||||
|
|
||||||
// If the operation is filter and the response body is null or empty, skip
|
|
||||||
// this
|
|
||||||
// file
|
|
||||||
if (operation.startsWith("filter-")
|
|
||||||
&& (response.getBody() == null || response.getBody().length == 0)) {
|
|
||||||
logger.info("Skipping file due to failing {}", operation);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!response.getStatusCode().equals(HttpStatus.OK)) {
|
|
||||||
logPrintStream.println("Error: " + response.getBody());
|
|
||||||
hasErrors = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
processOutputFiles(operation, file.getFilename(), response, newOutputFiles);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hasInputFileType) {
|
if (!hasInputFileType) {
|
||||||
logPrintStream.println(
|
logPrintStream.println(
|
||||||
"No files with extension "
|
"No files with extension "
|
||||||
+ inputFileExtension
|
+ String.join(", ", inputFileTypes)
|
||||||
+ " found for operation "
|
+ " found for operation "
|
||||||
+ operation);
|
+ operation);
|
||||||
hasErrors = true;
|
hasErrors = true;
|
||||||
@@ -138,13 +142,19 @@ public class PipelineProcessor {
|
|||||||
|
|
||||||
} else {
|
} else {
|
||||||
// Filter and collect all files that match the inputFileExtension
|
// Filter and collect all files that match the inputFileExtension
|
||||||
List<Resource> matchingFiles =
|
List<Resource> matchingFiles;
|
||||||
outputFiles.stream()
|
if (inputFileTypes.contains("ALL")) {
|
||||||
.filter(
|
matchingFiles = new ArrayList<>(outputFiles);
|
||||||
file ->
|
} else {
|
||||||
file.getFilename()
|
final List<String> finalinputFileTypes = inputFileTypes;
|
||||||
.endsWith(finalInputFileExtension))
|
matchingFiles =
|
||||||
.collect(Collectors.toList());
|
outputFiles.stream()
|
||||||
|
.filter(
|
||||||
|
file ->
|
||||||
|
finalinputFileTypes.stream()
|
||||||
|
.anyMatch(file.getFilename()::endsWith))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
// Check if there are matching files
|
// Check if there are matching files
|
||||||
if (!matchingFiles.isEmpty()) {
|
if (!matchingFiles.isEmpty()) {
|
||||||
@@ -164,11 +174,7 @@ public class PipelineProcessor {
|
|||||||
|
|
||||||
// Handle the response
|
// Handle the response
|
||||||
if (response.getStatusCode().equals(HttpStatus.OK)) {
|
if (response.getStatusCode().equals(HttpStatus.OK)) {
|
||||||
processOutputFiles(
|
processOutputFiles(operation, response, newOutputFiles);
|
||||||
operation,
|
|
||||||
matchingFiles.get(0).getFilename(),
|
|
||||||
response,
|
|
||||||
newOutputFiles);
|
|
||||||
} else {
|
} else {
|
||||||
// Log error if the response status is not OK
|
// Log error if the response status is not OK
|
||||||
logPrintStream.println(
|
logPrintStream.println(
|
||||||
@@ -178,7 +184,7 @@ public class PipelineProcessor {
|
|||||||
} else {
|
} else {
|
||||||
logPrintStream.println(
|
logPrintStream.println(
|
||||||
"No files with extension "
|
"No files with extension "
|
||||||
+ inputFileExtension
|
+ String.join(", ", inputFileTypes)
|
||||||
+ " found for multi-input operation "
|
+ " found for multi-input operation "
|
||||||
+ operation);
|
+ operation);
|
||||||
hasErrors = true;
|
hasErrors = true;
|
||||||
@@ -211,11 +217,29 @@ public class PipelineProcessor {
|
|||||||
return restTemplate.exchange(url, HttpMethod.POST, entity, byte[].class);
|
return restTemplate.exchange(url, HttpMethod.POST, entity, byte[].class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String removeTrailingNaming(String filename) {
|
||||||
|
// Splitting filename into name and extension
|
||||||
|
int dotIndex = filename.lastIndexOf(".");
|
||||||
|
if (dotIndex == -1) {
|
||||||
|
// No extension found
|
||||||
|
return filename;
|
||||||
|
}
|
||||||
|
String name = filename.substring(0, dotIndex);
|
||||||
|
String extension = filename.substring(dotIndex);
|
||||||
|
|
||||||
|
// Finding the last underscore
|
||||||
|
int underscoreIndex = name.lastIndexOf("_");
|
||||||
|
if (underscoreIndex == -1) {
|
||||||
|
// No underscore found
|
||||||
|
return filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Removing the last part and reattaching the extension
|
||||||
|
return name.substring(0, underscoreIndex) + extension;
|
||||||
|
}
|
||||||
|
|
||||||
private List<Resource> processOutputFiles(
|
private List<Resource> processOutputFiles(
|
||||||
String operation,
|
String operation, ResponseEntity<byte[]> response, List<Resource> newOutputFiles)
|
||||||
String fileName,
|
|
||||||
ResponseEntity<byte[]> response,
|
|
||||||
List<Resource> newOutputFiles)
|
|
||||||
throws IOException {
|
throws IOException {
|
||||||
// Define filename
|
// Define filename
|
||||||
String newFilename;
|
String newFilename;
|
||||||
@@ -227,7 +251,7 @@ public class PipelineProcessor {
|
|||||||
newFilename = extractFilename(response);
|
newFilename = extractFilename(response);
|
||||||
} else {
|
} else {
|
||||||
// Otherwise, keep the original filename.
|
// Otherwise, keep the original filename.
|
||||||
newFilename = fileName;
|
newFilename = removeTrailingNaming(extractFilename(response));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the response body is a zip file
|
// Check if the response body is a zip file
|
||||||
@@ -312,7 +336,7 @@ public class PipelineProcessor {
|
|||||||
new ByteArrayResource(file.getBytes()) {
|
new ByteArrayResource(file.getBytes()) {
|
||||||
@Override
|
@Override
|
||||||
public String getFilename() {
|
public String getFilename() {
|
||||||
return file.getOriginalFilename();
|
return Filenames.toSimpleFileName(file.getOriginalFilename());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
outputFiles.add(fileResource);
|
outputFiles.add(fileResource);
|
||||||
@@ -335,7 +359,7 @@ public class PipelineProcessor {
|
|||||||
List<Resource> unzippedFiles = new ArrayList<>();
|
List<Resource> unzippedFiles = new ArrayList<>();
|
||||||
|
|
||||||
try (ByteArrayInputStream bais = new ByteArrayInputStream(data);
|
try (ByteArrayInputStream bais = new ByteArrayInputStream(data);
|
||||||
ZipInputStream zis = new ZipInputStream(bais)) {
|
ZipInputStream zis = ZipSecurity.createHardenedInputStream(bais)) {
|
||||||
|
|
||||||
ZipEntry entry;
|
ZipEntry entry;
|
||||||
while ((entry = zis.getNextEntry()) != null) {
|
while ((entry = zis.getNextEntry()) != null) {
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import java.security.cert.CertificateException;
|
|||||||
import java.security.cert.CertificateFactory;
|
import java.security.cert.CertificateFactory;
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
|
|
||||||
|
import org.apache.pdfbox.Loader;
|
||||||
import org.apache.pdfbox.examples.signature.CreateSignatureBase;
|
import org.apache.pdfbox.examples.signature.CreateSignatureBase;
|
||||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature;
|
import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature;
|
||||||
@@ -41,6 +42,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import io.github.pixee.security.Filenames;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
|
||||||
@@ -73,7 +75,7 @@ public class CertSignController {
|
|||||||
@Operation(
|
@Operation(
|
||||||
summary = "Sign PDF with a Digital Certificate",
|
summary = "Sign PDF with a Digital Certificate",
|
||||||
description =
|
description =
|
||||||
"This endpoint accepts a PDF file, a digital certificate and related information to sign the PDF. It then returns the digitally signed PDF file. Input:PDF Output:PDF Type:MF-SISO")
|
"This endpoint accepts a PDF file, a digital certificate and related information to sign the PDF. It then returns the digitally signed PDF file. Input:PDF Output:PDF Type:SISO")
|
||||||
public ResponseEntity<byte[]> signPDFWithCert(@ModelAttribute SignPDFWithCertRequest request)
|
public ResponseEntity<byte[]> signPDFWithCert(@ModelAttribute SignPDFWithCertRequest request)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
MultipartFile pdf = request.getFileInput();
|
MultipartFile pdf = request.getFileInput();
|
||||||
@@ -122,7 +124,9 @@ public class CertSignController {
|
|||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
sign(pdf.getBytes(), baos, createSignature, name, location, reason);
|
sign(pdf.getBytes(), baos, createSignature, name, location, reason);
|
||||||
return WebResponseUtils.boasToWebResponse(
|
return WebResponseUtils.boasToWebResponse(
|
||||||
baos, pdf.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_signed.pdf");
|
baos,
|
||||||
|
Filenames.toSimpleFileName(pdf.getOriginalFilename()).replaceFirst("[.][^.]+$", "")
|
||||||
|
+ "_signed.pdf");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void sign(
|
private static void sign(
|
||||||
@@ -132,7 +136,7 @@ public class CertSignController {
|
|||||||
String name,
|
String name,
|
||||||
String location,
|
String location,
|
||||||
String reason) {
|
String reason) {
|
||||||
try (PDDocument doc = PDDocument.load(input)) {
|
try (PDDocument doc = Loader.loadPDF(input)) {
|
||||||
PDSignature signature = new PDSignature();
|
PDSignature signature = new PDSignature();
|
||||||
signature.setFilter(PDSignature.FILTER_ADOBE_PPKLITE);
|
signature.setFilter(PDSignature.FILTER_ADOBE_PPKLITE);
|
||||||
signature.setSubFilter(PDSignature.SUBFILTER_ADBE_PKCS7_DETACHED);
|
signature.setSubFilter(PDSignature.SUBFILTER_ADBE_PKCS7_DETACHED);
|
||||||
|
|||||||
@@ -11,11 +11,9 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.apache.pdfbox.cos.COSDocument;
|
import org.apache.pdfbox.Loader;
|
||||||
import org.apache.pdfbox.cos.COSInputStream;
|
import org.apache.pdfbox.cos.COSInputStream;
|
||||||
import org.apache.pdfbox.cos.COSName;
|
import org.apache.pdfbox.cos.COSName;
|
||||||
import org.apache.pdfbox.cos.COSObject;
|
|
||||||
import org.apache.pdfbox.cos.COSStream;
|
|
||||||
import org.apache.pdfbox.cos.COSString;
|
import org.apache.pdfbox.cos.COSString;
|
||||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
import org.apache.pdfbox.pdmodel.PDDocumentCatalog;
|
import org.apache.pdfbox.pdmodel.PDDocumentCatalog;
|
||||||
@@ -87,7 +85,7 @@ public class GetInfoOnPDF {
|
|||||||
@Operation(summary = "Summary here", description = "desc. Input:PDF Output:JSON Type:SISO")
|
@Operation(summary = "Summary here", description = "desc. Input:PDF Output:JSON Type:SISO")
|
||||||
public ResponseEntity<byte[]> getPdfInfo(@ModelAttribute PDFFile request) throws IOException {
|
public ResponseEntity<byte[]> getPdfInfo(@ModelAttribute PDFFile request) throws IOException {
|
||||||
MultipartFile inputFile = request.getFileInput();
|
MultipartFile inputFile = request.getFileInput();
|
||||||
try (PDDocument pdfBoxDoc = PDDocument.load(inputFile.getInputStream()); ) {
|
try (PDDocument pdfBoxDoc = Loader.loadPDF(inputFile.getBytes()); ) {
|
||||||
ObjectMapper objectMapper = new ObjectMapper();
|
ObjectMapper objectMapper = new ObjectMapper();
|
||||||
ObjectNode jsonOutput = objectMapper.createObjectNode();
|
ObjectNode jsonOutput = objectMapper.createObjectNode();
|
||||||
|
|
||||||
@@ -129,17 +127,6 @@ public class GetInfoOnPDF {
|
|||||||
boolean hasCompression = false;
|
boolean hasCompression = false;
|
||||||
String compressionType = "None";
|
String compressionType = "None";
|
||||||
|
|
||||||
COSDocument cosDoc = pdfBoxDoc.getDocument();
|
|
||||||
for (COSObject cosObject : cosDoc.getObjects()) {
|
|
||||||
if (cosObject.getObject() instanceof COSStream) {
|
|
||||||
COSStream cosStream = (COSStream) cosObject.getObject();
|
|
||||||
if (COSName.OBJ_STM.equals(cosStream.getItem(COSName.TYPE))) {
|
|
||||||
hasCompression = true;
|
|
||||||
compressionType = "Object Streams";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
basicInfo.put("Compression", hasCompression);
|
basicInfo.put("Compression", hasCompression);
|
||||||
if (hasCompression) basicInfo.put("CompressionType", compressionType);
|
if (hasCompression) basicInfo.put("CompressionType", compressionType);
|
||||||
|
|
||||||
@@ -343,7 +330,6 @@ public class GetInfoOnPDF {
|
|||||||
permissionsNode.put("CanModify", ap.canModify());
|
permissionsNode.put("CanModify", ap.canModify());
|
||||||
permissionsNode.put("CanModifyAnnotations", ap.canModifyAnnotations());
|
permissionsNode.put("CanModifyAnnotations", ap.canModifyAnnotations());
|
||||||
permissionsNode.put("CanPrint", ap.canPrint());
|
permissionsNode.put("CanPrint", ap.canPrint());
|
||||||
permissionsNode.put("CanPrintDegraded", ap.canPrintDegraded());
|
|
||||||
|
|
||||||
encryption.set(
|
encryption.set(
|
||||||
"Permissions", permissionsNode); // set the node under "Permissions"
|
"Permissions", permissionsNode); // set the node under "Permissions"
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package stirling.software.SPDF.controller.api.security;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.apache.pdfbox.Loader;
|
||||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
import org.apache.pdfbox.pdmodel.encryption.AccessPermission;
|
import org.apache.pdfbox.pdmodel.encryption.AccessPermission;
|
||||||
import org.apache.pdfbox.pdmodel.encryption.StandardProtectionPolicy;
|
import org.apache.pdfbox.pdmodel.encryption.StandardProtectionPolicy;
|
||||||
@@ -14,6 +15,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import io.github.pixee.security.Filenames;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
|
||||||
@@ -38,11 +40,12 @@ public class PasswordController {
|
|||||||
MultipartFile fileInput = request.getFileInput();
|
MultipartFile fileInput = request.getFileInput();
|
||||||
String password = request.getPassword();
|
String password = request.getPassword();
|
||||||
|
|
||||||
PDDocument document = PDDocument.load(fileInput.getBytes(), password);
|
PDDocument document = Loader.loadPDF(fileInput.getBytes(), password);
|
||||||
document.setAllSecurityToBeRemoved(true);
|
document.setAllSecurityToBeRemoved(true);
|
||||||
return WebResponseUtils.pdfDocToWebResponse(
|
return WebResponseUtils.pdfDocToWebResponse(
|
||||||
document,
|
document,
|
||||||
fileInput.getOriginalFilename().replaceFirst("[.][^.]+$", "")
|
Filenames.toSimpleFileName(fileInput.getOriginalFilename())
|
||||||
|
.replaceFirst("[.][^.]+$", "")
|
||||||
+ "_password_removed.pdf");
|
+ "_password_removed.pdf");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,7 +69,7 @@ public class PasswordController {
|
|||||||
boolean canPrint = request.isCanPrint();
|
boolean canPrint = request.isCanPrint();
|
||||||
boolean canPrintFaithful = request.isCanPrintFaithful();
|
boolean canPrintFaithful = request.isCanPrintFaithful();
|
||||||
|
|
||||||
PDDocument document = PDDocument.load(fileInput.getBytes());
|
PDDocument document = Loader.loadPDF(fileInput.getBytes());
|
||||||
AccessPermission ap = new AccessPermission();
|
AccessPermission ap = new AccessPermission();
|
||||||
ap.setCanAssembleDocument(!canAssembleDocument);
|
ap.setCanAssembleDocument(!canAssembleDocument);
|
||||||
ap.setCanExtractContent(!canExtractContent);
|
ap.setCanExtractContent(!canExtractContent);
|
||||||
@@ -87,10 +90,13 @@ public class PasswordController {
|
|||||||
if ("".equals(ownerPassword) && "".equals(password))
|
if ("".equals(ownerPassword) && "".equals(password))
|
||||||
return WebResponseUtils.pdfDocToWebResponse(
|
return WebResponseUtils.pdfDocToWebResponse(
|
||||||
document,
|
document,
|
||||||
fileInput.getOriginalFilename().replaceFirst("[.][^.]+$", "")
|
Filenames.toSimpleFileName(fileInput.getOriginalFilename())
|
||||||
|
.replaceFirst("[.][^.]+$", "")
|
||||||
+ "_permissions.pdf");
|
+ "_permissions.pdf");
|
||||||
return WebResponseUtils.pdfDocToWebResponse(
|
return WebResponseUtils.pdfDocToWebResponse(
|
||||||
document,
|
document,
|
||||||
fileInput.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_passworded.pdf");
|
Filenames.toSimpleFileName(fileInput.getOriginalFilename())
|
||||||
|
.replaceFirst("[.][^.]+$", "")
|
||||||
|
+ "_passworded.pdf");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,14 +2,15 @@ package stirling.software.SPDF.controller.api.security;
|
|||||||
|
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.pdfbox.Loader;
|
||||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
import org.apache.pdfbox.pdmodel.PDPage;
|
import org.apache.pdfbox.pdmodel.PDPage;
|
||||||
import org.apache.pdfbox.pdmodel.PDPageContentStream;
|
import org.apache.pdfbox.pdmodel.PDPageContentStream;
|
||||||
|
import org.apache.pdfbox.pdmodel.PDPageContentStream.AppendMode;
|
||||||
import org.apache.pdfbox.pdmodel.common.PDRectangle;
|
import org.apache.pdfbox.pdmodel.common.PDRectangle;
|
||||||
import org.apache.pdfbox.pdmodel.graphics.image.LosslessFactory;
|
import org.apache.pdfbox.pdmodel.graphics.image.LosslessFactory;
|
||||||
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
|
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
|
||||||
@@ -24,6 +25,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import io.github.pixee.security.Filenames;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
|
||||||
@@ -57,7 +59,7 @@ public class RedactController {
|
|||||||
System.out.println(listOfTextString);
|
System.out.println(listOfTextString);
|
||||||
String[] listOfText = listOfTextString.split("\n");
|
String[] listOfText = listOfTextString.split("\n");
|
||||||
byte[] bytes = file.getBytes();
|
byte[] bytes = file.getBytes();
|
||||||
PDDocument document = PDDocument.load(new ByteArrayInputStream(bytes));
|
PDDocument document = Loader.loadPDF(bytes);
|
||||||
|
|
||||||
Color redactColor;
|
Color redactColor;
|
||||||
try {
|
try {
|
||||||
@@ -86,7 +88,9 @@ public class RedactController {
|
|||||||
PDPage newPage = new PDPage(new PDRectangle(bim.getWidth(), bim.getHeight()));
|
PDPage newPage = new PDPage(new PDRectangle(bim.getWidth(), bim.getHeight()));
|
||||||
imageDocument.addPage(newPage);
|
imageDocument.addPage(newPage);
|
||||||
PDImageXObject pdImage = LosslessFactory.createFromImage(imageDocument, bim);
|
PDImageXObject pdImage = LosslessFactory.createFromImage(imageDocument, bim);
|
||||||
PDPageContentStream contentStream = new PDPageContentStream(imageDocument, newPage);
|
PDPageContentStream contentStream =
|
||||||
|
new PDPageContentStream(
|
||||||
|
imageDocument, newPage, AppendMode.APPEND, true, true);
|
||||||
contentStream.drawImage(pdImage, 0, 0);
|
contentStream.drawImage(pdImage, 0, 0);
|
||||||
contentStream.close();
|
contentStream.close();
|
||||||
}
|
}
|
||||||
@@ -101,7 +105,8 @@ public class RedactController {
|
|||||||
byte[] pdfContent = baos.toByteArray();
|
byte[] pdfContent = baos.toByteArray();
|
||||||
return WebResponseUtils.bytesToWebResponse(
|
return WebResponseUtils.bytesToWebResponse(
|
||||||
pdfContent,
|
pdfContent,
|
||||||
file.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_redacted.pdf");
|
Filenames.toSimpleFileName(file.getOriginalFilename()).replaceFirst("[.][^.]+$", "")
|
||||||
|
+ "_redacted.pdf");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void redactFoundText(
|
private void redactFoundText(
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package stirling.software.SPDF.controller.api.security;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.apache.pdfbox.Loader;
|
||||||
import org.apache.pdfbox.cos.COSDictionary;
|
import org.apache.pdfbox.cos.COSDictionary;
|
||||||
import org.apache.pdfbox.cos.COSName;
|
import org.apache.pdfbox.cos.COSName;
|
||||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
@@ -27,6 +28,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import io.github.pixee.security.Filenames;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
|
||||||
@@ -52,7 +54,7 @@ public class SanitizeController {
|
|||||||
boolean removeLinks = request.isRemoveLinks();
|
boolean removeLinks = request.isRemoveLinks();
|
||||||
boolean removeFonts = request.isRemoveFonts();
|
boolean removeFonts = request.isRemoveFonts();
|
||||||
|
|
||||||
try (PDDocument document = PDDocument.load(inputFile.getInputStream())) {
|
try (PDDocument document = Loader.loadPDF(inputFile.getBytes())) {
|
||||||
if (removeJavaScript) {
|
if (removeJavaScript) {
|
||||||
sanitizeJavaScript(document);
|
sanitizeJavaScript(document);
|
||||||
}
|
}
|
||||||
@@ -75,7 +77,8 @@ public class SanitizeController {
|
|||||||
|
|
||||||
return WebResponseUtils.pdfDocToWebResponse(
|
return WebResponseUtils.pdfDocToWebResponse(
|
||||||
document,
|
document,
|
||||||
inputFile.getOriginalFilename().replaceFirst("[.][^.]+$", "")
|
Filenames.toSimpleFileName(inputFile.getOriginalFilename())
|
||||||
|
.replaceFirst("[.][^.]+$", "")
|
||||||
+ "_sanitized.pdf");
|
+ "_sanitized.pdf");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,16 +6,19 @@ import java.io.File;
|
|||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
|
||||||
import javax.imageio.ImageIO;
|
import javax.imageio.ImageIO;
|
||||||
|
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.apache.pdfbox.Loader;
|
||||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
import org.apache.pdfbox.pdmodel.PDPage;
|
import org.apache.pdfbox.pdmodel.PDPage;
|
||||||
import org.apache.pdfbox.pdmodel.PDPageContentStream;
|
import org.apache.pdfbox.pdmodel.PDPageContentStream;
|
||||||
import org.apache.pdfbox.pdmodel.font.PDFont;
|
import org.apache.pdfbox.pdmodel.font.PDFont;
|
||||||
import org.apache.pdfbox.pdmodel.font.PDType0Font;
|
import org.apache.pdfbox.pdmodel.font.PDType0Font;
|
||||||
import org.apache.pdfbox.pdmodel.font.PDType1Font;
|
import org.apache.pdfbox.pdmodel.font.PDType1Font;
|
||||||
|
import org.apache.pdfbox.pdmodel.font.Standard14Fonts;
|
||||||
import org.apache.pdfbox.pdmodel.graphics.image.LosslessFactory;
|
import org.apache.pdfbox.pdmodel.graphics.image.LosslessFactory;
|
||||||
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
|
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
|
||||||
import org.apache.pdfbox.pdmodel.graphics.state.PDExtendedGraphicsState;
|
import org.apache.pdfbox.pdmodel.graphics.state.PDExtendedGraphicsState;
|
||||||
@@ -28,6 +31,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import io.github.pixee.security.Filenames;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
|
||||||
@@ -58,7 +62,7 @@ public class WatermarkController {
|
|||||||
int heightSpacer = request.getHeightSpacer();
|
int heightSpacer = request.getHeightSpacer();
|
||||||
|
|
||||||
// Load the input PDF
|
// Load the input PDF
|
||||||
PDDocument document = PDDocument.load(pdfFile.getInputStream());
|
PDDocument document = Loader.loadPDF(pdfFile.getBytes());
|
||||||
|
|
||||||
// Create a page in the document
|
// Create a page in the document
|
||||||
for (PDPage page : document.getPages()) {
|
for (PDPage page : document.getPages()) {
|
||||||
@@ -66,14 +70,14 @@ public class WatermarkController {
|
|||||||
// Get the page's content stream
|
// Get the page's content stream
|
||||||
PDPageContentStream contentStream =
|
PDPageContentStream contentStream =
|
||||||
new PDPageContentStream(
|
new PDPageContentStream(
|
||||||
document, page, PDPageContentStream.AppendMode.APPEND, true);
|
document, page, PDPageContentStream.AppendMode.APPEND, true, true);
|
||||||
|
|
||||||
// Set transparency
|
// Set transparency
|
||||||
PDExtendedGraphicsState graphicsState = new PDExtendedGraphicsState();
|
PDExtendedGraphicsState graphicsState = new PDExtendedGraphicsState();
|
||||||
graphicsState.setNonStrokingAlphaConstant(opacity);
|
graphicsState.setNonStrokingAlphaConstant(opacity);
|
||||||
contentStream.setGraphicsStateParameters(graphicsState);
|
contentStream.setGraphicsStateParameters(graphicsState);
|
||||||
|
|
||||||
if (watermarkType.equalsIgnoreCase("text")) {
|
if ("text".equalsIgnoreCase(watermarkType)) {
|
||||||
addTextWatermark(
|
addTextWatermark(
|
||||||
contentStream,
|
contentStream,
|
||||||
watermarkText,
|
watermarkText,
|
||||||
@@ -84,7 +88,7 @@ public class WatermarkController {
|
|||||||
heightSpacer,
|
heightSpacer,
|
||||||
fontSize,
|
fontSize,
|
||||||
alphabet);
|
alphabet);
|
||||||
} else if (watermarkType.equalsIgnoreCase("image")) {
|
} else if ("image".equalsIgnoreCase(watermarkType)) {
|
||||||
addImageWatermark(
|
addImageWatermark(
|
||||||
contentStream,
|
contentStream,
|
||||||
watermarkImage,
|
watermarkImage,
|
||||||
@@ -102,7 +106,9 @@ public class WatermarkController {
|
|||||||
|
|
||||||
return WebResponseUtils.pdfDocToWebResponse(
|
return WebResponseUtils.pdfDocToWebResponse(
|
||||||
document,
|
document,
|
||||||
pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_watermarked.pdf");
|
Filenames.toSimpleFileName(pdfFile.getOriginalFilename())
|
||||||
|
.replaceFirst("[.][^.]+$", "")
|
||||||
|
+ "_watermarked.pdf");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addTextWatermark(
|
private void addTextWatermark(
|
||||||
@@ -117,7 +123,7 @@ public class WatermarkController {
|
|||||||
String alphabet)
|
String alphabet)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
String resourceDir = "";
|
String resourceDir = "";
|
||||||
PDFont font = PDType1Font.HELVETICA_BOLD;
|
PDFont font = new PDType1Font(Standard14Fonts.FontName.HELVETICA);
|
||||||
switch (alphabet) {
|
switch (alphabet) {
|
||||||
case "arabic":
|
case "arabic":
|
||||||
resourceDir = "static/fonts/NotoSansArabic-Regular.ttf";
|
resourceDir = "static/fonts/NotoSansArabic-Regular.ttf";
|
||||||
@@ -137,10 +143,10 @@ public class WatermarkController {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!resourceDir.equals("")) {
|
if (!"".equals(resourceDir)) {
|
||||||
ClassPathResource classPathResource = new ClassPathResource(resourceDir);
|
ClassPathResource classPathResource = new ClassPathResource(resourceDir);
|
||||||
String fileExtension = resourceDir.substring(resourceDir.lastIndexOf("."));
|
String fileExtension = resourceDir.substring(resourceDir.lastIndexOf("."));
|
||||||
File tempFile = File.createTempFile("NotoSansFont", fileExtension);
|
File tempFile = Files.createTempFile("NotoSansFont", fileExtension).toFile();
|
||||||
try (InputStream is = classPathResource.getInputStream();
|
try (InputStream is = classPathResource.getInputStream();
|
||||||
FileOutputStream os = new FileOutputStream(tempFile)) {
|
FileOutputStream os = new FileOutputStream(tempFile)) {
|
||||||
IOUtils.copy(is, os);
|
IOUtils.copy(is, os);
|
||||||
@@ -153,9 +159,16 @@ public class WatermarkController {
|
|||||||
contentStream.setFont(font, fontSize);
|
contentStream.setFont(font, fontSize);
|
||||||
contentStream.setNonStrokingColor(Color.LIGHT_GRAY);
|
contentStream.setNonStrokingColor(Color.LIGHT_GRAY);
|
||||||
|
|
||||||
|
String[] textLines = watermarkText.split("\\\\n");
|
||||||
|
float maxLineWidth = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < textLines.length; ++i) {
|
||||||
|
maxLineWidth = Math.max(maxLineWidth, font.getStringWidth(textLines[i]));
|
||||||
|
}
|
||||||
|
|
||||||
// Set size and location of text watermark
|
// Set size and location of text watermark
|
||||||
float watermarkWidth = widthSpacer + font.getStringWidth(watermarkText) * fontSize / 1000;
|
float watermarkWidth = widthSpacer + maxLineWidth * fontSize / 1000;
|
||||||
float watermarkHeight = heightSpacer + fontSize;
|
float watermarkHeight = heightSpacer + fontSize * textLines.length;
|
||||||
float pageWidth = page.getMediaBox().getWidth();
|
float pageWidth = page.getMediaBox().getWidth();
|
||||||
float pageHeight = page.getMediaBox().getHeight();
|
float pageHeight = page.getMediaBox().getHeight();
|
||||||
int watermarkRows = (int) (pageHeight / watermarkHeight + 1);
|
int watermarkRows = (int) (pageHeight / watermarkHeight + 1);
|
||||||
@@ -170,7 +183,12 @@ public class WatermarkController {
|
|||||||
(float) Math.toRadians(rotation),
|
(float) Math.toRadians(rotation),
|
||||||
j * watermarkWidth,
|
j * watermarkWidth,
|
||||||
i * watermarkHeight));
|
i * watermarkHeight));
|
||||||
contentStream.showText(watermarkText);
|
|
||||||
|
for (int k = 0; k < textLines.length; ++k) {
|
||||||
|
contentStream.showText(textLines[k]);
|
||||||
|
contentStream.newLineAtOffset(0, -fontSize);
|
||||||
|
}
|
||||||
|
|
||||||
contentStream.endText();
|
contentStream.endText();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package stirling.software.SPDF.controller.web;
|
|||||||
|
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
@@ -33,6 +34,8 @@ public class AccountWebController {
|
|||||||
return "redirect:/";
|
return "redirect:/";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
model.addAttribute("currentPage", "login");
|
||||||
|
|
||||||
if (request.getParameter("error") != null) {
|
if (request.getParameter("error") != null) {
|
||||||
|
|
||||||
model.addAttribute("error", request.getParameter("error"));
|
model.addAttribute("error", request.getParameter("error"));
|
||||||
@@ -53,6 +56,7 @@ public class AccountWebController {
|
|||||||
public String showAddUserForm(Model model, Authentication authentication) {
|
public String showAddUserForm(Model model, Authentication authentication) {
|
||||||
List<User> allUsers = userRepository.findAll();
|
List<User> allUsers = userRepository.findAll();
|
||||||
Iterator<User> iterator = allUsers.iterator();
|
Iterator<User> iterator = allUsers.iterator();
|
||||||
|
Map<String, String> roleDetails = Role.getAllRoleDetails();
|
||||||
|
|
||||||
while (iterator.hasNext()) {
|
while (iterator.hasNext()) {
|
||||||
User user = iterator.next();
|
User user = iterator.next();
|
||||||
@@ -60,6 +64,7 @@ public class AccountWebController {
|
|||||||
for (Authority authority : user.getAuthorities()) {
|
for (Authority authority : user.getAuthorities()) {
|
||||||
if (authority.getAuthority().equals(Role.INTERNAL_API_USER.getRoleId())) {
|
if (authority.getAuthority().equals(Role.INTERNAL_API_USER.getRoleId())) {
|
||||||
iterator.remove();
|
iterator.remove();
|
||||||
|
roleDetails.remove(Role.INTERNAL_API_USER.getRoleId());
|
||||||
break; // Break out of the inner loop once the user is removed
|
break; // Break out of the inner loop once the user is removed
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -68,6 +73,7 @@ public class AccountWebController {
|
|||||||
|
|
||||||
model.addAttribute("users", allUsers);
|
model.addAttribute("users", allUsers);
|
||||||
model.addAttribute("currentUsername", authentication.getName());
|
model.addAttribute("currentUsername", authentication.getName());
|
||||||
|
model.addAttribute("roleDetails", roleDetails);
|
||||||
return "addUsers";
|
return "addUsers";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,6 +118,7 @@ public class AccountWebController {
|
|||||||
model.addAttribute("role", user.get().getRolesAsString());
|
model.addAttribute("role", user.get().getRolesAsString());
|
||||||
model.addAttribute("settings", settingsJson);
|
model.addAttribute("settings", settingsJson);
|
||||||
model.addAttribute("changeCredsFlag", user.get().isFirstLogin());
|
model.addAttribute("changeCredsFlag", user.get().isFirstLogin());
|
||||||
|
model.addAttribute("currentPage", "account");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return "redirect:/";
|
return "redirect:/";
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package stirling.software.SPDF.controller.web;
|
package stirling.software.SPDF.controller.web;
|
||||||
|
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
import org.springframework.ui.Model;
|
import org.springframework.ui.Model;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
@@ -12,6 +13,22 @@ import io.swagger.v3.oas.annotations.tags.Tag;
|
|||||||
@Tag(name = "Convert", description = "Convert APIs")
|
@Tag(name = "Convert", description = "Convert APIs")
|
||||||
public class ConverterWebController {
|
public class ConverterWebController {
|
||||||
|
|
||||||
|
@ConditionalOnExpression("#{bookAndHtmlFormatsInstalled}")
|
||||||
|
@GetMapping("/book-to-pdf")
|
||||||
|
@Hidden
|
||||||
|
public String convertBookToPdfForm(Model model) {
|
||||||
|
model.addAttribute("currentPage", "book-to-pdf");
|
||||||
|
return "convert/book-to-pdf";
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConditionalOnExpression("#{bookAndHtmlFormatsInstalled}")
|
||||||
|
@GetMapping("/pdf-to-book")
|
||||||
|
@Hidden
|
||||||
|
public String convertPdfToBookForm(Model model) {
|
||||||
|
model.addAttribute("currentPage", "pdf-to-book");
|
||||||
|
return "convert/pdf-to-book";
|
||||||
|
}
|
||||||
|
|
||||||
@GetMapping("/img-to-pdf")
|
@GetMapping("/img-to-pdf")
|
||||||
@Hidden
|
@Hidden
|
||||||
public String convertImgToPdfForm(Model model) {
|
public String convertImgToPdfForm(Model model) {
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
package stirling.software.SPDF.controller.web;
|
package stirling.software.SPDF.controller.web;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Files;
|
import java.io.InputStream;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@@ -38,7 +39,8 @@ public class HomeWebController {
|
|||||||
model.addAttribute("currentPage", "licenses");
|
model.addAttribute("currentPage", "licenses");
|
||||||
Resource resource = new ClassPathResource("static/3rdPartyLicenses.json");
|
Resource resource = new ClassPathResource("static/3rdPartyLicenses.json");
|
||||||
try {
|
try {
|
||||||
String json = new String(Files.readAllBytes(resource.getFile().toPath()));
|
InputStream is = resource.getInputStream();
|
||||||
|
String json = new String(is.readAllBytes(), StandardCharsets.UTF_8);
|
||||||
ObjectMapper mapper = new ObjectMapper();
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
Map<String, List<Dependency>> data =
|
Map<String, List<Dependency>> data =
|
||||||
mapper.readValue(json, new TypeReference<Map<String, List<Dependency>>>() {});
|
mapper.readValue(json, new TypeReference<Map<String, List<Dependency>>>() {});
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ public class MetricsController {
|
|||||||
for (Meter meter : meterRegistry.getMeters()) {
|
for (Meter meter : meterRegistry.getMeters()) {
|
||||||
if (meter.getId().getName().equals("http.requests")) {
|
if (meter.getId().getName().equals("http.requests")) {
|
||||||
String method = meter.getId().getTag("method");
|
String method = meter.getId().getTag("method");
|
||||||
if (method != null && method.equals("GET")) {
|
if (method != null && "GET".equals(method)) {
|
||||||
|
|
||||||
if (endpoint.isPresent() && !endpoint.get().isBlank()) {
|
if (endpoint.isPresent() && !endpoint.get().isBlank()) {
|
||||||
if (!endpoint.get().startsWith("/")) {
|
if (!endpoint.get().startsWith("/")) {
|
||||||
@@ -129,7 +129,7 @@ public class MetricsController {
|
|||||||
for (Meter meter : meterRegistry.getMeters()) {
|
for (Meter meter : meterRegistry.getMeters()) {
|
||||||
if (meter.getId().getName().equals("http.requests")) {
|
if (meter.getId().getName().equals("http.requests")) {
|
||||||
String method = meter.getId().getTag("method");
|
String method = meter.getId().getTag("method");
|
||||||
if (method != null && method.equals("GET")) {
|
if (method != null && "GET".equals(method)) {
|
||||||
String uri = meter.getId().getTag("uri");
|
String uri = meter.getId().getTag("uri");
|
||||||
if (uri != null) {
|
if (uri != null) {
|
||||||
double currentCount = counts.getOrDefault(uri, 0.0);
|
double currentCount = counts.getOrDefault(uri, 0.0);
|
||||||
@@ -197,7 +197,7 @@ public class MetricsController {
|
|||||||
for (Meter meter : meterRegistry.getMeters()) {
|
for (Meter meter : meterRegistry.getMeters()) {
|
||||||
if (meter.getId().getName().equals("http.requests")) {
|
if (meter.getId().getName().equals("http.requests")) {
|
||||||
String method = meter.getId().getTag("method");
|
String method = meter.getId().getTag("method");
|
||||||
if (method != null && method.equals("POST")) {
|
if (method != null && "POST".equals(method)) {
|
||||||
if (endpoint.isPresent() && !endpoint.get().isBlank()) {
|
if (endpoint.isPresent() && !endpoint.get().isBlank()) {
|
||||||
if (!endpoint.get().startsWith("/")) {
|
if (!endpoint.get().startsWith("/")) {
|
||||||
endpoint = Optional.of("/" + endpoint.get());
|
endpoint = Optional.of("/" + endpoint.get());
|
||||||
@@ -235,7 +235,7 @@ public class MetricsController {
|
|||||||
for (Meter meter : meterRegistry.getMeters()) {
|
for (Meter meter : meterRegistry.getMeters()) {
|
||||||
if (meter.getId().getName().equals("http.requests")) {
|
if (meter.getId().getName().equals("http.requests")) {
|
||||||
String method = meter.getId().getTag("method");
|
String method = meter.getId().getTag("method");
|
||||||
if (method != null && method.equals("POST")) {
|
if (method != null && "POST".equals(method)) {
|
||||||
String uri = meter.getId().getTag("uri");
|
String uri = meter.getId().getTag("uri");
|
||||||
if (uri != null) {
|
if (uri != null) {
|
||||||
double currentCount = counts.getOrDefault(uri, 0.0);
|
double currentCount = counts.getOrDefault(uri, 0.0);
|
||||||
|
|||||||
@@ -39,6 +39,13 @@ public class OtherWebController {
|
|||||||
return "misc/show-javascript";
|
return "misc/show-javascript";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping("/stamp")
|
||||||
|
@Hidden
|
||||||
|
public String stampForm(Model model) {
|
||||||
|
model.addAttribute("currentPage", "stamp");
|
||||||
|
return "misc/stamp";
|
||||||
|
}
|
||||||
|
|
||||||
@GetMapping("/add-page-numbers")
|
@GetMapping("/add-page-numbers")
|
||||||
@Hidden
|
@Hidden
|
||||||
public String addPageNumbersForm(Model model) {
|
public String addPageNumbersForm(Model model) {
|
||||||
@@ -75,7 +82,7 @@ public class OtherWebController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public List<String> getAvailableTesseractLanguages() {
|
public List<String> getAvailableTesseractLanguages() {
|
||||||
String tessdataDir = "/usr/share/tesseract-ocr/5/tessdata";
|
String tessdataDir = "/usr/share/tessdata";
|
||||||
File[] files = new File(tessdataDir).listFiles();
|
File[] files = new File(tessdataDir).listFiles();
|
||||||
if (files == null) {
|
if (files == null) {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
|
|||||||
@@ -1,34 +1,43 @@
|
|||||||
package stirling.software.SPDF.model;
|
package stirling.software.SPDF.model;
|
||||||
|
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
public enum Role {
|
public enum Role {
|
||||||
|
|
||||||
// Unlimited access
|
// Unlimited access
|
||||||
ADMIN("ROLE_ADMIN", Integer.MAX_VALUE, Integer.MAX_VALUE),
|
ADMIN("ROLE_ADMIN", Integer.MAX_VALUE, Integer.MAX_VALUE, "adminUserSettings.admin"),
|
||||||
|
|
||||||
// Unlimited access
|
// Unlimited access
|
||||||
USER("ROLE_USER", Integer.MAX_VALUE, Integer.MAX_VALUE),
|
USER("ROLE_USER", Integer.MAX_VALUE, Integer.MAX_VALUE, "adminUserSettings.user"),
|
||||||
|
|
||||||
// 40 API calls Per Day, 40 web calls
|
// 40 API calls Per Day, 40 web calls
|
||||||
LIMITED_API_USER("ROLE_LIMITED_API_USER", 40, 40),
|
LIMITED_API_USER("ROLE_LIMITED_API_USER", 40, 40, "adminUserSettings.apiUser"),
|
||||||
|
|
||||||
// 20 API calls Per Day, 20 web calls
|
// 20 API calls Per Day, 20 web calls
|
||||||
EXTRA_LIMITED_API_USER("ROLE_EXTRA_LIMITED_API_USER", 20, 20),
|
EXTRA_LIMITED_API_USER("ROLE_EXTRA_LIMITED_API_USER", 20, 20, "adminUserSettings.extraApiUser"),
|
||||||
|
|
||||||
// 0 API calls per day and 20 web calls
|
// 0 API calls per day and 20 web calls
|
||||||
WEB_ONLY_USER("ROLE_WEB_ONLY_USER", 0, 20),
|
WEB_ONLY_USER("ROLE_WEB_ONLY_USER", 0, 20, "adminUserSettings.webOnlyUser"),
|
||||||
|
|
||||||
INTERNAL_API_USER("STIRLING-PDF-BACKEND-API-USER", Integer.MAX_VALUE, Integer.MAX_VALUE),
|
INTERNAL_API_USER(
|
||||||
|
"STIRLING-PDF-BACKEND-API-USER",
|
||||||
|
Integer.MAX_VALUE,
|
||||||
|
Integer.MAX_VALUE,
|
||||||
|
"adminUserSettings.internalApiUser"),
|
||||||
|
|
||||||
DEMO_USER("ROLE_DEMO_USER", 100, 100);
|
DEMO_USER("ROLE_DEMO_USER", 100, 100, "adminUserSettings.demoUser");
|
||||||
|
|
||||||
private final String roleId;
|
private final String roleId;
|
||||||
private final int apiCallsPerDay;
|
private final int apiCallsPerDay;
|
||||||
private final int webCallsPerDay;
|
private final int webCallsPerDay;
|
||||||
|
private final String roleName;
|
||||||
|
|
||||||
Role(String roleId, int apiCallsPerDay, int webCallsPerDay) {
|
Role(String roleId, int apiCallsPerDay, int webCallsPerDay, String roleName) {
|
||||||
this.roleId = roleId;
|
this.roleId = roleId;
|
||||||
this.apiCallsPerDay = apiCallsPerDay;
|
this.apiCallsPerDay = apiCallsPerDay;
|
||||||
this.webCallsPerDay = webCallsPerDay;
|
this.webCallsPerDay = webCallsPerDay;
|
||||||
|
this.roleName = roleName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getRoleId() {
|
public String getRoleId() {
|
||||||
@@ -43,6 +52,27 @@ public enum Role {
|
|||||||
return webCallsPerDay;
|
return webCallsPerDay;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getRoleName() {
|
||||||
|
return roleName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getRoleNameByRoleId(String roleId) {
|
||||||
|
// Using the fromString method to get the Role enum based on the roleId
|
||||||
|
Role role = fromString(roleId);
|
||||||
|
// Return the roleName of the found Role enum
|
||||||
|
return role.getRoleName();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Method to retrieve all role IDs and role names
|
||||||
|
public static Map<String, String> getAllRoleDetails() {
|
||||||
|
// Using LinkedHashMap to preserve order
|
||||||
|
Map<String, String> roleDetails = new LinkedHashMap<>();
|
||||||
|
for (Role role : Role.values()) {
|
||||||
|
roleDetails.put(role.getRoleId(), role.getRoleName());
|
||||||
|
}
|
||||||
|
return roleDetails;
|
||||||
|
}
|
||||||
|
|
||||||
public static Role fromString(String roleId) {
|
public static Role fromString(String roleId) {
|
||||||
for (Role role : Role.values()) {
|
for (Role role : Role.values()) {
|
||||||
if (role.getRoleId().equalsIgnoreCase(roleId)) {
|
if (role.getRoleId().equalsIgnoreCase(roleId)) {
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user