Compare commits

...

40 Commits

Author SHA1 Message Date
sbplat
7fb605baa1 feat: only load pdfjs when its used 2024-09-14 21:02:10 -04:00
sbplat
7e2a53a02e feat: only load pdf-lib when its used 2024-09-14 20:55:35 -04:00
github-actions[bot]
d389b5e2f3 💾 Update Version (#1900)
💾 Sync Versions
> Made via sync_files.yml

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-09-14 23:44:13 +01:00
Anthony Stirling
de97492f39 Update build.gradle 2024-09-14 23:43:30 +01:00
Anthony Stirling
9661e94092 Css changes (#1899)
* #1841

Signed-off-by: a <a>

* #1841 for watermark

Signed-off-by: a <a>

* #1869 and ensure naming

Signed-off-by: a <a>

---------

Signed-off-by: a <a>
Co-authored-by: a <a>
2024-09-14 23:20:29 +01:00
Aharnish Solanki
2cfb553320 Fix: Left-align the submit button on each card for pdf operation (#1897) 2024-09-14 20:35:26 +01:00
Anthony Stirling
909a3347a0 Update messages_it_IT.properties 2024-09-14 16:37:02 +01:00
Anthony Stirling
de4144a1a4 Metadata handling for all PDF endpoints (#1894)
* Add image support to multi-tool page

Related to #278

* changes to support image types

* final touches

* final touches

* final touches

Signed-off-by: a <a>

* final touches

Signed-off-by: a <a>

* final touches

Signed-off-by: a <a>

* final touches

Signed-off-by: a <a>

* final touches

Signed-off-by: a <a>

* final touches

Signed-off-by: a <a>

* final touches

Signed-off-by: a <a>

* Update translation files (#1888)

Signed-off-by: GitHub Action <action@github.com>
Co-authored-by: GitHub Action <action@github.com>

* final touches

Signed-off-by: a <a>

---------

Signed-off-by: a <a>
Signed-off-by: GitHub Action <action@github.com>
Co-authored-by: a <a>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: GitHub Action <action@github.com>
2024-09-14 16:29:39 +01:00
github-actions[bot]
bb1c859e0d 📝 Update README: Translation Progress Table (#1890)
📝 Sync README
> Made via sync_files.yml

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-09-14 10:46:25 +01:00
Charan19001A0231
b2862a3fc4 Update add-watermark.html (#1893)
shifted add watermark submit button to left
2024-09-14 10:46:16 +01:00
albanobattistella
8788a7ee34 Update messages_it_IT.properties (#1891) 2024-09-13 18:17:19 +01:00
Anthony Stirling
8c01425eee Lots of changes (#1889)
* Add image support to multi-tool page

Related to #278

* changes to support image types

* final touches

* final touches

* final touches

Signed-off-by: a <a>

* final touches

Signed-off-by: a <a>

* final touches

Signed-off-by: a <a>

* final touches

Signed-off-by: a <a>

* final touches

Signed-off-by: a <a>

* final touches

Signed-off-by: a <a>

* final touches

Signed-off-by: a <a>

* Update translation files (#1888)

Signed-off-by: GitHub Action <action@github.com>
Co-authored-by: GitHub Action <action@github.com>

---------

Signed-off-by: a <a>
Signed-off-by: GitHub Action <action@github.com>
Co-authored-by: a <a>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: GitHub Action <action@github.com>
2024-09-13 16:42:38 +01:00
Anthony Stirling
4d47e535b5 Update README.md 2024-09-12 13:30:05 +01:00
see-more
1fb78c3124 fix:Remove add image and Align download input file with same width as pdf file input (#1884)
fix:Remove add image button as the button was non functional. Align download input file with same width as pdf file input
2024-09-12 11:47:13 +00:00
Anthony Stirling
6a9dd4ea95 Delete CNAME 2024-09-10 22:59:45 +01:00
Anthony Stirling
0773b8e11b Create CNAME 2024-09-10 22:10:15 +01:00
github-actions[bot]
184d89c44a Update 3rd Party Licenses (#1873)
Signed-off-by: GitHub Action <action@github.com>
Co-authored-by: GitHub Action <action@github.com>
2024-09-10 11:19:24 +01:00
github-actions[bot]
df2e9bfc6e 📝 Update README: Translation Progress Table (#1872)
📝 Sync README
> Made via sync_files.yml

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-09-10 11:18:49 +01:00
dependabot[bot]
0045fe852b Bump com.fathzer:javaluator from 3.0.4 to 3.0.5 (#1867)
Bumps [com.fathzer:javaluator](https://github.com/fathzer/javaluator) from 3.0.4 to 3.0.5.
- [Release notes](https://github.com/fathzer/javaluator/releases)
- [Commits](https://github.com/fathzer/javaluator/compare/v3.0.4...v3.0.5)

---
updated-dependencies:
- dependency-name: com.fathzer:javaluator
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-10 11:18:24 +01:00
dependabot[bot]
7d46d61d9e Bump io.micrometer:micrometer-core from 1.13.3 to 1.13.4 (#1866)
Bumps [io.micrometer:micrometer-core](https://github.com/micrometer-metrics/micrometer) from 1.13.3 to 1.13.4.
- [Release notes](https://github.com/micrometer-metrics/micrometer/releases)
- [Commits](https://github.com/micrometer-metrics/micrometer/compare/v1.13.3...v1.13.4)

---
updated-dependencies:
- dependency-name: io.micrometer:micrometer-core
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-10 11:14:30 +01:00
O2bmm
f8404ce9e9 Update messages_zh_CN.properties (#1871) 2024-09-10 11:14:01 +01:00
Tim
7fad973a77 Changed Spacing between between Buttons and Spacing of Settings Menu (#1864)
Co-authored-by: TSO <tim.sommer@bieber-marburg.de>
2024-09-10 08:02:56 +01:00
dependabot[bot]
6410a99cf3 Bump alpine from 3.20.2 to 3.20.3 (#1865)
Bumps alpine from 3.20.2 to 3.20.3.

---
updated-dependencies:
- dependency-name: alpine
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-09 23:47:16 +01:00
github-actions[bot]
62e7c7e073 📝 Update README: Translation Progress Table (#1863)
📝 Sync README
> Made via sync_files.yml

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-09-09 22:35:49 +01:00
Anthony Stirling
1d29a500b3 Update messages_en_US.properties 2024-09-09 22:19:23 +01:00
Anthony Stirling
08b085c763 Update messages_en_GB.properties 2024-09-09 22:19:06 +01:00
Tim
f256e8f029 Changed <br> to get a consistent overlay-pdf form (#1849)
Repaired the overlay-pdfs.counts.label to be displayed and not deleted on change
2024-09-09 18:58:35 +01:00
designtesbrot
0ad8c635ad fix(stamp): radius styles of color input (#1862)
Closes 1830
2024-09-09 18:58:04 +01:00
albanobattistella
12ff0ecac2 Update messages_it_IT.properties (#1854) 2024-09-09 14:50:57 +01:00
Anthony Stirling
291bad4a2a Update messages_en_US.properties 2024-09-09 11:35:36 +01:00
Anthony Stirling
c6ee96512a Update messages_en_GB.properties 2024-09-09 11:35:14 +01:00
Anthony Stirling
db563c765d Minor fixes stopping invalid sessions (#1850)
* Update UserAuthenticationFilter.java

* Update RequestUriUtils.java

* Update RequestUriUtils.java

* Update RequestUriUtilsTest.java
2024-09-08 22:06:46 +01:00
github-actions[bot]
6f52189ed2 📝 Update README: Translation Progress Table (#1851)
📝 Sync README
> Made via sync_files.yml

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-09-08 21:59:06 +01:00
Ignacio Carrera
580313151b Page Scale: add pageSize KEEP #1798 (#1800)
* add `scalePages.keepPageSize` i18n key (#1798)

* add KEEP option to frontend (#1798)

* extract ScalePagesController.getTargetSize() (#1798)

* make ScalePageController honor `pageSize` value `KEEP`

* PR feedback: make caption shorter, avoid unnecessary verbosity (#1798)

* Update messages_ar_AR.properties

---------

Co-authored-by: Anthony Stirling <77850077+Frooodle@users.noreply.github.com>
2024-09-08 21:52:50 +01:00
Dinesh Sharma
765289c89e Fixed reduce extra space between input field & button #1829 (#1848)
* Add <br> when Bored Waiting Button Appears

* Added <br> below progress bar as well

* Removed <br>

Two <br> tags were added which were taking lot of space. I dynamically added space when needed.
2024-09-08 19:08:21 +00:00
Anthony Stirling
3d8686211d [Snyk] Security upgrade alpine from 3.20.2 to 3.20.3 (#1840)
fix: Dockerfile to reduce vulnerabilities

The following vulnerabilities are fixed with an upgrade:
- https://snyk.io/vuln/SNYK-ALPINE320-OPENSSL-7895537
- https://snyk.io/vuln/SNYK-ALPINE320-OPENSSL-7895537

Co-authored-by: snyk-bot <snyk-bot@snyk.io>
2024-09-08 13:11:47 +01:00
Anthony Stirling
0a98e3bde3 [Snyk] Security upgrade alpine from 3.20.2 to 3.20.3 (#1839)
fix: Dockerfile-ultra-lite to reduce vulnerabilities

The following vulnerabilities are fixed with an upgrade:
- https://snyk.io/vuln/SNYK-ALPINE320-OPENSSL-7895537
- https://snyk.io/vuln/SNYK-ALPINE320-OPENSSL-7895537

Co-authored-by: snyk-bot <snyk-bot@snyk.io>
2024-09-08 13:11:33 +01:00
FiratUsta
78211d09c5 [Bug Fix] Fix Firefox Page Drag Bug (#1837)
Fix a bug where the file drop prompt would show during page drag on Firefox.

Co-authored-by: kazandaki <ahmetfiratusta@gmail.com>
2024-09-07 22:34:36 +01:00
FiratUsta
82219dd899 [Bug Fix] Multiple Bug Fixes (#1836)
* Fix the add file button on the multi-tools page.

* Fix a bug where the page numbers wouldn't be removed on page move on Firefox.

---------

Co-authored-by: kazandaki <ahmetfiratusta@gmail.com>
2024-09-07 21:40:19 +01:00
FiratUsta
3c04486348 Add document splitting functionality to the multi-tools page (#1808)
* Add a split button on top of the insert button in multitool viewer.

* Add placeholder splitFileButtonCallback method.

* Remove unused splitFileButtonContainer element.

* Add this binding to setActions for splitFileButtonCallback

* Add test log for adding separators.

* Add test log for adding separators.

* Remove test logs and add visual indicators to separators instead.

* Add splitting functionality to multi-tools.

* Prevent trying to split from index 0.

* Hide the split button for the first page to avoid confusion.

* Change the class name 'cutBefore' to 'split-before' to fall mroe in line with already existing classes.

* Add dummy methods for splitting and compressing documents.

* Remove form submission, begin work on client side splitting.

* Add client side document splitting.

* Add client side archiving for the split documents.

* Fix a bug that adds an empty page to splitted documents due to a sorting error.

* Add a 'Split All' button and the relevant functionality.

---------

Co-authored-by: kazandaki <ahmetfiratusta@gmail.com>
Co-authored-by: Anthony Stirling <77850077+Frooodle@users.noreply.github.com>
2024-09-07 11:25:39 +01:00
150 changed files with 1802 additions and 1147 deletions

View File

@@ -1,5 +1,5 @@
# Main stage # Main stage
FROM alpine:3.20.2 FROM alpine:3.20.3
# Copy necessary files # Copy necessary files
COPY scripts /scripts COPY scripts /scripts

View File

@@ -12,7 +12,7 @@ RUN DOCKER_ENABLE_SECURITY=true \
./gradlew clean build ./gradlew clean build
# Main stage # Main stage
FROM alpine:3.20.2 FROM alpine:3.20.3
# Copy necessary files # Copy necessary files
COPY scripts /scripts COPY scripts /scripts

View File

@@ -1,5 +1,5 @@
# use alpine # use alpine
FROM alpine:3.20.2 FROM alpine:3.20.3
ARG VERSION_TAG ARG VERSION_TAG

View File

@@ -100,6 +100,8 @@ Demo of the app is available [here](https://stirlingpdf.io).
- [PDF-LIB.js](https://github.com/Hopding/pdf-lib) - [PDF-LIB.js](https://github.com/Hopding/pdf-lib)
## How to use ## How to use
### Windows
For windows users download the latest Stirling-PDF.exe from our [release](https://github.com/Stirling-Tools/Stirling-PDF/releases) section or by clicking [here](https://github.com/Stirling-Tools/Stirling-PDF/releases/latest/download/Stirling-PDF.exe)
### Locally ### Locally
@@ -170,39 +172,39 @@ Stirling PDF currently supports 38!
| Language | Progress | | Language | Progress |
| ------------------------------------------- | -------------------------------------- | | ------------------------------------------- | -------------------------------------- |
| Arabic (العربية) (ar_AR) | ![100%](https://geps.dev/progress/100) | | Arabic (العربية) (ar_AR) | ![99%](https://geps.dev/progress/99) |
| Basque (Euskara) (eu_ES) | ![60%](https://geps.dev/progress/60) | | Basque (Euskara) (eu_ES) | ![60%](https://geps.dev/progress/60) |
| Bulgarian (Български) (bg_BG) | ![92%](https://geps.dev/progress/92) | | Bulgarian (Български) (bg_BG) | ![91%](https://geps.dev/progress/91) |
| Catalan (Català) (ca_CA) | ![47%](https://geps.dev/progress/47) | | Catalan (Català) (ca_CA) | ![47%](https://geps.dev/progress/47) |
| Croatian (Hrvatski) (hr_HR) | ![92%](https://geps.dev/progress/92) | | Croatian (Hrvatski) (hr_HR) | ![91%](https://geps.dev/progress/91) |
| Czech (Česky) (cs_CZ) | ![88%](https://geps.dev/progress/88) | | Czech (Česky) (cs_CZ) | ![87%](https://geps.dev/progress/87) |
| Danish (Dansk) (da_DK) | ![97%](https://geps.dev/progress/97) | | Danish (Dansk) (da_DK) | ![96%](https://geps.dev/progress/96) |
| Dutch (Nederlands) (nl_NL) | ![93%](https://geps.dev/progress/93) | | Dutch (Nederlands) (nl_NL) | ![93%](https://geps.dev/progress/93) |
| English (English) (en_GB) | ![100%](https://geps.dev/progress/100) | | English (English) (en_GB) | ![100%](https://geps.dev/progress/100) |
| English (US) (en_US) | ![100%](https://geps.dev/progress/100) | | English (US) (en_US) | ![100%](https://geps.dev/progress/100) |
| French (Français) (fr_FR) | ![91%](https://geps.dev/progress/91) | | French (Français) (fr_FR) | ![90%](https://geps.dev/progress/90) |
| German (Deutsch) (de_DE) | ![99%](https://geps.dev/progress/99) | | German (Deutsch) (de_DE) | ![98%](https://geps.dev/progress/98) |
| Greek (Ελληνικά) (el_GR) | ![80%](https://geps.dev/progress/80) | | Greek (Ελληνικά) (el_GR) | ![79%](https://geps.dev/progress/79) |
| Hindi (हिंदी) (hi_IN) | ![76%](https://geps.dev/progress/76) | | Hindi (हिंदी) (hi_IN) | ![76%](https://geps.dev/progress/76) |
| Hungarian (Magyar) (hu_HU) | ![73%](https://geps.dev/progress/73) | | Hungarian (Magyar) (hu_HU) | ![73%](https://geps.dev/progress/73) |
| Indonesia (Bahasa Indonesia) (id_ID) | ![74%](https://geps.dev/progress/74) | | Indonesia (Bahasa Indonesia) (id_ID) | ![74%](https://geps.dev/progress/74) |
| Irish (Gaeilge) (ga_IE) | ![96%](https://geps.dev/progress/96) | | Irish (Gaeilge) (ga_IE) | ![95%](https://geps.dev/progress/95) |
| Italian (Italiano) (it_IT) | ![99%](https://geps.dev/progress/99) | | Italian (Italiano) (it_IT) | ![99%](https://geps.dev/progress/99) |
| Japanese (日本語) (ja_JP) | ![90%](https://geps.dev/progress/90) | | Japanese (日本語) (ja_JP) | ![89%](https://geps.dev/progress/89) |
| Korean (한국어) (ko_KR) | ![82%](https://geps.dev/progress/82) | | Korean (한국어) (ko_KR) | ![81%](https://geps.dev/progress/81) |
| Norwegian (Norsk) (no_NB) | ![95%](https://geps.dev/progress/95) | | Norwegian (Norsk) (no_NB) | ![95%](https://geps.dev/progress/95) |
| Polish (Polski) (pl_PL) | ![90%](https://geps.dev/progress/90) | | Polish (Polski) (pl_PL) | ![89%](https://geps.dev/progress/89) |
| Portuguese (Português) (pt_PT) | ![76%](https://geps.dev/progress/76) | | Portuguese (Português) (pt_PT) | ![76%](https://geps.dev/progress/76) |
| Portuguese Brazilian (Português) (pt_BR) | ![99%](https://geps.dev/progress/99) | | Portuguese Brazilian (Português) (pt_BR) | ![98%](https://geps.dev/progress/98) |
| Romanian (Română) (ro_RO) | ![98%](https://geps.dev/progress/98) | | Romanian (Română) (ro_RO) | ![97%](https://geps.dev/progress/97) |
| Russian (Русский) (ru_RU) | ![82%](https://geps.dev/progress/82) | | Russian (Русский) (ru_RU) | ![81%](https://geps.dev/progress/81) |
| Serbian Latin alphabet (Srpski) (sr_LATN_RS) | ![76%](https://geps.dev/progress/76) | | Serbian Latin alphabet (Srpski) (sr_LATN_RS) | ![76%](https://geps.dev/progress/76) |
| Simplified Chinese (简体中文) (zh_CN) | ![96%](https://geps.dev/progress/96) | | Simplified Chinese (简体中文) (zh_CN) | ![98%](https://geps.dev/progress/98) |
| Slovakian (Slovensky) (sk_SK) | ![90%](https://geps.dev/progress/90) | | Slovakian (Slovensky) (sk_SK) | ![89%](https://geps.dev/progress/89) |
| Spanish (Español) (es_ES) | ![99%](https://geps.dev/progress/99) | | Spanish (Español) (es_ES) | ![98%](https://geps.dev/progress/98) |
| Swedish (Svenska) (sv_SE) | ![98%](https://geps.dev/progress/98) | | Swedish (Svenska) (sv_SE) | ![97%](https://geps.dev/progress/97) |
| Thai (ไทย) (th_TH) | ![97%](https://geps.dev/progress/97) | | Thai (ไทย) (th_TH) | ![96%](https://geps.dev/progress/96) |
| Traditional Chinese (繁體中文) (zh_TW) | ![96%](https://geps.dev/progress/96) | | Traditional Chinese (繁體中文) (zh_TW) | ![95%](https://geps.dev/progress/95) |
| Turkish (Türkçe) (tr_TR) | ![96%](https://geps.dev/progress/96) | | Turkish (Türkçe) (tr_TR) | ![96%](https://geps.dev/progress/96) |
| Ukrainian (Українська) (uk_UA) | ![87%](https://geps.dev/progress/87) | | Ukrainian (Українська) (uk_UA) | ![87%](https://geps.dev/progress/87) |
| Vietnamese (Tiếng Việt) (vi_VN) | ![96%](https://geps.dev/progress/96) | | Vietnamese (Tiếng Việt) (vi_VN) | ![96%](https://geps.dev/progress/96) |

View File

@@ -22,7 +22,7 @@ ext {
} }
group = "stirling.software" group = "stirling.software"
version = "0.28.3" version = "0.29.0"
java { java {
// 17 is lowest but we support and recommend 21 // 17 is lowest but we support and recommend 21
@@ -184,14 +184,14 @@ dependencies {
implementation "org.bouncycastle:bcprov-jdk18on:$bouncycastleVersion" implementation "org.bouncycastle:bcprov-jdk18on:$bouncycastleVersion"
implementation "org.bouncycastle:bcpkix-jdk18on:$bouncycastleVersion" implementation "org.bouncycastle:bcpkix-jdk18on:$bouncycastleVersion"
implementation "org.springframework.boot:spring-boot-starter-actuator:$springBootVersion" implementation "org.springframework.boot:spring-boot-starter-actuator:$springBootVersion"
implementation "io.micrometer:micrometer-core:1.13.3" implementation "io.micrometer:micrometer-core:1.13.4"
implementation group: "com.google.zxing", name: "core", version: "3.5.3" implementation group: "com.google.zxing", name: "core", version: "3.5.3"
// https://mvnrepository.com/artifact/org.commonmark/commonmark // https://mvnrepository.com/artifact/org.commonmark/commonmark
implementation "org.commonmark:commonmark:0.22.0" implementation "org.commonmark:commonmark:0.22.0"
implementation "org.commonmark:commonmark-ext-gfm-tables:0.22.0" implementation "org.commonmark:commonmark-ext-gfm-tables:0.22.0"
// https://mvnrepository.com/artifact/com.bucket4j/bucket4j_jdk17 // https://mvnrepository.com/artifact/com.bucket4j/bucket4j_jdk17
implementation "com.bucket4j:bucket4j_jdk17-core:8.14.0" implementation "com.bucket4j:bucket4j_jdk17-core:8.14.0"
implementation "com.fathzer:javaluator:3.0.4" implementation "com.fathzer:javaluator:3.0.5"
developmentOnly("org.springframework.boot:spring-boot-devtools:$springBootVersion") developmentOnly("org.springframework.boot:spring-boot-devtools:$springBootVersion")
compileOnly "org.projectlombok:lombok:$lombokVersion" compileOnly "org.projectlombok:lombok:$lombokVersion"

View File

@@ -1,5 +1,5 @@
apiVersion: v2 apiVersion: v2
appVersion: 0.28.3 appVersion: 0.29.0
description: locally hosted web application that allows you to perform various operations description: locally hosted web application that allows you to perform various operations
on PDF files on PDF files
home: https://github.com/Stirling-Tools/Stirling-PDF home: https://github.com/Stirling-Tools/Stirling-PDF

View File

@@ -1,4 +1,3 @@
version: '3.3'
services: services:
stirling-pdf: stirling-pdf:
container_name: Stirling-PDF-Security-Fat container_name: Stirling-PDF-Security-Fat

View File

@@ -1,4 +1,3 @@
version: '3.3'
services: services:
stirling-pdf: stirling-pdf:
container_name: Stirling-PDF-Security container_name: Stirling-PDF-Security

View File

@@ -1,4 +1,3 @@
version: '3.3'
services: services:
stirling-pdf: stirling-pdf:
container_name: Stirling-PDF-Security container_name: Stirling-PDF-Security

View File

@@ -1,4 +1,3 @@
version: '3.3'
services: services:
stirling-pdf: stirling-pdf:
container_name: Stirling-PDF-Ultra-Lite-Security container_name: Stirling-PDF-Ultra-Lite-Security

View File

@@ -1,4 +1,3 @@
version: '3.3'
services: services:
stirling-pdf: stirling-pdf:
container_name: Stirling-PDF-Ultra-Lite container_name: Stirling-PDF-Ultra-Lite

View File

@@ -1,4 +1,3 @@
version: '3.3'
services: services:
stirling-pdf: stirling-pdf:
container_name: Stirling-PDF container_name: Stirling-PDF

View File

@@ -0,0 +1,25 @@
package stirling.software.SPDF.EE;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import stirling.software.SPDF.model.ApplicationProperties;
@Configuration
@Lazy
public class EEAppConfig {
private static final Logger logger = LoggerFactory.getLogger(EEAppConfig.class);
@Autowired ApplicationProperties applicationProperties;
@Bean(name = "RunningEE")
public boolean runningEnterpriseEdition() {
// TODO: Implement EE detection
return false;
}
}

View File

@@ -31,7 +31,6 @@ public class SPdfApplication {
@Autowired private Environment env; @Autowired private Environment env;
@Autowired ApplicationProperties applicationProperties; @Autowired ApplicationProperties applicationProperties;
private static String serverPortStatic; private static String serverPortStatic;

View File

@@ -135,4 +135,29 @@ public class AppConfig {
} }
}; };
} }
@Bean(name = "termsAndConditions")
public String termsAndConditions() {
return applicationProperties.getLegal().getTermsAndConditions();
}
@Bean(name = "privacyPolicy")
public String privacyPolicy() {
return applicationProperties.getLegal().getPrivacyPolicy();
}
@Bean(name = "cookiePolicy")
public String cookiePolicy() {
return applicationProperties.getLegal().getCookiePolicy();
}
@Bean(name = "impressum")
public String impressum() {
return applicationProperties.getLegal().getImpressum();
}
@Bean(name = "accessibilityStatement")
public String accessibilityStatement() {
return applicationProperties.getLegal().getAccessibilityStatement();
}
} }

View File

@@ -18,7 +18,7 @@ class AppUpdateService {
@Bean(name = "shouldShow") @Bean(name = "shouldShow")
@Scope("request") @Scope("request")
public boolean shouldShow() { public boolean shouldShow() {
boolean showUpdate = applicationProperties.getSystem().getShowUpdate(); boolean showUpdate = applicationProperties.getSystem().isShowUpdate();
boolean showAdminResult = (showAdmin != null) ? showAdmin.getShowUpdateOnlyAdmins() : true; boolean showAdminResult = (showAdmin != null) ? showAdmin.getShowUpdateOnlyAdmins() : true;
return showUpdate && showAdminResult; return showUpdate && showAdminResult;
} }

View File

@@ -13,6 +13,7 @@ import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException; import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import stirling.software.SPDF.utils.RequestUriUtils;
@Component @Component
public class MetricsFilter extends OncePerRequestFilter { public class MetricsFilter extends OncePerRequestFilter {
@@ -30,32 +31,16 @@ public class MetricsFilter extends OncePerRequestFilter {
throws ServletException, IOException { throws ServletException, IOException {
String uri = request.getRequestURI(); String uri = request.getRequestURI();
// System.out.println("uri="+uri + ", method=" + request.getMethod() ); if (RequestUriUtils.isTrackableResource(request.getContextPath(), uri)) {
// Ignore static resources
if (!(uri.startsWith("/js")
|| uri.startsWith("/v1/api-docs")
|| uri.endsWith("robots.txt")
|| uri.startsWith("/images")
|| uri.endsWith(".png")
|| uri.endsWith(".ico")
|| uri.endsWith(".css")
|| uri.endsWith(".map")
|| uri.endsWith(".svg")
|| uri.endsWith(".js")
|| uri.contains("swagger")
|| uri.startsWith("/api/v1/info")
|| uri.startsWith("/site.webmanifest")
|| uri.startsWith("/fonts")
|| uri.startsWith("/pdfjs"))) {
Counter counter = Counter counter =
Counter.builder("http.requests") Counter.builder("http.requests")
.tag("uri", uri) .tag("session", request.getSession().getId())
.tag("method", request.getMethod()) .tag("method", request.getMethod())
.tag("uri", uri)
.register(meterRegistry); .register(meterRegistry);
counter.increment(); counter.increment();
// System.out.println("Counted");
} }
filterChain.doFilter(request, response); filterChain.doFilter(request, response);

View File

@@ -0,0 +1,109 @@
package stirling.software.SPDF.config;
import java.util.Calendar;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import stirling.software.SPDF.controller.api.pipeline.UserServiceInterface;
import stirling.software.SPDF.model.ApplicationProperties;
import stirling.software.SPDF.model.PdfMetadata;
@Service
public class PdfMetadataService {
private final ApplicationProperties applicationProperties;
private final String appVersion;
private final UserServiceInterface userService;
@Autowired
public PdfMetadataService(
ApplicationProperties applicationProperties,
@Qualifier("appVersion") String appVersion,
@Autowired(required = false) UserServiceInterface userService) {
this.applicationProperties = applicationProperties;
this.appVersion = appVersion;
this.userService = userService;
}
public PdfMetadata extractMetadataFromPdf(PDDocument pdf) {
return PdfMetadata.builder()
.author(pdf.getDocumentInformation().getAuthor())
.producer(pdf.getDocumentInformation().getProducer())
.title(pdf.getDocumentInformation().getTitle())
.creator(pdf.getDocumentInformation().getCreator())
.subject(pdf.getDocumentInformation().getSubject())
.keywords(pdf.getDocumentInformation().getKeywords())
.creationDate(pdf.getDocumentInformation().getCreationDate())
.modificationDate(pdf.getDocumentInformation().getModificationDate())
.build();
}
public void setDefaultMetadata(PDDocument pdf) {
PdfMetadata metadata = extractMetadataFromPdf(pdf);
setMetadataToPdf(pdf, metadata);
}
public void setMetadataToPdf(PDDocument pdf, PdfMetadata pdfMetadata) {
setMetadataToPdf(pdf, pdfMetadata, false);
}
public void setMetadataToPdf(PDDocument pdf, PdfMetadata pdfMetadata, boolean newlyCreated) {
if (newlyCreated || pdfMetadata.getCreationDate() == null) {
setNewDocumentMetadata(pdf, pdfMetadata);
}
setCommonMetadata(pdf, pdfMetadata);
}
private void setNewDocumentMetadata(PDDocument pdf, PdfMetadata pdfMetadata) {
String creator = "Stirling-PDF";
// if (applicationProperties
// .getEnterpriseEdition()
// .getCustomMetadata()
// .isAutoUpdateMetadata()) {
// producer =
//
// applicationProperties.getEnterpriseEdition().getCustomMetadata().getProducer();
// creator =
// applicationProperties.getEnterpriseEdition().getCustomMetadata().getCreator();
// title = applicationProperties.getEnterpriseEdition().getCustomMetadata().getTitle();
// if ("{filename}".equals(title)) {
// title = "Filename"; // Replace with actual filename logic
// } else if ("{unchanged}".equals(title)) {
// title = pdfMetadata.getTitle(); // Keep the original title
// }
// }
pdf.getDocumentInformation().setCreator(creator + " " + appVersion);
pdf.getDocumentInformation().setCreationDate(Calendar.getInstance());
}
private void setCommonMetadata(PDDocument pdf, PdfMetadata pdfMetadata) {
String producer = "Stirling-PDF";
String title = pdfMetadata.getTitle();
pdf.getDocumentInformation().setTitle(title);
pdf.getDocumentInformation().setProducer(producer + " " + appVersion);
pdf.getDocumentInformation().setSubject(pdfMetadata.getSubject());
pdf.getDocumentInformation().setKeywords(pdfMetadata.getKeywords());
pdf.getDocumentInformation().setModificationDate(Calendar.getInstance());
String author = pdfMetadata.getAuthor();
// if (applicationProperties
// .getEnterpriseEdition()
// .getCustomMetadata()
// .isAutoUpdateMetadata()) {
// author = applicationProperties.getEnterpriseEdition().getCustomMetadata().getAuthor();
// if (userService != null) {
// author = author.replace("username", userService.getCurrentUsername());
// }
// }
pdf.getDocumentInformation().setAuthor(author);
}
}

View File

@@ -20,7 +20,7 @@ class AppUpdateAuthService implements ShowAdminInterface {
@Override @Override
public boolean getShowUpdateOnlyAdmins() { public boolean getShowUpdateOnlyAdmins() {
boolean showUpdate = applicationProperties.getSystem().getShowUpdate(); boolean showUpdate = applicationProperties.getSystem().isShowUpdate();
if (!showUpdate) { if (!showUpdate) {
return showUpdate; return showUpdate;
} }

View File

@@ -152,8 +152,8 @@ public class SecurityConfiguration {
.authenticated()); .authenticated());
// Handle OAUTH2 Logins // Handle OAUTH2 Logins
if (applicationProperties.getSecurity().getOAUTH2() != null if (applicationProperties.getSecurity().getOauth2() != null
&& applicationProperties.getSecurity().getOAUTH2().getEnabled() && applicationProperties.getSecurity().getOauth2().getEnabled()
&& !applicationProperties && !applicationProperties
.getSecurity() .getSecurity()
.getLoginMethod() .getLoginMethod()
@@ -222,7 +222,7 @@ public class SecurityConfiguration {
} }
private Optional<ClientRegistration> googleClientRegistration() { private Optional<ClientRegistration> googleClientRegistration() {
OAUTH2 oauth = applicationProperties.getSecurity().getOAUTH2(); OAUTH2 oauth = applicationProperties.getSecurity().getOauth2();
if (oauth == null || !oauth.getEnabled()) { if (oauth == null || !oauth.getEnabled()) {
return Optional.empty(); return Optional.empty();
} }
@@ -251,7 +251,7 @@ public class SecurityConfiguration {
} }
private Optional<ClientRegistration> keycloakClientRegistration() { private Optional<ClientRegistration> keycloakClientRegistration() {
OAUTH2 oauth = applicationProperties.getSecurity().getOAUTH2(); OAUTH2 oauth = applicationProperties.getSecurity().getOauth2();
if (oauth == null || !oauth.getEnabled()) { if (oauth == null || !oauth.getEnabled()) {
return Optional.empty(); return Optional.empty();
} }
@@ -275,7 +275,7 @@ public class SecurityConfiguration {
} }
private Optional<ClientRegistration> githubClientRegistration() { private Optional<ClientRegistration> githubClientRegistration() {
OAUTH2 oauth = applicationProperties.getSecurity().getOAUTH2(); OAUTH2 oauth = applicationProperties.getSecurity().getOauth2();
if (oauth == null || !oauth.getEnabled()) { if (oauth == null || !oauth.getEnabled()) {
return Optional.empty(); return Optional.empty();
} }
@@ -304,7 +304,7 @@ public class SecurityConfiguration {
} }
private Optional<ClientRegistration> oidcClientRegistration() { private Optional<ClientRegistration> oidcClientRegistration() {
OAUTH2 oauth = applicationProperties.getSecurity().getOAUTH2(); OAUTH2 oauth = applicationProperties.getSecurity().getOauth2();
if (oauth == null if (oauth == null
|| oauth.getIssuer() == null || oauth.getIssuer() == null
|| oauth.getIssuer().isEmpty() || oauth.getIssuer().isEmpty()
@@ -352,7 +352,7 @@ public class SecurityConfiguration {
String useAsUsername = String useAsUsername =
applicationProperties applicationProperties
.getSecurity() .getSecurity()
.getOAUTH2() .getOauth2()
.getUseAsUsername(); .getUseAsUsername();
Optional<User> userOpt = Optional<User> userOpt =
userService.findByUsernameIgnoreCase( userService.findByUsernameIgnoreCase(

View File

@@ -159,7 +159,10 @@ public class UserAuthenticationFilter extends OncePerRequestFilter {
}; };
for (String pattern : permitAllPatterns) { for (String pattern : permitAllPatterns) {
if (uri.startsWith(pattern) || uri.endsWith(".svg")) { if (uri.startsWith(pattern)
|| uri.endsWith(".svg")
|| uri.endsWith(".png")
|| uri.endsWith(".ico")) {
return true; return true;
} }
} }

View File

@@ -11,6 +11,7 @@ import org.springframework.security.authentication.UsernamePasswordAuthenticatio
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.session.SessionInformation; import org.springframework.security.core.session.SessionInformation;
import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.core.userdetails.UsernameNotFoundException;
@@ -342,4 +343,14 @@ public class UserService implements UserServiceInterface {
} }
} }
} }
public String getCurrentUsername() {
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if (principal instanceof UserDetails) {
return ((UserDetails) principal).getUsername();
} else {
return principal.toString();
}
}
} }

View File

@@ -66,7 +66,7 @@ public class CustomOAuth2AuthenticationSuccessHandler
// Redirect to the original destination // Redirect to the original destination
super.onAuthenticationSuccess(request, response, authentication); super.onAuthenticationSuccess(request, response, authentication);
} else { } else {
OAUTH2 oAuth = applicationProperties.getSecurity().getOAUTH2(); OAUTH2 oAuth = applicationProperties.getSecurity().getOauth2();
if (loginAttemptService.isBlocked(username)) { if (loginAttemptService.isBlocked(username)) {
if (session != null) { if (session != null) {

View File

@@ -43,7 +43,7 @@ public class CustomOAuth2LogoutSuccessHandler extends SimpleUrlLogoutSuccessHand
} }
return; return;
} }
OAUTH2 oauth = applicationProperties.getSecurity().getOAUTH2(); OAUTH2 oauth = applicationProperties.getSecurity().getOauth2();
if (authentication instanceof OAuth2AuthenticationToken) { if (authentication instanceof OAuth2AuthenticationToken) {
OAuth2AuthenticationToken oauthToken = (OAuth2AuthenticationToken) authentication; OAuth2AuthenticationToken oauthToken = (OAuth2AuthenticationToken) authentication;

View File

@@ -43,7 +43,7 @@ public class CustomOAuth2UserService implements OAuth2UserService<OidcUserReques
@Override @Override
public OidcUser loadUser(OidcUserRequest userRequest) throws OAuth2AuthenticationException { public OidcUser loadUser(OidcUserRequest userRequest) throws OAuth2AuthenticationException {
OAUTH2 oauth2 = applicationProperties.getSecurity().getOAUTH2(); OAUTH2 oauth2 = applicationProperties.getSecurity().getOauth2();
String usernameAttribute = oauth2.getUseAsUsername(); String usernameAttribute = oauth2.getUseAsUsername();
if (usernameAttribute == null || usernameAttribute.trim().isEmpty()) { if (usernameAttribute == null || usernameAttribute.trim().isEmpty()) {
Client client = oauth2.getClient(); Client client = oauth2.getClient();

View File

@@ -13,6 +13,7 @@ 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;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
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;
@@ -23,6 +24,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.CropPdfForm; import stirling.software.SPDF.model.api.general.CropPdfForm;
import stirling.software.SPDF.service.CustomPDDocumentFactory;
import stirling.software.SPDF.utils.WebResponseUtils; import stirling.software.SPDF.utils.WebResponseUtils;
@RestController @RestController
@@ -32,6 +34,13 @@ public class CropController {
private static final Logger logger = LoggerFactory.getLogger(CropController.class); private static final Logger logger = LoggerFactory.getLogger(CropController.class);
private final CustomPDDocumentFactory pdfDocumentFactory;
@Autowired
public CropController(CustomPDDocumentFactory pdfDocumentFactory) {
this.pdfDocumentFactory = pdfDocumentFactory;
}
@PostMapping(value = "/crop", consumes = "multipart/form-data") @PostMapping(value = "/crop", consumes = "multipart/form-data")
@Operation( @Operation(
summary = "Crops a PDF document", summary = "Crops a PDF document",
@@ -40,7 +49,8 @@ public class CropController {
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 = Loader.loadPDF(form.getFileInput().getBytes());
PDDocument newDocument = new PDDocument(); PDDocument newDocument =
pdfDocumentFactory.createNewDocumentBasedOnOldDocument(sourceDocument);
int totalPages = sourceDocument.getNumberOfPages(); int totalPages = sourceDocument.getNumberOfPages();

View File

@@ -22,6 +22,7 @@ import org.apache.pdfbox.pdmodel.interactive.form.PDField;
import org.apache.pdfbox.pdmodel.interactive.form.PDSignatureField; import org.apache.pdfbox.pdmodel.interactive.form.PDSignatureField;
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.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;
@@ -33,6 +34,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.service.CustomPDDocumentFactory;
import stirling.software.SPDF.utils.GeneralUtils; import stirling.software.SPDF.utils.GeneralUtils;
import stirling.software.SPDF.utils.WebResponseUtils; import stirling.software.SPDF.utils.WebResponseUtils;
@@ -43,9 +45,16 @@ public class MergeController {
private static final Logger logger = LoggerFactory.getLogger(MergeController.class); private static final Logger logger = LoggerFactory.getLogger(MergeController.class);
private final CustomPDDocumentFactory pdfDocumentFactory;
@Autowired
public MergeController(CustomPDDocumentFactory pdfDocumentFactory) {
this.pdfDocumentFactory = pdfDocumentFactory;
}
// Merges a list of PDDocument objects into a single PDDocument // Merges a list of PDDocument objects into a single PDDocument
public PDDocument mergeDocuments(List<PDDocument> documents) throws IOException { public PDDocument mergeDocuments(List<PDDocument> documents) throws IOException {
PDDocument mergedDoc = new PDDocument(); PDDocument mergedDoc = pdfDocumentFactory.createNewDocument();
for (PDDocument doc : documents) { for (PDDocument doc : documents) {
for (PDPage page : doc.getPages()) { for (PDPage page : doc.getPages()) {
mergedDoc.addPage(page); mergedDoc.addPage(page);

View File

@@ -14,6 +14,7 @@ import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject;
import org.apache.pdfbox.util.Matrix; import org.apache.pdfbox.util.Matrix;
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.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 +27,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.MergeMultiplePagesRequest; import stirling.software.SPDF.model.api.general.MergeMultiplePagesRequest;
import stirling.software.SPDF.service.CustomPDDocumentFactory;
import stirling.software.SPDF.utils.WebResponseUtils; import stirling.software.SPDF.utils.WebResponseUtils;
@RestController @RestController
@@ -35,6 +37,13 @@ public class MultiPageLayoutController {
private static final Logger logger = LoggerFactory.getLogger(MultiPageLayoutController.class); private static final Logger logger = LoggerFactory.getLogger(MultiPageLayoutController.class);
private final CustomPDDocumentFactory pdfDocumentFactory;
@Autowired
public MultiPageLayoutController(CustomPDDocumentFactory pdfDocumentFactory) {
this.pdfDocumentFactory = pdfDocumentFactory;
}
@PostMapping(value = "/multi-page-layout", consumes = "multipart/form-data") @PostMapping(value = "/multi-page-layout", consumes = "multipart/form-data")
@Operation( @Operation(
summary = "Merge multiple pages of a PDF document into a single page", summary = "Merge multiple pages of a PDF document into a single page",
@@ -60,7 +69,8 @@ public class MultiPageLayoutController {
int rows = pagesPerSheet == 2 || pagesPerSheet == 3 ? 1 : (int) Math.sqrt(pagesPerSheet); int rows = pagesPerSheet == 2 || pagesPerSheet == 3 ? 1 : (int) Math.sqrt(pagesPerSheet);
PDDocument sourceDocument = Loader.loadPDF(file.getBytes()); PDDocument sourceDocument = Loader.loadPDF(file.getBytes());
PDDocument newDocument = new PDDocument(); PDDocument newDocument =
pdfDocumentFactory.createNewDocumentBasedOnOldDocument(sourceDocument);
PDPage newPage = new PDPage(PDRectangle.A4); PDPage newPage = new PDPage(PDRectangle.A4);
newDocument.addPage(newPage); newDocument.addPage(newPage);

View File

@@ -3,16 +3,15 @@ package stirling.software.SPDF.controller.api;
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.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocument;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import stirling.software.SPDF.model.api.PDFFile; import stirling.software.SPDF.model.api.PDFFile;
import stirling.software.SPDF.service.CustomPDDocumentFactory;
import stirling.software.SPDF.service.PdfImageRemovalService; import stirling.software.SPDF.service.PdfImageRemovalService;
import stirling.software.SPDF.utils.WebResponseUtils; import stirling.software.SPDF.utils.WebResponseUtils;
@@ -25,15 +24,21 @@ import stirling.software.SPDF.utils.WebResponseUtils;
public class PdfImageRemovalController { public class PdfImageRemovalController {
// Service for removing images from PDFs // Service for removing images from PDFs
@Autowired private PdfImageRemovalService pdfImageRemovalService; private final PdfImageRemovalService pdfImageRemovalService;
private final CustomPDDocumentFactory pdfDocumentFactory;
/** /**
* Constructor for dependency injection of PdfImageRemovalService. * Constructor for dependency injection of PdfImageRemovalService.
* *
* @param pdfImageRemovalService The service used for removing images from PDFs. * @param pdfImageRemovalService The service used for removing images from PDFs.
*/ */
public PdfImageRemovalController(PdfImageRemovalService pdfImageRemovalService) { @Autowired
public PdfImageRemovalController(
PdfImageRemovalService pdfImageRemovalService,
CustomPDDocumentFactory pdfDocumentFactory) {
this.pdfImageRemovalService = pdfImageRemovalService; this.pdfImageRemovalService = pdfImageRemovalService;
this.pdfDocumentFactory = pdfDocumentFactory;
} }
/** /**
@@ -53,14 +58,8 @@ public class PdfImageRemovalController {
description = description =
"This endpoint remove images from file to reduce the file size.Input:PDF Output:PDF Type:MISO") "This endpoint remove images from file to reduce the file size.Input:PDF Output:PDF Type:MISO")
public ResponseEntity<byte[]> removeImages(@ModelAttribute PDFFile file) throws IOException { public ResponseEntity<byte[]> removeImages(@ModelAttribute PDFFile file) throws IOException {
// Load the PDF document
MultipartFile pdf = file.getFileInput(); PDDocument document = pdfDocumentFactory.load(file);
// Convert the MultipartFile to a byte array
byte[] pdfBytes = pdf.getBytes();
// Load the PDF document from the byte array
PDDocument document = Loader.loadPDF(pdfBytes);
// Remove images from the PDF document using the service // Remove images from the PDF document using the service
PDDocument modifiedDocument = pdfImageRemovalService.removeImagesFromPdf(document); PDDocument modifiedDocument = pdfImageRemovalService.removeImagesFromPdf(document);
@@ -74,7 +73,8 @@ public class PdfImageRemovalController {
// Generate a new filename for the modified PDF // Generate a new filename for the modified PDF
String mergedFileName = String mergedFileName =
pdf.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_removed_images.pdf"; file.getFileInput().getOriginalFilename().replaceFirst("[.][^.]+$", "")
+ "_removed_images.pdf";
// Convert the byte array to a web response and return it // Convert the byte array to a web response and return it
return WebResponseUtils.bytesToWebResponse(outputStream.toByteArray(), mergedFileName); return WebResponseUtils.bytesToWebResponse(outputStream.toByteArray(), mergedFileName);

View File

@@ -12,6 +12,7 @@ import java.util.Map;
import org.apache.pdfbox.Loader; 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.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType; 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;
@@ -25,6 +26,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.OverlayPdfsRequest; import stirling.software.SPDF.model.api.general.OverlayPdfsRequest;
import stirling.software.SPDF.service.CustomPDDocumentFactory;
import stirling.software.SPDF.utils.GeneralUtils; import stirling.software.SPDF.utils.GeneralUtils;
import stirling.software.SPDF.utils.WebResponseUtils; import stirling.software.SPDF.utils.WebResponseUtils;
@@ -33,6 +35,13 @@ import stirling.software.SPDF.utils.WebResponseUtils;
@Tag(name = "General", description = "General APIs") @Tag(name = "General", description = "General APIs")
public class PdfOverlayController { public class PdfOverlayController {
private final CustomPDDocumentFactory pdfDocumentFactory;
@Autowired
public PdfOverlayController(CustomPDDocumentFactory pdfDocumentFactory) {
this.pdfDocumentFactory = pdfDocumentFactory;
}
@PostMapping(value = "/overlay-pdfs", consumes = "multipart/form-data") @PostMapping(value = "/overlay-pdfs", consumes = "multipart/form-data")
@Operation( @Operation(
summary = "Overlay PDF files in various modes", summary = "Overlay PDF files in various modes",
@@ -56,7 +65,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 = Loader.loadPDF(baseFile.getBytes()); try (PDDocument basePdf = pdfDocumentFactory.load(baseFile);
Overlay overlay = new Overlay()) { Overlay overlay = new Overlay()) {
Map<Integer, String> overlayGuide = Map<Integer, String> overlayGuide =
prepareOverlayGuide( prepareOverlayGuide(

View File

@@ -10,6 +10,7 @@ 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;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
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;
@@ -24,6 +25,7 @@ import io.swagger.v3.oas.annotations.tags.Tag;
import stirling.software.SPDF.model.SortTypes; import stirling.software.SPDF.model.SortTypes;
import stirling.software.SPDF.model.api.PDFWithPageNums; import stirling.software.SPDF.model.api.PDFWithPageNums;
import stirling.software.SPDF.model.api.general.RearrangePagesRequest; import stirling.software.SPDF.model.api.general.RearrangePagesRequest;
import stirling.software.SPDF.service.CustomPDDocumentFactory;
import stirling.software.SPDF.utils.GeneralUtils; import stirling.software.SPDF.utils.GeneralUtils;
import stirling.software.SPDF.utils.WebResponseUtils; import stirling.software.SPDF.utils.WebResponseUtils;
@@ -34,6 +36,13 @@ public class RearrangePagesPDFController {
private static final Logger logger = LoggerFactory.getLogger(RearrangePagesPDFController.class); private static final Logger logger = LoggerFactory.getLogger(RearrangePagesPDFController.class);
private final CustomPDDocumentFactory pdfDocumentFactory;
@Autowired
public RearrangePagesPDFController(CustomPDDocumentFactory pdfDocumentFactory) {
this.pdfDocumentFactory = pdfDocumentFactory;
}
@PostMapping(consumes = "multipart/form-data", value = "/remove-pages") @PostMapping(consumes = "multipart/form-data", value = "/remove-pages")
@Operation( @Operation(
summary = "Remove pages from a PDF file", summary = "Remove pages from a PDF file",
@@ -45,7 +54,7 @@ public class RearrangePagesPDFController {
MultipartFile pdfFile = request.getFileInput(); MultipartFile pdfFile = request.getFileInput();
String pagesToDelete = request.getPageNumbers(); String pagesToDelete = request.getPageNumbers();
PDDocument document = Loader.loadPDF(pdfFile.getBytes()); PDDocument document = pdfDocumentFactory.load(pdfFile);
// 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(",");

View File

@@ -2,12 +2,12 @@ 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;
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.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;
@@ -20,6 +20,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.RotatePDFRequest; import stirling.software.SPDF.model.api.general.RotatePDFRequest;
import stirling.software.SPDF.service.CustomPDDocumentFactory;
import stirling.software.SPDF.utils.WebResponseUtils; import stirling.software.SPDF.utils.WebResponseUtils;
@RestController @RestController
@@ -29,6 +30,13 @@ public class RotationController {
private static final Logger logger = LoggerFactory.getLogger(RotationController.class); private static final Logger logger = LoggerFactory.getLogger(RotationController.class);
private final CustomPDDocumentFactory pdfDocumentFactory;
@Autowired
public RotationController(CustomPDDocumentFactory pdfDocumentFactory) {
this.pdfDocumentFactory = pdfDocumentFactory;
}
@PostMapping(consumes = "multipart/form-data", value = "/rotate-pdf") @PostMapping(consumes = "multipart/form-data", value = "/rotate-pdf")
@Operation( @Operation(
summary = "Rotate a PDF file", summary = "Rotate a PDF file",
@@ -39,7 +47,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 = Loader.loadPDF(pdfFile.getBytes()); PDDocument document = pdfDocumentFactory.load(request);
// Get the list of pages in the document // Get the list of pages in the document
PDPageTree pages = document.getPages(); PDPageTree pages = document.getPages();

View File

@@ -15,6 +15,7 @@ import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject;
import org.apache.pdfbox.util.Matrix; import org.apache.pdfbox.util.Matrix;
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.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;
@@ -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.ScalePagesRequest; import stirling.software.SPDF.model.api.general.ScalePagesRequest;
import stirling.software.SPDF.service.CustomPDDocumentFactory;
import stirling.software.SPDF.utils.WebResponseUtils; import stirling.software.SPDF.utils.WebResponseUtils;
@RestController @RestController
@@ -36,6 +38,13 @@ public class ScalePagesController {
private static final Logger logger = LoggerFactory.getLogger(ScalePagesController.class); private static final Logger logger = LoggerFactory.getLogger(ScalePagesController.class);
private final CustomPDDocumentFactory pdfDocumentFactory;
@Autowired
public ScalePagesController(CustomPDDocumentFactory pdfDocumentFactory) {
this.pdfDocumentFactory = pdfDocumentFactory;
}
@PostMapping(value = "/scale-pages", consumes = "multipart/form-data") @PostMapping(value = "/scale-pages", consumes = "multipart/form-data")
@Operation( @Operation(
summary = "Change the size of a PDF page/document", summary = "Change the size of a PDF page/document",
@@ -47,29 +56,11 @@ public class ScalePagesController {
String targetPDRectangle = request.getPageSize(); String targetPDRectangle = request.getPageSize();
float scaleFactor = request.getScaleFactor(); float scaleFactor = request.getScaleFactor();
Map<String, PDRectangle> sizeMap = new HashMap<>();
// Add A0 - A10
sizeMap.put("A0", PDRectangle.A0);
sizeMap.put("A1", PDRectangle.A1);
sizeMap.put("A2", PDRectangle.A2);
sizeMap.put("A3", PDRectangle.A3);
sizeMap.put("A4", PDRectangle.A4);
sizeMap.put("A5", PDRectangle.A5);
sizeMap.put("A6", PDRectangle.A6);
// Add other sizes
sizeMap.put("LETTER", PDRectangle.LETTER);
sizeMap.put("LEGAL", PDRectangle.LEGAL);
if (!sizeMap.containsKey(targetPDRectangle)) {
throw new IllegalArgumentException(
"Invalid PDRectangle. It must be one of the following: A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10");
}
PDRectangle targetSize = sizeMap.get(targetPDRectangle);
PDDocument sourceDocument = Loader.loadPDF(file.getBytes()); PDDocument sourceDocument = Loader.loadPDF(file.getBytes());
PDDocument outputDocument = new PDDocument(); PDDocument outputDocument =
pdfDocumentFactory.createNewDocumentBasedOnOldDocument(sourceDocument);
PDRectangle targetSize = getTargetSize(targetPDRectangle, sourceDocument);
int totalPages = sourceDocument.getNumberOfPages(); int totalPages = sourceDocument.getNumberOfPages();
for (int i = 0; i < totalPages; i++) { for (int i = 0; i < totalPages; i++) {
@@ -116,4 +107,45 @@ public class ScalePagesController {
Filenames.toSimpleFileName(file.getOriginalFilename()).replaceFirst("[.][^.]+$", "") Filenames.toSimpleFileName(file.getOriginalFilename()).replaceFirst("[.][^.]+$", "")
+ "_scaled.pdf"); + "_scaled.pdf");
} }
private PDRectangle getTargetSize(String targetPDRectangle, PDDocument sourceDocument) {
if (targetPDRectangle.equals("KEEP")) {
if (sourceDocument.getNumberOfPages() == 0) {
return null;
}
// use the first page to determine the target page size
PDPage sourcePage = sourceDocument.getPage(0);
PDRectangle sourceSize = sourcePage.getMediaBox();
return sourceSize;
}
Map<String, PDRectangle> sizeMap = getSizeMap();
if (sizeMap.containsKey(targetPDRectangle)) {
return sizeMap.get(targetPDRectangle);
}
throw new IllegalArgumentException(
"Invalid PDRectangle. It must be one of the following: A0, A1, A2, A3, A4, A5, A6, LETTER, LEGAL, KEEP");
}
private Map<String, PDRectangle> getSizeMap() {
Map<String, PDRectangle> sizeMap = new HashMap<>();
// Add A0 - A6
sizeMap.put("A0", PDRectangle.A0);
sizeMap.put("A1", PDRectangle.A1);
sizeMap.put("A2", PDRectangle.A2);
sizeMap.put("A3", PDRectangle.A3);
sizeMap.put("A4", PDRectangle.A4);
sizeMap.put("A5", PDRectangle.A5);
sizeMap.put("A6", PDRectangle.A6);
// Add other sizes
sizeMap.put("LETTER", PDRectangle.LETTER);
sizeMap.put("LEGAL", PDRectangle.LEGAL);
return sizeMap;
}
} }

View File

@@ -15,6 +15,7 @@ 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;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType; 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;
@@ -27,9 +28,8 @@ 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.PdfMetadata;
import stirling.software.SPDF.model.api.PDFWithPageNums; import stirling.software.SPDF.model.api.PDFWithPageNums;
import stirling.software.SPDF.utils.PdfUtils; import stirling.software.SPDF.service.CustomPDDocumentFactory;
import stirling.software.SPDF.utils.WebResponseUtils; import stirling.software.SPDF.utils.WebResponseUtils;
@RestController @RestController
@@ -38,6 +38,12 @@ import stirling.software.SPDF.utils.WebResponseUtils;
public class SplitPDFController { public class SplitPDFController {
private static final Logger logger = LoggerFactory.getLogger(SplitPDFController.class); private static final Logger logger = LoggerFactory.getLogger(SplitPDFController.class);
private final CustomPDDocumentFactory pdfDocumentFactory;
@Autowired
public SplitPDFController(CustomPDDocumentFactory pdfDocumentFactory) {
this.pdfDocumentFactory = pdfDocumentFactory;
}
@PostMapping(consumes = "multipart/form-data", value = "/split-pages") @PostMapping(consumes = "multipart/form-data", value = "/split-pages")
@Operation( @Operation(
@@ -51,7 +57,7 @@ public class SplitPDFController {
// open the pdf document // open the pdf document
PDDocument document = Loader.loadPDF(file.getBytes()); PDDocument document = Loader.loadPDF(file.getBytes());
PdfMetadata metadata = PdfUtils.extractMetadataFromPdf(document); // PdfMetadata metadata = PdfMetadataService.extractMetadataFromPdf(document);
int totalPages = document.getNumberOfPages(); int totalPages = document.getNumberOfPages();
List<Integer> pageNumbers = request.getPageNumbersList(document, false); List<Integer> pageNumbers = request.getPageNumbersList(document, false);
System.out.println( System.out.println(
@@ -70,7 +76,8 @@ public class SplitPDFController {
List<ByteArrayOutputStream> splitDocumentsBoas = new ArrayList<>(); List<ByteArrayOutputStream> splitDocumentsBoas = new ArrayList<>();
int previousPageNumber = 0; int previousPageNumber = 0;
for (int splitPoint : pageNumbers) { for (int splitPoint : pageNumbers) {
try (PDDocument splitDocument = new PDDocument()) { try (PDDocument splitDocument =
pdfDocumentFactory.createNewDocumentBasedOnOldDocument(document)) {
for (int i = previousPageNumber; i <= splitPoint; i++) { for (int i = previousPageNumber; i <= splitPoint; i++) {
PDPage page = document.getPage(i); PDPage page = document.getPage(i);
splitDocument.addPage(page); splitDocument.addPage(page);
@@ -79,7 +86,7 @@ public class SplitPDFController {
previousPageNumber = splitPoint + 1; previousPageNumber = splitPoint + 1;
// Transfer metadata to split pdf // Transfer metadata to split pdf
PdfUtils.setMetadataToPdf(splitDocument, metadata); // PdfMetadataService.setMetadataToPdf(splitDocument, metadata);
ByteArrayOutputStream baos = new ByteArrayOutputStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream();
splitDocument.save(baos); splitDocument.save(baos);

View File

@@ -15,6 +15,7 @@ import org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline.PDDocume
import org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline.PDOutlineItem; import org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline.PDOutlineItem;
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.http.MediaType; 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;
@@ -31,9 +32,9 @@ import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import stirling.software.SPDF.config.PdfMetadataService;
import stirling.software.SPDF.model.PdfMetadata; import stirling.software.SPDF.model.PdfMetadata;
import stirling.software.SPDF.model.api.SplitPdfByChaptersRequest; import stirling.software.SPDF.model.api.SplitPdfByChaptersRequest;
import stirling.software.SPDF.utils.PdfUtils;
import stirling.software.SPDF.utils.WebResponseUtils; import stirling.software.SPDF.utils.WebResponseUtils;
@RestController @RestController
@@ -44,6 +45,13 @@ public class SplitPdfByChaptersController {
private static final Logger logger = private static final Logger logger =
LoggerFactory.getLogger(SplitPdfByChaptersController.class); LoggerFactory.getLogger(SplitPdfByChaptersController.class);
private final PdfMetadataService pdfMetadataService;
@Autowired
public SplitPdfByChaptersController(PdfMetadataService pdfMetadataService) {
this.pdfMetadataService = pdfMetadataService;
}
@PostMapping(value = "/split-pdf-by-chapters", consumes = "multipart/form-data") @PostMapping(value = "/split-pdf-by-chapters", consumes = "multipart/form-data")
@Operation( @Operation(
summary = "Split PDFs by Chapters", summary = "Split PDFs by Chapters",
@@ -258,7 +266,7 @@ public class SplitPdfByChaptersController {
List<ByteArrayOutputStream> splitDocumentsBoas = new ArrayList<>(); List<ByteArrayOutputStream> splitDocumentsBoas = new ArrayList<>();
PdfMetadata metadata = null; PdfMetadata metadata = null;
if (includeMetadata) { if (includeMetadata) {
metadata = PdfUtils.extractMetadataFromPdf(sourceDocument); metadata = pdfMetadataService.extractMetadataFromPdf(sourceDocument);
} }
for (Bookmark bookmark : bookmarks) { for (Bookmark bookmark : bookmarks) {
try (PDDocument splitDocument = new PDDocument()) { try (PDDocument splitDocument = new PDDocument()) {
@@ -273,7 +281,7 @@ public class SplitPdfByChaptersController {
} }
ByteArrayOutputStream baos = new ByteArrayOutputStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream();
if (includeMetadata) { if (includeMetadata) {
PdfUtils.setMetadataToPdf(splitDocument, metadata); pdfMetadataService.setMetadataToPdf(splitDocument, metadata);
} }
splitDocument.save(baos); splitDocument.save(baos);

View File

@@ -20,6 +20,7 @@ import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject;
import org.apache.pdfbox.util.Matrix; import org.apache.pdfbox.util.Matrix;
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.http.MediaType; 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;
@@ -33,6 +34,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.SplitPdfBySectionsRequest; import stirling.software.SPDF.model.api.SplitPdfBySectionsRequest;
import stirling.software.SPDF.service.CustomPDDocumentFactory;
import stirling.software.SPDF.utils.WebResponseUtils; import stirling.software.SPDF.utils.WebResponseUtils;
@RestController @RestController
@@ -43,6 +45,13 @@ public class SplitPdfBySectionsController {
private static final Logger logger = private static final Logger logger =
LoggerFactory.getLogger(SplitPdfBySectionsController.class); LoggerFactory.getLogger(SplitPdfBySectionsController.class);
private final CustomPDDocumentFactory pdfDocumentFactory;
@Autowired
public SplitPdfBySectionsController(CustomPDDocumentFactory pdfDocumentFactory) {
this.pdfDocumentFactory = pdfDocumentFactory;
}
@PostMapping(value = "/split-pdf-by-sections", consumes = "multipart/form-data") @PostMapping(value = "/split-pdf-by-sections", consumes = "multipart/form-data")
@Operation( @Operation(
summary = "Split PDF pages into smaller sections", summary = "Split PDF pages into smaller sections",
@@ -65,7 +74,7 @@ public class SplitPdfBySectionsController {
Filenames.toSimpleFileName(file.getOriginalFilename()) Filenames.toSimpleFileName(file.getOriginalFilename())
.replaceFirst("[.][^.]+$", ""); .replaceFirst("[.][^.]+$", "");
if (merge) { if (merge) {
MergeController mergeController = new MergeController(); MergeController mergeController = new MergeController(pdfDocumentFactory);
ByteArrayOutputStream baos = new ByteArrayOutputStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream();
mergeController.mergeDocuments(splitDocuments).save(baos); mergeController.mergeDocuments(splitDocuments).save(baos);
return WebResponseUtils.bytesToWebResponse(baos.toByteArray(), filename + "_split.pdf"); return WebResponseUtils.bytesToWebResponse(baos.toByteArray(), filename + "_split.pdf");

View File

@@ -12,6 +12,7 @@ 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;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType; 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;
@@ -25,6 +26,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.SplitPdfBySizeOrCountRequest; import stirling.software.SPDF.model.api.general.SplitPdfBySizeOrCountRequest;
import stirling.software.SPDF.service.CustomPDDocumentFactory;
import stirling.software.SPDF.utils.GeneralUtils; import stirling.software.SPDF.utils.GeneralUtils;
import stirling.software.SPDF.utils.WebResponseUtils; import stirling.software.SPDF.utils.WebResponseUtils;
@@ -34,6 +36,12 @@ import stirling.software.SPDF.utils.WebResponseUtils;
public class SplitPdfBySizeController { public class SplitPdfBySizeController {
private static final Logger logger = LoggerFactory.getLogger(SplitPdfBySizeController.class); private static final Logger logger = LoggerFactory.getLogger(SplitPdfBySizeController.class);
private final CustomPDDocumentFactory pdfDocumentFactory;
@Autowired
public SplitPdfBySizeController(CustomPDDocumentFactory pdfDocumentFactory) {
this.pdfDocumentFactory = pdfDocumentFactory;
}
@PostMapping(value = "/split-by-size-or-count", consumes = "multipart/form-data") @PostMapping(value = "/split-by-size-or-count", consumes = "multipart/form-data")
@Operation( @Operation(
@@ -84,7 +92,8 @@ public class SplitPdfBySizeController {
PDDocument sourceDocument, long maxBytes, ZipOutputStream zipOut, String baseFilename) PDDocument sourceDocument, long maxBytes, ZipOutputStream zipOut, String baseFilename)
throws IOException { throws IOException {
long currentSize = 0; long currentSize = 0;
PDDocument currentDoc = new PDDocument(); PDDocument currentDoc =
pdfDocumentFactory.createNewDocumentBasedOnOldDocument(sourceDocument);
int fileIndex = 1; int fileIndex = 1;
for (int pageIndex = 0; pageIndex < sourceDocument.getNumberOfPages(); pageIndex++) { for (int pageIndex = 0; pageIndex < sourceDocument.getNumberOfPages(); pageIndex++) {
@@ -121,7 +130,8 @@ public class SplitPdfBySizeController {
PDDocument sourceDocument, int pageCount, ZipOutputStream zipOut, String baseFilename) PDDocument sourceDocument, int pageCount, ZipOutputStream zipOut, String baseFilename)
throws IOException { throws IOException {
int currentPageCount = 0; int currentPageCount = 0;
PDDocument currentDoc = new PDDocument(); PDDocument currentDoc =
pdfDocumentFactory.createNewDocumentBasedOnOldDocument(sourceDocument);
int fileIndex = 1; int fileIndex = 1;
for (PDPage page : sourceDocument.getPages()) { for (PDPage page : sourceDocument.getPages()) {
currentDoc.addPage(page); currentDoc.addPage(page);
@@ -152,7 +162,8 @@ public class SplitPdfBySizeController {
int currentPageIndex = 0; int currentPageIndex = 0;
int fileIndex = 1; int fileIndex = 1;
for (int i = 0; i < documentCount; i++) { for (int i = 0; i < documentCount; i++) {
PDDocument currentDoc = new PDDocument(); PDDocument currentDoc =
pdfDocumentFactory.createNewDocumentBasedOnOldDocument(sourceDocument);
int pagesToAdd = pagesPerDocument + (i < extraPages ? 1 : 0); int pagesToAdd = pagesPerDocument + (i < extraPages ? 1 : 0);
for (int j = 0; j < pagesToAdd; j++) { for (int j = 0; j < pagesToAdd; j++) {

View File

@@ -13,6 +13,7 @@ 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;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
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;
@@ -23,6 +24,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.PDFFile; import stirling.software.SPDF.model.api.PDFFile;
import stirling.software.SPDF.service.CustomPDDocumentFactory;
import stirling.software.SPDF.utils.WebResponseUtils; import stirling.software.SPDF.utils.WebResponseUtils;
@RestController @RestController
@@ -32,6 +34,13 @@ public class ToSinglePageController {
private static final Logger logger = LoggerFactory.getLogger(ToSinglePageController.class); private static final Logger logger = LoggerFactory.getLogger(ToSinglePageController.class);
private final CustomPDDocumentFactory pdfDocumentFactory;
@Autowired
public ToSinglePageController(CustomPDDocumentFactory pdfDocumentFactory) {
this.pdfDocumentFactory = pdfDocumentFactory;
}
@PostMapping(consumes = "multipart/form-data", value = "/pdf-to-single-page") @PostMapping(consumes = "multipart/form-data", value = "/pdf-to-single-page")
@Operation( @Operation(
summary = "Convert a multi-page PDF into a single long page PDF", summary = "Convert a multi-page PDF into a single long page PDF",
@@ -53,7 +62,8 @@ public class ToSinglePageController {
} }
// Create new document and page with calculated dimensions // Create new document and page with calculated dimensions
PDDocument newDocument = new PDDocument(); PDDocument newDocument =
pdfDocumentFactory.createNewDocumentBasedOnOldDocument(sourceDocument);
PDPage newPage = new PDPage(new PDRectangle(maxWidth, totalHeight)); PDPage newPage = new PDPage(new PDRectangle(maxWidth, totalHeight));
newDocument.addPage(newPage); newDocument.addPage(newPage);

View File

@@ -208,8 +208,8 @@ public class UserController {
@PreAuthorize("hasRole('ROLE_ADMIN')") @PreAuthorize("hasRole('ROLE_ADMIN')")
@PostMapping("/admin/saveUser") @PostMapping("/admin/saveUser")
public RedirectView saveUser( public RedirectView saveUser(
@RequestParam String username, @RequestParam(name = "username", required = true) String username,
@RequestParam(name = "password", required = false) String password, @RequestParam(name = "password", required = true) String password,
@RequestParam(name = "role") String role, @RequestParam(name = "role") String role,
@RequestParam(name = "authType") String authType, @RequestParam(name = "authType") String authType,
@RequestParam(name = "forceChange", required = false, defaultValue = "false") @RequestParam(name = "forceChange", required = false, defaultValue = "false")

View File

@@ -1,30 +1,36 @@
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.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;
import org.springframework.web.bind.annotation.RequestMapping;
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.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 stirling.software.SPDF.model.api.GeneralFile; import stirling.software.SPDF.model.api.GeneralFile;
import stirling.software.SPDF.service.CustomPDDocumentFactory;
import stirling.software.SPDF.utils.FileToPdf; import stirling.software.SPDF.utils.FileToPdf;
import stirling.software.SPDF.utils.WebResponseUtils; import stirling.software.SPDF.utils.WebResponseUtils;
@RestController // Disabled for now
@Tag(name = "Convert", description = "Convert APIs") // @RestController
@RequestMapping("/api/v1/convert") // @Tag(name = "Convert", description = "Convert APIs")
// @RequestMapping("/api/v1/convert")
public class ConvertBookToPDFController { public class ConvertBookToPDFController {
@Autowired private final boolean bookAndHtmlFormatsInstalled;
@Qualifier("bookAndHtmlFormatsInstalled")
private boolean bookAndHtmlFormatsInstalled; private final CustomPDDocumentFactory pdfDocumentFactory;
// @Autowired
public ConvertBookToPDFController(
CustomPDDocumentFactory pdfDocumentFactory,
@Qualifier("bookAndHtmlFormatsInstalled") boolean bookAndHtmlFormatsInstalled) {
this.pdfDocumentFactory = pdfDocumentFactory;
this.bookAndHtmlFormatsInstalled = bookAndHtmlFormatsInstalled;
}
@PostMapping(consumes = "multipart/form-data", value = "/book/pdf") @PostMapping(consumes = "multipart/form-data", value = "/book/pdf")
@Operation( @Operation(

View File

@@ -1,28 +1,25 @@
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.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;
import org.springframework.web.bind.annotation.RequestMapping;
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.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 stirling.software.SPDF.model.api.converters.HTMLToPdfRequest; 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;
@RestController // Disabled for now
@Tag(name = "Convert", description = "Convert APIs") // @RestController
@RequestMapping("/api/v1/convert") // @Tag(name = "Convert", description = "Convert APIs")
// @RequestMapping("/api/v1/convert")
public class ConvertHtmlToPDF { public class ConvertHtmlToPDF {
@Autowired // @Autowired
@Qualifier("bookAndHtmlFormatsInstalled") @Qualifier("bookAndHtmlFormatsInstalled")
private boolean bookAndHtmlFormatsInstalled; private boolean bookAndHtmlFormatsInstalled;

View File

@@ -16,6 +16,7 @@ import org.apache.commons.io.FileUtils;
import org.apache.pdfbox.rendering.ImageType; import org.apache.pdfbox.rendering.ImageType;
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.http.MediaType; 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;
@@ -30,6 +31,7 @@ import io.swagger.v3.oas.annotations.tags.Tag;
import stirling.software.SPDF.model.api.converters.ConvertToImageRequest; import stirling.software.SPDF.model.api.converters.ConvertToImageRequest;
import stirling.software.SPDF.model.api.converters.ConvertToPdfRequest; import stirling.software.SPDF.model.api.converters.ConvertToPdfRequest;
import stirling.software.SPDF.service.CustomPDDocumentFactory;
import stirling.software.SPDF.utils.CheckProgramInstall; import stirling.software.SPDF.utils.CheckProgramInstall;
import stirling.software.SPDF.utils.PdfUtils; import stirling.software.SPDF.utils.PdfUtils;
import stirling.software.SPDF.utils.ProcessExecutor; import stirling.software.SPDF.utils.ProcessExecutor;
@@ -43,6 +45,13 @@ public class ConvertImgPDFController {
private static final Logger logger = LoggerFactory.getLogger(ConvertImgPDFController.class); private static final Logger logger = LoggerFactory.getLogger(ConvertImgPDFController.class);
private final CustomPDDocumentFactory pdfDocumentFactory;
@Autowired
public ConvertImgPDFController(CustomPDDocumentFactory pdfDocumentFactory) {
this.pdfDocumentFactory = pdfDocumentFactory;
}
@PostMapping(consumes = "multipart/form-data", value = "/pdf/img") @PostMapping(consumes = "multipart/form-data", value = "/pdf/img")
@Operation( @Operation(
summary = "Convert PDF to image(s)", summary = "Convert PDF to image(s)",
@@ -178,7 +187,8 @@ public class ConvertImgPDFController {
boolean autoRotate = request.isAutoRotate(); boolean autoRotate = request.isAutoRotate();
// Convert the file to PDF and get the resulting bytes // Convert the file to PDF and get the resulting bytes
byte[] bytes = PdfUtils.imageToPdf(file, fitOption, autoRotate, colorType); byte[] bytes =
PdfUtils.imageToPdf(file, fitOption, autoRotate, colorType, pdfDocumentFactory);
return WebResponseUtils.bytesToWebResponse( return WebResponseUtils.bytesToWebResponse(
bytes, bytes,
file[0].getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_converted.pdf"); file[0].getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_converted.pdf");

View File

@@ -10,29 +10,26 @@ 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.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.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;
import org.springframework.web.bind.annotation.RequestMapping;
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.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 stirling.software.SPDF.model.api.GeneralFile; import stirling.software.SPDF.model.api.GeneralFile;
import stirling.software.SPDF.utils.FileToPdf; import stirling.software.SPDF.utils.FileToPdf;
import stirling.software.SPDF.utils.WebResponseUtils; import stirling.software.SPDF.utils.WebResponseUtils;
@RestController // Disabled for now
@Tag(name = "Convert", description = "Convert APIs") // @RestController
@RequestMapping("/api/v1/convert") // @Tag(name = "Convert", description = "Convert APIs")
// @RequestMapping("/api/v1/convert")
public class ConvertMarkdownToPdf { public class ConvertMarkdownToPdf {
@Autowired // @Autowired
@Qualifier("bookAndHtmlFormatsInstalled") @Qualifier("bookAndHtmlFormatsInstalled")
private boolean bookAndHtmlFormatsInstalled; private boolean bookAndHtmlFormatsInstalled;

View File

@@ -1,5 +1,6 @@
package stirling.software.SPDF.controller.api.converters; package stirling.software.SPDF.controller.api.converters;
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.Path; import java.nio.file.Path;
@@ -8,6 +9,8 @@ import java.util.Arrays;
import java.util.List; import java.util.List;
import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.FilenameUtils;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.springframework.beans.factory.annotation.Autowired;
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;
@@ -20,6 +23,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.GeneralFile; import stirling.software.SPDF.model.api.GeneralFile;
import stirling.software.SPDF.service.CustomPDDocumentFactory;
import stirling.software.SPDF.utils.ProcessExecutor; import stirling.software.SPDF.utils.ProcessExecutor;
import stirling.software.SPDF.utils.ProcessExecutor.ProcessExecutorResult; import stirling.software.SPDF.utils.ProcessExecutor.ProcessExecutorResult;
import stirling.software.SPDF.utils.WebResponseUtils; import stirling.software.SPDF.utils.WebResponseUtils;
@@ -29,7 +33,7 @@ import stirling.software.SPDF.utils.WebResponseUtils;
@RequestMapping("/api/v1/convert") @RequestMapping("/api/v1/convert")
public class ConvertOfficeController { public class ConvertOfficeController {
public byte[] convertToPdf(MultipartFile inputFile) throws IOException, InterruptedException { public File convertToPdf(MultipartFile inputFile) throws IOException, InterruptedException {
// Check for valid file extension // Check for valid file extension
String originalFilename = Filenames.toSimpleFileName(inputFile.getOriginalFilename()); String originalFilename = Filenames.toSimpleFileName(inputFile.getOriginalFilename());
if (originalFilename == null if (originalFilename == null
@@ -62,12 +66,10 @@ public class ConvertOfficeController {
.runCommandWithOutputHandling(command); .runCommandWithOutputHandling(command);
// Read the converted PDF file // Read the converted PDF file
byte[] pdfBytes = Files.readAllBytes(tempOutputFile); return tempOutputFile.toFile();
return pdfBytes;
} finally { } finally {
// Clean up the temporary files // Clean up the temporary files
if (tempInputFile != null) Files.deleteIfExists(tempInputFile); if (tempInputFile != null) Files.deleteIfExists(tempInputFile);
Files.deleteIfExists(tempOutputFile);
} }
} }
@@ -76,6 +78,13 @@ public class ConvertOfficeController {
return fileExtension.matches(extensionPattern); return fileExtension.matches(extensionPattern);
} }
private final CustomPDDocumentFactory pdfDocumentFactory;
@Autowired
public ConvertOfficeController(CustomPDDocumentFactory pdfDocumentFactory) {
this.pdfDocumentFactory = pdfDocumentFactory;
}
@PostMapping(consumes = "multipart/form-data", value = "/file/pdf") @PostMapping(consumes = "multipart/form-data", value = "/file/pdf")
@Operation( @Operation(
summary = "Convert a file to a PDF using LibreOffice", summary = "Convert a file to a PDF using LibreOffice",
@@ -86,12 +95,18 @@ public class ConvertOfficeController {
MultipartFile inputFile = request.getFileInput(); MultipartFile inputFile = request.getFileInput();
// unused but can start server instance if startup time is to long // unused but can start server instance if startup time is to long
// LibreOfficeListener.getInstance().start(); // LibreOfficeListener.getInstance().start();
File file = null;
try {
file = convertToPdf(inputFile);
byte[] pdfByteArray = convertToPdf(inputFile); PDDocument doc = pdfDocumentFactory.load(file);
return WebResponseUtils.bytesToWebResponse( return WebResponseUtils.pdfDocToWebResponse(
pdfByteArray, doc,
Filenames.toSimpleFileName(inputFile.getOriginalFilename()) Filenames.toSimpleFileName(inputFile.getOriginalFilename())
.replaceFirst("[.][^.]+$", "") .replaceFirst("[.][^.]+$", "")
+ "_convertedToPDF.pdf"); + "_convertedToPDF.pdf");
} finally {
if (file != null) file.delete();
}
} }
} }

View File

@@ -6,30 +6,27 @@ import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
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.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;
import org.springframework.web.bind.annotation.RequestMapping;
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.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 stirling.software.SPDF.model.api.converters.PdfToBookRequest; import stirling.software.SPDF.model.api.converters.PdfToBookRequest;
import stirling.software.SPDF.utils.ProcessExecutor; import stirling.software.SPDF.utils.ProcessExecutor;
import stirling.software.SPDF.utils.ProcessExecutor.ProcessExecutorResult; import stirling.software.SPDF.utils.ProcessExecutor.ProcessExecutorResult;
import stirling.software.SPDF.utils.WebResponseUtils; import stirling.software.SPDF.utils.WebResponseUtils;
@RestController // Disabled for now
@Tag(name = "Convert", description = "Convert APIs") // @RestController
@RequestMapping("/api/v1/convert") // @Tag(name = "Convert", description = "Convert APIs")
// @RequestMapping("/api/v1/convert")
public class ConvertPDFToBookController { public class ConvertPDFToBookController {
@Autowired // @Autowired
@Qualifier("bookAndHtmlFormatsInstalled") @Qualifier("bookAndHtmlFormatsInstalled")
private boolean bookAndHtmlFormatsInstalled; private boolean bookAndHtmlFormatsInstalled;

View File

@@ -9,7 +9,6 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDDocumentCatalog; import org.apache.pdfbox.pdmodel.PDDocumentCatalog;
import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm; import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm;
@@ -17,6 +16,7 @@ import org.apache.pdfbox.pdmodel.interactive.form.PDField;
import org.apache.pdfbox.pdmodel.interactive.form.PDSignatureField; import org.apache.pdfbox.pdmodel.interactive.form.PDSignatureField;
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.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;
@@ -29,6 +29,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.converters.PdfToPdfARequest; import stirling.software.SPDF.model.api.converters.PdfToPdfARequest;
import stirling.software.SPDF.service.CustomPDDocumentFactory;
import stirling.software.SPDF.utils.ProcessExecutor; import stirling.software.SPDF.utils.ProcessExecutor;
import stirling.software.SPDF.utils.ProcessExecutor.ProcessExecutorResult; import stirling.software.SPDF.utils.ProcessExecutor.ProcessExecutorResult;
import stirling.software.SPDF.utils.WebResponseUtils; import stirling.software.SPDF.utils.WebResponseUtils;
@@ -40,6 +41,13 @@ public class ConvertPDFToPDFA {
private static final Logger logger = LoggerFactory.getLogger(ConvertPDFToPDFA.class); private static final Logger logger = LoggerFactory.getLogger(ConvertPDFToPDFA.class);
private final CustomPDDocumentFactory pdfDocumentFactory;
@Autowired
public ConvertPDFToPDFA(CustomPDDocumentFactory pdfDocumentFactory) {
this.pdfDocumentFactory = pdfDocumentFactory;
}
@PostMapping(consumes = "multipart/form-data", value = "/pdf/pdfa") @PostMapping(consumes = "multipart/form-data", value = "/pdf/pdfa")
@Operation( @Operation(
summary = "Convert a PDF to a PDF/A", summary = "Convert a PDF to a PDF/A",
@@ -54,7 +62,7 @@ public class ConvertPDFToPDFA {
byte[] pdfBytes = inputFile.getBytes(); byte[] pdfBytes = inputFile.getBytes();
// Load the PDF document // Load the PDF document
PDDocument document = Loader.loadPDF(pdfBytes); PDDocument document = pdfDocumentFactory.load(pdfBytes);
// Get the document catalog // Get the document catalog
PDDocumentCatalog catalog = document.getDocumentCatalog(); PDDocumentCatalog catalog = document.getDocumentCatalog();
@@ -101,18 +109,18 @@ public class ConvertPDFToPDFA {
ProcessExecutor.getInstance(ProcessExecutor.Processes.OCR_MY_PDF) ProcessExecutor.getInstance(ProcessExecutor.Processes.OCR_MY_PDF)
.runCommandWithOutputHandling(command); .runCommandWithOutputHandling(command);
// Read the optimized PDF file try {
byte[] optimizedPdfBytes = Files.readAllBytes(tempOutputFile); PDDocument doc = pdfDocumentFactory.load(tempOutputFile.toFile());
// Return the optimized PDF as a response
// Clean up the temporary files String outputFilename =
Files.deleteIfExists(tempInputFile); Filenames.toSimpleFileName(inputFile.getOriginalFilename())
Files.deleteIfExists(tempOutputFile); .replaceFirst("[.][^.]+$", "")
+ "_PDFA.pdf";
// Return the optimized PDF as a response return WebResponseUtils.pdfDocToWebResponse(doc, outputFilename);
String outputFilename = } finally {
Filenames.toSimpleFileName(inputFile.getOriginalFilename()) // Clean up the temporary files
.replaceFirst("[.][^.]+$", "") Files.deleteIfExists(tempInputFile);
+ "_PDFA.pdf"; Files.deleteIfExists(tempOutputFile);
return WebResponseUtils.bytesToWebResponse(optimizedPdfBytes, outputFilename); }
} }
} }

View File

@@ -6,6 +6,10 @@ import java.nio.file.Path;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
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;
@@ -16,6 +20,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.converters.UrlToPdfRequest; import stirling.software.SPDF.model.api.converters.UrlToPdfRequest;
import stirling.software.SPDF.service.CustomPDDocumentFactory;
import stirling.software.SPDF.utils.GeneralUtils; import stirling.software.SPDF.utils.GeneralUtils;
import stirling.software.SPDF.utils.ProcessExecutor; import stirling.software.SPDF.utils.ProcessExecutor;
import stirling.software.SPDF.utils.ProcessExecutor.ProcessExecutorResult; import stirling.software.SPDF.utils.ProcessExecutor.ProcessExecutorResult;
@@ -26,6 +31,15 @@ import stirling.software.SPDF.utils.WebResponseUtils;
@RequestMapping("/api/v1/convert") @RequestMapping("/api/v1/convert")
public class ConvertWebsiteToPDF { public class ConvertWebsiteToPDF {
private static final Logger logger = LoggerFactory.getLogger(ConvertWebsiteToPDF.class);
private final CustomPDDocumentFactory pdfDocumentFactory;
@Autowired
public ConvertWebsiteToPDF(CustomPDDocumentFactory pdfDocumentFactory) {
this.pdfDocumentFactory = pdfDocumentFactory;
}
@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",
@@ -46,12 +60,12 @@ public class ConvertWebsiteToPDF {
} }
Path tempOutputFile = null; Path tempOutputFile = null;
byte[] pdfBytes; PDDocument doc = null;
try { try {
// Prepare the output file path // Prepare the output file path
tempOutputFile = Files.createTempFile("output_", ".pdf"); tempOutputFile = Files.createTempFile("output_", ".pdf");
// Prepare the OCRmyPDF command // Prepare the WeasyPrint command
List<String> command = new ArrayList<>(); List<String> command = new ArrayList<>();
command.add("weasyprint"); command.add("weasyprint");
command.add(URL); command.add(URL);
@@ -61,16 +75,23 @@ public class ConvertWebsiteToPDF {
ProcessExecutor.getInstance(ProcessExecutor.Processes.WEASYPRINT) ProcessExecutor.getInstance(ProcessExecutor.Processes.WEASYPRINT)
.runCommandWithOutputHandling(command); .runCommandWithOutputHandling(command);
// Read the optimized PDF file // Load the PDF using pdfDocumentFactory
pdfBytes = Files.readAllBytes(tempOutputFile); doc = pdfDocumentFactory.load(tempOutputFile.toFile());
} finally {
// Clean up the temporary files
Files.deleteIfExists(tempOutputFile);
}
// Convert URL to a safe filename
String outputFilename = convertURLToFileName(URL);
return WebResponseUtils.bytesToWebResponse(pdfBytes, outputFilename); // Convert URL to a safe filename
String outputFilename = convertURLToFileName(URL);
return WebResponseUtils.pdfDocToWebResponse(doc, outputFilename);
} finally {
if (tempOutputFile != null) {
try {
Files.deleteIfExists(tempOutputFile);
} catch (IOException e) {
logger.error("Error deleting temporary output file", e);
}
}
}
} }
private String convertURLToFileName(String url) { private String convertURLToFileName(String url) {

View File

@@ -30,13 +30,13 @@ import stirling.software.SPDF.model.api.extract.PDFFilePage;
@RestController @RestController
@RequestMapping("/api/v1/convert") @RequestMapping("/api/v1/convert")
@Tag(name = "Convert", description = "Convert APIs") @Tag(name = "Convert", description = "Convert APIs")
public class ExtractController { public class ExtractCSVController {
private static final Logger logger = LoggerFactory.getLogger(CropController.class); private static final Logger logger = LoggerFactory.getLogger(CropController.class);
@PostMapping(value = "/pdf/csv", consumes = "multipart/form-data") @PostMapping(value = "/pdf/csv", consumes = "multipart/form-data")
@Operation( @Operation(
summary = "Extracts a PDF document to csv", summary = "Extracts a CSV document from a PDF",
description = description =
"This operation takes an input PDF file and returns CSV file of whole page. Input:PDF Output:CSV Type:SISO") "This operation takes an input PDF file and returns CSV file of whole page. Input:PDF Output:CSV Type:SISO")
public ResponseEntity<String> PdfToCsv(@ModelAttribute PDFFilePage form) throws Exception { public ResponseEntity<String> PdfToCsv(@ModelAttribute PDFFilePage form) throws Exception {

View File

@@ -12,11 +12,11 @@ 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.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType; 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;
@@ -38,6 +38,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.misc.AutoSplitPdfRequest; import stirling.software.SPDF.model.api.misc.AutoSplitPdfRequest;
import stirling.software.SPDF.service.CustomPDDocumentFactory;
import stirling.software.SPDF.utils.WebResponseUtils; import stirling.software.SPDF.utils.WebResponseUtils;
@RestController @RestController
@@ -49,6 +50,13 @@ 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"; private static final String QR_CONTENT_OLD = "https://github.com/Frooodle/Stirling-PDF";
private final CustomPDDocumentFactory pdfDocumentFactory;
@Autowired
public AutoSplitPdfController(CustomPDDocumentFactory pdfDocumentFactory) {
this.pdfDocumentFactory = pdfDocumentFactory;
}
@PostMapping(value = "/auto-split-pdf", consumes = "multipart/form-data") @PostMapping(value = "/auto-split-pdf", consumes = "multipart/form-data")
@Operation( @Operation(
summary = "Auto split PDF pages into separate documents", summary = "Auto split PDF pages into separate documents",
@@ -59,73 +67,93 @@ public class AutoSplitPdfController {
MultipartFile file = request.getFileInput(); MultipartFile file = request.getFileInput();
boolean duplexMode = request.isDuplexMode(); boolean duplexMode = request.isDuplexMode();
PDDocument document = Loader.loadPDF(file.getBytes()); PDDocument document = null;
PDFRenderer pdfRenderer = new PDFRenderer(document);
pdfRenderer.setSubsamplingAllowed(true);
List<PDDocument> splitDocuments = new ArrayList<>(); List<PDDocument> splitDocuments = new ArrayList<>();
List<ByteArrayOutputStream> splitDocumentsBoas = new ArrayList<>(); Path zipFile = null;
byte[] data = null;
for (int page = 0; page < document.getNumberOfPages(); ++page) { try {
BufferedImage bim = pdfRenderer.renderImageWithDPI(page, 150); document = pdfDocumentFactory.load(file.getInputStream());
String result = decodeQRCode(bim); PDFRenderer pdfRenderer = new PDFRenderer(document);
if ((QR_CONTENT.equals(result) || QR_CONTENT_OLD.equals(result)) && page != 0) { pdfRenderer.setSubsamplingAllowed(true);
splitDocuments.add(new PDDocument());
for (int page = 0; page < document.getNumberOfPages(); ++page) {
BufferedImage bim = pdfRenderer.renderImageWithDPI(page, 150);
String result = decodeQRCode(bim);
if ((QR_CONTENT.equals(result) || QR_CONTENT_OLD.equals(result)) && page != 0) {
splitDocuments.add(new PDDocument());
}
if (!splitDocuments.isEmpty()
&& !QR_CONTENT.equals(result)
&& !QR_CONTENT_OLD.equals(result)) {
splitDocuments.get(splitDocuments.size() - 1).addPage(document.getPage(page));
} else if (page == 0) {
PDDocument firstDocument = new PDDocument();
firstDocument.addPage(document.getPage(page));
splitDocuments.add(firstDocument);
}
// If duplexMode is true and current page is a divider, then skip next page
if (duplexMode && (QR_CONTENT.equals(result) || QR_CONTENT_OLD.equals(result))) {
page++;
}
} }
if (!splitDocuments.isEmpty() // Remove split documents that have no pages
&& !QR_CONTENT.equals(result) splitDocuments.removeIf(pdDocument -> pdDocument.getNumberOfPages() == 0);
&& !QR_CONTENT_OLD.equals(result)) {
splitDocuments.get(splitDocuments.size() - 1).addPage(document.getPage(page)); zipFile = Files.createTempFile("split_documents", ".zip");
} else if (page == 0) { String filename =
PDDocument firstDocument = new PDDocument(); Filenames.toSimpleFileName(file.getOriginalFilename())
firstDocument.addPage(document.getPage(page)); .replaceFirst("[.][^.]+$", "");
splitDocuments.add(firstDocument);
try (ZipOutputStream zipOut = new ZipOutputStream(Files.newOutputStream(zipFile))) {
for (int i = 0; i < splitDocuments.size(); i++) {
String fileName = filename + "_" + (i + 1) + ".pdf";
PDDocument splitDocument = splitDocuments.get(i);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
splitDocument.save(baos);
byte[] pdf = baos.toByteArray();
ZipEntry pdfEntry = new ZipEntry(fileName);
zipOut.putNextEntry(pdfEntry);
zipOut.write(pdf);
zipOut.closeEntry();
}
} }
// If duplexMode is true and current page is a divider, then skip next page
if (duplexMode && (QR_CONTENT.equals(result) || QR_CONTENT_OLD.equals(result))) {
page++;
}
}
// Remove split documents that have no pages
splitDocuments.removeIf(pdDocument -> pdDocument.getNumberOfPages() == 0);
for (PDDocument splitDocument : splitDocuments) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
splitDocument.save(baos);
splitDocumentsBoas.add(baos);
splitDocument.close();
}
document.close();
Path zipFile = Files.createTempFile("split_documents", ".zip");
String filename =
Filenames.toSimpleFileName(file.getOriginalFilename())
.replaceFirst("[.][^.]+$", "");
byte[] data;
try (ZipOutputStream zipOut = new ZipOutputStream(Files.newOutputStream(zipFile))) {
for (int i = 0; i < splitDocumentsBoas.size(); i++) {
String fileName = filename + "_" + (i + 1) + ".pdf";
ByteArrayOutputStream baos = splitDocumentsBoas.get(i);
byte[] pdf = baos.toByteArray();
ZipEntry pdfEntry = new ZipEntry(fileName);
zipOut.putNextEntry(pdfEntry);
zipOut.write(pdf);
zipOut.closeEntry();
}
} catch (Exception e) {
logger.error("exception", e);
} finally {
data = Files.readAllBytes(zipFile); data = Files.readAllBytes(zipFile);
Files.deleteIfExists(zipFile);
}
return WebResponseUtils.bytesToWebResponse( return WebResponseUtils.bytesToWebResponse(
data, filename + ".zip", MediaType.APPLICATION_OCTET_STREAM); data, filename + ".zip", MediaType.APPLICATION_OCTET_STREAM);
} finally {
// Clean up resources
if (document != null) {
try {
document.close();
} catch (IOException e) {
logger.error("Error closing main PDDocument", e);
}
}
for (PDDocument splitDoc : splitDocuments) {
try {
splitDoc.close();
} catch (IOException e) {
logger.error("Error closing split PDDocument", e);
}
}
if (zipFile != null) {
try {
Files.deleteIfExists(zipFile);
} catch (IOException e) {
logger.error("Error deleting temporary zip file", e);
}
}
}
} }
private static String decodeQRCode(BufferedImage bufferedImage) { private static String decodeQRCode(BufferedImage bufferedImage) {

View File

@@ -16,6 +16,7 @@ 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.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
@@ -30,6 +31,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.misc.RemoveBlankPagesRequest; import stirling.software.SPDF.model.api.misc.RemoveBlankPagesRequest;
import stirling.software.SPDF.service.CustomPDDocumentFactory;
import stirling.software.SPDF.utils.PdfUtils; import stirling.software.SPDF.utils.PdfUtils;
import stirling.software.SPDF.utils.WebResponseUtils; import stirling.software.SPDF.utils.WebResponseUtils;
@@ -40,6 +42,13 @@ public class BlankPageController {
private static final Logger logger = LoggerFactory.getLogger(BlankPageController.class); private static final Logger logger = LoggerFactory.getLogger(BlankPageController.class);
private final CustomPDDocumentFactory pdfDocumentFactory;
@Autowired
public BlankPageController(CustomPDDocumentFactory pdfDocumentFactory) {
this.pdfDocumentFactory = pdfDocumentFactory;
}
@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",
@@ -124,7 +133,7 @@ public class BlankPageController {
public void createZipEntry(ZipOutputStream zos, List<PDPage> pages, String entryName) public void createZipEntry(ZipOutputStream zos, List<PDPage> pages, String entryName)
throws IOException { throws IOException {
try (PDDocument document = new PDDocument()) { try (PDDocument document = pdfDocumentFactory.createNewDocument()) {
for (PDPage page : pages) { for (PDPage page : pages) {
document.addPage(page); document.addPage(page);

View File

@@ -20,6 +20,7 @@ import org.apache.pdfbox.pdmodel.graphics.PDXObject;
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject; import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
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.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;
@@ -32,6 +33,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.misc.OptimizePdfRequest; import stirling.software.SPDF.model.api.misc.OptimizePdfRequest;
import stirling.software.SPDF.service.CustomPDDocumentFactory;
import stirling.software.SPDF.utils.GeneralUtils; import stirling.software.SPDF.utils.GeneralUtils;
import stirling.software.SPDF.utils.ProcessExecutor; import stirling.software.SPDF.utils.ProcessExecutor;
import stirling.software.SPDF.utils.ProcessExecutor.ProcessExecutorResult; import stirling.software.SPDF.utils.ProcessExecutor.ProcessExecutorResult;
@@ -44,6 +46,13 @@ public class CompressController {
private static final Logger logger = LoggerFactory.getLogger(CompressController.class); private static final Logger logger = LoggerFactory.getLogger(CompressController.class);
private final CustomPDDocumentFactory pdfDocumentFactory;
@Autowired
public CompressController(CustomPDDocumentFactory pdfDocumentFactory) {
this.pdfDocumentFactory = pdfDocumentFactory;
}
@PostMapping(consumes = "multipart/form-data", value = "/compress-pdf") @PostMapping(consumes = "multipart/form-data", value = "/compress-pdf")
@Operation( @Operation(
summary = "Optimize PDF file", summary = "Optimize PDF file",
@@ -258,7 +267,7 @@ public class CompressController {
} }
// Read the optimized PDF file // Read the optimized PDF file
pdfBytes = Files.readAllBytes(tempOutputFile); pdfBytes = Files.readAllBytes(tempOutputFile);
Path finalFile = tempOutputFile;
// Check if optimized file is larger than the original // Check if optimized file is larger than the original
if (pdfBytes.length > inputFileSize) { if (pdfBytes.length > inputFileSize) {
// Log the occurrence // Log the occurrence
@@ -266,14 +275,15 @@ public class CompressController {
"Optimized file is larger than the original. Returning the original file instead."); "Optimized file is larger than the original. Returning the original file instead.");
// Read the original file again // Read the original file again
pdfBytes = Files.readAllBytes(tempInputFile); finalFile = tempInputFile;
} }
// Return the optimized PDF as a response // Return the optimized PDF as a response
String outputFilename = String outputFilename =
Filenames.toSimpleFileName(inputFile.getOriginalFilename()) Filenames.toSimpleFileName(inputFile.getOriginalFilename())
.replaceFirst("[.][^.]+$", "") .replaceFirst("[.][^.]+$", "")
+ "_Optimized.pdf"; + "_Optimized.pdf";
return WebResponseUtils.bytesToWebResponse(pdfBytes, outputFilename); return WebResponseUtils.pdfDocToWebResponse(
pdfDocumentFactory.load(finalFile.toFile()), outputFilename);
} finally { } finally {
// Clean up the temporary files // Clean up the temporary files

View File

@@ -14,6 +14,7 @@ import org.apache.pdfbox.rendering.ImageType;
import org.apache.pdfbox.rendering.PDFRenderer; import org.apache.pdfbox.rendering.PDFRenderer;
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.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;
@@ -25,9 +26,8 @@ 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.PdfMetadata;
import stirling.software.SPDF.model.api.misc.FlattenRequest; import stirling.software.SPDF.model.api.misc.FlattenRequest;
import stirling.software.SPDF.utils.PdfUtils; import stirling.software.SPDF.service.CustomPDDocumentFactory;
import stirling.software.SPDF.utils.WebResponseUtils; import stirling.software.SPDF.utils.WebResponseUtils;
@RestController @RestController
@@ -37,6 +37,13 @@ public class FlattenController {
private static final Logger logger = LoggerFactory.getLogger(FlattenController.class); private static final Logger logger = LoggerFactory.getLogger(FlattenController.class);
private final CustomPDDocumentFactory pdfDocumentFactory;
@Autowired
public FlattenController(CustomPDDocumentFactory pdfDocumentFactory) {
this.pdfDocumentFactory = pdfDocumentFactory;
}
@PostMapping(consumes = "multipart/form-data", value = "/flatten") @PostMapping(consumes = "multipart/form-data", value = "/flatten")
@Operation( @Operation(
summary = "Flatten PDF form fields or full page", summary = "Flatten PDF form fields or full page",
@@ -46,7 +53,6 @@ public class FlattenController {
MultipartFile file = request.getFileInput(); MultipartFile file = request.getFileInput();
PDDocument document = Loader.loadPDF(file.getBytes()); PDDocument document = Loader.loadPDF(file.getBytes());
PdfMetadata metadata = PdfUtils.extractMetadataFromPdf(document);
Boolean flattenOnlyForms = request.getFlattenOnlyForms(); Boolean flattenOnlyForms = request.getFlattenOnlyForms();
if (Boolean.TRUE.equals(flattenOnlyForms)) { if (Boolean.TRUE.equals(flattenOnlyForms)) {
@@ -60,7 +66,8 @@ public class FlattenController {
// flatten whole page aka convert each page to image and readd it (making text // flatten whole page aka convert each page to image and readd it (making text
// unselectable) // unselectable)
PDFRenderer pdfRenderer = new PDFRenderer(document); PDFRenderer pdfRenderer = new PDFRenderer(document);
PDDocument newDocument = new PDDocument(); PDDocument newDocument =
pdfDocumentFactory.createNewDocumentBasedOnOldDocument(document);
int numPages = document.getNumberOfPages(); int numPages = document.getNumberOfPages();
for (int i = 0; i < numPages; i++) { for (int i = 0; i < numPages; i++) {
try { try {
@@ -80,7 +87,6 @@ public class FlattenController {
logger.error("exception", e); logger.error("exception", e);
} }
} }
PdfUtils.setMetadataToPdf(newDocument, metadata);
return WebResponseUtils.pdfDocToWebResponse( return WebResponseUtils.pdfDocToWebResponse(
newDocument, Filenames.toSimpleFileName(file.getOriginalFilename())); newDocument, Filenames.toSimpleFileName(file.getOriginalFilename()));
} }

View File

@@ -1,5 +1,6 @@
package stirling.software.SPDF.controller.api.misc; package stirling.software.SPDF.controller.api.misc;
import java.io.ByteArrayInputStream;
import java.io.File; import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
@@ -28,6 +29,7 @@ import io.swagger.v3.oas.annotations.tags.Tag;
import stirling.software.SPDF.model.ApplicationProperties; import stirling.software.SPDF.model.ApplicationProperties;
import stirling.software.SPDF.model.api.misc.ProcessPdfWithOcrRequest; import stirling.software.SPDF.model.api.misc.ProcessPdfWithOcrRequest;
import stirling.software.SPDF.service.CustomPDDocumentFactory;
import stirling.software.SPDF.utils.ProcessExecutor; import stirling.software.SPDF.utils.ProcessExecutor;
import stirling.software.SPDF.utils.ProcessExecutor.ProcessExecutorResult; import stirling.software.SPDF.utils.ProcessExecutor.ProcessExecutorResult;
import stirling.software.SPDF.utils.WebResponseUtils; import stirling.software.SPDF.utils.WebResponseUtils;
@@ -52,6 +54,13 @@ public class OCRController {
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
private final CustomPDDocumentFactory pdfDocumentFactory;
@Autowired
public OCRController(CustomPDDocumentFactory pdfDocumentFactory) {
this.pdfDocumentFactory = pdfDocumentFactory;
}
@PostMapping(consumes = "multipart/form-data", value = "/ocr-pdf") @PostMapping(consumes = "multipart/form-data", value = "/ocr-pdf")
@Operation( @Operation(
summary = "Process a PDF file with OCR", summary = "Process a PDF file with OCR",
@@ -175,7 +184,7 @@ public class OCRController {
tempOutputFile = tempPdfWithoutImages; tempOutputFile = tempPdfWithoutImages;
} }
// Read the OCR processed PDF file // Read the OCR processed PDF file
byte[] pdfBytes = Files.readAllBytes(tempOutputFile); byte[] pdfBytes = pdfDocumentFactory.loadToBytes(tempOutputFile.toFile());
// Return the OCR processed PDF as a response // Return the OCR processed PDF as a response
String outputFilename = String outputFilename =
@@ -196,7 +205,13 @@ public class OCRController {
// Add PDF file to the zip // Add PDF file to the zip
ZipEntry pdfEntry = new ZipEntry(outputFilename); ZipEntry pdfEntry = new ZipEntry(outputFilename);
zipOut.putNextEntry(pdfEntry); zipOut.putNextEntry(pdfEntry);
Files.copy(tempOutputFile, zipOut); try (ByteArrayInputStream pdfInputStream = new ByteArrayInputStream(pdfBytes)) {
byte[] buffer = new byte[1024];
int length;
while ((length = pdfInputStream.read(buffer)) != -1) {
zipOut.write(buffer, 0, length);
}
}
zipOut.closeEntry(); zipOut.closeEntry();
// Add text file to the zip // Add text file to the zip

View File

@@ -4,6 +4,7 @@ import java.io.IOException;
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.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;
@@ -17,6 +18,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.misc.OverlayImageRequest; import stirling.software.SPDF.model.api.misc.OverlayImageRequest;
import stirling.software.SPDF.service.CustomPDDocumentFactory;
import stirling.software.SPDF.utils.PdfUtils; import stirling.software.SPDF.utils.PdfUtils;
import stirling.software.SPDF.utils.WebResponseUtils; import stirling.software.SPDF.utils.WebResponseUtils;
@@ -27,6 +29,13 @@ public class OverlayImageController {
private static final Logger logger = LoggerFactory.getLogger(OverlayImageController.class); private static final Logger logger = LoggerFactory.getLogger(OverlayImageController.class);
private final CustomPDDocumentFactory pdfDocumentFactory;
@Autowired
public OverlayImageController(CustomPDDocumentFactory pdfDocumentFactory) {
this.pdfDocumentFactory = pdfDocumentFactory;
}
@PostMapping(consumes = "multipart/form-data", value = "/add-image") @PostMapping(consumes = "multipart/form-data", value = "/add-image")
@Operation( @Operation(
summary = "Overlay image onto a PDF file", summary = "Overlay image onto a PDF file",
@@ -41,7 +50,9 @@ public class OverlayImageController {
try { try {
byte[] pdfBytes = pdfFile.getBytes(); byte[] pdfBytes = pdfFile.getBytes();
byte[] imageBytes = imageFile.getBytes(); byte[] imageBytes = imageFile.getBytes();
byte[] result = PdfUtils.overlayImage(pdfBytes, imageBytes, x, y, everyPage); byte[] result =
PdfUtils.overlayImage(
pdfDocumentFactory, pdfBytes, imageBytes, x, y, everyPage);
return WebResponseUtils.bytesToWebResponse( return WebResponseUtils.bytesToWebResponse(
result, result,

View File

@@ -4,7 +4,6 @@ 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;
@@ -13,6 +12,7 @@ import org.apache.pdfbox.pdmodel.font.PDType1Font;
import org.apache.pdfbox.pdmodel.font.Standard14Fonts; 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.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType; 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;
@@ -26,6 +26,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.misc.AddPageNumbersRequest; import stirling.software.SPDF.model.api.misc.AddPageNumbersRequest;
import stirling.software.SPDF.service.CustomPDDocumentFactory;
import stirling.software.SPDF.utils.GeneralUtils; import stirling.software.SPDF.utils.GeneralUtils;
import stirling.software.SPDF.utils.WebResponseUtils; import stirling.software.SPDF.utils.WebResponseUtils;
@@ -36,6 +37,13 @@ public class PageNumbersController {
private static final Logger logger = LoggerFactory.getLogger(PageNumbersController.class); private static final Logger logger = LoggerFactory.getLogger(PageNumbersController.class);
private final CustomPDDocumentFactory pdfDocumentFactory;
@Autowired
public PageNumbersController(CustomPDDocumentFactory pdfDocumentFactory) {
this.pdfDocumentFactory = pdfDocumentFactory;
}
@PostMapping(value = "/add-page-numbers", consumes = "multipart/form-data") @PostMapping(value = "/add-page-numbers", consumes = "multipart/form-data")
@Operation( @Operation(
summary = "Add page numbers to a PDF document", summary = "Add page numbers to a PDF document",
@@ -52,7 +60,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 = Loader.loadPDF(fileBytes); PDDocument document = pdfDocumentFactory.load(fileBytes);
float font_size = request.getFontSize(); float font_size = request.getFontSize();
String font_type = request.getFontType(); String font_type = request.getFontType();
float marginFactor; float marginFactor;

View File

@@ -8,6 +8,7 @@ import java.util.List;
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.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;
@@ -20,6 +21,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.PDFFile; import stirling.software.SPDF.model.api.PDFFile;
import stirling.software.SPDF.service.CustomPDDocumentFactory;
import stirling.software.SPDF.utils.ProcessExecutor; import stirling.software.SPDF.utils.ProcessExecutor;
import stirling.software.SPDF.utils.ProcessExecutor.ProcessExecutorResult; import stirling.software.SPDF.utils.ProcessExecutor.ProcessExecutorResult;
import stirling.software.SPDF.utils.WebResponseUtils; import stirling.software.SPDF.utils.WebResponseUtils;
@@ -31,6 +33,13 @@ public class RepairController {
private static final Logger logger = LoggerFactory.getLogger(RepairController.class); private static final Logger logger = LoggerFactory.getLogger(RepairController.class);
private final CustomPDDocumentFactory pdfDocumentFactory;
@Autowired
public RepairController(CustomPDDocumentFactory pdfDocumentFactory) {
this.pdfDocumentFactory = pdfDocumentFactory;
}
@PostMapping(consumes = "multipart/form-data", value = "/repair") @PostMapping(consumes = "multipart/form-data", value = "/repair")
@Operation( @Operation(
summary = "Repair a PDF file", summary = "Repair a PDF file",
@@ -58,7 +67,7 @@ public class RepairController {
.runCommandWithOutputHandling(command); .runCommandWithOutputHandling(command);
// Read the optimized PDF file // Read the optimized PDF file
pdfBytes = Files.readAllBytes(tempOutputFile); pdfBytes = pdfDocumentFactory.loadToBytes(tempOutputFile.toFile());
// Return the optimized PDF as a response // Return the optimized PDF as a response
String outputFilename = String outputFilename =

View File

@@ -9,6 +9,7 @@ import org.apache.pdfbox.pdmodel.common.PDNameTreeNode;
import org.apache.pdfbox.pdmodel.interactive.action.PDActionJavaScript; import org.apache.pdfbox.pdmodel.interactive.action.PDActionJavaScript;
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.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;
@@ -75,7 +76,8 @@ public class ShowJavascript {
return WebResponseUtils.bytesToWebResponse( return WebResponseUtils.bytesToWebResponse(
script.getBytes(StandardCharsets.UTF_8), script.getBytes(StandardCharsets.UTF_8),
Filenames.toSimpleFileName(inputFile.getOriginalFilename()) + ".js"); Filenames.toSimpleFileName(inputFile.getOriginalFilename()) + ".js",
MediaType.TEXT_PLAIN);
} }
} }
} }

View File

@@ -12,7 +12,6 @@ import java.util.List;
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;
@@ -25,6 +24,7 @@ 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;
import org.apache.pdfbox.util.Matrix; import org.apache.pdfbox.util.Matrix;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.ClassPathResource;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.ModelAttribute;
@@ -38,6 +38,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.misc.AddStampRequest; import stirling.software.SPDF.model.api.misc.AddStampRequest;
import stirling.software.SPDF.service.CustomPDDocumentFactory;
import stirling.software.SPDF.utils.WebResponseUtils; import stirling.software.SPDF.utils.WebResponseUtils;
@RestController @RestController
@@ -45,6 +46,13 @@ import stirling.software.SPDF.utils.WebResponseUtils;
@Tag(name = "Misc", description = "Miscellaneous APIs") @Tag(name = "Misc", description = "Miscellaneous APIs")
public class StampController { public class StampController {
private final CustomPDDocumentFactory pdfDocumentFactory;
@Autowired
public StampController(CustomPDDocumentFactory pdfDocumentFactory) {
this.pdfDocumentFactory = pdfDocumentFactory;
}
@PostMapping(consumes = "multipart/form-data", value = "/add-stamp") @PostMapping(consumes = "multipart/form-data", value = "/add-stamp")
@Operation( @Operation(
summary = "Add stamp to a PDF file", summary = "Add stamp to a PDF file",
@@ -86,7 +94,7 @@ public class StampController {
} }
// Load the input PDF // Load the input PDF
PDDocument document = Loader.loadPDF(pdfFile.getBytes()); PDDocument document = pdfDocumentFactory.load(pdfFile);
List<Integer> pageNumbers = request.getPageNumbersList(document, true); List<Integer> pageNumbers = request.getPageNumbersList(document, true);

View File

@@ -2,4 +2,6 @@ package stirling.software.SPDF.controller.api.pipeline;
public interface UserServiceInterface { public interface UserServiceInterface {
String getApiKeyForUser(String username); String getApiKeyForUser(String username);
String getCurrentUsername();
} }

View File

@@ -16,7 +16,6 @@ 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;
@@ -35,6 +34,7 @@ import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo;
import org.bouncycastle.pkcs.PKCSException; import org.bouncycastle.pkcs.PKCSException;
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.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;
@@ -47,6 +47,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.security.SignPDFWithCertRequest; import stirling.software.SPDF.model.api.security.SignPDFWithCertRequest;
import stirling.software.SPDF.service.CustomPDDocumentFactory;
import stirling.software.SPDF.utils.WebResponseUtils; import stirling.software.SPDF.utils.WebResponseUtils;
@RestController @RestController
@@ -71,6 +72,13 @@ public class CertSignController {
} }
} }
private final CustomPDDocumentFactory pdfDocumentFactory;
@Autowired
public CertSignController(CustomPDDocumentFactory pdfDocumentFactory) {
this.pdfDocumentFactory = pdfDocumentFactory;
}
@PostMapping(consumes = "multipart/form-data", value = "/cert-sign") @PostMapping(consumes = "multipart/form-data", value = "/cert-sign")
@Operation( @Operation(
summary = "Sign PDF with a Digital Certificate", summary = "Sign PDF with a Digital Certificate",
@@ -122,7 +130,7 @@ public class CertSignController {
CreateSignature createSignature = new CreateSignature(ks, password.toCharArray()); CreateSignature createSignature = new CreateSignature(ks, password.toCharArray());
ByteArrayOutputStream baos = new ByteArrayOutputStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream();
sign(pdf.getBytes(), baos, createSignature, name, location, reason); sign(pdfDocumentFactory, pdf.getBytes(), baos, createSignature, name, location, reason);
return WebResponseUtils.boasToWebResponse( return WebResponseUtils.boasToWebResponse(
baos, baos,
Filenames.toSimpleFileName(pdf.getOriginalFilename()).replaceFirst("[.][^.]+$", "") Filenames.toSimpleFileName(pdf.getOriginalFilename()).replaceFirst("[.][^.]+$", "")
@@ -130,13 +138,14 @@ public class CertSignController {
} }
private static void sign( private static void sign(
CustomPDDocumentFactory pdfDocumentFactory,
byte[] input, byte[] input,
OutputStream output, OutputStream output,
CreateSignature instance, CreateSignature instance,
String name, String name,
String location, String location,
String reason) { String reason) {
try (PDDocument doc = Loader.loadPDF(input)) { try (PDDocument doc = pdfDocumentFactory.load(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);

View File

@@ -2,12 +2,12 @@ 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;
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.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;
@@ -21,6 +21,7 @@ import io.swagger.v3.oas.annotations.tags.Tag;
import stirling.software.SPDF.model.api.security.AddPasswordRequest; import stirling.software.SPDF.model.api.security.AddPasswordRequest;
import stirling.software.SPDF.model.api.security.PDFPasswordRequest; import stirling.software.SPDF.model.api.security.PDFPasswordRequest;
import stirling.software.SPDF.service.CustomPDDocumentFactory;
import stirling.software.SPDF.utils.WebResponseUtils; import stirling.software.SPDF.utils.WebResponseUtils;
@RestController @RestController
@@ -30,6 +31,13 @@ public class PasswordController {
private static final Logger logger = LoggerFactory.getLogger(PasswordController.class); private static final Logger logger = LoggerFactory.getLogger(PasswordController.class);
private final CustomPDDocumentFactory pdfDocumentFactory;
@Autowired
public PasswordController(CustomPDDocumentFactory pdfDocumentFactory) {
this.pdfDocumentFactory = pdfDocumentFactory;
}
@PostMapping(consumes = "multipart/form-data", value = "/remove-password") @PostMapping(consumes = "multipart/form-data", value = "/remove-password")
@Operation( @Operation(
summary = "Remove password from a PDF file", summary = "Remove password from a PDF file",
@@ -39,8 +47,7 @@ public class PasswordController {
throws IOException { throws IOException {
MultipartFile fileInput = request.getFileInput(); MultipartFile fileInput = request.getFileInput();
String password = request.getPassword(); String password = request.getPassword();
PDDocument document = pdfDocumentFactory.load(fileInput, password);
PDDocument document = Loader.loadPDF(fileInput.getBytes(), password);
document.setAllSecurityToBeRemoved(true); document.setAllSecurityToBeRemoved(true);
return WebResponseUtils.pdfDocToWebResponse( return WebResponseUtils.pdfDocToWebResponse(
document, document,
@@ -69,7 +76,7 @@ public class PasswordController {
boolean canPrint = request.isCanPrint(); boolean canPrint = request.isCanPrint();
boolean canPrintFaithful = request.isCanPrintFaithful(); boolean canPrintFaithful = request.isCanPrintFaithful();
PDDocument document = Loader.loadPDF(fileInput.getBytes()); PDDocument document = pdfDocumentFactory.load(fileInput);
AccessPermission ap = new AccessPermission(); AccessPermission ap = new AccessPermission();
ap.setCanAssembleDocument(!canAssembleDocument); ap.setCanAssembleDocument(!canAssembleDocument);
ap.setCanExtractContent(!canExtractContent); ap.setCanExtractContent(!canExtractContent);

View File

@@ -5,12 +5,12 @@ 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.PDPageContentStream; import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.common.PDRectangle; import org.apache.pdfbox.pdmodel.common.PDRectangle;
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.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;
@@ -25,6 +25,7 @@ import io.swagger.v3.oas.annotations.tags.Tag;
import stirling.software.SPDF.model.PDFText; import stirling.software.SPDF.model.PDFText;
import stirling.software.SPDF.model.api.security.RedactPdfRequest; import stirling.software.SPDF.model.api.security.RedactPdfRequest;
import stirling.software.SPDF.pdf.TextFinder; import stirling.software.SPDF.pdf.TextFinder;
import stirling.software.SPDF.service.CustomPDDocumentFactory;
import stirling.software.SPDF.utils.PdfUtils; import stirling.software.SPDF.utils.PdfUtils;
import stirling.software.SPDF.utils.WebResponseUtils; import stirling.software.SPDF.utils.WebResponseUtils;
@@ -35,6 +36,13 @@ public class RedactController {
private static final Logger logger = LoggerFactory.getLogger(RedactController.class); private static final Logger logger = LoggerFactory.getLogger(RedactController.class);
private final CustomPDDocumentFactory pdfDocumentFactory;
@Autowired
public RedactController(CustomPDDocumentFactory pdfDocumentFactory) {
this.pdfDocumentFactory = pdfDocumentFactory;
}
@PostMapping(value = "/auto-redact", consumes = "multipart/form-data") @PostMapping(value = "/auto-redact", consumes = "multipart/form-data")
@Operation( @Operation(
summary = "Redacts listOfText in a PDF document", summary = "Redacts listOfText in a PDF document",
@@ -52,8 +60,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(); PDDocument document = pdfDocumentFactory.load(file);
PDDocument document = Loader.loadPDF(bytes);
Color redactColor; Color redactColor;
try { try {

View File

@@ -1,10 +1,8 @@
package stirling.software.SPDF.controller.api.security; package stirling.software.SPDF.controller.api.security;
import java.io.ByteArrayOutputStream;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDDocumentCatalog; import org.apache.pdfbox.pdmodel.PDDocumentCatalog;
import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm; import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm;
@@ -12,6 +10,7 @@ import org.apache.pdfbox.pdmodel.interactive.form.PDField;
import org.apache.pdfbox.pdmodel.interactive.form.PDSignatureField; import org.apache.pdfbox.pdmodel.interactive.form.PDSignatureField;
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.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;
@@ -24,6 +23,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.PDFFile; import stirling.software.SPDF.model.api.PDFFile;
import stirling.software.SPDF.service.CustomPDDocumentFactory;
import stirling.software.SPDF.utils.WebResponseUtils; import stirling.software.SPDF.utils.WebResponseUtils;
@RestController @RestController
@@ -33,6 +33,13 @@ public class RemoveCertSignController {
private static final Logger logger = LoggerFactory.getLogger(RemoveCertSignController.class); private static final Logger logger = LoggerFactory.getLogger(RemoveCertSignController.class);
private final CustomPDDocumentFactory pdfDocumentFactory;
@Autowired
public RemoveCertSignController(CustomPDDocumentFactory pdfDocumentFactory) {
this.pdfDocumentFactory = pdfDocumentFactory;
}
@PostMapping(consumes = "multipart/form-data", value = "/remove-cert-sign") @PostMapping(consumes = "multipart/form-data", value = "/remove-cert-sign")
@Operation( @Operation(
summary = "Remove digital signature from PDF", summary = "Remove digital signature from PDF",
@@ -42,14 +49,8 @@ public class RemoveCertSignController {
throws Exception { throws Exception {
MultipartFile pdf = request.getFileInput(); MultipartFile pdf = request.getFileInput();
// Convert MultipartFile to byte[]
byte[] pdfBytes = pdf.getBytes();
// Create a ByteArrayOutputStream to hold the resulting PDF
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// Load the PDF document // Load the PDF document
PDDocument document = Loader.loadPDF(pdfBytes); PDDocument document = pdfDocumentFactory.load(pdf);
// Get the document catalog // Get the document catalog
PDDocumentCatalog catalog = document.getDocumentCatalog(); PDDocumentCatalog catalog = document.getDocumentCatalog();
@@ -67,14 +68,9 @@ public class RemoveCertSignController {
acroForm.flatten(fieldsToRemove, false); acroForm.flatten(fieldsToRemove, false);
} }
} }
// Save the modified document to the ByteArrayOutputStream
document.save(baos);
document.close();
// Return the modified PDF as a response // Return the modified PDF as a response
return WebResponseUtils.boasToWebResponse( return WebResponseUtils.pdfDocToWebResponse(
baos, document,
Filenames.toSimpleFileName(pdf.getOriginalFilename()).replaceFirst("[.][^.]+$", "") Filenames.toSimpleFileName(pdf.getOriginalFilename()).replaceFirst("[.][^.]+$", "")
+ "_unsigned.pdf"); + "_unsigned.pdf");
} }

View File

@@ -2,7 +2,6 @@ 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;
@@ -21,6 +20,7 @@ import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationLink;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationWidget; import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationWidget;
import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm; import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm;
import org.apache.pdfbox.pdmodel.interactive.form.PDField; import org.apache.pdfbox.pdmodel.interactive.form.PDField;
import org.springframework.beans.factory.annotation.Autowired;
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;
@@ -33,6 +33,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.security.SanitizePdfRequest; import stirling.software.SPDF.model.api.security.SanitizePdfRequest;
import stirling.software.SPDF.service.CustomPDDocumentFactory;
import stirling.software.SPDF.utils.WebResponseUtils; import stirling.software.SPDF.utils.WebResponseUtils;
@RestController @RestController
@@ -40,6 +41,13 @@ import stirling.software.SPDF.utils.WebResponseUtils;
@Tag(name = "Security", description = "Security APIs") @Tag(name = "Security", description = "Security APIs")
public class SanitizeController { public class SanitizeController {
private final CustomPDDocumentFactory pdfDocumentFactory;
@Autowired
public SanitizeController(CustomPDDocumentFactory pdfDocumentFactory) {
this.pdfDocumentFactory = pdfDocumentFactory;
}
@PostMapping(consumes = "multipart/form-data", value = "/sanitize-pdf") @PostMapping(consumes = "multipart/form-data", value = "/sanitize-pdf")
@Operation( @Operation(
summary = "Sanitize a PDF file", summary = "Sanitize a PDF file",
@@ -54,7 +62,7 @@ public class SanitizeController {
boolean removeLinks = request.isRemoveLinks(); boolean removeLinks = request.isRemoveLinks();
boolean removeFonts = request.isRemoveFonts(); boolean removeFonts = request.isRemoveFonts();
PDDocument document = Loader.loadPDF(inputFile.getBytes()); PDDocument document = pdfDocumentFactory.load(inputFile);
if (removeJavaScript) { if (removeJavaScript) {
sanitizeJavaScript(document); sanitizeJavaScript(document);
} }

View File

@@ -11,7 +11,6 @@ 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;
@@ -23,6 +22,7 @@ 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;
import org.apache.pdfbox.util.Matrix; import org.apache.pdfbox.util.Matrix;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.ClassPathResource;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.ModelAttribute;
@@ -36,6 +36,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.security.AddWatermarkRequest; import stirling.software.SPDF.model.api.security.AddWatermarkRequest;
import stirling.software.SPDF.service.CustomPDDocumentFactory;
import stirling.software.SPDF.utils.PdfUtils; import stirling.software.SPDF.utils.PdfUtils;
import stirling.software.SPDF.utils.WebResponseUtils; import stirling.software.SPDF.utils.WebResponseUtils;
@@ -44,6 +45,13 @@ import stirling.software.SPDF.utils.WebResponseUtils;
@Tag(name = "Security", description = "Security APIs") @Tag(name = "Security", description = "Security APIs")
public class WatermarkController { public class WatermarkController {
private final CustomPDDocumentFactory pdfDocumentFactory;
@Autowired
public WatermarkController(CustomPDDocumentFactory pdfDocumentFactory) {
this.pdfDocumentFactory = pdfDocumentFactory;
}
@PostMapping(consumes = "multipart/form-data", value = "/add-watermark") @PostMapping(consumes = "multipart/form-data", value = "/add-watermark")
@Operation( @Operation(
summary = "Add watermark to a PDF file", summary = "Add watermark to a PDF file",
@@ -64,7 +72,7 @@ public class WatermarkController {
boolean convertPdfToImage = request.isConvertPDFToImage(); boolean convertPdfToImage = request.isConvertPDFToImage();
// Load the input PDF // Load the input PDF
PDDocument document = Loader.loadPDF(pdfFile.getBytes()); PDDocument document = pdfDocumentFactory.load(pdfFile);
// Create a page in the document // Create a page in the document
for (PDPage page : document.getPages()) { for (PDPage page : document.getPages()) {

View File

@@ -51,7 +51,7 @@ public class AccountWebController {
Map<String, String> providerList = new HashMap<>(); Map<String, String> providerList = new HashMap<>();
OAUTH2 oauth = applicationProperties.getSecurity().getOAUTH2(); OAUTH2 oauth = applicationProperties.getSecurity().getOauth2();
if (oauth != null) { if (oauth != null) {
if (oauth.isSettingsValid()) { if (oauth.isSettingsValid()) {
providerList.put("oidc", oauth.getProvider()); providerList.put("oidc", oauth.getProvider());
@@ -82,7 +82,7 @@ public class AccountWebController {
model.addAttribute("loginMethod", applicationProperties.getSecurity().getLoginMethod()); model.addAttribute("loginMethod", applicationProperties.getSecurity().getLoginMethod());
model.addAttribute( model.addAttribute(
"oAuth2Enabled", applicationProperties.getSecurity().getOAUTH2().getEnabled()); "oAuth2Enabled", applicationProperties.getSecurity().getOauth2().getEnabled());
model.addAttribute("currentPage", "login"); model.addAttribute("currentPage", "login");
@@ -345,7 +345,7 @@ public class AccountWebController {
// Retrieve username and other attributes // Retrieve username and other attributes
username = username =
userDetails.getAttribute( userDetails.getAttribute(
applicationProperties.getSecurity().getOAUTH2().getUseAsUsername()); applicationProperties.getSecurity().getOauth2().getUseAsUsername());
// Add oAuth2 Login attributes to the model // Add oAuth2 Login attributes to the model
model.addAttribute("oAuth2Login", true); model.addAttribute("oAuth2Login", true);
} }

View File

@@ -2,11 +2,7 @@ package stirling.software.SPDF.controller.web;
import java.time.Duration; import java.time.Duration;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.Comparator; import java.util.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@@ -18,19 +14,20 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import io.micrometer.core.instrument.Counter; import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.Meter;
import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.MeterRegistry;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.PostConstruct; import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.config.StartupApplicationListener; import stirling.software.SPDF.config.StartupApplicationListener;
import stirling.software.SPDF.model.ApplicationProperties; import stirling.software.SPDF.model.ApplicationProperties;
@RestController @RestController
@RequestMapping("/api/v1/info") @RequestMapping("/api/v1/info")
@Tag(name = "Info", description = "Info APIs") @Tag(name = "Info", description = "Info APIs")
@Slf4j
public class MetricsController { public class MetricsController {
@Autowired ApplicationProperties applicationProperties; @Autowired ApplicationProperties applicationProperties;
@@ -46,6 +43,7 @@ public class MetricsController {
this.metricsEnabled = metricsEnabled; this.metricsEnabled = metricsEnabled;
} }
@Autowired
public MetricsController(MeterRegistry meterRegistry) { public MetricsController(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry; this.meterRegistry = meterRegistry;
} }
@@ -66,11 +64,11 @@ public class MetricsController {
return ResponseEntity.ok(status); return ResponseEntity.ok(status);
} }
@GetMapping("/loads") @GetMapping("/load")
@Operation( @Operation(
summary = "GET request count", summary = "GET request count",
description = description =
"This endpoint returns the total count of GET requests or the count of GET requests for a specific endpoint.") "This endpoint returns the total count of GET requests for a specific endpoint or all endpoints.")
public ResponseEntity<?> getPageLoads( public ResponseEntity<?> getPageLoads(
@RequestParam(required = false, name = "endpoint") @Parameter(description = "endpoint") @RequestParam(required = false, name = "endpoint") @Parameter(description = "endpoint")
Optional<String> endpoint) { Optional<String> endpoint) {
@@ -78,44 +76,33 @@ public class MetricsController {
return ResponseEntity.status(HttpStatus.FORBIDDEN).body("This endpoint is disabled."); return ResponseEntity.status(HttpStatus.FORBIDDEN).body("This endpoint is disabled.");
} }
try { try {
double count = getRequestCount("GET", endpoint);
double count = 0.0;
for (Meter meter : meterRegistry.getMeters()) {
if (meter.getId().getName().equals("http.requests")) {
String method = meter.getId().getTag("method");
if (method != null && "GET".equals(method)) {
if (endpoint.isPresent() && !endpoint.get().isBlank()) {
if (!endpoint.get().startsWith("/")) {
endpoint = Optional.of("/" + endpoint.get());
}
System.out.println(
"loads "
+ endpoint.get()
+ " vs "
+ meter.getId().getTag("uri"));
if (endpoint.get().equals(meter.getId().getTag("uri"))) {
if (meter instanceof Counter) {
count += ((Counter) meter).count();
}
}
} else {
if (meter instanceof Counter) {
count += ((Counter) meter).count();
}
}
}
}
}
return ResponseEntity.ok(count); return ResponseEntity.ok(count);
} catch (Exception e) { } catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
} }
} }
@GetMapping("/loads/all") @GetMapping("/load/unique")
@Operation(
summary = "Unique users count for GET requests",
description =
"This endpoint returns the count of unique users for GET requests for a specific endpoint or all endpoints.")
public ResponseEntity<?> getUniquePageLoads(
@RequestParam(required = false, name = "endpoint") @Parameter(description = "endpoint")
Optional<String> endpoint) {
if (!metricsEnabled) {
return ResponseEntity.status(HttpStatus.FORBIDDEN).body("This endpoint is disabled.");
}
try {
double count = getUniqueUserCount("GET", endpoint);
return ResponseEntity.ok(count);
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}
@GetMapping("/load/all")
@Operation( @Operation(
summary = "GET requests count for all endpoints", summary = "GET requests count for all endpoints",
description = "This endpoint returns the count of GET requests for each endpoint.") description = "This endpoint returns the count of GET requests for each endpoint.")
@@ -124,37 +111,191 @@ public class MetricsController {
return ResponseEntity.status(HttpStatus.FORBIDDEN).body("This endpoint is disabled."); return ResponseEntity.status(HttpStatus.FORBIDDEN).body("This endpoint is disabled.");
} }
try { try {
Map<String, Double> counts = new HashMap<>(); List<EndpointCount> results = getEndpointCounts("GET");
for (Meter meter : meterRegistry.getMeters()) {
if (meter.getId().getName().equals("http.requests")) {
String method = meter.getId().getTag("method");
if (method != null && "GET".equals(method)) {
String uri = meter.getId().getTag("uri");
if (uri != null) {
double currentCount = counts.getOrDefault(uri, 0.0);
if (meter instanceof Counter) {
currentCount += ((Counter) meter).count();
}
counts.put(uri, currentCount);
}
}
}
}
List<EndpointCount> results =
counts.entrySet().stream()
.map(entry -> new EndpointCount(entry.getKey(), entry.getValue()))
.sorted(Comparator.comparing(EndpointCount::getCount).reversed())
.collect(Collectors.toList());
return ResponseEntity.ok(results); return ResponseEntity.ok(results);
} catch (Exception e) { } catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
} }
} }
public class EndpointCount { @GetMapping("/load/all/unique")
@Operation(
summary = "Unique users count for GET requests for all endpoints",
description =
"This endpoint returns the count of unique users for GET requests for each endpoint.")
public ResponseEntity<?> getAllUniqueEndpointLoads() {
if (!metricsEnabled) {
return ResponseEntity.status(HttpStatus.FORBIDDEN).body("This endpoint is disabled.");
}
try {
List<EndpointCount> results = getUniqueUserCounts("GET");
return ResponseEntity.ok(results);
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}
@GetMapping("/requests")
@Operation(
summary = "POST request count",
description =
"This endpoint returns the total count of POST requests for a specific endpoint or all endpoints.")
public ResponseEntity<?> getTotalRequests(
@RequestParam(required = false, name = "endpoint") @Parameter(description = "endpoint")
Optional<String> endpoint) {
if (!metricsEnabled) {
return ResponseEntity.status(HttpStatus.FORBIDDEN).body("This endpoint is disabled.");
}
try {
double count = getRequestCount("POST", endpoint);
return ResponseEntity.ok(count);
} catch (Exception e) {
return ResponseEntity.ok(-1);
}
}
@GetMapping("/requests/unique")
@Operation(
summary = "Unique users count for POST requests",
description =
"This endpoint returns the count of unique users for POST requests for a specific endpoint or all endpoints.")
public ResponseEntity<?> getUniqueTotalRequests(
@RequestParam(required = false, name = "endpoint") @Parameter(description = "endpoint")
Optional<String> endpoint) {
if (!metricsEnabled) {
return ResponseEntity.status(HttpStatus.FORBIDDEN).body("This endpoint is disabled.");
}
try {
double count = getUniqueUserCount("POST", endpoint);
return ResponseEntity.ok(count);
} catch (Exception e) {
return ResponseEntity.ok(-1);
}
}
@GetMapping("/requests/all")
@Operation(
summary = "POST requests count for all endpoints",
description = "This endpoint returns the count of POST requests for each endpoint.")
public ResponseEntity<?> getAllPostRequests() {
if (!metricsEnabled) {
return ResponseEntity.status(HttpStatus.FORBIDDEN).body("This endpoint is disabled.");
}
try {
List<EndpointCount> results = getEndpointCounts("POST");
return ResponseEntity.ok(results);
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}
@GetMapping("/requests/all/unique")
@Operation(
summary = "Unique users count for POST requests for all endpoints",
description =
"This endpoint returns the count of unique users for POST requests for each endpoint.")
public ResponseEntity<?> getAllUniquePostRequests() {
if (!metricsEnabled) {
return ResponseEntity.status(HttpStatus.FORBIDDEN).body("This endpoint is disabled.");
}
try {
List<EndpointCount> results = getUniqueUserCounts("POST");
return ResponseEntity.ok(results);
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}
private double getRequestCount(String method, Optional<String> endpoint) {
log.info(
"Getting request count for method: {}, endpoint: {}",
method,
endpoint.orElse("all"));
double count =
meterRegistry.find("http.requests").tag("method", method).counters().stream()
.filter(
counter ->
!endpoint.isPresent()
|| endpoint.get()
.equals(counter.getId().getTag("uri")))
.mapToDouble(Counter::count)
.sum();
log.info("Request count: {}", count);
return count;
}
private List<EndpointCount> getEndpointCounts(String method) {
log.info("Getting endpoint counts for method: {}", method);
Map<String, Double> counts = new HashMap<>();
meterRegistry
.find("http.requests")
.tag("method", method)
.counters()
.forEach(
counter -> {
String uri = counter.getId().getTag("uri");
counts.merge(uri, counter.count(), Double::sum);
});
List<EndpointCount> result =
counts.entrySet().stream()
.map(entry -> new EndpointCount(entry.getKey(), entry.getValue()))
.sorted(Comparator.comparing(EndpointCount::getCount).reversed())
.collect(Collectors.toList());
log.info("Found {} endpoints with counts", result.size());
return result;
}
private double getUniqueUserCount(String method, Optional<String> endpoint) {
log.info(
"Getting unique user count for method: {}, endpoint: {}",
method,
endpoint.orElse("all"));
Set<String> uniqueUsers = new HashSet<>();
meterRegistry.find("http.requests").tag("method", method).counters().stream()
.filter(
counter ->
!endpoint.isPresent()
|| endpoint.get().equals(counter.getId().getTag("uri")))
.forEach(
counter -> {
String session = counter.getId().getTag("session");
if (session != null) {
uniqueUsers.add(session);
}
});
log.info("Unique user count: {}", uniqueUsers.size());
return uniqueUsers.size();
}
private List<EndpointCount> getUniqueUserCounts(String method) {
log.info("Getting unique user counts for method: {}", method);
Map<String, Set<String>> uniqueUsers = new HashMap<>();
meterRegistry
.find("http.requests")
.tag("method", method)
.counters()
.forEach(
counter -> {
String uri = counter.getId().getTag("uri");
String session = counter.getId().getTag("session");
if (uri != null && session != null) {
uniqueUsers.computeIfAbsent(uri, k -> new HashSet<>()).add(session);
}
});
List<EndpointCount> result =
uniqueUsers.entrySet().stream()
.map(entry -> new EndpointCount(entry.getKey(), entry.getValue().size()))
.sorted(Comparator.comparing(EndpointCount::getCount).reversed())
.collect(Collectors.toList());
log.info("Found {} endpoints with unique user counts", result.size());
return result;
}
public static class EndpointCount {
private String endpoint; private String endpoint;
private double count; private double count;
@@ -180,86 +321,6 @@ public class MetricsController {
} }
} }
@GetMapping("/requests")
@Operation(
summary = "POST request count",
description =
"This endpoint returns the total count of POST requests or the count of POST requests for a specific endpoint.")
public ResponseEntity<?> getTotalRequests(
@RequestParam(required = false, name = "endpoint") @Parameter(description = "endpoint")
Optional<String> endpoint) {
if (!metricsEnabled) {
return ResponseEntity.status(HttpStatus.FORBIDDEN).body("This endpoint is disabled.");
}
try {
double count = 0.0;
for (Meter meter : meterRegistry.getMeters()) {
if (meter.getId().getName().equals("http.requests")) {
String method = meter.getId().getTag("method");
if (method != null && "POST".equals(method)) {
if (endpoint.isPresent() && !endpoint.get().isBlank()) {
if (!endpoint.get().startsWith("/")) {
endpoint = Optional.of("/" + endpoint.get());
}
if (endpoint.get().equals(meter.getId().getTag("uri"))) {
if (meter instanceof Counter) {
count += ((Counter) meter).count();
}
}
} else {
if (meter instanceof Counter) {
count += ((Counter) meter).count();
}
}
}
}
}
return ResponseEntity.ok(count);
} catch (Exception e) {
return ResponseEntity.ok(-1);
}
}
@GetMapping("/requests/all")
@Operation(
summary = "POST requests count for all endpoints",
description = "This endpoint returns the count of POST requests for each endpoint.")
public ResponseEntity<?> getAllPostRequests() {
if (!metricsEnabled) {
return ResponseEntity.status(HttpStatus.FORBIDDEN).body("This endpoint is disabled.");
}
try {
Map<String, Double> counts = new HashMap<>();
for (Meter meter : meterRegistry.getMeters()) {
if (meter.getId().getName().equals("http.requests")) {
String method = meter.getId().getTag("method");
if (method != null && "POST".equals(method)) {
String uri = meter.getId().getTag("uri");
if (uri != null) {
double currentCount = counts.getOrDefault(uri, 0.0);
if (meter instanceof Counter) {
currentCount += ((Counter) meter).count();
}
counts.put(uri, currentCount);
}
}
}
}
List<EndpointCount> results =
counts.entrySet().stream()
.map(entry -> new EndpointCount(entry.getKey(), entry.getValue()))
.sorted(Comparator.comparing(EndpointCount::getCount).reversed())
.collect(Collectors.toList());
return ResponseEntity.ok(results);
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}
@GetMapping("/uptime") @GetMapping("/uptime")
public ResponseEntity<?> getUptime() { public ResponseEntity<?> getUptime() {
if (!metricsEnabled) { if (!metricsEnabled) {

View File

@@ -12,6 +12,8 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource; import org.springframework.context.annotation.PropertySource;
import lombok.Data;
import lombok.ToString;
import stirling.software.SPDF.config.YamlPropertySourceFactory; import stirling.software.SPDF.config.YamlPropertySourceFactory;
import stirling.software.SPDF.model.provider.GithubProvider; import stirling.software.SPDF.model.provider.GithubProvider;
import stirling.software.SPDF.model.provider.GoogleProvider; import stirling.software.SPDF.model.provider.GoogleProvider;
@@ -21,225 +23,56 @@ import stirling.software.SPDF.model.provider.UnsupportedProviderException;
@Configuration @Configuration
@ConfigurationProperties(prefix = "") @ConfigurationProperties(prefix = "")
@PropertySource(value = "file:./configs/settings.yml", factory = YamlPropertySourceFactory.class) @PropertySource(value = "file:./configs/settings.yml", factory = YamlPropertySourceFactory.class)
@Data
public class ApplicationProperties { public class ApplicationProperties {
private Security security;
private System system; private Legal legal = new Legal();
private Ui ui; private Security security = new Security();
private Endpoints endpoints; private System system = new System();
private Metrics metrics; private Ui ui = new Ui();
private AutomaticallyGenerated automaticallyGenerated; private Endpoints endpoints = new Endpoints();
private AutoPipeline autoPipeline; private Metrics metrics = new Metrics();
private AutomaticallyGenerated automaticallyGenerated = new AutomaticallyGenerated();
private EnterpriseEdition enterpriseEdition = new EnterpriseEdition();
private AutoPipeline autoPipeline = new AutoPipeline();
private static final Logger logger = LoggerFactory.getLogger(ApplicationProperties.class); private static final Logger logger = LoggerFactory.getLogger(ApplicationProperties.class);
public AutoPipeline getAutoPipeline() { @Data
return autoPipeline != null ? autoPipeline : new AutoPipeline();
}
public void setAutoPipeline(AutoPipeline autoPipeline) {
this.autoPipeline = autoPipeline;
}
public Security getSecurity() {
return security != null ? security : new Security();
}
public void setSecurity(Security security) {
this.security = security;
}
public System getSystem() {
return system != null ? system : new System();
}
public void setSystem(System system) {
this.system = system;
}
public Ui getUi() {
return ui != null ? ui : new Ui();
}
public void setUi(Ui ui) {
this.ui = ui;
}
public Endpoints getEndpoints() {
return endpoints != null ? endpoints : new Endpoints();
}
public void setEndpoints(Endpoints endpoints) {
this.endpoints = endpoints;
}
public Metrics getMetrics() {
return metrics != null ? metrics : new Metrics();
}
public void setMetrics(Metrics metrics) {
this.metrics = metrics;
}
public AutomaticallyGenerated getAutomaticallyGenerated() {
return automaticallyGenerated != null
? automaticallyGenerated
: new AutomaticallyGenerated();
}
public void setAutomaticallyGenerated(AutomaticallyGenerated automaticallyGenerated) {
this.automaticallyGenerated = automaticallyGenerated;
}
@Override
public String toString() {
return "ApplicationProperties [security="
+ security
+ ", system="
+ system
+ ", ui="
+ ui
+ ", endpoints="
+ endpoints
+ ", metrics="
+ metrics
+ ", automaticallyGenerated="
+ automaticallyGenerated
+ ", autoPipeline="
+ autoPipeline
+ "]";
}
public static class AutoPipeline { public static class AutoPipeline {
private String outputFolder; private String outputFolder;
public String getOutputFolder() {
return outputFolder;
}
public void setOutputFolder(String outputFolder) {
this.outputFolder = outputFolder;
}
@Override
public String toString() {
return "AutoPipeline [outputFolder=" + outputFolder + "]";
}
} }
@Data
public static class Legal {
private String termsAndConditions;
private String privacyPolicy;
private String accessibilityStatement;
private String cookiePolicy;
private String impressum;
}
@Data
public static class Security { public static class Security {
private Boolean enableLogin; private Boolean enableLogin;
private Boolean csrfDisabled; private Boolean csrfDisabled;
private InitialLogin initialLogin; private InitialLogin initialLogin = new InitialLogin();
private OAUTH2 oauth2; private OAUTH2 oauth2 = new OAUTH2();
private int loginAttemptCount; private int loginAttemptCount;
private long loginResetTimeMinutes; private long loginResetTimeMinutes;
private String loginMethod = "all"; private String loginMethod = "all";
public String getLoginMethod() { @Data
return loginMethod;
}
public void setLoginMethod(String loginMethod) {
this.loginMethod = loginMethod;
}
public int getLoginAttemptCount() {
return loginAttemptCount;
}
public void setLoginAttemptCount(int loginAttemptCount) {
this.loginAttemptCount = loginAttemptCount;
}
public long getLoginResetTimeMinutes() {
return loginResetTimeMinutes;
}
public void setLoginResetTimeMinutes(long loginResetTimeMinutes) {
this.loginResetTimeMinutes = loginResetTimeMinutes;
}
public InitialLogin getInitialLogin() {
return initialLogin != null ? initialLogin : new InitialLogin();
}
public void setInitialLogin(InitialLogin initialLogin) {
this.initialLogin = initialLogin;
}
public OAUTH2 getOAUTH2() {
return oauth2 != null ? oauth2 : new OAUTH2();
}
public void setOAUTH2(OAUTH2 oauth2) {
this.oauth2 = oauth2;
}
public Boolean getEnableLogin() {
return enableLogin;
}
public void setEnableLogin(Boolean enableLogin) {
this.enableLogin = enableLogin;
}
public Boolean getCsrfDisabled() {
return csrfDisabled;
}
public void setCsrfDisabled(Boolean csrfDisabled) {
this.csrfDisabled = csrfDisabled;
}
@Override
public String toString() {
return "Security [enableLogin="
+ enableLogin
+ ", oauth2="
+ oauth2
+ ", initialLogin="
+ initialLogin
+ ", csrfDisabled="
+ csrfDisabled
+ ", loginMethod="
+ loginMethod
+ "]";
}
public static class InitialLogin { public static class InitialLogin {
private String username; private String username;
private String password; @ToString.Exclude private String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "InitialLogin [username="
+ username
+ ", password="
+ (password != null && !password.isEmpty() ? "MASKED" : "NULL")
+ "]";
}
} }
@Data
public static class OAUTH2 { public static class OAUTH2 {
private Boolean enabled = false; private Boolean enabled = false;
private String issuer; private String issuer;
private String clientId; private String clientId;
private String clientSecret; @ToString.Exclude private String clientSecret;
private Boolean autoCreateUser = false; private Boolean autoCreateUser = false;
private Boolean blockRegistration = false; private Boolean blockRegistration = false;
private String useAsUsername; private String useAsUsername;
@@ -247,74 +80,6 @@ public class ApplicationProperties {
private String provider; private String provider;
private Client client = new Client(); private Client client = new Client();
public Boolean getEnabled() {
return enabled;
}
public void setEnabled(Boolean enabled) {
this.enabled = enabled;
}
public String getIssuer() {
return issuer;
}
public void setIssuer(String issuer) {
this.issuer = issuer;
}
public String getClientId() {
return clientId;
}
public void setClientId(String clientId) {
this.clientId = clientId;
}
public String getClientSecret() {
return clientSecret;
}
public void setClientSecret(String clientSecret) {
this.clientSecret = clientSecret;
}
public Boolean getAutoCreateUser() {
return autoCreateUser;
}
public void setAutoCreateUser(Boolean autoCreateUser) {
this.autoCreateUser = autoCreateUser;
}
public Boolean getBlockRegistration() {
return blockRegistration;
}
public void setBlockRegistration(Boolean blockRegistration) {
this.blockRegistration = blockRegistration;
}
public String getUseAsUsername() {
return useAsUsername;
}
public void setUseAsUsername(String useAsUsername) {
this.useAsUsername = useAsUsername;
}
public String getProvider() {
return provider;
}
public void setProvider(String provider) {
this.provider = provider;
}
public Collection<String> getScopes() {
return scopes;
}
public void setScopes(String scopes) { public void setScopes(String scopes) {
List<String> scopesList = List<String> scopesList =
Arrays.stream(scopes.split(",")) Arrays.stream(scopes.split(","))
@@ -323,26 +88,12 @@ public class ApplicationProperties {
this.scopes.addAll(scopesList); this.scopes.addAll(scopesList);
} }
public Client getClient() {
return client;
}
public void setClient(Client client) {
this.client = client;
}
protected boolean isValid(String value, String name) { protected boolean isValid(String value, String name) {
if (value != null && !value.trim().isEmpty()) { return value != null && !value.trim().isEmpty();
return true;
}
return false;
} }
protected boolean isValid(Collection<String> value, String name) { protected boolean isValid(Collection<String> value, String name) {
if (value != null && !value.isEmpty()) { return value != null && !value.isEmpty();
return true;
}
return false;
} }
public boolean isSettingsValid() { public boolean isSettingsValid() {
@@ -353,31 +104,7 @@ public class ApplicationProperties {
&& isValid(this.getUseAsUsername(), "useAsUsername"); && isValid(this.getUseAsUsername(), "useAsUsername");
} }
@Override @Data
public String toString() {
return "OAUTH2 [enabled="
+ enabled
+ ", issuer="
+ issuer
+ ", clientId="
+ clientId
+ ", clientSecret="
+ (clientSecret != null && !clientSecret.isEmpty() ? "MASKED" : "NULL")
+ ", autoCreateUser="
+ autoCreateUser
+ ", blockRegistration="
+ blockRegistration
+ ", useAsUsername="
+ useAsUsername
+ ", provider="
+ provider
+ ", client="
+ client
+ ", scopes="
+ scopes
+ "]";
}
public static class Client { public static class Client {
private GoogleProvider google = new GoogleProvider(); private GoogleProvider google = new GoogleProvider();
private GithubProvider github = new GithubProvider(); private GithubProvider github = new GithubProvider();
@@ -392,50 +119,15 @@ public class ApplicationProperties {
case "keycloak": case "keycloak":
return getKeycloak(); return getKeycloak();
default: default:
break; throw new UnsupportedProviderException(
"Logout from the provider is not supported? Report it at https://github.com/Stirling-Tools/Stirling-PDF/issues");
} }
throw new UnsupportedProviderException(
"Logout from the provider is not supported? Report it at https://github.com/Stirling-Tools/Stirling-PDF/issues");
}
public GoogleProvider getGoogle() {
return google;
}
public void setGoogle(GoogleProvider google) {
this.google = google;
}
public GithubProvider getGithub() {
return github;
}
public void setGithub(GithubProvider github) {
this.github = github;
}
public KeycloakProvider getKeycloak() {
return keycloak;
}
public void setKeycloak(KeycloakProvider keycloak) {
this.keycloak = keycloak;
}
@Override
public String toString() {
return "Client [google="
+ google
+ ", github="
+ github
+ ", keycloak="
+ keycloak
+ "]";
} }
} }
} }
} }
@Data
public static class System { public static class System {
private String defaultLocale; private String defaultLocale;
private Boolean googlevisibility; private Boolean googlevisibility;
@@ -443,184 +135,67 @@ public class ApplicationProperties {
private Boolean showUpdateOnlyAdmin; private Boolean showUpdateOnlyAdmin;
private boolean customHTMLFiles; private boolean customHTMLFiles;
private String tessdataDir; private String tessdataDir;
public String getTessdataDir() {
return tessdataDir;
}
public void setTessdataDir(String tessdataDir) {
this.tessdataDir = tessdataDir;
}
public boolean isCustomHTMLFiles() {
return customHTMLFiles;
}
public void setCustomHTMLFiles(boolean customHTMLFiles) {
this.customHTMLFiles = customHTMLFiles;
}
public boolean getShowUpdateOnlyAdmin() {
return showUpdateOnlyAdmin;
}
public void setShowUpdateOnlyAdmin(boolean showUpdateOnlyAdmin) {
this.showUpdateOnlyAdmin = showUpdateOnlyAdmin;
}
public boolean getShowUpdate() {
return showUpdate;
}
public void setShowUpdate(boolean showUpdate) {
this.showUpdate = showUpdate;
}
private Boolean enableAlphaFunctionality; private Boolean enableAlphaFunctionality;
public Boolean getEnableAlphaFunctionality() {
return enableAlphaFunctionality;
}
public void setEnableAlphaFunctionality(Boolean enableAlphaFunctionality) {
this.enableAlphaFunctionality = enableAlphaFunctionality;
}
public String getDefaultLocale() {
return defaultLocale;
}
public void setDefaultLocale(String defaultLocale) {
this.defaultLocale = defaultLocale;
}
public Boolean getGooglevisibility() {
return googlevisibility;
}
public void setGooglevisibility(Boolean googlevisibility) {
this.googlevisibility = googlevisibility;
}
@Override
public String toString() {
return "System [defaultLocale="
+ defaultLocale
+ ", googlevisibility="
+ googlevisibility
+ ", enableAlphaFunctionality="
+ enableAlphaFunctionality
+ ", showUpdate="
+ showUpdate
+ ", showUpdateOnlyAdmin="
+ showUpdateOnlyAdmin
+ "]";
}
} }
@Data
public static class Ui { public static class Ui {
private String appName; private String appName;
private String homeDescription; private String homeDescription;
private String appNameNavbar; private String appNameNavbar;
public String getAppName() { public String getAppName() {
if (appName != null && appName.trim().length() == 0) return null; return appName != null && appName.trim().length() > 0 ? appName : null;
return appName;
}
public void setAppName(String appName) {
this.appName = appName;
} }
public String getHomeDescription() { public String getHomeDescription() {
if (homeDescription != null && homeDescription.trim().length() == 0) return null; return homeDescription != null && homeDescription.trim().length() > 0
return homeDescription; ? homeDescription
} : null;
public void setHomeDescription(String homeDescription) {
this.homeDescription = homeDescription;
} }
public String getAppNameNavbar() { public String getAppNameNavbar() {
if (appNameNavbar != null && appNameNavbar.trim().length() == 0) return null; return appNameNavbar != null && appNameNavbar.trim().length() > 0
return appNameNavbar; ? appNameNavbar
} : null;
public void setAppNameNavbar(String appNameNavbar) {
this.appNameNavbar = appNameNavbar;
}
@Override
public String toString() {
return "UserInterface [appName="
+ appName
+ ", homeDescription="
+ homeDescription
+ ", appNameNavbar="
+ appNameNavbar
+ "]";
} }
} }
@Data
public static class Endpoints { public static class Endpoints {
private List<String> toRemove; private List<String> toRemove;
private List<String> groupsToRemove; private List<String> groupsToRemove;
public List<String> getToRemove() {
return toRemove;
}
public void setToRemove(List<String> toRemove) {
this.toRemove = toRemove;
}
public List<String> getGroupsToRemove() {
return groupsToRemove;
}
public void setGroupsToRemove(List<String> groupsToRemove) {
this.groupsToRemove = groupsToRemove;
}
@Override
public String toString() {
return "Endpoints [toRemove=" + toRemove + ", groupsToRemove=" + groupsToRemove + "]";
}
} }
@Data
public static class Metrics { public static class Metrics {
private Boolean enabled; private Boolean enabled;
public Boolean getEnabled() {
return enabled;
}
public void setEnabled(Boolean enabled) {
this.enabled = enabled;
}
@Override
public String toString() {
return "Metrics [enabled=" + enabled + "]";
}
} }
@Data
public static class AutomaticallyGenerated { public static class AutomaticallyGenerated {
private String key; @ToString.Exclude private String key;
}
public String getKey() { @Data
return key; public static class EnterpriseEdition {
} @ToString.Exclude private String key;
private CustomMetadata customMetadata = new CustomMetadata();
public void setKey(String key) { @Data
this.key = key; public static class CustomMetadata {
} private boolean autoUpdateMetadata;
private String author;
private String creator;
private String producer;
@Override public String getCreator() {
public String toString() { return creator == null || creator.trim().isEmpty() ? "Stirling-PDF" : creator;
return "AutomaticallyGenerated [key=" }
+ (key != null && !key.isEmpty() ? "MASKED" : "NULL")
+ "]"; public String getProducer() {
return producer == null || producer.trim().isEmpty() ? "Stirling-PDF" : producer;
}
} }
} }
} }

View File

@@ -11,7 +11,7 @@ public class PDFWithPageSize extends PDFFile {
@Schema( @Schema(
description = description =
"The scale of pages in the output PDF. Acceptable values are A0-A6, LETTER, LEGAL.", "The scale of pages in the output PDF. Acceptable values are A0-A6, LETTER, LEGAL, KEEP.",
allowableValues = {"A0", "A1", "A2", "A3", "A4", "A5", "A6", "LETTER", "LEGAL"}) allowableValues = {"A0", "A1", "A2", "A3", "A4", "A5", "A6", "LETTER", "LEGAL", "KEEP"})
private String pageSize; private String pageSize;
} }

View File

@@ -16,7 +16,6 @@ public interface UserRepository extends JpaRepository<User, Long> {
@Query("FROM User u LEFT JOIN FETCH u.settings where upper(u.username) = upper(:username)") @Query("FROM User u LEFT JOIN FETCH u.settings where upper(u.username) = upper(:username)")
Optional<User> findByUsernameIgnoreCaseWithSettings(@Param("username") String username); Optional<User> findByUsernameIgnoreCaseWithSettings(@Param("username") String username);
Optional<User> findByUsername(String username); Optional<User> findByUsername(String username);
Optional<User> findByApiKey(String apiKey); Optional<User> findByApiKey(String apiKey);

View File

@@ -0,0 +1,100 @@
package stirling.software.SPDF.service;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import stirling.software.SPDF.config.PdfMetadataService;
import stirling.software.SPDF.model.PdfMetadata;
import stirling.software.SPDF.model.api.PDFFile;
@Component
public class CustomPDDocumentFactory {
private final PdfMetadataService pdfMetadataService;
@Autowired
public CustomPDDocumentFactory(PdfMetadataService pdfMetadataService) {
this.pdfMetadataService = pdfMetadataService;
}
public PDDocument createNewDocument() throws IOException {
PDDocument document = new PDDocument();
pdfMetadataService.setMetadataToPdf(document, PdfMetadata.builder().build(), true);
return document;
}
public PDDocument createNewDocumentBasedOnOldDocument(PDDocument oldDocument)
throws IOException {
PDDocument document = new PDDocument();
pdfMetadataService.setMetadataToPdf(
document, pdfMetadataService.extractMetadataFromPdf(oldDocument), true);
return document;
}
public byte[] loadToBytes(File file) throws IOException {
PDDocument document = load(file);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
document.save(baos);
// Close the document
document.close();
return baos.toByteArray();
}
public byte[] loadToBytes(byte[] bytes) throws IOException {
PDDocument document = load(bytes);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
document.save(baos);
// Close the document
document.close();
return baos.toByteArray();
}
// if loading from a file, assume the file has been made with Stirling-PDF
public PDDocument load(File file) throws IOException {
PDDocument document = Loader.loadPDF(file);
pdfMetadataService.setMetadataToPdf(document, PdfMetadata.builder().build(), true);
return document;
}
public PDDocument load(InputStream input) throws IOException {
return load(input.readAllBytes());
}
public PDDocument load(byte[] input) throws IOException {
PDDocument document = Loader.loadPDF(input);
pdfMetadataService.setDefaultMetadata(document);
return document;
}
public PDDocument load(PDFFile pdfFile) throws IOException {
return load(pdfFile.getFileInput());
}
public PDDocument load(MultipartFile pdfFile) throws IOException {
return load(pdfFile.getBytes());
}
public PDDocument load(String path) throws IOException {
return load(new File(path));
}
public PDDocument load(MultipartFile fileInput, String password) throws IOException {
return load(fileInput.getBytes(), password);
}
private PDDocument load(byte[] bytes, String password) throws IOException {
PDDocument document = Loader.loadPDF(bytes, password);
pdfMetadataService.setDefaultMetadata(document);
return document;
}
// Add other load methods as needed, following the same pattern
}

View File

@@ -6,7 +6,6 @@ import java.awt.image.RenderedImage;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
@@ -37,7 +36,7 @@ import org.springframework.web.multipart.MultipartFile;
import io.github.pixee.security.Filenames; import io.github.pixee.security.Filenames;
import stirling.software.SPDF.model.PdfMetadata; import stirling.software.SPDF.service.CustomPDDocumentFactory;
public class PdfUtils { public class PdfUtils {
@@ -383,9 +382,13 @@ public class PdfUtils {
} }
public static byte[] imageToPdf( public static byte[] imageToPdf(
MultipartFile[] files, String fitOption, boolean autoRotate, String colorType) MultipartFile[] files,
String fitOption,
boolean autoRotate,
String colorType,
CustomPDDocumentFactory pdfDocumentFactory)
throws IOException { throws IOException {
try (PDDocument doc = new PDDocument()) { try (PDDocument doc = pdfDocumentFactory.createNewDocument()) {
for (MultipartFile file : files) { for (MultipartFile file : files) {
String contentType = file.getContentType(); String contentType = file.getContentType();
String originalFilename = Filenames.toSimpleFileName(file.getOriginalFilename()); String originalFilename = Filenames.toSimpleFileName(file.getOriginalFilename());
@@ -473,10 +476,15 @@ public class PdfUtils {
} }
public static byte[] overlayImage( public static byte[] overlayImage(
byte[] pdfBytes, byte[] imageBytes, float x, float y, boolean everyPage) CustomPDDocumentFactory pdfDocumentFactory,
byte[] pdfBytes,
byte[] imageBytes,
float x,
float y,
boolean everyPage)
throws IOException { throws IOException {
PDDocument document = Loader.loadPDF(pdfBytes); PDDocument document = pdfDocumentFactory.load(pdfBytes);
// Get the first page of the PDF // Get the first page of the PDF
int pages = document.getNumberOfPages(); int pages = document.getNumberOfPages();
@@ -506,30 +514,6 @@ public class PdfUtils {
return baos.toByteArray(); return baos.toByteArray();
} }
public static PdfMetadata extractMetadataFromPdf(PDDocument pdf) {
return PdfMetadata.builder()
.author(pdf.getDocumentInformation().getAuthor())
.producer(pdf.getDocumentInformation().getProducer())
.title(pdf.getDocumentInformation().getTitle())
.creator(pdf.getDocumentInformation().getCreator())
.subject(pdf.getDocumentInformation().getSubject())
.keywords(pdf.getDocumentInformation().getKeywords())
.creationDate(pdf.getDocumentInformation().getCreationDate())
.modificationDate(pdf.getDocumentInformation().getModificationDate())
.build();
}
public static void setMetadataToPdf(PDDocument pdf, PdfMetadata pdfMetadata) {
pdf.getDocumentInformation().setAuthor(pdfMetadata.getAuthor());
pdf.getDocumentInformation().setProducer(pdfMetadata.getProducer());
pdf.getDocumentInformation().setTitle(pdfMetadata.getTitle());
pdf.getDocumentInformation().setCreator(pdfMetadata.getCreator());
pdf.getDocumentInformation().setSubject(pdfMetadata.getSubject());
pdf.getDocumentInformation().setKeywords(pdfMetadata.getKeywords());
pdf.getDocumentInformation().setCreationDate(pdfMetadata.getCreationDate());
pdf.getDocumentInformation().setModificationDate(Calendar.getInstance());
}
/** Key for storing the dimensions of a rendered image in a map. */ /** Key for storing the dimensions of a rendered image in a map. */
private record PdfRenderSettingsKey(float mediaBoxWidth, float mediaBoxHeight, int rotation) {} private record PdfRenderSettingsKey(float mediaBoxWidth, float mediaBoxHeight, int rotation) {}

View File

@@ -4,16 +4,7 @@ public class RequestUriUtils {
public static boolean isStaticResource(String requestURI) { public static boolean isStaticResource(String requestURI) {
return requestURI.startsWith("/css/") return isStaticResource("", requestURI);
|| requestURI.startsWith("/fonts/")
|| requestURI.startsWith("/js/")
|| requestURI.startsWith("/images/")
|| requestURI.startsWith("/public/")
|| requestURI.startsWith("/pdfjs/")
|| requestURI.startsWith("/pdfjs-legacy/")
|| requestURI.endsWith(".svg")
|| requestURI.endsWith(".webmanifest")
|| requestURI.startsWith("/api/v1/info/status");
} }
public static boolean isStaticResource(String contextPath, String requestURI) { public static boolean isStaticResource(String contextPath, String requestURI) {
@@ -21,11 +12,37 @@ public class RequestUriUtils {
return requestURI.startsWith(contextPath + "/css/") return requestURI.startsWith(contextPath + "/css/")
|| requestURI.startsWith(contextPath + "/fonts/") || requestURI.startsWith(contextPath + "/fonts/")
|| requestURI.startsWith(contextPath + "/js/") || requestURI.startsWith(contextPath + "/js/")
|| requestURI.endsWith(contextPath + "robots.txt")
|| requestURI.startsWith(contextPath + "/images/") || requestURI.startsWith(contextPath + "/images/")
|| requestURI.startsWith(contextPath + "/public/") || requestURI.startsWith(contextPath + "/public/")
|| requestURI.startsWith(contextPath + "/pdfjs/") || requestURI.startsWith(contextPath + "/pdfjs/")
|| requestURI.startsWith(contextPath + "/login")
|| requestURI.endsWith(".svg") || requestURI.endsWith(".svg")
|| requestURI.endsWith(".png")
|| requestURI.endsWith(".ico")
|| requestURI.endsWith(".webmanifest") || requestURI.endsWith(".webmanifest")
|| requestURI.startsWith(contextPath + "/api/v1/info/status"); || requestURI.startsWith(contextPath + "/api/v1/info/status");
} }
public static boolean isTrackableResource(String requestURI) {
return isTrackableResource("", requestURI);
}
public static boolean isTrackableResource(String contextPath, String requestURI) {
return !(requestURI.startsWith("/js")
|| requestURI.startsWith("/v1/api-docs")
|| requestURI.endsWith("robots.txt")
|| requestURI.startsWith("/images")
|| requestURI.endsWith(".png")
|| requestURI.endsWith(".ico")
|| requestURI.endsWith(".css")
|| requestURI.endsWith(".map")
|| requestURI.endsWith(".svg")
|| requestURI.endsWith(".js")
|| requestURI.contains("swagger")
|| requestURI.startsWith("/api/v1/info")
|| requestURI.startsWith("/site.webmanifest")
|| requestURI.startsWith("/fonts")
|| requestURI.startsWith("/pdfjs"));
}
} }

View File

@@ -77,7 +77,11 @@ color=لون
sponsor=راعٍ sponsor=راعٍ
info=معلومات info=معلومات
legal.privacy=Privacy Policy
legal.terms=Terms and Conditions
legal.accessibility=Accessibility
legal.cookie=Cookie Policy
legal.impressum=Impressum
############### ###############
# Pipeline # # Pipeline #
@@ -675,6 +679,7 @@ pageLayout.submit=إرسال
scalePages.title=ضبط مقياس الصفحة scalePages.title=ضبط مقياس الصفحة
scalePages.header=ضبط مقياس الصفحة scalePages.header=ضبط مقياس الصفحة
scalePages.pageSize=حجم صفحة المستند. scalePages.pageSize=حجم صفحة المستند.
scalePages.keepPageSize=Original Size
scalePages.scaleFactor=مستوى التكبير (الاقتصاص) للصفحة. scalePages.scaleFactor=مستوى التكبير (الاقتصاص) للصفحة.
scalePages.submit=إرسال scalePages.submit=إرسال

View File

@@ -77,7 +77,11 @@ color=Цвят
sponsor=Спонсор sponsor=Спонсор
info=Info info=Info
legal.privacy=Privacy Policy
legal.terms=Terms and Conditions
legal.accessibility=Accessibility
legal.cookie=Cookie Policy
legal.impressum=Impressum
############### ###############
# Pipeline # # Pipeline #
@@ -675,6 +679,7 @@ pageLayout.submit=Подайте
scalePages.title=Коригиране на мащаба на страницата scalePages.title=Коригиране на мащаба на страницата
scalePages.header=Коригиране на мащаба на страницата scalePages.header=Коригиране на мащаба на страницата
scalePages.pageSize=Размер на страница от документа. scalePages.pageSize=Размер на страница от документа.
scalePages.keepPageSize=Original Size
scalePages.scaleFactor=Ниво на мащабиране (изрязване) на страница. scalePages.scaleFactor=Ниво на мащабиране (изрязване) на страница.
scalePages.submit=Подайте scalePages.submit=Подайте

View File

@@ -77,7 +77,11 @@ color=Color
sponsor=Sponsor sponsor=Sponsor
info=Info info=Info
legal.privacy=Privacy Policy
legal.terms=Terms and Conditions
legal.accessibility=Accessibility
legal.cookie=Cookie Policy
legal.impressum=Impressum
############### ###############
# Pipeline # # Pipeline #
@@ -675,6 +679,7 @@ pageLayout.submit=Submit
scalePages.title=Adjust page-scale scalePages.title=Adjust page-scale
scalePages.header=Adjust page-scale scalePages.header=Adjust page-scale
scalePages.pageSize=Size of a page of the document. scalePages.pageSize=Size of a page of the document.
scalePages.keepPageSize=Original Size
scalePages.scaleFactor=Zoom level (crop) of a page. scalePages.scaleFactor=Zoom level (crop) of a page.
scalePages.submit=Submit scalePages.submit=Submit

View File

@@ -77,7 +77,11 @@ color=Barva
sponsor=Sponzor sponsor=Sponzor
info=Info info=Info
legal.privacy=Privacy Policy
legal.terms=Terms and Conditions
legal.accessibility=Accessibility
legal.cookie=Cookie Policy
legal.impressum=Impressum
############### ###############
# Pipeline # # Pipeline #
@@ -675,6 +679,7 @@ pageLayout.submit=Odeslat
scalePages.title=Upravit měřítko stránky scalePages.title=Upravit měřítko stránky
scalePages.header=Upravit měřítko stránky scalePages.header=Upravit měřítko stránky
scalePages.pageSize=Velikost stránky dokumentu. scalePages.pageSize=Velikost stránky dokumentu.
scalePages.keepPageSize=Original Size
scalePages.scaleFactor=Úroveň přiblížení (oříznutí) stránky. scalePages.scaleFactor=Úroveň přiblížení (oříznutí) stránky.
scalePages.submit=Odeslat scalePages.submit=Odeslat

View File

@@ -77,7 +77,11 @@ color=Farve
sponsor=Sponsor sponsor=Sponsor
info=Info info=Info
legal.privacy=Privacy Policy
legal.terms=Terms and Conditions
legal.accessibility=Accessibility
legal.cookie=Cookie Policy
legal.impressum=Impressum
############### ###############
# Pipeline # # Pipeline #
@@ -675,6 +679,7 @@ pageLayout.submit=Indsend
scalePages.title=Justér sidestørrelse scalePages.title=Justér sidestørrelse
scalePages.header=Justér sidestørrelse scalePages.header=Justér sidestørrelse
scalePages.pageSize=Størrelse på en side i dokumentet. scalePages.pageSize=Størrelse på en side i dokumentet.
scalePages.keepPageSize=Original Size
scalePages.scaleFactor=Zoom-niveau (beskæring) af en side. scalePages.scaleFactor=Zoom-niveau (beskæring) af en side.
scalePages.submit=Indsend scalePages.submit=Indsend

View File

@@ -77,7 +77,11 @@ color=Farbe
sponsor=Sponsor sponsor=Sponsor
info=Informationen info=Informationen
legal.privacy=Privacy Policy
legal.terms=Terms and Conditions
legal.accessibility=Accessibility
legal.cookie=Cookie Policy
legal.impressum=Impressum
############### ###############
# Pipeline # # Pipeline #
@@ -675,6 +679,7 @@ pageLayout.submit=Abschicken
scalePages.title=Seitengröße anpassen scalePages.title=Seitengröße anpassen
scalePages.header=Seitengröße anpassen scalePages.header=Seitengröße anpassen
scalePages.pageSize=Format der Seiten des Dokuments scalePages.pageSize=Format der Seiten des Dokuments
scalePages.keepPageSize=Original Size
scalePages.scaleFactor=Zoomstufe (Ausschnitt) einer Seite scalePages.scaleFactor=Zoomstufe (Ausschnitt) einer Seite
scalePages.submit=Abschicken scalePages.submit=Abschicken

View File

@@ -77,7 +77,11 @@ color=Χρώμα
sponsor=οστηρικτής sponsor=οστηρικτής
info=Info info=Info
legal.privacy=Privacy Policy
legal.terms=Terms and Conditions
legal.accessibility=Accessibility
legal.cookie=Cookie Policy
legal.impressum=Impressum
############### ###############
# Pipeline # # Pipeline #
@@ -675,6 +679,7 @@ pageLayout.submit=Υποβολή
scalePages.title=Προσαρμογή κλίμακας σελίδας scalePages.title=Προσαρμογή κλίμακας σελίδας
scalePages.header=Προσαρμογή κλίμακας σελίδας scalePages.header=Προσαρμογή κλίμακας σελίδας
scalePages.pageSize=Μέγεθος μιας σελίδας του εγγράφου. scalePages.pageSize=Μέγεθος μιας σελίδας του εγγράφου.
scalePages.keepPageSize=Original Size
scalePages.scaleFactor=Επίπεδο ζουμ (περικοπή) σελίδας. scalePages.scaleFactor=Επίπεδο ζουμ (περικοπή) σελίδας.
scalePages.submit=Υποβολή scalePages.submit=Υποβολή

View File

@@ -77,7 +77,11 @@ color=Color
sponsor=Sponsor sponsor=Sponsor
info=Info info=Info
legal.privacy=Privacy Policy
legal.terms=Terms and Conditions
legal.accessibility=Accessibility
legal.cookie=Cookie Policy
legal.impressum=Impressum
############### ###############
# Pipeline # # Pipeline #
@@ -115,7 +119,7 @@ navbar.darkmode=Dark Mode
navbar.language=Languages navbar.language=Languages
navbar.settings=Settings navbar.settings=Settings
navbar.allTools=Tools navbar.allTools=Tools
navbar.multiTool=Multi Tools navbar.multiTool=Multi Tool
navbar.sections.organize=Organize navbar.sections.organize=Organize
navbar.sections.convertTo=Convert to PDF navbar.sections.convertTo=Convert to PDF
navbar.sections.convertFrom=Convert from PDF navbar.sections.convertFrom=Convert from PDF
@@ -231,8 +235,8 @@ home.viewPdf.desc=View, annotate, add text or images
viewPdf.tags=view,read,annotate,text,image viewPdf.tags=view,read,annotate,text,image
home.multiTool.title=PDF Multi Tool home.multiTool.title=PDF Multi Tool
home.multiTool.desc=Merge, Rotate, Rearrange, and Remove pages home.multiTool.desc=Merge, Rotate, Rearrange, Split, and Remove pages
multiTool.tags=Multi Tool,Multi operation,UI,click drag,front end,client side,interactive,intractable,move multiTool.tags=Multi Tool,Multi operation,UI,click drag,front end,client side,interactive,intractable,move,delete,migrate,divide
home.merge.title=Merge home.merge.title=Merge
home.merge.desc=Easily merge multiple PDFs into one. home.merge.desc=Easily merge multiple PDFs into one.
@@ -675,6 +679,7 @@ pageLayout.submit=Submit
scalePages.title=Adjust page-scale scalePages.title=Adjust page-scale
scalePages.header=Adjust page-scale scalePages.header=Adjust page-scale
scalePages.pageSize=Size of a page of the document. scalePages.pageSize=Size of a page of the document.
scalePages.keepPageSize=Original Size
scalePages.scaleFactor=Zoom level (crop) of a page. scalePages.scaleFactor=Zoom level (crop) of a page.
scalePages.submit=Submit scalePages.submit=Submit

View File

@@ -77,7 +77,11 @@ color=Color
sponsor=Sponsor sponsor=Sponsor
info=Info info=Info
legal.privacy=Privacy Policy
legal.terms=Terms and Conditions
legal.accessibility=Accessibility
legal.cookie=Cookie Policy
legal.impressum=Impressum
############### ###############
# Pipeline # # Pipeline #
@@ -115,7 +119,7 @@ navbar.darkmode=Dark Mode
navbar.language=Languages navbar.language=Languages
navbar.settings=Settings navbar.settings=Settings
navbar.allTools=Tools navbar.allTools=Tools
navbar.multiTool=Multi Tools navbar.multiTool=Multi Tool
navbar.sections.organize=Organize navbar.sections.organize=Organize
navbar.sections.convertTo=Convert to PDF navbar.sections.convertTo=Convert to PDF
navbar.sections.convertFrom=Convert from PDF navbar.sections.convertFrom=Convert from PDF
@@ -231,8 +235,8 @@ home.viewPdf.desc=View, annotate, add text or images
viewPdf.tags=view,read,annotate,text,image viewPdf.tags=view,read,annotate,text,image
home.multiTool.title=PDF Multi Tool home.multiTool.title=PDF Multi Tool
home.multiTool.desc=Merge, Rotate, Rearrange, and Remove pages home.multiTool.desc=Merge, Rotate, Rearrange, Split, and Remove pages
multiTool.tags=Multi Tool,Multi operation,UI,click drag,front end,client side,interactive,intractable,move multiTool.tags=Multi Tool,Multi operation,UI,click drag,front end,client side,interactive,intractable,move,delete,migrate,divide
home.merge.title=Merge home.merge.title=Merge
home.merge.desc=Easily merge multiple PDFs into one. home.merge.desc=Easily merge multiple PDFs into one.
@@ -675,6 +679,7 @@ pageLayout.submit=Submit
scalePages.title=Adjust page-scale scalePages.title=Adjust page-scale
scalePages.header=Adjust page-scale scalePages.header=Adjust page-scale
scalePages.pageSize=Size of a page of the document. scalePages.pageSize=Size of a page of the document.
scalePages.keepPageSize=Original Size
scalePages.scaleFactor=Zoom level (crop) of a page. scalePages.scaleFactor=Zoom level (crop) of a page.
scalePages.submit=Submit scalePages.submit=Submit

View File

@@ -77,7 +77,11 @@ color=Color
sponsor=Patrocinador sponsor=Patrocinador
info=Info info=Info
legal.privacy=Privacy Policy
legal.terms=Terms and Conditions
legal.accessibility=Accessibility
legal.cookie=Cookie Policy
legal.impressum=Impressum
############### ###############
# Pipeline # # Pipeline #
@@ -675,6 +679,7 @@ pageLayout.submit=Entregar
scalePages.title=Ajustar escala de la página scalePages.title=Ajustar escala de la página
scalePages.header=Adjustar escala de la página scalePages.header=Adjustar escala de la página
scalePages.pageSize=Tamaño de la página del documento scalePages.pageSize=Tamaño de la página del documento
scalePages.keepPageSize=Tamaño Original
scalePages.scaleFactor=Nivel de zoom (recorte) de la página scalePages.scaleFactor=Nivel de zoom (recorte) de la página
scalePages.submit=Entregar scalePages.submit=Entregar

View File

@@ -77,7 +77,11 @@ color=Color
sponsor=Sponsor sponsor=Sponsor
info=Info info=Info
legal.privacy=Privacy Policy
legal.terms=Terms and Conditions
legal.accessibility=Accessibility
legal.cookie=Cookie Policy
legal.impressum=Impressum
############### ###############
# Pipeline # # Pipeline #
@@ -675,6 +679,7 @@ pageLayout.submit=Entregatu
scalePages.title=Doitu orrialdearen eskala scalePages.title=Doitu orrialdearen eskala
scalePages.header=Doitu orrialdearen eskala scalePages.header=Doitu orrialdearen eskala
scalePages.pageSize=Dokumentuaren orrialdearen tamaina scalePages.pageSize=Dokumentuaren orrialdearen tamaina
scalePages.keepPageSize=Original Size
scalePages.scaleFactor=Orriaren zoom maila (moztea) scalePages.scaleFactor=Orriaren zoom maila (moztea)
scalePages.submit=Entregatu scalePages.submit=Entregatu

View File

@@ -77,7 +77,11 @@ color=Couleur
sponsor=Sponsor sponsor=Sponsor
info=Info info=Info
legal.privacy=Privacy Policy
legal.terms=Terms and Conditions
legal.accessibility=Accessibility
legal.cookie=Cookie Policy
legal.impressum=Impressum
############### ###############
# Pipeline # # Pipeline #
@@ -675,6 +679,7 @@ pageLayout.submit=Fusionner
scalePages.title=Ajuster la taille ou léchelle scalePages.title=Ajuster la taille ou léchelle
scalePages.header=Ajuster la taille ou léchelle scalePages.header=Ajuster la taille ou léchelle
scalePages.pageSize=Taille dune page du document scalePages.pageSize=Taille dune page du document
scalePages.keepPageSize=Original Size
scalePages.scaleFactor=Niveau de zoom (recadrage) dune page scalePages.scaleFactor=Niveau de zoom (recadrage) dune page
scalePages.submit=Ajuster scalePages.submit=Ajuster

View File

@@ -77,7 +77,11 @@ color=Dath
sponsor=Urraitheoir sponsor=Urraitheoir
info=Eolas info=Eolas
legal.privacy=Privacy Policy
legal.terms=Terms and Conditions
legal.accessibility=Accessibility
legal.cookie=Cookie Policy
legal.impressum=Impressum
############### ###############
# Pipeline # # Pipeline #
@@ -675,6 +679,7 @@ pageLayout.submit=Cuir isteach
scalePages.title=Coigeartaigh scála an leathanaigh scalePages.title=Coigeartaigh scála an leathanaigh
scalePages.header=Coigeartaigh scála an leathanaigh scalePages.header=Coigeartaigh scála an leathanaigh
scalePages.pageSize=Méid leathanach den doiciméad. scalePages.pageSize=Méid leathanach den doiciméad.
scalePages.keepPageSize=Original Size
scalePages.scaleFactor=Leibhéal súmáil (barr) de leathanach. scalePages.scaleFactor=Leibhéal súmáil (barr) de leathanach.
scalePages.submit=Cuir isteach scalePages.submit=Cuir isteach

View File

@@ -77,7 +77,11 @@ color=Color
sponsor=Sponsor sponsor=Sponsor
info=Info info=Info
legal.privacy=Privacy Policy
legal.terms=Terms and Conditions
legal.accessibility=Accessibility
legal.cookie=Cookie Policy
legal.impressum=Impressum
############### ###############
# Pipeline # # Pipeline #
@@ -675,6 +679,7 @@ pageLayout.submit=प्रस्तुत क
scalePages.title=पृष्ठ-स्केल समायोजित करें scalePages.title=पृष्ठ-स्केल समायोजित करें
scalePages.header=पृष्ठ-स्केल समायोजित करें scalePages.header=पृष्ठ-स्केल समायोजित करें
scalePages.pageSize=दस्तावेज़ के पृष्ठ का आकार। scalePages.pageSize=दस्तावेज़ के पृष्ठ का आकार।
scalePages.keepPageSize=Original Size
scalePages.scaleFactor=पृष्ठ का ज़ूम स्तर (क्रॉप)। scalePages.scaleFactor=पृष्ठ का ज़ूम स्तर (क्रॉप)।
scalePages.submit=प्रस्तुत करें scalePages.submit=प्रस्तुत करें

View File

@@ -77,7 +77,11 @@ color=Boja
sponsor=Sponzor sponsor=Sponzor
info=Info info=Info
legal.privacy=Privacy Policy
legal.terms=Terms and Conditions
legal.accessibility=Accessibility
legal.cookie=Cookie Policy
legal.impressum=Impressum
############### ###############
# Pipeline # # Pipeline #
@@ -675,6 +679,7 @@ pageLayout.submit=Potvrdi
scalePages.title=Podesite veličinu stranice scalePages.title=Podesite veličinu stranice
scalePages.header=Podesite veličinu stranice scalePages.header=Podesite veličinu stranice
scalePages.pageSize=Veličina stranice dokumenta. scalePages.pageSize=Veličina stranice dokumenta.
scalePages.keepPageSize=Original Size
scalePages.scaleFactor=Razina zumiranja (obrezivanje) stranice. scalePages.scaleFactor=Razina zumiranja (obrezivanje) stranice.
scalePages.submit=Potvrdi scalePages.submit=Potvrdi

View File

@@ -77,7 +77,11 @@ color=Color
sponsor=Sponsor sponsor=Sponsor
info=Info info=Info
legal.privacy=Privacy Policy
legal.terms=Terms and Conditions
legal.accessibility=Accessibility
legal.cookie=Cookie Policy
legal.impressum=Impressum
############### ###############
# Pipeline # # Pipeline #
@@ -675,6 +679,7 @@ pageLayout.submit=Elküldés
scalePages.title=Oldalméret beállítása scalePages.title=Oldalméret beállítása
scalePages.header=Oldalméret beállítása scalePages.header=Oldalméret beállítása
scalePages.pageSize=A dokumentum egy oldalának mérete. scalePages.pageSize=A dokumentum egy oldalának mérete.
scalePages.keepPageSize=Original Size
scalePages.scaleFactor=Az oldal nagyításának szintje (vágás). scalePages.scaleFactor=Az oldal nagyításának szintje (vágás).
scalePages.submit=Küldés scalePages.submit=Küldés

View File

@@ -77,7 +77,11 @@ color=Color
sponsor=Sponsor sponsor=Sponsor
info=Info info=Info
legal.privacy=Privacy Policy
legal.terms=Terms and Conditions
legal.accessibility=Accessibility
legal.cookie=Cookie Policy
legal.impressum=Impressum
############### ###############
# Pipeline # # Pipeline #
@@ -675,6 +679,7 @@ pageLayout.submit=Kirim
scalePages.title=Sesuaikan skala halaman scalePages.title=Sesuaikan skala halaman
scalePages.header=Sesuaikan skala halaman scalePages.header=Sesuaikan skala halaman
scalePages.pageSize=Ukuran halaman dokumen. scalePages.pageSize=Ukuran halaman dokumen.
scalePages.keepPageSize=Original Size
scalePages.scaleFactor=Tingkat zoom (potong) halaman. scalePages.scaleFactor=Tingkat zoom (potong) halaman.
scalePages.submit=Kirim scalePages.submit=Kirim

View File

@@ -77,7 +77,11 @@ color=Colore
sponsor=Sponsor sponsor=Sponsor
info=Info info=Info
legal.privacy=Informativa sulla privacy
legal.terms=Termini e Condizioni
legal.accessibility=Accessibilità
legal.cookie=Informativa sui cookie
legal.impressum=Impressum
############### ###############
# Pipeline # # Pipeline #
@@ -675,6 +679,7 @@ pageLayout.submit=Invia
scalePages.title=Regola la scala della pagina scalePages.title=Regola la scala della pagina
scalePages.header=Regola la scala della pagina scalePages.header=Regola la scala della pagina
scalePages.pageSize=Dimensione di una pagina del documento. scalePages.pageSize=Dimensione di una pagina del documento.
scalePages.keepPageSize=Dimensione originale
scalePages.scaleFactor=Livello di zoom (ritaglio) di una pagina. scalePages.scaleFactor=Livello di zoom (ritaglio) di una pagina.
scalePages.submit=Invia scalePages.submit=Invia
@@ -965,7 +970,7 @@ watermark.selectText.6=spazio verticale (tra ogni filigrana):
watermark.selectText.7=Opacità (0% - 100%): watermark.selectText.7=Opacità (0% - 100%):
watermark.selectText.8=Tipo di filigrana: watermark.selectText.8=Tipo di filigrana:
watermark.selectText.9=Immagine filigrana: watermark.selectText.9=Immagine filigrana:
watermark.selectText.10=Convert PDF to PDF-Image watermark.selectText.10=Converti PDF in PDF-Immagine
watermark.submit=Aggiungi Filigrana watermark.submit=Aggiungi Filigrana
watermark.type.1=Testo watermark.type.1=Testo
watermark.type.2=Immagine watermark.type.2=Immagine

View File

@@ -77,7 +77,11 @@ color=色
sponsor=スポンサー sponsor=スポンサー
info=Info info=Info
legal.privacy=Privacy Policy
legal.terms=Terms and Conditions
legal.accessibility=Accessibility
legal.cookie=Cookie Policy
legal.impressum=Impressum
############### ###############
# Pipeline # # Pipeline #
@@ -675,6 +679,7 @@ pageLayout.submit=送信
scalePages.title=ページの縮尺の調整 scalePages.title=ページの縮尺の調整
scalePages.header=ページの縮尺の調整 scalePages.header=ページの縮尺の調整
scalePages.pageSize=1ページのサイズ scalePages.pageSize=1ページのサイズ
scalePages.keepPageSize=Original Size
scalePages.scaleFactor=1ページの拡大レベル (トリミング)。 scalePages.scaleFactor=1ページの拡大レベル (トリミング)。
scalePages.submit=送信 scalePages.submit=送信

View File

@@ -77,7 +77,11 @@ color=색상
sponsor=스폰서 sponsor=스폰서
info=Info info=Info
legal.privacy=Privacy Policy
legal.terms=Terms and Conditions
legal.accessibility=Accessibility
legal.cookie=Cookie Policy
legal.impressum=Impressum
############### ###############
# Pipeline # # Pipeline #
@@ -675,6 +679,7 @@ pageLayout.submit=확인
scalePages.title=페이지 배율 조절 scalePages.title=페이지 배율 조절
scalePages.header=페이지 배율 조절 scalePages.header=페이지 배율 조절
scalePages.pageSize=페이지의 크기를 조절합니다. scalePages.pageSize=페이지의 크기를 조절합니다.
scalePages.keepPageSize=Original Size
scalePages.scaleFactor=페이지 배율 조절 (잘라내기) scalePages.scaleFactor=페이지 배율 조절 (잘라내기)
scalePages.submit=제출 scalePages.submit=제출

View File

@@ -77,7 +77,11 @@ color=Kleur
sponsor=Sponsor sponsor=Sponsor
info=Info info=Info
legal.privacy=Privacy Policy
legal.terms=Terms and Conditions
legal.accessibility=Accessibility
legal.cookie=Cookie Policy
legal.impressum=Impressum
############### ###############
# Pipeline # # Pipeline #
@@ -675,6 +679,7 @@ pageLayout.submit=Indienen
scalePages.title=Pagina-schaal aanpassen scalePages.title=Pagina-schaal aanpassen
scalePages.header=Pagina-schaal aanpassen scalePages.header=Pagina-schaal aanpassen
scalePages.pageSize=Grootte van een pagina van het document. scalePages.pageSize=Grootte van een pagina van het document.
scalePages.keepPageSize=Original Size
scalePages.scaleFactor=Zoomniveau (uitsnede) van een pagina. scalePages.scaleFactor=Zoomniveau (uitsnede) van een pagina.
scalePages.submit=Indienen scalePages.submit=Indienen

View File

@@ -77,7 +77,11 @@ color=Farge
sponsor=Sponsor sponsor=Sponsor
info=Info info=Info
legal.privacy=Privacy Policy
legal.terms=Terms and Conditions
legal.accessibility=Accessibility
legal.cookie=Cookie Policy
legal.impressum=Impressum
############### ###############
# Pipeline # # Pipeline #
@@ -675,6 +679,7 @@ pageLayout.submit=Send inn
scalePages.title=Juster side-skala scalePages.title=Juster side-skala
scalePages.header=Juster side-skala scalePages.header=Juster side-skala
scalePages.pageSize=Størrelse på et ark i dokumentet. scalePages.pageSize=Størrelse på et ark i dokumentet.
scalePages.keepPageSize=Original Size
scalePages.scaleFactor=Zoom-nivå (beskjær) for en side. scalePages.scaleFactor=Zoom-nivå (beskjær) for en side.
scalePages.submit=Send inn scalePages.submit=Send inn

View File

@@ -77,7 +77,11 @@ color=kolor
sponsor=sponsor sponsor=sponsor
info=informacje info=informacje
legal.privacy=Privacy Policy
legal.terms=Terms and Conditions
legal.accessibility=Accessibility
legal.cookie=Cookie Policy
legal.impressum=Impressum
############### ###############
# Pipeline # # Pipeline #
@@ -675,6 +679,7 @@ pageLayout.submit=Wykonaj
scalePages.title=Dopasuj rozmiar stron scalePages.title=Dopasuj rozmiar stron
scalePages.header=Dopasuj rozmiar stron scalePages.header=Dopasuj rozmiar stron
scalePages.pageSize=Rozmiar stron dokumentu: scalePages.pageSize=Rozmiar stron dokumentu:
scalePages.keepPageSize=Original Size
scalePages.scaleFactor=Poziom powiększenia (przycięcia) stron: scalePages.scaleFactor=Poziom powiększenia (przycięcia) stron:
scalePages.submit=Wykonaj scalePages.submit=Wykonaj

View File

@@ -77,7 +77,11 @@ color=Cor
sponsor=Patrocine sponsor=Patrocine
info=Informações info=Informações
legal.privacy=Privacy Policy
legal.terms=Terms and Conditions
legal.accessibility=Accessibility
legal.cookie=Cookie Policy
legal.impressum=Impressum
############### ###############
# Pipeline # # Pipeline #
@@ -675,6 +679,7 @@ pageLayout.submit=Enviar
scalePages.title=Ajustar Tamanho/Escala da Página scalePages.title=Ajustar Tamanho/Escala da Página
scalePages.header=Ajustar Tamanho/Escala da Página scalePages.header=Ajustar Tamanho/Escala da Página
scalePages.pageSize=Tamanho de uma página do documento. scalePages.pageSize=Tamanho de uma página do documento.
scalePages.keepPageSize=Original Size
scalePages.scaleFactor=Fator de zoom (corte) de uma página. scalePages.scaleFactor=Fator de zoom (corte) de uma página.
scalePages.submit=Enviar scalePages.submit=Enviar

View File

@@ -77,7 +77,11 @@ color=Color
sponsor=Sponsor sponsor=Sponsor
info=Info info=Info
legal.privacy=Privacy Policy
legal.terms=Terms and Conditions
legal.accessibility=Accessibility
legal.cookie=Cookie Policy
legal.impressum=Impressum
############### ###############
# Pipeline # # Pipeline #
@@ -675,6 +679,7 @@ pageLayout.submit=Enviar
scalePages.title=Ajustar Tamanho/Escala da Página scalePages.title=Ajustar Tamanho/Escala da Página
scalePages.header=Ajustar Tamanho/Escala da Página scalePages.header=Ajustar Tamanho/Escala da Página
scalePages.pageSize=Tamanho de uma página do documento. scalePages.pageSize=Tamanho de uma página do documento.
scalePages.keepPageSize=Original Size
scalePages.scaleFactor=Fator de zoom (corte) de uma página. scalePages.scaleFactor=Fator de zoom (corte) de uma página.
scalePages.submit=Enviar scalePages.submit=Enviar

View File

@@ -77,7 +77,11 @@ color=Culoare
sponsor=Sponsor sponsor=Sponsor
info=Informații info=Informații
legal.privacy=Privacy Policy
legal.terms=Terms and Conditions
legal.accessibility=Accessibility
legal.cookie=Cookie Policy
legal.impressum=Impressum
############### ###############
# Pipeline # # Pipeline #
@@ -675,6 +679,7 @@ pageLayout.submit=Trimite
scalePages.title=Ajustează scala paginii scalePages.title=Ajustează scala paginii
scalePages.header=Ajustează scala paginii scalePages.header=Ajustează scala paginii
scalePages.pageSize=Dimensiunea unei pagini a documentului. scalePages.pageSize=Dimensiunea unei pagini a documentului.
scalePages.keepPageSize=Original Size
scalePages.scaleFactor=Nivel de zoom (decupare) al unei pagini. scalePages.scaleFactor=Nivel de zoom (decupare) al unei pagini.
scalePages.submit=Trimite scalePages.submit=Trimite

View File

@@ -77,7 +77,11 @@ color=Цвет
sponsor=Спонсор sponsor=Спонсор
info=Info info=Info
legal.privacy=Privacy Policy
legal.terms=Terms and Conditions
legal.accessibility=Accessibility
legal.cookie=Cookie Policy
legal.impressum=Impressum
############### ###############
# Pipeline # # Pipeline #
@@ -675,6 +679,7 @@ pageLayout.submit=Отправить
scalePages.title=Отрегулировать масштаб страницы scalePages.title=Отрегулировать масштаб страницы
scalePages.header=Отрегулировать масштаб страницы scalePages.header=Отрегулировать масштаб страницы
scalePages.pageSize=Размер страницы документа. scalePages.pageSize=Размер страницы документа.
scalePages.keepPageSize=Original Size
scalePages.scaleFactor=Уровень масштабирования (обрезки) страницы. scalePages.scaleFactor=Уровень масштабирования (обрезки) страницы.
scalePages.submit=Отправить scalePages.submit=Отправить

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