diff --git a/.github/labeler-config.yml b/.github/labeler-config.yml index 73f92aaa3..a0a634840 100644 --- a/.github/labeler-config.yml +++ b/.github/labeler-config.yml @@ -72,7 +72,8 @@ Devtools: Test: - changed-files: - any-glob-to-any-file: 'cucumber/**/*' - - any-glob-to-any-file: 'src/test**/*' + - any-glob-to-any-file: 'src/test/**/*' + - any-glob-to-any-file: 'src/testing/**/*' - any-glob-to-any-file: '.pre-commit-config' - any-glob-to-any-file: '.github/workflows/pre_commit.yml' - any-glob-to-any-file: '.github/workflows/scorecards.yml' diff --git a/.github/workflows/swagger.yml b/.github/workflows/swagger.yml index 95e2529e9..97ad11efc 100644 --- a/.github/workflows/swagger.yml +++ b/.github/workflows/swagger.yml @@ -35,6 +35,7 @@ jobs: run: ./gradlew swaggerhubUpload env: SWAGGERHUB_API_KEY: ${{ secrets.SWAGGERHUB_API_KEY }} + SWAGGERHUB_USER: "Frooodle" - name: Get version number id: versionNumber @@ -42,6 +43,7 @@ jobs: - name: Set API version as published and default on SwaggerHub run: | - curl -X PUT -H "Authorization: ${SWAGGERHUB_API_KEY}" "https://api.swaggerhub.com/apis/Frooodle/Stirling-PDF/${{ steps.versionNumber.outputs.versionNumber }}/settings/lifecycle" -H "accept: application/json" -H "Content-Type: application/json" -d "{\"published\":true,\"default\":true}" + curl -X PUT -H "Authorization: ${SWAGGERHUB_API_KEY}" "https://api.swaggerhub.com/apis/${SWAGGERHUB_USER}/Stirling-PDF/${{ steps.versionNumber.outputs.versionNumber }}/settings/lifecycle" -H "accept: application/json" -H "Content-Type: application/json" -d "{\"published\":true,\"default\":true}" env: SWAGGERHUB_API_KEY: ${{ secrets.SWAGGERHUB_API_KEY }} + SWAGGERHUB_USER: "Frooodle" diff --git a/.gitignore b/.gitignore index e5d8ad209..90d48ccea 100644 --- a/.gitignore +++ b/.gitignore @@ -27,6 +27,8 @@ clientWebUI/ !cucumber/exampleFiles/example_html.zip exampleYmlFiles/stirling/ /testing/file_snapshots +SwaggerDoc.json + # Gradle .gradle .lock @@ -188,3 +190,7 @@ id_ed25519.pub .ipynb_checkpoints **/jcef-bundle/ + +# node_modules +node_modules/ +*.mjs diff --git a/Dockerfile.fat b/Dockerfile.fat index 2cc2d2133..8855be6c0 100644 --- a/Dockerfile.fat +++ b/Dockerfile.fat @@ -1,5 +1,5 @@ # Build the application -FROM gradle:8.12-jdk21 AS build +FROM gradle:8.13-jdk21 AS build COPY build.gradle . COPY settings.gradle . diff --git a/README.md b/README.md index 4f07c2428..a152fddb5 100644 --- a/README.md +++ b/README.md @@ -120,17 +120,17 @@ Stirling-PDF currently supports 39 languages! | Azerbaijani (Azərbaycan Dili) (az_AZ) | ![87%](https://geps.dev/progress/87) | | Basque (Euskara) (eu_ES) | ![51%](https://geps.dev/progress/51) | | Bulgarian (Български) (bg_BG) | ![98%](https://geps.dev/progress/98) | -| Catalan (Català) (ca_CA) | ![95%](https://geps.dev/progress/95) | +| Catalan (Català) (ca_CA) | ![94%](https://geps.dev/progress/94) | | Croatian (Hrvatski) (hr_HR) | ![85%](https://geps.dev/progress/85) | | Czech (Česky) (cs_CZ) | ![96%](https://geps.dev/progress/96) | | Danish (Dansk) (da_DK) | ![84%](https://geps.dev/progress/84) | -| Dutch (Nederlands) (nl_NL) | ![84%](https://geps.dev/progress/84) | +| Dutch (Nederlands) (nl_NL) | ![83%](https://geps.dev/progress/83) | | English (English) (en_GB) | ![100%](https://geps.dev/progress/100) | | English (US) (en_US) | ![100%](https://geps.dev/progress/100) | | French (Français) (fr_FR) | ![96%](https://geps.dev/progress/96) | | German (Deutsch) (de_DE) | ![99%](https://geps.dev/progress/99) | | Greek (Ελληνικά) (el_GR) | ![96%](https://geps.dev/progress/96) | -| Hindi (हिंदी) (hi_IN) | ![97%](https://geps.dev/progress/97) | +| Hindi (हिंदी) (hi_IN) | ![96%](https://geps.dev/progress/96) | | Hungarian (Magyar) (hu_HU) | ![94%](https://geps.dev/progress/94) | | Indonesian (Bahasa Indonesia) (id_ID) | ![85%](https://geps.dev/progress/85) | | Irish (Gaeilge) (ga_IE) | ![96%](https://geps.dev/progress/96) | @@ -138,7 +138,7 @@ Stirling-PDF currently supports 39 languages! | Japanese (日本語) (ja_JP) | ![93%](https://geps.dev/progress/93) | | Korean (한국어) (ko_KR) | ![97%](https://geps.dev/progress/97) | | Norwegian (Norsk) (no_NB) | ![77%](https://geps.dev/progress/77) | -| Persian (فارسی) (fa_IR) | ![93%](https://geps.dev/progress/93) | +| Persian (فارسی) (fa_IR) | ![92%](https://geps.dev/progress/92) | | Polish (Polski) (pl_PL) | ![84%](https://geps.dev/progress/84) | | Portuguese (Português) (pt_PT) | ![96%](https://geps.dev/progress/96) | | Portuguese Brazilian (Português) (pt_BR) | ![97%](https://geps.dev/progress/97) | @@ -146,7 +146,7 @@ Stirling-PDF currently supports 39 languages! | Russian (Русский) (ru_RU) | ![96%](https://geps.dev/progress/96) | | Serbian Latin alphabet (Srpski) (sr_LATN_RS) | ![63%](https://geps.dev/progress/63) | | Simplified Chinese (简体中文) (zh_CN) | ![98%](https://geps.dev/progress/98) | -| Slovakian (Slovensky) (sk_SK) | ![73%](https://geps.dev/progress/73) | +| Slovakian (Slovensky) (sk_SK) | ![72%](https://geps.dev/progress/72) | | Slovenian (Slovenščina) (sl_SI) | ![95%](https://geps.dev/progress/95) | | Spanish (Español) (es_ES) | ![98%](https://geps.dev/progress/98) | | Swedish (Svenska) (sv_SE) | ![92%](https://geps.dev/progress/92) | @@ -154,7 +154,7 @@ Stirling-PDF currently supports 39 languages! | Tibetan (བོད་ཡིག་) (zh_BO) | ![93%](https://geps.dev/progress/93) | | Traditional Chinese (繁體中文) (zh_TW) | ![99%](https://geps.dev/progress/99) | | Turkish (Türkçe) (tr_TR) | ![81%](https://geps.dev/progress/81) | -| Ukrainian (Українська) (uk_UA) | ![98%](https://geps.dev/progress/98) | +| Ukrainian (Українська) (uk_UA) | ![97%](https://geps.dev/progress/97) | | Vietnamese (Tiếng Việt) (vi_VN) | ![78%](https://geps.dev/progress/78) | diff --git a/build.gradle b/build.gradle index 0e1ccb47b..f883c55ea 100644 --- a/build.gradle +++ b/build.gradle @@ -2,7 +2,7 @@ plugins { id "java" id "org.springframework.boot" version "3.4.3" id "io.spring.dependency-management" version "1.1.7" - id "org.springdoc.openapi-gradle-plugin" version "1.8.0" + id "org.springdoc.openapi-gradle-plugin" version "1.9.0" id "io.swagger.swaggerhub" version "1.3.2" id "edu.sc.seis.launch4j" version "3.0.6" id "com.diffplug.spotless" version "7.0.2" @@ -25,7 +25,7 @@ ext { } group = "stirling.software" -version = "0.44.1" +version = "0.44.2" java { // 17 is lowest but we support and recommend 21 @@ -98,6 +98,7 @@ openApi { apiDocsUrl = "http://localhost:8080/v1/api-docs" outputDir = file("$projectDir") outputFileName = "SwaggerDoc.json" + waitTimeInSeconds = 60 // Increase the wait time to 60 seconds } //0.11.5 to 2024.11.5 @@ -256,7 +257,7 @@ launch4j { spotless { java { - target project.fileTree('src/main/java') + target project.fileTree('src').include('**/*.java') googleJavaFormat("1.25.2").aosp().reorderImports(false) @@ -284,17 +285,21 @@ sonar { // } tasks.wrapper { gradleVersion = "8.12" + distributionType = Wrapper.DistributionType.ALL } //tasks.withType(JavaCompile) { // options.compilerArgs << "-Xlint:deprecation" //} configurations.all { + // Remove all commons-logging dependencies so that only spring-jcl is used + exclude group: 'commons-logging', module: 'commons-logging' + // Exclude Tomcat exclude group: "org.springframework.boot", module: "spring-boot-starter-tomcat" } dependencies { //tmp for security bumps - implementation 'ch.qos.logback:logback-core:1.5.17' + implementation 'ch.qos.logback:logback-core:1.5.17' implementation 'ch.qos.logback:logback-classic:1.5.17' @@ -381,19 +386,13 @@ dependencies { //general PDF // https://mvnrepository.com/artifact/com.opencsv/opencsv - implementation ("com.opencsv:opencsv:5.10") { - exclude group: "commons-logging", module: "commons-logging" - } + implementation ("com.opencsv:opencsv:5.10") - implementation ("org.apache.pdfbox:pdfbox:$pdfboxVersion") { - exclude group: "commons-logging", module: "commons-logging" - } + implementation ("org.apache.pdfbox:pdfbox:$pdfboxVersion") implementation "org.apache.pdfbox:preflight:$pdfboxVersion" - implementation ("org.apache.pdfbox:xmpbox:$pdfboxVersion") { - exclude group: "commons-logging", module: "commons-logging" - } + implementation ("org.apache.pdfbox:xmpbox:$pdfboxVersion") // https://mvnrepository.com/artifact/technology.tabula/tabula implementation ('technology.tabula:tabula:1.0.5') { @@ -407,7 +406,7 @@ dependencies { implementation "org.bouncycastle:bcprov-jdk18on:$bouncycastleVersion" implementation "org.bouncycastle:bcpkix-jdk18on:$bouncycastleVersion" implementation "org.springframework.boot:spring-boot-starter-actuator:$springBootVersion" - implementation "io.micrometer:micrometer-core:1.14.4" + implementation "io.micrometer:micrometer-core:1.14.5" implementation group: "com.google.zxing", name: "core", version: "3.5.3" // https://mvnrepository.com/artifact/org.commonmark/commonmark implementation "org.commonmark:commonmark:0.24.0" @@ -443,9 +442,9 @@ task writeVersion { swaggerhubUpload { // dependsOn = generateOpenApiDocs // Depends on your task generating Swagger docs api = "Stirling-PDF" // The name of your API on SwaggerHub - owner = "Frooodle" // Your SwaggerHub username (or organization name) + owner = "${System.getenv().getOrDefault('SWAGGERHUB_USER', 'Frooodle')}" // Your SwaggerHub username (or organization name) version = project.version // The version of your API - inputFile = "./SwaggerDoc.json" // The path to your Swagger docs + inputFile = file("SwaggerDoc.json") // The path to your Swagger docs token = "${System.getenv("SWAGGERHUB_API_KEY")}" // Your SwaggerHub API key, passed as an environment variable oas = "3.0.0" // The version of the OpenAPI Specification you"re using } @@ -473,3 +472,7 @@ task printMacVersion { println getMacVersion(project.version.toString()) } } + +tasks.named('generateOpenApiDocs') { + doNotTrackState("Tracking state is not supported for this task") +} diff --git a/src/main/java/stirling/software/SPDF/controller/api/AnalysisController.java b/src/main/java/stirling/software/SPDF/controller/api/AnalysisController.java index 37941017e..2406e1168 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/AnalysisController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/AnalysisController.java @@ -18,17 +18,17 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import stirling.software.SPDF.model.api.PDFFile; -import stirling.software.SPDF.service.CustomPDDocumentFactory; +import stirling.software.SPDF.service.CustomPDFDocumentFactory; @RestController @RequestMapping("/api/v1/analysis") @Tag(name = "Analysis", description = "Analysis APIs") public class AnalysisController { - private final CustomPDDocumentFactory pdfDocumentFactory; + private final CustomPDFDocumentFactory pdfDocumentFactory; @Autowired - public AnalysisController(CustomPDDocumentFactory pdfDocumentFactory) { + public AnalysisController(CustomPDFDocumentFactory pdfDocumentFactory) { this.pdfDocumentFactory = pdfDocumentFactory; } diff --git a/src/main/java/stirling/software/SPDF/controller/api/CropController.java b/src/main/java/stirling/software/SPDF/controller/api/CropController.java index 68d252a47..68c3a44f3 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/CropController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/CropController.java @@ -21,7 +21,7 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import stirling.software.SPDF.model.api.general.CropPdfForm; -import stirling.software.SPDF.service.CustomPDDocumentFactory; +import stirling.software.SPDF.service.CustomPDFDocumentFactory; import stirling.software.SPDF.utils.WebResponseUtils; @RestController @@ -29,10 +29,10 @@ import stirling.software.SPDF.utils.WebResponseUtils; @Tag(name = "General", description = "General APIs") public class CropController { - private final CustomPDDocumentFactory pdfDocumentFactory; + private final CustomPDFDocumentFactory pdfDocumentFactory; @Autowired - public CropController(CustomPDDocumentFactory pdfDocumentFactory) { + public CropController(CustomPDFDocumentFactory pdfDocumentFactory) { this.pdfDocumentFactory = pdfDocumentFactory; } @@ -40,7 +40,8 @@ public class CropController { @Operation( summary = "Crops a PDF document", description = - "This operation takes an input PDF file and crops it according to the given coordinates. Input:PDF Output:PDF Type:SISO") + "This operation takes an input PDF file and crops it according to the given" + + " coordinates. Input:PDF Output:PDF Type:SISO") public ResponseEntity cropPdf(@ModelAttribute CropPdfForm form) throws IOException { PDDocument sourceDocument = pdfDocumentFactory.load(form); diff --git a/src/main/java/stirling/software/SPDF/controller/api/MergeController.java b/src/main/java/stirling/software/SPDF/controller/api/MergeController.java index 8c63f817e..c95aff8fe 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/MergeController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/MergeController.java @@ -33,7 +33,7 @@ import io.swagger.v3.oas.annotations.tags.Tag; import lombok.extern.slf4j.Slf4j; import stirling.software.SPDF.model.api.general.MergePdfsRequest; -import stirling.software.SPDF.service.CustomPDDocumentFactory; +import stirling.software.SPDF.service.CustomPDFDocumentFactory; import stirling.software.SPDF.utils.GeneralUtils; import stirling.software.SPDF.utils.WebResponseUtils; @@ -43,10 +43,10 @@ import stirling.software.SPDF.utils.WebResponseUtils; @Tag(name = "General", description = "General APIs") public class MergeController { - private final CustomPDDocumentFactory pdfDocumentFactory; + private final CustomPDFDocumentFactory pdfDocumentFactory; @Autowired - public MergeController(CustomPDDocumentFactory pdfDocumentFactory) { + public MergeController(CustomPDFDocumentFactory pdfDocumentFactory) { this.pdfDocumentFactory = pdfDocumentFactory; } @@ -119,7 +119,9 @@ public class MergeController { @Operation( summary = "Merge multiple PDF files into one", description = - "This endpoint merges multiple PDF files into a single PDF file. The merged file will contain all pages from the input files in the order they were provided. Input:PDF Output:PDF Type:MISO") + "This endpoint merges multiple PDF files into a single PDF file. The merged" + + " file will contain all pages from the input files in the order they were" + + " provided. Input:PDF Output:PDF Type:MISO") public ResponseEntity mergePdfs(@ModelAttribute MergePdfsRequest form) throws IOException { List filesToDelete = new ArrayList<>(); // List of temporary files to delete diff --git a/src/main/java/stirling/software/SPDF/controller/api/MultiPageLayoutController.java b/src/main/java/stirling/software/SPDF/controller/api/MultiPageLayoutController.java index 56d02a686..73bbe98a9 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/MultiPageLayoutController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/MultiPageLayoutController.java @@ -24,7 +24,7 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import stirling.software.SPDF.model.api.general.MergeMultiplePagesRequest; -import stirling.software.SPDF.service.CustomPDDocumentFactory; +import stirling.software.SPDF.service.CustomPDFDocumentFactory; import stirling.software.SPDF.utils.WebResponseUtils; @RestController @@ -32,10 +32,10 @@ import stirling.software.SPDF.utils.WebResponseUtils; @Tag(name = "General", description = "General APIs") public class MultiPageLayoutController { - private final CustomPDDocumentFactory pdfDocumentFactory; + private final CustomPDFDocumentFactory pdfDocumentFactory; @Autowired - public MultiPageLayoutController(CustomPDDocumentFactory pdfDocumentFactory) { + public MultiPageLayoutController(CustomPDFDocumentFactory pdfDocumentFactory) { this.pdfDocumentFactory = pdfDocumentFactory; } @@ -43,7 +43,8 @@ public class MultiPageLayoutController { @Operation( summary = "Merge multiple pages of a PDF document into a single page", description = - "This operation takes an input PDF file and the number of pages to merge into a single sheet in the output PDF file. Input:PDF Output:PDF Type:SISO") + "This operation takes an input PDF file and the number of pages to merge into a" + + " single sheet in the output PDF file. Input:PDF Output:PDF Type:SISO") public ResponseEntity mergeMultiplePagesIntoOne( @ModelAttribute MergeMultiplePagesRequest request) throws IOException { diff --git a/src/main/java/stirling/software/SPDF/controller/api/PdfImageRemovalController.java b/src/main/java/stirling/software/SPDF/controller/api/PdfImageRemovalController.java index fdcb55a24..fce61d3e5 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/PdfImageRemovalController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/PdfImageRemovalController.java @@ -15,7 +15,7 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import stirling.software.SPDF.model.api.PDFFile; -import stirling.software.SPDF.service.CustomPDDocumentFactory; +import stirling.software.SPDF.service.CustomPDFDocumentFactory; import stirling.software.SPDF.service.PdfImageRemovalService; import stirling.software.SPDF.utils.WebResponseUtils; @@ -31,7 +31,7 @@ public class PdfImageRemovalController { // Service for removing images from PDFs private final PdfImageRemovalService pdfImageRemovalService; - private final CustomPDDocumentFactory pdfDocumentFactory; + private final CustomPDFDocumentFactory pdfDocumentFactory; /** * Constructor for dependency injection of PdfImageRemovalService. @@ -41,7 +41,7 @@ public class PdfImageRemovalController { @Autowired public PdfImageRemovalController( PdfImageRemovalService pdfImageRemovalService, - CustomPDDocumentFactory pdfDocumentFactory) { + CustomPDFDocumentFactory pdfDocumentFactory) { this.pdfImageRemovalService = pdfImageRemovalService; this.pdfDocumentFactory = pdfDocumentFactory; } @@ -61,7 +61,8 @@ public class PdfImageRemovalController { @Operation( summary = "Remove images from file to reduce the file size.", 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 removeImages(@ModelAttribute PDFFile file) throws IOException { // Load the PDF document PDDocument document = pdfDocumentFactory.load(file); diff --git a/src/main/java/stirling/software/SPDF/controller/api/PdfOverlayController.java b/src/main/java/stirling/software/SPDF/controller/api/PdfOverlayController.java index 7ff17ccb5..996ed45bb 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/PdfOverlayController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/PdfOverlayController.java @@ -26,7 +26,7 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import stirling.software.SPDF.model.api.general.OverlayPdfsRequest; -import stirling.software.SPDF.service.CustomPDDocumentFactory; +import stirling.software.SPDF.service.CustomPDFDocumentFactory; import stirling.software.SPDF.utils.GeneralUtils; import stirling.software.SPDF.utils.WebResponseUtils; @@ -35,10 +35,10 @@ import stirling.software.SPDF.utils.WebResponseUtils; @Tag(name = "General", description = "General APIs") public class PdfOverlayController { - private final CustomPDDocumentFactory pdfDocumentFactory; + private final CustomPDFDocumentFactory pdfDocumentFactory; @Autowired - public PdfOverlayController(CustomPDDocumentFactory pdfDocumentFactory) { + public PdfOverlayController(CustomPDFDocumentFactory pdfDocumentFactory) { this.pdfDocumentFactory = pdfDocumentFactory; } @@ -46,7 +46,8 @@ public class PdfOverlayController { @Operation( summary = "Overlay PDF files in various modes", description = - "Overlay PDF files onto a base PDF with different modes: Sequential, Interleaved, or Fixed Repeat. Input:PDF Output:PDF Type:MIMO") + "Overlay PDF files onto a base PDF with different modes: Sequential," + + " Interleaved, or Fixed Repeat. Input:PDF Output:PDF Type:MIMO") public ResponseEntity overlayPdfs(@ModelAttribute OverlayPdfsRequest request) throws IOException { MultipartFile baseFile = request.getFileInput(); diff --git a/src/main/java/stirling/software/SPDF/controller/api/RearrangePagesPDFController.java b/src/main/java/stirling/software/SPDF/controller/api/RearrangePagesPDFController.java index 09f8afe9c..342d3cb8b 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/RearrangePagesPDFController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/RearrangePagesPDFController.java @@ -24,7 +24,7 @@ import lombok.extern.slf4j.Slf4j; import stirling.software.SPDF.model.SortTypes; import stirling.software.SPDF.model.api.PDFWithPageNums; import stirling.software.SPDF.model.api.general.RearrangePagesRequest; -import stirling.software.SPDF.service.CustomPDDocumentFactory; +import stirling.software.SPDF.service.CustomPDFDocumentFactory; import stirling.software.SPDF.utils.GeneralUtils; import stirling.software.SPDF.utils.WebResponseUtils; @@ -34,10 +34,10 @@ import stirling.software.SPDF.utils.WebResponseUtils; @Tag(name = "General", description = "General APIs") public class RearrangePagesPDFController { - private final CustomPDDocumentFactory pdfDocumentFactory; + private final CustomPDFDocumentFactory pdfDocumentFactory; @Autowired - public RearrangePagesPDFController(CustomPDDocumentFactory pdfDocumentFactory) { + public RearrangePagesPDFController(CustomPDFDocumentFactory pdfDocumentFactory) { this.pdfDocumentFactory = pdfDocumentFactory; } @@ -45,7 +45,9 @@ public class RearrangePagesPDFController { @Operation( summary = "Remove pages from a PDF file", description = - "This endpoint removes specified pages from a given PDF file. Users can provide a comma-separated list of page numbers or ranges to delete. Input:PDF Output:PDF Type:SISO") + "This endpoint removes specified pages from a given PDF file. Users can provide" + + " a comma-separated list of page numbers or ranges to delete. Input:PDF" + + " Output:PDF Type:SISO") public ResponseEntity deletePages(@ModelAttribute PDFWithPageNums request) throws IOException { @@ -242,7 +244,10 @@ public class RearrangePagesPDFController { @Operation( summary = "Rearrange pages in a PDF file", description = - "This endpoint rearranges pages in a given PDF file based on the specified page order or custom mode. Users can provide a page order as a comma-separated list of page numbers or page ranges, or a custom mode. Input:PDF Output:PDF") + "This endpoint rearranges pages in a given PDF file based on the specified page" + + " order or custom mode. Users can provide a page order as a" + + " comma-separated list of page numbers or page ranges, or a custom mode." + + " Input:PDF Output:PDF") public ResponseEntity rearrangePages(@ModelAttribute RearrangePagesRequest request) throws IOException { MultipartFile pdfFile = request.getFileInput(); diff --git a/src/main/java/stirling/software/SPDF/controller/api/RotationController.java b/src/main/java/stirling/software/SPDF/controller/api/RotationController.java index 9ffc61b65..b029450ef 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/RotationController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/RotationController.java @@ -18,7 +18,7 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import stirling.software.SPDF.model.api.general.RotatePDFRequest; -import stirling.software.SPDF.service.CustomPDDocumentFactory; +import stirling.software.SPDF.service.CustomPDFDocumentFactory; import stirling.software.SPDF.utils.WebResponseUtils; @RestController @@ -26,10 +26,10 @@ import stirling.software.SPDF.utils.WebResponseUtils; @Tag(name = "General", description = "General APIs") public class RotationController { - private final CustomPDDocumentFactory pdfDocumentFactory; + private final CustomPDFDocumentFactory pdfDocumentFactory; @Autowired - public RotationController(CustomPDDocumentFactory pdfDocumentFactory) { + public RotationController(CustomPDFDocumentFactory pdfDocumentFactory) { this.pdfDocumentFactory = pdfDocumentFactory; } @@ -37,11 +37,18 @@ public class RotationController { @Operation( summary = "Rotate a PDF file", description = - "This endpoint rotates a given PDF file by a specified angle. The angle must be a multiple of 90. Input:PDF Output:PDF Type:SISO") + "This endpoint rotates a given PDF file by a specified angle. The angle must be" + + " a multiple of 90. Input:PDF Output:PDF Type:SISO") public ResponseEntity rotatePDF(@ModelAttribute RotatePDFRequest request) throws IOException { MultipartFile pdfFile = request.getFileInput(); Integer angle = request.getAngle(); + + // Validate the angle is a multiple of 90 + if (angle % 90 != 0) { + throw new IllegalArgumentException("Angle must be a multiple of 90"); + } + // Load the PDF document PDDocument document = pdfDocumentFactory.load(request); diff --git a/src/main/java/stirling/software/SPDF/controller/api/ScalePagesController.java b/src/main/java/stirling/software/SPDF/controller/api/ScalePagesController.java index 5fb62cafc..48e3c0f44 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/ScalePagesController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/ScalePagesController.java @@ -25,7 +25,7 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import stirling.software.SPDF.model.api.general.ScalePagesRequest; -import stirling.software.SPDF.service.CustomPDDocumentFactory; +import stirling.software.SPDF.service.CustomPDFDocumentFactory; import stirling.software.SPDF.utils.WebResponseUtils; @RestController @@ -33,10 +33,10 @@ import stirling.software.SPDF.utils.WebResponseUtils; @Tag(name = "General", description = "General APIs") public class ScalePagesController { - private final CustomPDDocumentFactory pdfDocumentFactory; + private final CustomPDFDocumentFactory pdfDocumentFactory; @Autowired - public ScalePagesController(CustomPDDocumentFactory pdfDocumentFactory) { + public ScalePagesController(CustomPDFDocumentFactory pdfDocumentFactory) { this.pdfDocumentFactory = pdfDocumentFactory; } @@ -44,7 +44,8 @@ public class ScalePagesController { @Operation( summary = "Change the size of a PDF page/document", description = - "This operation takes an input PDF file and the size to scale the pages to in the output PDF file. Input:PDF Output:PDF Type:SISO") + "This operation takes an input PDF file and the size to scale the pages to in" + + " the output PDF file. Input:PDF Output:PDF Type:SISO") public ResponseEntity scalePages(@ModelAttribute ScalePagesRequest request) throws IOException { MultipartFile file = request.getFileInput(); @@ -123,7 +124,8 @@ public class ScalePagesController { } throw new IllegalArgumentException( - "Invalid PDRectangle. It must be one of the following: A0, A1, A2, A3, A4, A5, A6, LETTER, LEGAL, KEEP"); + "Invalid PDRectangle. It must be one of the following: A0, A1, A2, A3, A4, A5, A6," + + " LETTER, LEGAL, KEEP"); } private Map getSizeMap() { diff --git a/src/main/java/stirling/software/SPDF/controller/api/SplitPDFController.java b/src/main/java/stirling/software/SPDF/controller/api/SplitPDFController.java index d0df4ced5..696482b63 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/SplitPDFController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/SplitPDFController.java @@ -28,7 +28,7 @@ import io.swagger.v3.oas.annotations.tags.Tag; import lombok.extern.slf4j.Slf4j; import stirling.software.SPDF.model.api.PDFWithPageNums; -import stirling.software.SPDF.service.CustomPDDocumentFactory; +import stirling.software.SPDF.service.CustomPDFDocumentFactory; import stirling.software.SPDF.utils.WebResponseUtils; @RestController @@ -37,10 +37,10 @@ import stirling.software.SPDF.utils.WebResponseUtils; @Tag(name = "General", description = "General APIs") public class SplitPDFController { - private final CustomPDDocumentFactory pdfDocumentFactory; + private final CustomPDFDocumentFactory pdfDocumentFactory; @Autowired - public SplitPDFController(CustomPDDocumentFactory pdfDocumentFactory) { + public SplitPDFController(CustomPDFDocumentFactory pdfDocumentFactory) { this.pdfDocumentFactory = pdfDocumentFactory; } @@ -48,7 +48,10 @@ public class SplitPDFController { @Operation( summary = "Split a PDF file into separate documents", description = - "This endpoint splits a given PDF file into separate documents based on the specified page numbers or ranges. Users can specify pages using individual numbers, ranges, or 'all' for every page. Input:PDF Output:PDF Type:SIMO") + "This endpoint splits a given PDF file into separate documents based on the" + + " specified page numbers or ranges. Users can specify pages using" + + " individual numbers, ranges, or 'all' for every page. Input:PDF" + + " Output:PDF Type:SIMO") public ResponseEntity splitPdf(@ModelAttribute PDFWithPageNums request) throws IOException { diff --git a/src/main/java/stirling/software/SPDF/controller/api/SplitPdfByChaptersController.java b/src/main/java/stirling/software/SPDF/controller/api/SplitPdfByChaptersController.java index b774d3ced..698e2b50e 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/SplitPdfByChaptersController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/SplitPdfByChaptersController.java @@ -33,7 +33,7 @@ import lombok.extern.slf4j.Slf4j; import stirling.software.SPDF.model.PdfMetadata; import stirling.software.SPDF.model.api.SplitPdfByChaptersRequest; -import stirling.software.SPDF.service.CustomPDDocumentFactory; +import stirling.software.SPDF.service.CustomPDFDocumentFactory; import stirling.software.SPDF.service.PdfMetadataService; import stirling.software.SPDF.utils.WebResponseUtils; @@ -45,11 +45,11 @@ public class SplitPdfByChaptersController { private final PdfMetadataService pdfMetadataService; - private final CustomPDDocumentFactory pdfDocumentFactory; + private final CustomPDFDocumentFactory pdfDocumentFactory; @Autowired public SplitPdfByChaptersController( - PdfMetadataService pdfMetadataService, CustomPDDocumentFactory pdfDocumentFactory) { + PdfMetadataService pdfMetadataService, CustomPDFDocumentFactory pdfDocumentFactory) { this.pdfMetadataService = pdfMetadataService; this.pdfDocumentFactory = pdfDocumentFactory; } diff --git a/src/main/java/stirling/software/SPDF/controller/api/SplitPdfBySectionsController.java b/src/main/java/stirling/software/SPDF/controller/api/SplitPdfBySectionsController.java index 2a692cbca..83f0cb0e9 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/SplitPdfBySectionsController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/SplitPdfBySectionsController.java @@ -31,7 +31,7 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import stirling.software.SPDF.model.api.SplitPdfBySectionsRequest; -import stirling.software.SPDF.service.CustomPDDocumentFactory; +import stirling.software.SPDF.service.CustomPDFDocumentFactory; import stirling.software.SPDF.utils.WebResponseUtils; @RestController @@ -39,10 +39,10 @@ import stirling.software.SPDF.utils.WebResponseUtils; @Tag(name = "General", description = "General APIs") public class SplitPdfBySectionsController { - private final CustomPDDocumentFactory pdfDocumentFactory; + private final CustomPDFDocumentFactory pdfDocumentFactory; @Autowired - public SplitPdfBySectionsController(CustomPDDocumentFactory pdfDocumentFactory) { + public SplitPdfBySectionsController(CustomPDFDocumentFactory pdfDocumentFactory) { this.pdfDocumentFactory = pdfDocumentFactory; } @@ -50,7 +50,9 @@ public class SplitPdfBySectionsController { @Operation( summary = "Split PDF pages into smaller sections", description = - "Split each page of a PDF into smaller sections based on the user's choice (halves, thirds, quarters, etc.), both vertically and horizontally. Input:PDF Output:ZIP-PDF Type:SISO") + "Split each page of a PDF into smaller sections based on the user's choice" + + " (halves, thirds, quarters, etc.), both vertically and horizontally." + + " Input:PDF Output:ZIP-PDF Type:SISO") public ResponseEntity splitPdf(@ModelAttribute SplitPdfBySectionsRequest request) throws Exception { List splitDocumentsBoas = new ArrayList<>(); diff --git a/src/main/java/stirling/software/SPDF/controller/api/SplitPdfBySizeController.java b/src/main/java/stirling/software/SPDF/controller/api/SplitPdfBySizeController.java index ee73cbb89..4ec2bd361 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/SplitPdfBySizeController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/SplitPdfBySizeController.java @@ -25,7 +25,7 @@ import io.swagger.v3.oas.annotations.tags.Tag; import lombok.extern.slf4j.Slf4j; import stirling.software.SPDF.model.api.general.SplitPdfBySizeOrCountRequest; -import stirling.software.SPDF.service.CustomPDDocumentFactory; +import stirling.software.SPDF.service.CustomPDFDocumentFactory; import stirling.software.SPDF.utils.GeneralUtils; import stirling.software.SPDF.utils.WebResponseUtils; @@ -35,22 +35,25 @@ import stirling.software.SPDF.utils.WebResponseUtils; @Tag(name = "General", description = "General APIs") public class SplitPdfBySizeController { - private final CustomPDDocumentFactory pdfDocumentFactory; + private final CustomPDFDocumentFactory pdfDocumentFactory; @Autowired - public SplitPdfBySizeController(CustomPDDocumentFactory pdfDocumentFactory) { + public SplitPdfBySizeController(CustomPDFDocumentFactory pdfDocumentFactory) { this.pdfDocumentFactory = pdfDocumentFactory; log.info( "SplitPdfBySizeController initialized with pdfDocumentFactory: {}", - pdfDocumentFactory); + pdfDocumentFactory.getClass().getSimpleName()); } @PostMapping(value = "/split-by-size-or-count", consumes = "multipart/form-data") @Operation( summary = "Auto split PDF pages into separate documents based on size or count", description = - "split PDF into multiple paged documents based on size/count, ie if 20 pages and split into 5, it does 5 documents each 4 pages\r\n" - + " if 10MB and each page is 1MB and you enter 2MB then 5 docs each 2MB (rounded so that it accepts 1.9MB but not 2.1MB) Input:PDF Output:ZIP-PDF Type:SISO") + "split PDF into multiple paged documents based on size/count, ie if 20 pages" + + " and split into 5, it does 5 documents each 4 pages\r\n" + + " if 10MB and each page is 1MB and you enter 2MB then 5 docs each 2MB" + + " (rounded so that it accepts 1.9MB but not 2.1MB) Input:PDF" + + " Output:ZIP-PDF Type:SISO") public ResponseEntity autoSplitPdf(@ModelAttribute SplitPdfBySizeOrCountRequest request) throws Exception { diff --git a/src/main/java/stirling/software/SPDF/controller/api/ToSinglePageController.java b/src/main/java/stirling/software/SPDF/controller/api/ToSinglePageController.java index 00bdf827e..f6b0ed947 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/ToSinglePageController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/ToSinglePageController.java @@ -21,7 +21,7 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import stirling.software.SPDF.model.api.PDFFile; -import stirling.software.SPDF.service.CustomPDDocumentFactory; +import stirling.software.SPDF.service.CustomPDFDocumentFactory; import stirling.software.SPDF.utils.WebResponseUtils; @RestController @@ -29,10 +29,10 @@ import stirling.software.SPDF.utils.WebResponseUtils; @Tag(name = "General", description = "General APIs") public class ToSinglePageController { - private final CustomPDDocumentFactory pdfDocumentFactory; + private final CustomPDFDocumentFactory pdfDocumentFactory; @Autowired - public ToSinglePageController(CustomPDDocumentFactory pdfDocumentFactory) { + public ToSinglePageController(CustomPDFDocumentFactory pdfDocumentFactory) { this.pdfDocumentFactory = pdfDocumentFactory; } @@ -40,7 +40,10 @@ public class ToSinglePageController { @Operation( summary = "Convert a multi-page PDF into a single long page PDF", description = - "This endpoint converts a multi-page PDF document into a single paged PDF document. The width of the single page will be same as the input's width, but the height will be the sum of all the pages' heights. Input:PDF Output:PDF Type:SISO") + "This endpoint converts a multi-page PDF document into a single paged PDF" + + " document. The width of the single page will be same as the input's" + + " width, but the height will be the sum of all the pages' heights." + + " Input:PDF Output:PDF Type:SISO") public ResponseEntity pdfToSinglePage(@ModelAttribute PDFFile request) throws IOException { diff --git a/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertHtmlToPDF.java b/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertHtmlToPDF.java index cd68798bb..52b124e53 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertHtmlToPDF.java +++ b/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertHtmlToPDF.java @@ -15,7 +15,7 @@ import io.swagger.v3.oas.annotations.tags.Tag; import stirling.software.SPDF.config.RuntimePathConfig; import stirling.software.SPDF.model.ApplicationProperties; import stirling.software.SPDF.model.api.converters.HTMLToPdfRequest; -import stirling.software.SPDF.service.CustomPDDocumentFactory; +import stirling.software.SPDF.service.CustomPDFDocumentFactory; import stirling.software.SPDF.utils.FileToPdf; import stirling.software.SPDF.utils.WebResponseUtils; @@ -24,7 +24,7 @@ import stirling.software.SPDF.utils.WebResponseUtils; @RequestMapping("/api/v1/convert") public class ConvertHtmlToPDF { - private final CustomPDDocumentFactory pdfDocumentFactory; + private final CustomPDFDocumentFactory pdfDocumentFactory; private final ApplicationProperties applicationProperties; @@ -32,7 +32,7 @@ public class ConvertHtmlToPDF { @Autowired public ConvertHtmlToPDF( - CustomPDDocumentFactory pdfDocumentFactory, + CustomPDFDocumentFactory pdfDocumentFactory, ApplicationProperties applicationProperties, RuntimePathConfig runtimePathConfig) { this.pdfDocumentFactory = pdfDocumentFactory; @@ -45,7 +45,8 @@ public class ConvertHtmlToPDF { @Operation( summary = "Convert an HTML or ZIP (containing HTML and CSS) to PDF", description = - "This endpoint takes an HTML or ZIP file input and converts it to a PDF format. Input:HTML Output:PDF Type:SISO") + "This endpoint takes an HTML or ZIP file input and converts it to a PDF format." + + " Input:HTML Output:PDF Type:SISO") public ResponseEntity HtmlToPdf(@ModelAttribute HTMLToPdfRequest request) throws Exception { MultipartFile fileInput = request.getFileInput(); diff --git a/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertImgPDFController.java b/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertImgPDFController.java index 103f6e1c8..2aa75f159 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertImgPDFController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertImgPDFController.java @@ -33,7 +33,7 @@ import lombok.extern.slf4j.Slf4j; import stirling.software.SPDF.model.api.converters.ConvertToImageRequest; import stirling.software.SPDF.model.api.converters.ConvertToPdfRequest; -import stirling.software.SPDF.service.CustomPDDocumentFactory; +import stirling.software.SPDF.service.CustomPDFDocumentFactory; import stirling.software.SPDF.utils.*; import stirling.software.SPDF.utils.ProcessExecutor.ProcessExecutorResult; @@ -43,10 +43,10 @@ import stirling.software.SPDF.utils.ProcessExecutor.ProcessExecutorResult; @Tag(name = "Convert", description = "Convert APIs") public class ConvertImgPDFController { - private final CustomPDDocumentFactory pdfDocumentFactory; + private final CustomPDFDocumentFactory pdfDocumentFactory; @Autowired - public ConvertImgPDFController(CustomPDDocumentFactory pdfDocumentFactory) { + public ConvertImgPDFController(CustomPDFDocumentFactory pdfDocumentFactory) { this.pdfDocumentFactory = pdfDocumentFactory; } @@ -54,7 +54,9 @@ public class ConvertImgPDFController { @Operation( summary = "Convert PDF to image(s)", description = - "This endpoint converts a PDF file to image(s) with the specified image format, color type, and DPI. Users can choose to get a single image or multiple images. Input:PDF Output:Image Type:SI-Conditional") + "This endpoint converts a PDF file to image(s) with the specified image format," + + " color type, and DPI. Users can choose to get a single image or multiple" + + " images. Input:PDF Output:Image Type:SI-Conditional") public ResponseEntity convertToImage(@ModelAttribute ConvertToImageRequest request) throws NumberFormatException, Exception { MultipartFile file = request.getFileInput(); @@ -208,7 +210,9 @@ public class ConvertImgPDFController { @Operation( summary = "Convert images to a PDF file", description = - "This endpoint converts one or more images to a PDF file. Users can specify whether to stretch the images to fit the PDF page, and whether to automatically rotate the images. Input:Image Output:PDF Type:MISO") + "This endpoint converts one or more images to a PDF file. Users can specify" + + " whether to stretch the images to fit the PDF page, and whether to" + + " automatically rotate the images. Input:Image Output:PDF Type:MISO") public ResponseEntity convertToPdf(@ModelAttribute ConvertToPdfRequest request) throws IOException { MultipartFile[] file = request.getFileInput(); diff --git a/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertMarkdownToPdf.java b/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertMarkdownToPdf.java index 121539778..547d9c646 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertMarkdownToPdf.java +++ b/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertMarkdownToPdf.java @@ -25,7 +25,7 @@ import io.swagger.v3.oas.annotations.tags.Tag; import stirling.software.SPDF.config.RuntimePathConfig; import stirling.software.SPDF.model.ApplicationProperties; import stirling.software.SPDF.model.api.GeneralFile; -import stirling.software.SPDF.service.CustomPDDocumentFactory; +import stirling.software.SPDF.service.CustomPDFDocumentFactory; import stirling.software.SPDF.utils.FileToPdf; import stirling.software.SPDF.utils.WebResponseUtils; @@ -34,14 +34,14 @@ import stirling.software.SPDF.utils.WebResponseUtils; @RequestMapping("/api/v1/convert") public class ConvertMarkdownToPdf { - private final CustomPDDocumentFactory pdfDocumentFactory; + private final CustomPDFDocumentFactory pdfDocumentFactory; private final ApplicationProperties applicationProperties; private final RuntimePathConfig runtimePathConfig; @Autowired public ConvertMarkdownToPdf( - CustomPDDocumentFactory pdfDocumentFactory, + CustomPDFDocumentFactory pdfDocumentFactory, ApplicationProperties applicationProperties, RuntimePathConfig runtimePathConfig) { this.pdfDocumentFactory = pdfDocumentFactory; @@ -54,7 +54,8 @@ public class ConvertMarkdownToPdf { @Operation( summary = "Convert a Markdown file to PDF", description = - "This endpoint takes a Markdown file input, converts it to HTML, and then to PDF format. Input:MARKDOWN Output:PDF Type:SISO") + "This endpoint takes a Markdown file input, converts it to HTML, and then to" + + " PDF format. Input:MARKDOWN Output:PDF Type:SISO") public ResponseEntity markdownToPdf(@ModelAttribute GeneralFile request) throws Exception { MultipartFile fileInput = request.getFileInput(); diff --git a/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertOfficeController.java b/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertOfficeController.java index 4cdfd1bdf..46bf4c033 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertOfficeController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertOfficeController.java @@ -24,7 +24,7 @@ import io.swagger.v3.oas.annotations.tags.Tag; import stirling.software.SPDF.config.RuntimePathConfig; import stirling.software.SPDF.model.api.GeneralFile; -import stirling.software.SPDF.service.CustomPDDocumentFactory; +import stirling.software.SPDF.service.CustomPDFDocumentFactory; import stirling.software.SPDF.utils.ProcessExecutor; import stirling.software.SPDF.utils.ProcessExecutor.ProcessExecutorResult; import stirling.software.SPDF.utils.WebResponseUtils; @@ -34,12 +34,12 @@ import stirling.software.SPDF.utils.WebResponseUtils; @RequestMapping("/api/v1/convert") public class ConvertOfficeController { - private final CustomPDDocumentFactory pdfDocumentFactory; + private final CustomPDFDocumentFactory pdfDocumentFactory; private final RuntimePathConfig runtimePathConfig; @Autowired public ConvertOfficeController( - CustomPDDocumentFactory pdfDocumentFactory, RuntimePathConfig runtimePathConfig) { + CustomPDFDocumentFactory pdfDocumentFactory, RuntimePathConfig runtimePathConfig) { this.pdfDocumentFactory = pdfDocumentFactory; this.runtimePathConfig = runtimePathConfig; } @@ -93,7 +93,8 @@ public class ConvertOfficeController { @Operation( summary = "Convert a file to a PDF using LibreOffice", description = - "This endpoint converts a given file to a PDF using LibreOffice API Input:ANY Output:PDF Type:SISO") + "This endpoint converts a given file to a PDF using LibreOffice API Input:ANY" + + " Output:PDF Type:SISO") public ResponseEntity processFileToPDF(@ModelAttribute GeneralFile request) throws Exception { MultipartFile inputFile = request.getFileInput(); diff --git a/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertPDFToOffice.java b/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertPDFToOffice.java index 39c808096..f603aa277 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertPDFToOffice.java +++ b/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertPDFToOffice.java @@ -21,7 +21,7 @@ import stirling.software.SPDF.model.api.PDFFile; import stirling.software.SPDF.model.api.converters.PdfToPresentationRequest; import stirling.software.SPDF.model.api.converters.PdfToTextOrRTFRequest; import stirling.software.SPDF.model.api.converters.PdfToWordRequest; -import stirling.software.SPDF.service.CustomPDDocumentFactory; +import stirling.software.SPDF.service.CustomPDFDocumentFactory; import stirling.software.SPDF.utils.PDFToFile; import stirling.software.SPDF.utils.WebResponseUtils; @@ -30,10 +30,10 @@ import stirling.software.SPDF.utils.WebResponseUtils; @Tag(name = "Convert", description = "Convert APIs") public class ConvertPDFToOffice { - private final CustomPDDocumentFactory pdfDocumentFactory; + private final CustomPDFDocumentFactory pdfDocumentFactory; @Autowired - public ConvertPDFToOffice(CustomPDDocumentFactory pdfDocumentFactory) { + public ConvertPDFToOffice(CustomPDFDocumentFactory pdfDocumentFactory) { this.pdfDocumentFactory = pdfDocumentFactory; } @@ -41,7 +41,8 @@ public class ConvertPDFToOffice { @Operation( summary = "Convert PDF to Presentation format", description = - "This endpoint converts a given PDF file to a Presentation format. Input:PDF Output:PPT Type:SISO") + "This endpoint converts a given PDF file to a Presentation format. Input:PDF" + + " Output:PPT Type:SISO") public ResponseEntity processPdfToPresentation( @ModelAttribute PdfToPresentationRequest request) throws IOException, InterruptedException { @@ -55,7 +56,8 @@ public class ConvertPDFToOffice { @Operation( summary = "Convert PDF to Text or RTF format", description = - "This endpoint converts a given PDF file to Text or RTF format. Input:PDF Output:TXT Type:SISO") + "This endpoint converts a given PDF file to Text or RTF format. Input:PDF" + + " Output:TXT Type:SISO") public ResponseEntity processPdfToRTForTXT( @ModelAttribute PdfToTextOrRTFRequest request) throws IOException, InterruptedException { @@ -82,7 +84,8 @@ public class ConvertPDFToOffice { @Operation( summary = "Convert PDF to Word document", description = - "This endpoint converts a given PDF file to a Word document format. Input:PDF Output:WORD Type:SISO") + "This endpoint converts a given PDF file to a Word document format. Input:PDF" + + " Output:WORD Type:SISO") public ResponseEntity processPdfToWord(@ModelAttribute PdfToWordRequest request) throws IOException, InterruptedException { MultipartFile inputFile = request.getFileInput(); @@ -95,7 +98,8 @@ public class ConvertPDFToOffice { @Operation( summary = "Convert PDF to XML", description = - "This endpoint converts a PDF file to an XML file. Input:PDF Output:XML Type:SISO") + "This endpoint converts a PDF file to an XML file. Input:PDF Output:XML" + + " Type:SISO") public ResponseEntity processPdfToXML(@ModelAttribute PDFFile request) throws Exception { MultipartFile inputFile = request.getFileInput(); diff --git a/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertWebsiteToPDF.java b/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertWebsiteToPDF.java index ebf9321aa..b762c0881 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertWebsiteToPDF.java +++ b/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertWebsiteToPDF.java @@ -21,7 +21,7 @@ import lombok.extern.slf4j.Slf4j; import stirling.software.SPDF.config.RuntimePathConfig; import stirling.software.SPDF.model.api.converters.UrlToPdfRequest; -import stirling.software.SPDF.service.CustomPDDocumentFactory; +import stirling.software.SPDF.service.CustomPDFDocumentFactory; import stirling.software.SPDF.utils.GeneralUtils; import stirling.software.SPDF.utils.ProcessExecutor; import stirling.software.SPDF.utils.ProcessExecutor.ProcessExecutorResult; @@ -33,12 +33,12 @@ import stirling.software.SPDF.utils.WebResponseUtils; @RequestMapping("/api/v1/convert") public class ConvertWebsiteToPDF { - private final CustomPDDocumentFactory pdfDocumentFactory; + private final CustomPDFDocumentFactory pdfDocumentFactory; private final RuntimePathConfig runtimePathConfig; @Autowired public ConvertWebsiteToPDF( - CustomPDDocumentFactory pdfDocumentFactory, RuntimePathConfig runtimePathConfig) { + CustomPDFDocumentFactory pdfDocumentFactory, RuntimePathConfig runtimePathConfig) { this.pdfDocumentFactory = pdfDocumentFactory; this.runtimePathConfig = runtimePathConfig; } @@ -47,7 +47,8 @@ public class ConvertWebsiteToPDF { @Operation( summary = "Convert a URL to a PDF", description = - "This endpoint fetches content from a URL and converts it to a PDF format. Input:N/A Output:PDF Type:SISO") + "This endpoint fetches content from a URL and converts it to a PDF format." + + " Input:N/A Output:PDF Type:SISO") public ResponseEntity urlToPdf(@ModelAttribute UrlToPdfRequest request) throws IOException, InterruptedException { String URL = request.getUrlInput(); diff --git a/src/main/java/stirling/software/SPDF/controller/api/converters/ExtractCSVController.java b/src/main/java/stirling/software/SPDF/controller/api/converters/ExtractCSVController.java index 359d353d7..06cc45094 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/converters/ExtractCSVController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/converters/ExtractCSVController.java @@ -30,7 +30,7 @@ import lombok.extern.slf4j.Slf4j; import stirling.software.SPDF.model.api.PDFWithPageNums; import stirling.software.SPDF.pdf.FlexibleCSVWriter; -import stirling.software.SPDF.service.CustomPDDocumentFactory; +import stirling.software.SPDF.service.CustomPDFDocumentFactory; import technology.tabula.ObjectExtractor; import technology.tabula.Page; @@ -43,10 +43,10 @@ import technology.tabula.extractors.SpreadsheetExtractionAlgorithm; @Slf4j public class ExtractCSVController { - private final CustomPDDocumentFactory pdfDocumentFactory; + private final CustomPDFDocumentFactory pdfDocumentFactory; @Autowired - public ExtractCSVController(CustomPDDocumentFactory pdfDocumentFactory) { + public ExtractCSVController(CustomPDFDocumentFactory pdfDocumentFactory) { this.pdfDocumentFactory = pdfDocumentFactory; } @@ -54,7 +54,8 @@ public class ExtractCSVController { @Operation( summary = "Extracts a CSV document from a PDF", description = - "This operation takes an input PDF file and returns CSV file of whole page. Input:PDF Output:CSV Type:SISO") + "This operation takes an input PDF file and returns CSV file of whole page." + + " Input:PDF Output:CSV Type:SISO") public ResponseEntity pdfToCsv(@ModelAttribute PDFWithPageNums form) throws Exception { String baseName = getBaseName(form.getFileInput().getOriginalFilename()); List csvEntries = new ArrayList<>(); diff --git a/src/main/java/stirling/software/SPDF/controller/api/filters/FilterController.java b/src/main/java/stirling/software/SPDF/controller/api/filters/FilterController.java index 66ac4cb8b..006598bd1 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/filters/FilterController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/filters/FilterController.java @@ -23,7 +23,7 @@ import stirling.software.SPDF.model.api.filter.ContainsTextRequest; import stirling.software.SPDF.model.api.filter.FileSizeRequest; import stirling.software.SPDF.model.api.filter.PageRotationRequest; import stirling.software.SPDF.model.api.filter.PageSizeRequest; -import stirling.software.SPDF.service.CustomPDDocumentFactory; +import stirling.software.SPDF.service.CustomPDFDocumentFactory; import stirling.software.SPDF.utils.PdfUtils; import stirling.software.SPDF.utils.WebResponseUtils; @@ -32,10 +32,10 @@ import stirling.software.SPDF.utils.WebResponseUtils; @Tag(name = "Filter", description = "Filter APIs") public class FilterController { - private final CustomPDDocumentFactory pdfDocumentFactory; + private final CustomPDFDocumentFactory pdfDocumentFactory; @Autowired - public FilterController(CustomPDDocumentFactory pdfDocumentFactory) { + public FilterController(CustomPDFDocumentFactory pdfDocumentFactory) { this.pdfDocumentFactory = pdfDocumentFactory; } diff --git a/src/main/java/stirling/software/SPDF/controller/api/misc/AutoRenameController.java b/src/main/java/stirling/software/SPDF/controller/api/misc/AutoRenameController.java index d3d2e91cc..1d71d94b7 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/misc/AutoRenameController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/misc/AutoRenameController.java @@ -23,7 +23,7 @@ import io.swagger.v3.oas.annotations.tags.Tag; import lombok.extern.slf4j.Slf4j; import stirling.software.SPDF.model.api.misc.ExtractHeaderRequest; -import stirling.software.SPDF.service.CustomPDDocumentFactory; +import stirling.software.SPDF.service.CustomPDFDocumentFactory; import stirling.software.SPDF.utils.WebResponseUtils; @RestController @@ -35,10 +35,10 @@ public class AutoRenameController { private static final float TITLE_FONT_SIZE_THRESHOLD = 20.0f; private static final int LINE_LIMIT = 200; - private final CustomPDDocumentFactory pdfDocumentFactory; + private final CustomPDFDocumentFactory pdfDocumentFactory; @Autowired - public AutoRenameController(CustomPDDocumentFactory pdfDocumentFactory) { + public AutoRenameController(CustomPDFDocumentFactory pdfDocumentFactory) { this.pdfDocumentFactory = pdfDocumentFactory; } @@ -46,7 +46,8 @@ public class AutoRenameController { @Operation( summary = "Extract header from PDF file", description = - "This endpoint accepts a PDF file and attempts to extract its title or header based on heuristics. Input:PDF Output:PDF Type:SISO") + "This endpoint accepts a PDF file and attempts to extract its title or header" + + " based on heuristics. Input:PDF Output:PDF Type:SISO") public ResponseEntity extractHeader(@ModelAttribute ExtractHeaderRequest request) throws Exception { MultipartFile file = request.getFileInput(); diff --git a/src/main/java/stirling/software/SPDF/controller/api/misc/AutoSplitPdfController.java b/src/main/java/stirling/software/SPDF/controller/api/misc/AutoSplitPdfController.java index 16b021144..7a5c730fe 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/misc/AutoSplitPdfController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/misc/AutoSplitPdfController.java @@ -35,7 +35,7 @@ import io.swagger.v3.oas.annotations.tags.Tag; import lombok.extern.slf4j.Slf4j; import stirling.software.SPDF.model.api.misc.AutoSplitPdfRequest; -import stirling.software.SPDF.service.CustomPDDocumentFactory; +import stirling.software.SPDF.service.CustomPDFDocumentFactory; import stirling.software.SPDF.utils.WebResponseUtils; @RestController @@ -51,10 +51,10 @@ public class AutoSplitPdfController { "https://github.com/Frooodle/Stirling-PDF", "https://stirlingpdf.com")); - private final CustomPDDocumentFactory pdfDocumentFactory; + private final CustomPDFDocumentFactory pdfDocumentFactory; @Autowired - public AutoSplitPdfController(CustomPDDocumentFactory pdfDocumentFactory) { + public AutoSplitPdfController(CustomPDFDocumentFactory pdfDocumentFactory) { this.pdfDocumentFactory = pdfDocumentFactory; } diff --git a/src/main/java/stirling/software/SPDF/controller/api/misc/BlankPageController.java b/src/main/java/stirling/software/SPDF/controller/api/misc/BlankPageController.java index 0195382ea..c50c0a685 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/misc/BlankPageController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/misc/BlankPageController.java @@ -30,7 +30,7 @@ import io.swagger.v3.oas.annotations.tags.Tag; import lombok.extern.slf4j.Slf4j; import stirling.software.SPDF.model.api.misc.RemoveBlankPagesRequest; -import stirling.software.SPDF.service.CustomPDDocumentFactory; +import stirling.software.SPDF.service.CustomPDFDocumentFactory; import stirling.software.SPDF.utils.PdfUtils; import stirling.software.SPDF.utils.WebResponseUtils; @@ -40,10 +40,10 @@ import stirling.software.SPDF.utils.WebResponseUtils; @Tag(name = "Misc", description = "Miscellaneous APIs") public class BlankPageController { - private final CustomPDDocumentFactory pdfDocumentFactory; + private final CustomPDFDocumentFactory pdfDocumentFactory; @Autowired - public BlankPageController(CustomPDDocumentFactory pdfDocumentFactory) { + public BlankPageController(CustomPDFDocumentFactory pdfDocumentFactory) { this.pdfDocumentFactory = pdfDocumentFactory; } @@ -77,7 +77,9 @@ public class BlankPageController { @Operation( summary = "Remove blank pages from a PDF file", description = - "This endpoint removes blank pages from a given PDF file. Users can specify the threshold and white percentage to tune the detection of blank pages. Input:PDF Output:PDF Type:SISO") + "This endpoint removes blank pages from a given PDF file. Users can specify the" + + " threshold and white percentage to tune the detection of blank pages." + + " Input:PDF Output:PDF Type:SISO") public ResponseEntity removeBlankPages(@ModelAttribute RemoveBlankPagesRequest request) throws IOException, InterruptedException { MultipartFile inputFile = request.getFileInput(); diff --git a/src/main/java/stirling/software/SPDF/controller/api/misc/CompressController.java b/src/main/java/stirling/software/SPDF/controller/api/misc/CompressController.java index deb4e7aa0..7d1985cec 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/misc/CompressController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/misc/CompressController.java @@ -3,13 +3,19 @@ package stirling.software.SPDF.controller.api.misc; import java.awt.*; import java.awt.image.BufferedImage; import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Path; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import java.util.ArrayList; -import java.util.HashSet; +import java.util.Arrays; +import java.util.HashMap; import java.util.Iterator; import java.util.List; -import java.util.Set; +import java.util.Map; +import java.util.Map.Entry; import javax.imageio.IIOImage; import javax.imageio.ImageIO; @@ -36,11 +42,15 @@ import io.github.pixee.security.Filenames; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; import lombok.extern.slf4j.Slf4j; import stirling.software.SPDF.model.api.misc.OptimizePdfRequest; -import stirling.software.SPDF.service.CustomPDDocumentFactory; +import stirling.software.SPDF.service.CustomPDFDocumentFactory; import stirling.software.SPDF.utils.GeneralUtils; +import stirling.software.SPDF.utils.ImageProcessingUtils; import stirling.software.SPDF.utils.ProcessExecutor; import stirling.software.SPDF.utils.ProcessExecutor.ProcessExecutorResult; import stirling.software.SPDF.utils.WebResponseUtils; @@ -51,307 +61,373 @@ import stirling.software.SPDF.utils.WebResponseUtils; @Tag(name = "Misc", description = "Miscellaneous APIs") public class CompressController { - private final CustomPDDocumentFactory pdfDocumentFactory; + private final CustomPDFDocumentFactory pdfDocumentFactory; @Autowired - public CompressController(CustomPDDocumentFactory pdfDocumentFactory) { + public CompressController(CustomPDFDocumentFactory pdfDocumentFactory) { this.pdfDocumentFactory = pdfDocumentFactory; } - private void compressImagesInPDF(Path pdfFile, double scaleFactor, float jpegQuality) + @Data + @AllArgsConstructor + @NoArgsConstructor + private static class ImageReference { + int pageNum; // Page number where the image appears + COSName name; // The name used to reference this image + } + + public Path compressImagesInPDF( + Path pdfFile, double scaleFactor, float jpegQuality, boolean convertToGrayscale) throws Exception { - byte[] fileBytes = Files.readAllBytes(pdfFile); - long originalFileSize = fileBytes.length; + Path newCompressedPDF = Files.createTempFile("compressedPDF", ".pdf"); + long originalFileSize = Files.size(pdfFile); log.info( - "Starting image compression with scale factor: {} and JPEG quality: {} on file size: {}", + "Starting image compression with scale factor: {}, JPEG quality: {}, grayscale: {} on file size: {}", scaleFactor, jpegQuality, + convertToGrayscale, GeneralUtils.formatBytes(originalFileSize)); - // Track processed images to avoid recompression - Set processedImages = new HashSet<>(); + try (PDDocument doc = pdfDocumentFactory.load(pdfFile)) { + + // Collect all unique images by content hash + Map> uniqueImages = new HashMap<>(); + Map compressedVersions = new HashMap<>(); - try (PDDocument doc = pdfDocumentFactory.load(fileBytes)) { int totalImages = 0; + + for (int pageNum = 0; pageNum < doc.getNumberOfPages(); pageNum++) { + PDPage page = doc.getPage(pageNum); + PDResources res = page.getResources(); + if (res == null || res.getXObjectNames() == null) continue; + + for (COSName name : res.getXObjectNames()) { + PDXObject xobj = res.getXObject(name); + if (!(xobj instanceof PDImageXObject)) continue; + + totalImages++; + PDImageXObject image = (PDImageXObject) xobj; + String imageHash = generateImageHash(image); + + // Store only page number and name reference + ImageReference ref = new ImageReference(); + ref.pageNum = pageNum; + ref.name = name; + + uniqueImages.computeIfAbsent(imageHash, k -> new ArrayList<>()).add(ref); + } + } + + int uniqueImagesCount = uniqueImages.size(); + int duplicatedImages = totalImages - uniqueImagesCount; + log.info( + "Found {} unique images and {} duplicated instances across {} pages", + uniqueImagesCount, + duplicatedImages, + doc.getNumberOfPages()); + + // SECOND PASS: Process each unique image exactly once int compressedImages = 0; int skippedImages = 0; long totalOriginalBytes = 0; long totalCompressedBytes = 0; - // Minimum dimensions to preserve reasonable quality - int MIN_WIDTH = 400; // Higher minimum - int MIN_HEIGHT = 400; // Higher minimum + for (Entry> entry : uniqueImages.entrySet()) { + String imageHash = entry.getKey(); + List references = entry.getValue(); - log.info("PDF has {} pages", doc.getNumberOfPages()); + if (references.isEmpty()) continue; - for (int pageNum = 0; pageNum < doc.getNumberOfPages(); pageNum++) { - PDPage page = doc.getPage(pageNum); - PDResources res = page.getResources(); + // Get the first instance of this image + ImageReference firstRef = references.get(0); + PDPage firstPage = doc.getPage(firstRef.pageNum); + PDResources firstPageResources = firstPage.getResources(); + PDImageXObject originalImage = + (PDImageXObject) firstPageResources.getXObject(firstRef.name); - if (res == null || res.getXObjectNames() == null) { - continue; - } + // Track original size + int originalSize = (int) originalImage.getCOSObject().getLength(); + totalOriginalBytes += originalSize; - int pageImages = 0; + // Process this unique image once + BufferedImage processedImage = + processAndCompressImage( + originalImage, scaleFactor, jpegQuality, convertToGrayscale); - for (COSName name : res.getXObjectNames()) { - String imageName = name.getName(); + if (processedImage != null) { + // Convert to bytes for storage + byte[] compressedData = convertToBytes(processedImage, jpegQuality); - // Skip already processed images - if (processedImages.contains(imageName)) { - skippedImages++; - continue; - } + // Check if compression is beneficial + if (compressedData.length < originalSize || convertToGrayscale) { + // Create a single compressed version + PDImageXObject compressedImage = + PDImageXObject.createFromByteArray( + doc, + compressedData, + originalImage.getCOSObject().toString()); - PDXObject xobj = res.getXObject(name); - if (!(xobj instanceof PDImageXObject)) { - continue; - } + // Store the compressed version only once in our map + compressedVersions.put(imageHash, compressedImage); - totalImages++; - pageImages++; - PDImageXObject image = (PDImageXObject) xobj; - BufferedImage bufferedImage = image.getImage(); - - int originalWidth = bufferedImage.getWidth(); - int originalHeight = bufferedImage.getHeight(); - - log.info( - "Page {}, Image {}: Original dimensions: {}x{}", - pageNum + 1, - imageName, - originalWidth, - originalHeight); - - // Skip if already small enough - if (originalWidth <= MIN_WIDTH || originalHeight <= MIN_HEIGHT) { + // Report compression stats + double reductionPercentage = + 100.0 - ((compressedData.length * 100.0) / originalSize); log.info( - "Page {}, Image {}: Skipping - below minimum dimensions threshold", - pageNum + 1, - imageName); - skippedImages++; - processedImages.add(imageName); - continue; - } + "Image hash {}: Compressed from {} to {} (reduced by {}%)", + imageHash, + GeneralUtils.formatBytes(originalSize), + GeneralUtils.formatBytes(compressedData.length), + String.format("%.1f", reductionPercentage)); - // Adjust scale factor for very large or very small images - double adjustedScaleFactor = scaleFactor; - if (originalWidth > 3000 || originalHeight > 3000) { - // More aggressive for very large images - adjustedScaleFactor = Math.min(scaleFactor, 0.75); - log.info( - "Page {}, Image {}: Very large image, using more aggressive scale: {}", - pageNum + 1, - imageName, - adjustedScaleFactor); - } else if (originalWidth < 1000 || originalHeight < 1000) { - // More conservative for smaller images - adjustedScaleFactor = Math.max(scaleFactor, 0.9); - log.info( - "Page {}, Image {}: Smaller image, using conservative scale: {}", - pageNum + 1, - imageName, - adjustedScaleFactor); - } + // Replace ALL instances with the compressed version + for (ImageReference ref : references) { + // Get the page and resources when needed + PDPage page = doc.getPage(ref.pageNum); + PDResources resources = page.getResources(); + resources.put(ref.name, compressedImage); - int newWidth = (int) (originalWidth * adjustedScaleFactor); - int newHeight = (int) (originalHeight * adjustedScaleFactor); - - // Ensure minimum dimensions - newWidth = Math.max(newWidth, MIN_WIDTH); - newHeight = Math.max(newHeight, MIN_HEIGHT); - - // Skip if change is negligible - if ((double) newWidth / originalWidth > 0.95 - && (double) newHeight / originalHeight > 0.95) { - log.info( - "Page {}, Image {}: Change too small, skipping compression", - pageNum + 1, - imageName); - skippedImages++; - processedImages.add(imageName); - continue; - } - - log.info( - "Page {}, Image {}: Resizing to {}x{} ({}% of original)", - pageNum + 1, - imageName, - newWidth, - newHeight, - Math.round((newWidth * 100.0) / originalWidth)); - - // Use high quality scaling - BufferedImage scaledImage = - new BufferedImage( - newWidth, - newHeight, - bufferedImage.getColorModel().hasAlpha() - ? BufferedImage.TYPE_INT_ARGB - : BufferedImage.TYPE_INT_RGB); - - Graphics2D g2d = scaledImage.createGraphics(); - g2d.setRenderingHint( - RenderingHints.KEY_INTERPOLATION, - RenderingHints.VALUE_INTERPOLATION_BICUBIC); - g2d.setRenderingHint( - RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); - g2d.setRenderingHint( - RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - g2d.drawImage(bufferedImage, 0, 0, newWidth, newHeight, null); - g2d.dispose(); - - // Choose appropriate format and compression - String format = bufferedImage.getColorModel().hasAlpha() ? "png" : "jpeg"; - - // First get the actual size of the original image by encoding it to the chosen - // format - ByteArrayOutputStream originalImageStream = new ByteArrayOutputStream(); - if (format.equals("jpeg")) { - // Get the best available JPEG writer (prioritizes TwelveMonkeys if - // available) - Iterator writers = ImageIO.getImageWritersByFormatName("jpeg"); - ImageWriter writer = null; - - // Prefer TwelveMonkeys writer if available - while (writers.hasNext()) { - ImageWriter candidate = writers.next(); - if (candidate.getClass().getName().contains("twelvemonkeys")) { - writer = candidate; - break; - } - } - if (writer == null) { - writer = ImageIO.getImageWritersByFormatName("jpeg").next(); + log.info( + "Replaced image on page {} with compressed version", + ref.pageNum + 1); } - JPEGImageWriteParam param = - (JPEGImageWriteParam) writer.getDefaultWriteParam(); - - // Set advanced compression parameters - param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); - param.setCompressionQuality(jpegQuality); - param.setOptimizeHuffmanTables(true); // Better compression - param.setProgressiveMode( - ImageWriteParam.MODE_DEFAULT); // Progressive scanning - - // Write compressed image - try (ImageOutputStream ios = - ImageIO.createImageOutputStream(originalImageStream)) { - writer.setOutput(ios); - writer.write(null, new IIOImage(scaledImage, null, null), param); - } - writer.dispose(); + totalCompressedBytes += compressedData.length * references.size(); + compressedImages++; } else { - ImageIO.write(bufferedImage, format, originalImageStream); - } - int originalEncodedSize = (int) image.getCOSObject().getLength(); - originalImageStream.close(); - - // Now compress the scaled image - ByteArrayOutputStream compressedImageStream = new ByteArrayOutputStream(); - if (format.equals("jpeg")) { - Iterator writers = ImageIO.getImageWritersByFormatName(format); - if (writers.hasNext()) { - ImageWriter writer = writers.next(); - ImageWriteParam param = writer.getDefaultWriteParam(); - - if (param.canWriteCompressed()) { - param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); - param.setCompressionQuality(jpegQuality); - - ImageOutputStream imageOut = - ImageIO.createImageOutputStream(compressedImageStream); - writer.setOutput(imageOut); - writer.write(null, new IIOImage(scaledImage, null, null), param); - writer.dispose(); - imageOut.close(); - } else { - ImageIO.write(scaledImage, format, compressedImageStream); - } - } else { - ImageIO.write(scaledImage, format, compressedImageStream); - } - } else { - ImageIO.write(scaledImage, format, compressedImageStream); - } - byte[] imageBytes = compressedImageStream.toByteArray(); - compressedImageStream.close(); - - // Format sizes using our utility method - String originalSizeStr = GeneralUtils.formatBytes(originalEncodedSize); - String compressedSizeStr = GeneralUtils.formatBytes(imageBytes.length); - - // Calculate reduction percentage (how much smaller the new file is) - double reductionPercentage = - 100.0 - ((imageBytes.length * 100.0) / originalEncodedSize); - - if (imageBytes.length >= originalEncodedSize) { - log.info( - "Page {}, Image {}: Compressed size {} not smaller than original {}, skipping replacement", - pageNum + 1, - imageName, - GeneralUtils.formatBytes(imageBytes.length), - GeneralUtils.formatBytes(originalEncodedSize)); - - // Accumulate original size for both counters (no change) - totalOriginalBytes += originalEncodedSize; - totalCompressedBytes += originalEncodedSize; + log.info("Image hash {}: Compression not beneficial, skipping", imageHash); + totalCompressedBytes += originalSize * references.size(); skippedImages++; - processedImages.add(imageName); - continue; } - log.info( - "Page {}, Image {}: Compressed from {} to {} (reduced by {}%)", - pageNum + 1, - imageName, - originalSizeStr, - compressedSizeStr, - String.format("%.1f", reductionPercentage)); - - // Only replace if compressed size is smaller - PDImageXObject compressedImage = - PDImageXObject.createFromByteArray( - doc, imageBytes, image.getCOSObject().toString()); - res.put(name, compressedImage); - - // Update counters with compressed size - totalOriginalBytes += originalEncodedSize; - totalCompressedBytes += imageBytes.length; - compressedImages++; - processedImages.add(imageName); + } else { + log.info("Image hash {}: Not suitable for compression, skipping", imageHash); + totalCompressedBytes += originalSize * references.size(); + skippedImages++; } } - // Log overall image compression statistics + // Log compression statistics double overallImageReduction = totalOriginalBytes > 0 ? 100.0 - ((totalCompressedBytes * 100.0) / totalOriginalBytes) : 0; log.info( - "Image compression summary - Total: {}, Compressed: {}, Skipped: {}", - totalImages, + "Image compression summary - Total unique: {}, Compressed: {}, Skipped: {}, Duplicates: {}", + uniqueImagesCount, compressedImages, - skippedImages); + skippedImages, + duplicatedImages); log.info( - "Total original image size: {}, compressed: {} (reduced by {:.1f}%)", + "Total original image size: {}, compressed: {} (reduced by {}%)", GeneralUtils.formatBytes(totalOriginalBytes), GeneralUtils.formatBytes(totalCompressedBytes), - overallImageReduction); + String.format("%.1f", overallImageReduction)); + + // Free memory before saving + compressedVersions.clear(); + uniqueImages.clear(); // Save the document - log.info("Saving compressed PDF to {}", pdfFile.toString()); - doc.save(pdfFile.toString()); + log.info("Saving compressed PDF to {}", newCompressedPDF.toString()); + doc.save(newCompressedPDF.toString()); // Log overall file size reduction - long compressedFileSize = Files.size(pdfFile); + long compressedFileSize = Files.size(newCompressedPDF); double overallReduction = 100.0 - ((compressedFileSize * 100.0) / originalFileSize); log.info( - "Overall PDF compression: {} → {} (reduced by {:.1f}%)", + "Overall PDF compression: {} → {} (reduced by {}%)", GeneralUtils.formatBytes(originalFileSize), GeneralUtils.formatBytes(compressedFileSize), - overallReduction); + String.format("%.1f", overallReduction)); + return newCompressedPDF; + } + + } + + private BufferedImage convertToGrayscale(BufferedImage image) { + BufferedImage grayImage = + new BufferedImage( + image.getWidth(), image.getHeight(), BufferedImage.TYPE_BYTE_GRAY); + + Graphics2D g = grayImage.createGraphics(); + g.drawImage(image, 0, 0, null); + g.dispose(); + + return grayImage; + } + + /** + * Processes and compresses an image if beneficial. Returns the processed image if compression + * is worthwhile, null otherwise. + */ + private BufferedImage processAndCompressImage( + PDImageXObject image, double scaleFactor, float jpegQuality, boolean convertToGrayscale) + throws IOException { + BufferedImage bufferedImage = image.getImage(); + int originalWidth = bufferedImage.getWidth(); + int originalHeight = bufferedImage.getHeight(); + + // Minimum dimensions to preserve reasonable quality + int MIN_WIDTH = 400; + int MIN_HEIGHT = 400; + + log.info("Original dimensions: {}x{}", originalWidth, originalHeight); + + // Skip if already small enough + if ((originalWidth <= MIN_WIDTH || originalHeight <= MIN_HEIGHT) && !convertToGrayscale) { + log.info("Skipping - below minimum dimensions threshold"); + return null; + } + + // Convert to grayscale first if requested (before resizing for better quality) + if (convertToGrayscale) { + bufferedImage = convertToGrayscale(bufferedImage); + log.info("Converted image to grayscale"); + } + + // Adjust scale factor for very large or very small images + double adjustedScaleFactor = scaleFactor; + if (originalWidth > 3000 || originalHeight > 3000) { + // More aggressive for very large images + adjustedScaleFactor = Math.min(scaleFactor, 0.75); + log.info("Very large image, using more aggressive scale: {}", adjustedScaleFactor); + } else if (originalWidth < 1000 || originalHeight < 1000) { + // More conservative for smaller images + adjustedScaleFactor = Math.max(scaleFactor, 0.9); + log.info("Smaller image, using conservative scale: {}", adjustedScaleFactor); + } + + int newWidth = (int) (originalWidth * adjustedScaleFactor); + int newHeight = (int) (originalHeight * adjustedScaleFactor); + + // Ensure minimum dimensions + newWidth = Math.max(newWidth, MIN_WIDTH); + newHeight = Math.max(newHeight, MIN_HEIGHT); + + // Skip if change is negligible + if ((double) newWidth / originalWidth > 0.95 + && (double) newHeight / originalHeight > 0.95 + && !convertToGrayscale) { + log.info("Change too small, skipping compression"); + return null; + } + + log.info( + "Resizing to {}x{} ({}% of original)", + newWidth, newHeight, Math.round((newWidth * 100.0) / originalWidth)); + + BufferedImage scaledImage; + if (convertToGrayscale) { + // If already grayscale, maintain the grayscale format + scaledImage = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_BYTE_GRAY); + } else { + // Otherwise use original color model + scaledImage = + new BufferedImage( + newWidth, + newHeight, + bufferedImage.getColorModel().hasAlpha() + ? BufferedImage.TYPE_INT_ARGB + : BufferedImage.TYPE_INT_RGB); + } + Graphics2D g2d = scaledImage.createGraphics(); + g2d.setRenderingHint( + RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC); + g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + g2d.drawImage(bufferedImage, 0, 0, newWidth, newHeight, null); + g2d.dispose(); + + return scaledImage; + } + + /** + * Converts a BufferedImage to a byte array with specified JPEG quality. Checks if compression + * is beneficial compared to original. + */ + private byte[] convertToBytes(BufferedImage scaledImage, float jpegQuality) throws IOException { + String format = scaledImage.getColorModel().hasAlpha() ? "png" : "jpeg"; + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + + if ("jpeg".equals(format)) { + // Get the best available JPEG writer + Iterator writers = ImageIO.getImageWritersByFormatName("jpeg"); + ImageWriter writer = writers.next(); + + JPEGImageWriteParam param = (JPEGImageWriteParam) writer.getDefaultWriteParam(); + + // Set compression parameters + param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); + param.setCompressionQuality(jpegQuality); + param.setOptimizeHuffmanTables(true); // Better compression + param.setProgressiveMode(ImageWriteParam.MODE_DEFAULT); // Progressive scanning + + // Write compressed image + try (ImageOutputStream ios = ImageIO.createImageOutputStream(outputStream)) { + writer.setOutput(ios); + writer.write(null, new IIOImage(scaledImage, null, null), param); + } + writer.dispose(); + } else { + ImageIO.write(scaledImage, format, outputStream); + } + + return outputStream.toByteArray(); + } + + /** Modified hash function to consistently identify identical image content */ + private String generateImageHash(PDImageXObject image) { + try { + // Create a stream for the raw stream data + try (InputStream stream = image.getCOSObject().createRawInputStream()) { + // Read up to first 8KB of data for the hash + byte[] buffer = new byte[8192]; + int bytesRead = stream.read(buffer); + if (bytesRead > 0) { + byte[] dataToHash = + bytesRead == buffer.length ? buffer : Arrays.copyOf(buffer, bytesRead); + return bytesToHexString(generatMD5(dataToHash)); + } + return "empty-stream"; + } + } catch (Exception e) { + log.error("Error generating image hash", e); + return "fallback-" + System.identityHashCode(image); + } + } + + private String bytesToHexString(byte[] bytes) { + StringBuilder sb = new StringBuilder(); + for (byte b : bytes) { + sb.append(String.format("%02x", b)); + } + return sb.toString(); + } + + private byte[] generatMD5(byte[] data) throws IOException { + try { + MessageDigest md = MessageDigest.getInstance("MD5"); + return md.digest(data); // Get the MD5 hash of the image bytes + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException("MD5 algorithm not available", e); + } + } + + private byte[] generateImageMD5(PDImageXObject image) throws IOException { + return generatMD5(ImageProcessingUtils.getImageData(image.getImage())); + } + + /** Generates a hash string from a byte array */ + private String generateHashFromBytes(byte[] data) { + try { + // Use the existing method to generate MD5 hash + byte[] hash = generatMD5(data); + return bytesToHexString(hash); + } catch (Exception e) { + log.error("Error generating hash from bytes", e); + // Return a unique string as fallback + return "fallback-" + System.identityHashCode(data); } } @@ -382,13 +458,14 @@ public class CompressController { @Operation( summary = "Optimize PDF file", description = - "This endpoint accepts a PDF file and optimizes it based on the provided parameters. Input:PDF Output:PDF Type:SISO") + "This endpoint accepts a PDF file and optimizes it based on the provided" + + " parameters. Input:PDF Output:PDF Type:SISO") public ResponseEntity optimizePdf(@ModelAttribute OptimizePdfRequest request) throws Exception { MultipartFile inputFile = request.getFileInput(); Integer optimizeLevel = request.getOptimizeLevel(); String expectedOutputSizeString = request.getExpectedOutputSize(); - + Boolean convertToGrayscale = request.getGrayscale(); if (expectedOutputSizeString == null && optimizeLevel == null) { throw new Exception("Both expected output size and optimize level are not specified"); } @@ -400,48 +477,61 @@ public class CompressController { autoMode = true; } - Path tempInputFile = Files.createTempFile("input_", ".pdf"); - inputFile.transferTo(tempInputFile.toFile()); - - long inputFileSize = Files.size(tempInputFile); - - Path tempOutputFile = null; - byte[] pdfBytes; + // Create initial input file + Path originalFile = Files.createTempFile("input_", ".pdf"); + inputFile.transferTo(originalFile.toFile()); + long inputFileSize = Files.size(originalFile); + + // Start with original as current working file + Path currentFile = originalFile; + + // Keep track of all temporary files for cleanup + List tempFiles = new ArrayList<>(); + tempFiles.add(originalFile); + try { - tempOutputFile = Files.createTempFile("output_", ".pdf"); - if (autoMode) { double sizeReductionRatio = expectedOutputSize / (double) inputFileSize; optimizeLevel = determineOptimizeLevel(sizeReductionRatio); } boolean sizeMet = false; - boolean imageCompressionApplied = false; // Track if we've already compressed images + boolean imageCompressionApplied = false; boolean qpdfCompressionApplied = false; while (!sizeMet && optimizeLevel <= 9) { - // Apply appropriate compression based on level - - // Levels 4-9: Apply image compression - if (optimizeLevel >= 4 && !imageCompressionApplied) { + // Apply image compression for levels 4-9 + if ((optimizeLevel >= 4 || Boolean.TRUE.equals(convertToGrayscale)) + && !imageCompressionApplied) { double scaleFactor = getScaleFactorForLevel(optimizeLevel); float jpegQuality = getJpegQualityForLevel(optimizeLevel); - compressImagesInPDF(tempInputFile, scaleFactor, jpegQuality); - imageCompressionApplied = true; // Mark that we've compressed images + + // Use the returned path from compressImagesInPDF + Path compressedImageFile = compressImagesInPDF( + currentFile, + scaleFactor, + jpegQuality, + Boolean.TRUE.equals(convertToGrayscale)); + + // Add to temp files list and update current file + tempFiles.add(compressedImageFile); + currentFile = compressedImageFile; + imageCompressionApplied = true; } - // All levels (1-9): Apply QPDF compression + // Apply QPDF compression for all levels if (!qpdfCompressionApplied) { - long preQpdfSize = Files.size(tempInputFile); + long preQpdfSize = Files.size(currentFile); log.info("Pre-QPDF file size: {}", GeneralUtils.formatBytes(preQpdfSize)); - // For levels 1-3, map to qpdf compression levels 1-9 - int qpdfCompressionLevel = optimizeLevel; - if (optimizeLevel <= 3) { - qpdfCompressionLevel = optimizeLevel * 3; // Level 1->3, 2->6, 3->9 - } else { - qpdfCompressionLevel = 9; // Max QPDF compression for levels 4-9 - } + // Map optimization levels to QPDF compression levels + int qpdfCompressionLevel = optimizeLevel <= 3 + ? optimizeLevel * 3 // Level 1->3, 2->6, 3->9 + : 9; // Max compression for levels 4-9 + + // Create output file for QPDF + Path qpdfOutputFile = Files.createTempFile("qpdf_output_", ".pdf"); + tempFiles.add(qpdfOutputFile); // Run QPDF optimization List command = new ArrayList<>(); @@ -456,48 +546,50 @@ public class CompressController { command.add("--compression-level=" + qpdfCompressionLevel); command.add("--compress-streams=y"); command.add("--object-streams=generate"); - command.add(tempInputFile.toString()); - command.add(tempOutputFile.toString()); + command.add(currentFile.toString()); + command.add(qpdfOutputFile.toString()); ProcessExecutorResult returnCode = null; try { - returnCode = - ProcessExecutor.getInstance(ProcessExecutor.Processes.QPDF) - .runCommandWithOutputHandling(command); + returnCode = ProcessExecutor.getInstance(ProcessExecutor.Processes.QPDF) + .runCommandWithOutputHandling(command); qpdfCompressionApplied = true; + + // Update current file to the QPDF output + currentFile = qpdfOutputFile; + + long postQpdfSize = Files.size(currentFile); + double qpdfReduction = 100.0 - ((postQpdfSize * 100.0) / preQpdfSize); + log.info( + "Post-QPDF file size: {} (reduced by {}%)", + GeneralUtils.formatBytes(postQpdfSize), + String.format("%.1f", qpdfReduction)); + } catch (Exception e) { if (returnCode != null && returnCode.getRc() != 3) { throw e; } + // If QPDF fails, keep using the current file + log.warn("QPDF compression failed, continuing with current file"); } - long postQpdfSize = Files.size(tempOutputFile); - double qpdfReduction = 100.0 - ((postQpdfSize * 100.0) / preQpdfSize); - log.info( - "Post-QPDF file size: {} (reduced by {:.1f}%)", - GeneralUtils.formatBytes(postQpdfSize), qpdfReduction); - - } else { - tempOutputFile = tempInputFile; } // Check if file size is within expected size or not auto mode - long outputFileSize = Files.size(tempOutputFile); + long outputFileSize = Files.size(currentFile); if (outputFileSize <= expectedOutputSize || !autoMode) { sizeMet = true; } else { - int newOptimizeLevel = - incrementOptimizeLevel( - optimizeLevel, outputFileSize, expectedOutputSize); + int newOptimizeLevel = incrementOptimizeLevel( + optimizeLevel, outputFileSize, expectedOutputSize); // Check if we can't increase the level further if (newOptimizeLevel == optimizeLevel) { if (autoMode) { - log.info( - "Maximum optimization level reached without meeting target size."); + log.info("Maximum optimization level reached without meeting target size."); sizeMet = true; } } else { - // Reset image compression if moving to a new level + // Reset flags for next iteration with higher optimization level imageCompressionApplied = false; qpdfCompressionApplied = false; optimizeLevel = newOptimizeLevel; @@ -505,26 +597,30 @@ public class CompressController { } } - // Read the optimized PDF file - pdfBytes = Files.readAllBytes(tempOutputFile); - Path finalFile = tempOutputFile; - // Check if optimized file is larger than the original - if (pdfBytes.length > inputFileSize) { - log.warn( - "Optimized file is larger than the original. Returning the original file instead."); - finalFile = tempInputFile; + long finalFileSize = Files.size(currentFile); + if (finalFileSize > inputFileSize) { + log.warn("Optimized file is larger than the original. Using the original file instead."); + // Use the stored reference to the original file + currentFile = originalFile; } - String outputFilename = - Filenames.toSimpleFileName(inputFile.getOriginalFilename()) + String outputFilename = Filenames.toSimpleFileName(inputFile.getOriginalFilename()) .replaceFirst("[.][^.]+$", "") + "_Optimized.pdf"; + return WebResponseUtils.pdfDocToWebResponse( - pdfDocumentFactory.load(finalFile.toFile()), outputFilename); + pdfDocumentFactory.load(currentFile.toFile()), outputFilename); } finally { - Files.deleteIfExists(tempOutputFile); + // Clean up all temporary files + for (Path tempFile : tempFiles) { + try { + Files.deleteIfExists(tempFile); + } catch (IOException e) { + log.warn("Failed to delete temporary file: " + tempFile, e); + } + } } } diff --git a/src/main/java/stirling/software/SPDF/controller/api/misc/DecompressPdfController.java b/src/main/java/stirling/software/SPDF/controller/api/misc/DecompressPdfController.java index 45767fc82..090b2b2e1 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/misc/DecompressPdfController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/misc/DecompressPdfController.java @@ -25,7 +25,7 @@ import io.swagger.v3.oas.annotations.tags.Tag; import lombok.extern.slf4j.Slf4j; import stirling.software.SPDF.model.api.PDFFile; -import stirling.software.SPDF.service.CustomPDDocumentFactory; +import stirling.software.SPDF.service.CustomPDFDocumentFactory; import stirling.software.SPDF.utils.WebResponseUtils; @RestController @@ -34,10 +34,10 @@ import stirling.software.SPDF.utils.WebResponseUtils; @Tag(name = "Misc", description = "Miscellaneous APIs") public class DecompressPdfController { - private final CustomPDDocumentFactory pdfDocumentFactory; + private final CustomPDFDocumentFactory pdfDocumentFactory; @Autowired - public DecompressPdfController(CustomPDDocumentFactory pdfDocumentFactory) { + public DecompressPdfController(CustomPDFDocumentFactory pdfDocumentFactory) { this.pdfDocumentFactory = pdfDocumentFactory; } diff --git a/src/main/java/stirling/software/SPDF/controller/api/misc/ExtractImageScansController.java b/src/main/java/stirling/software/SPDF/controller/api/misc/ExtractImageScansController.java index 7c4d9137f..fb27bf75a 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/misc/ExtractImageScansController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/misc/ExtractImageScansController.java @@ -32,7 +32,7 @@ import io.swagger.v3.oas.annotations.tags.Tag; import lombok.extern.slf4j.Slf4j; import stirling.software.SPDF.model.api.misc.ExtractImageScansRequest; -import stirling.software.SPDF.service.CustomPDDocumentFactory; +import stirling.software.SPDF.service.CustomPDFDocumentFactory; import stirling.software.SPDF.utils.CheckProgramInstall; import stirling.software.SPDF.utils.ProcessExecutor; import stirling.software.SPDF.utils.ProcessExecutor.ProcessExecutorResult; @@ -46,10 +46,10 @@ public class ExtractImageScansController { private static final String REPLACEFIRST = "[.][^.]+$"; - private final CustomPDDocumentFactory pdfDocumentFactory; + private final CustomPDFDocumentFactory pdfDocumentFactory; @Autowired - public ExtractImageScansController(CustomPDDocumentFactory pdfDocumentFactory) { + public ExtractImageScansController(CustomPDFDocumentFactory pdfDocumentFactory) { this.pdfDocumentFactory = pdfDocumentFactory; } @@ -57,7 +57,10 @@ public class ExtractImageScansController { @Operation( summary = "Extract image scans from an input file", description = - "This endpoint extracts image scans from a given file based on certain parameters. Users can specify angle threshold, tolerance, minimum area, minimum contour area, and border size. Input:PDF Output:IMAGE/ZIP Type:SIMO") + "This endpoint extracts image scans from a given file based on certain" + + " parameters. Users can specify angle threshold, tolerance, minimum area," + + " minimum contour area, and border size. Input:PDF Output:IMAGE/ZIP" + + " Type:SIMO") public ResponseEntity extractImageScans( @RequestBody( description = "Form data containing file and extraction parameters", diff --git a/src/main/java/stirling/software/SPDF/controller/api/misc/ExtractImagesController.java b/src/main/java/stirling/software/SPDF/controller/api/misc/ExtractImagesController.java index 3010c1be6..af865f8f4 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/misc/ExtractImagesController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/misc/ExtractImagesController.java @@ -40,7 +40,7 @@ import io.swagger.v3.oas.annotations.tags.Tag; import lombok.extern.slf4j.Slf4j; import stirling.software.SPDF.model.api.PDFExtractImagesRequest; -import stirling.software.SPDF.service.CustomPDDocumentFactory; +import stirling.software.SPDF.service.CustomPDFDocumentFactory; import stirling.software.SPDF.utils.ImageProcessingUtils; import stirling.software.SPDF.utils.WebResponseUtils; @@ -50,10 +50,10 @@ import stirling.software.SPDF.utils.WebResponseUtils; @Tag(name = "Misc", description = "Miscellaneous APIs") public class ExtractImagesController { - private final CustomPDDocumentFactory pdfDocumentFactory; + private final CustomPDFDocumentFactory pdfDocumentFactory; @Autowired - public ExtractImagesController(CustomPDDocumentFactory pdfDocumentFactory) { + public ExtractImagesController(CustomPDFDocumentFactory pdfDocumentFactory) { this.pdfDocumentFactory = pdfDocumentFactory; } @@ -61,7 +61,9 @@ public class ExtractImagesController { @Operation( summary = "Extract images from a PDF file", description = - "This endpoint extracts images from a given PDF file and returns them in a zip file. Users can specify the output image format. Input:PDF Output:IMAGE/ZIP Type:SIMO") + "This endpoint extracts images from a given PDF file and returns them in a zip" + + " file. Users can specify the output image format. Input:PDF" + + " Output:IMAGE/ZIP Type:SIMO") public ResponseEntity extractImages(@ModelAttribute PDFExtractImagesRequest request) throws IOException, InterruptedException, ExecutionException { MultipartFile file = request.getFileInput(); diff --git a/src/main/java/stirling/software/SPDF/controller/api/misc/FlattenController.java b/src/main/java/stirling/software/SPDF/controller/api/misc/FlattenController.java index 39991a1f6..8ddfa1657 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/misc/FlattenController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/misc/FlattenController.java @@ -26,7 +26,7 @@ import io.swagger.v3.oas.annotations.tags.Tag; import lombok.extern.slf4j.Slf4j; import stirling.software.SPDF.model.api.misc.FlattenRequest; -import stirling.software.SPDF.service.CustomPDDocumentFactory; +import stirling.software.SPDF.service.CustomPDFDocumentFactory; import stirling.software.SPDF.utils.WebResponseUtils; @RestController @@ -35,10 +35,10 @@ import stirling.software.SPDF.utils.WebResponseUtils; @Tag(name = "Misc", description = "Miscellaneous APIs") public class FlattenController { - private final CustomPDDocumentFactory pdfDocumentFactory; + private final CustomPDFDocumentFactory pdfDocumentFactory; @Autowired - public FlattenController(CustomPDDocumentFactory pdfDocumentFactory) { + public FlattenController(CustomPDFDocumentFactory pdfDocumentFactory) { this.pdfDocumentFactory = pdfDocumentFactory; } @@ -46,7 +46,8 @@ public class FlattenController { @Operation( summary = "Flatten PDF form fields or full page", description = - "Flattening just PDF form fields or converting each page to images to make text unselectable. Input:PDF, Output:PDF. Type:SISO") + "Flattening just PDF form fields or converting each page to images to make text" + + " unselectable. Input:PDF, Output:PDF. Type:SISO") public ResponseEntity flatten(@ModelAttribute FlattenRequest request) throws Exception { MultipartFile file = request.getFileInput(); diff --git a/src/main/java/stirling/software/SPDF/controller/api/misc/MetadataController.java b/src/main/java/stirling/software/SPDF/controller/api/misc/MetadataController.java index 66c1c8b66..4bca4988a 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/misc/MetadataController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/misc/MetadataController.java @@ -23,7 +23,7 @@ import io.swagger.v3.oas.annotations.tags.Tag; import lombok.extern.slf4j.Slf4j; import stirling.software.SPDF.model.api.misc.MetadataRequest; -import stirling.software.SPDF.service.CustomPDDocumentFactory; +import stirling.software.SPDF.service.CustomPDFDocumentFactory; import stirling.software.SPDF.utils.WebResponseUtils; import stirling.software.SPDF.utils.propertyeditor.StringToMapPropertyEditor; @@ -33,10 +33,10 @@ import stirling.software.SPDF.utils.propertyeditor.StringToMapPropertyEditor; @Tag(name = "Misc", description = "Miscellaneous APIs") public class MetadataController { - private final CustomPDDocumentFactory pdfDocumentFactory; + private final CustomPDFDocumentFactory pdfDocumentFactory; @Autowired - public MetadataController(CustomPDDocumentFactory pdfDocumentFactory) { + public MetadataController(CustomPDFDocumentFactory pdfDocumentFactory) { this.pdfDocumentFactory = pdfDocumentFactory; } @@ -59,7 +59,9 @@ public class MetadataController { @Operation( summary = "Update metadata of a PDF file", description = - "This endpoint allows you to update the metadata of a given PDF file. You can add, modify, or delete standard and custom metadata fields. Input:PDF Output:PDF Type:SISO") + "This endpoint allows you to update the metadata of a given PDF file. You can" + + " add, modify, or delete standard and custom metadata fields. Input:PDF" + + " Output:PDF Type:SISO") public ResponseEntity metadata(@ModelAttribute MetadataRequest request) throws IOException { diff --git a/src/main/java/stirling/software/SPDF/controller/api/misc/OCRController.java b/src/main/java/stirling/software/SPDF/controller/api/misc/OCRController.java index 44fbb65b5..47d6d5b35 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/misc/OCRController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/misc/OCRController.java @@ -33,7 +33,7 @@ import lombok.extern.slf4j.Slf4j; import stirling.software.SPDF.model.ApplicationProperties; import stirling.software.SPDF.model.api.misc.ProcessPdfWithOcrRequest; -import stirling.software.SPDF.service.CustomPDDocumentFactory; +import stirling.software.SPDF.service.CustomPDFDocumentFactory; @RestController @RequestMapping("/api/v1/misc") @@ -43,11 +43,11 @@ public class OCRController { private final ApplicationProperties applicationProperties; - private final CustomPDDocumentFactory pdfDocumentFactory; + private final CustomPDFDocumentFactory pdfDocumentFactory; public OCRController( ApplicationProperties applicationProperties, - CustomPDDocumentFactory pdfDocumentFactory) { + CustomPDFDocumentFactory pdfDocumentFactory) { this.applicationProperties = applicationProperties; this.pdfDocumentFactory = pdfDocumentFactory; } @@ -70,7 +70,9 @@ public class OCRController { @Operation( summary = "Process PDF files with OCR using Tesseract", description = - "Takes a PDF file as input, performs OCR using specified languages and OCR type (skip-text/force-ocr), and returns the processed PDF. Input:PDF Output:PDF Type:SISO") + "Takes a PDF file as input, performs OCR using specified languages and OCR type" + + " (skip-text/force-ocr), and returns the processed PDF. Input:PDF" + + " Output:PDF Type:SISO") public ResponseEntity processPdfWithOCR( @ModelAttribute ProcessPdfWithOcrRequest request) throws IOException, InterruptedException { diff --git a/src/main/java/stirling/software/SPDF/controller/api/misc/OverlayImageController.java b/src/main/java/stirling/software/SPDF/controller/api/misc/OverlayImageController.java index 2814149e8..a953586fb 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/misc/OverlayImageController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/misc/OverlayImageController.java @@ -18,7 +18,7 @@ import io.swagger.v3.oas.annotations.tags.Tag; import lombok.extern.slf4j.Slf4j; import stirling.software.SPDF.model.api.misc.OverlayImageRequest; -import stirling.software.SPDF.service.CustomPDDocumentFactory; +import stirling.software.SPDF.service.CustomPDFDocumentFactory; import stirling.software.SPDF.utils.PdfUtils; import stirling.software.SPDF.utils.WebResponseUtils; @@ -28,10 +28,10 @@ import stirling.software.SPDF.utils.WebResponseUtils; @Tag(name = "Misc", description = "Miscellaneous APIs") public class OverlayImageController { - private final CustomPDDocumentFactory pdfDocumentFactory; + private final CustomPDFDocumentFactory pdfDocumentFactory; @Autowired - public OverlayImageController(CustomPDDocumentFactory pdfDocumentFactory) { + public OverlayImageController(CustomPDFDocumentFactory pdfDocumentFactory) { this.pdfDocumentFactory = pdfDocumentFactory; } @@ -39,7 +39,9 @@ public class OverlayImageController { @Operation( summary = "Overlay image onto a PDF file", description = - "This endpoint overlays an image onto a PDF file at the specified coordinates. The image can be overlaid on every page of the PDF if specified. Input:PDF/IMAGE Output:PDF Type:SISO") + "This endpoint overlays an image onto a PDF file at the specified coordinates." + + " The image can be overlaid on every page of the PDF if specified. " + + " Input:PDF/IMAGE Output:PDF Type:SISO") public ResponseEntity overlayImage(@ModelAttribute OverlayImageRequest request) { MultipartFile pdfFile = request.getFileInput(); MultipartFile imageFile = request.getImageFile(); diff --git a/src/main/java/stirling/software/SPDF/controller/api/misc/PageNumbersController.java b/src/main/java/stirling/software/SPDF/controller/api/misc/PageNumbersController.java index 17d1bbaa9..c66b55d73 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/misc/PageNumbersController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/misc/PageNumbersController.java @@ -24,7 +24,7 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import stirling.software.SPDF.model.api.misc.AddPageNumbersRequest; -import stirling.software.SPDF.service.CustomPDDocumentFactory; +import stirling.software.SPDF.service.CustomPDFDocumentFactory; import stirling.software.SPDF.utils.GeneralUtils; import stirling.software.SPDF.utils.WebResponseUtils; @@ -33,10 +33,10 @@ import stirling.software.SPDF.utils.WebResponseUtils; @Tag(name = "Misc", description = "Miscellaneous APIs") public class PageNumbersController { - private final CustomPDDocumentFactory pdfDocumentFactory; + private final CustomPDFDocumentFactory pdfDocumentFactory; @Autowired - public PageNumbersController(CustomPDDocumentFactory pdfDocumentFactory) { + public PageNumbersController(CustomPDFDocumentFactory pdfDocumentFactory) { this.pdfDocumentFactory = pdfDocumentFactory; } @@ -44,7 +44,8 @@ public class PageNumbersController { @Operation( summary = "Add page numbers to a PDF document", description = - "This operation takes an input PDF file and adds page numbers to it. Input:PDF Output:PDF Type:SISO") + "This operation takes an input PDF file and adds page numbers to it. Input:PDF" + + " Output:PDF Type:SISO") public ResponseEntity addPageNumbers(@ModelAttribute AddPageNumbersRequest request) throws IOException { diff --git a/src/main/java/stirling/software/SPDF/controller/api/misc/RepairController.java b/src/main/java/stirling/software/SPDF/controller/api/misc/RepairController.java index 374d0c53b..e75a2e20d 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/misc/RepairController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/misc/RepairController.java @@ -19,7 +19,7 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import stirling.software.SPDF.model.api.PDFFile; -import stirling.software.SPDF.service.CustomPDDocumentFactory; +import stirling.software.SPDF.service.CustomPDFDocumentFactory; import stirling.software.SPDF.utils.ProcessExecutor; import stirling.software.SPDF.utils.ProcessExecutor.ProcessExecutorResult; import stirling.software.SPDF.utils.WebResponseUtils; @@ -29,10 +29,10 @@ import stirling.software.SPDF.utils.WebResponseUtils; @Tag(name = "Misc", description = "Miscellaneous APIs") public class RepairController { - private final CustomPDDocumentFactory pdfDocumentFactory; + private final CustomPDFDocumentFactory pdfDocumentFactory; @Autowired - public RepairController(CustomPDDocumentFactory pdfDocumentFactory) { + public RepairController(CustomPDFDocumentFactory pdfDocumentFactory) { this.pdfDocumentFactory = pdfDocumentFactory; } @@ -40,7 +40,9 @@ public class RepairController { @Operation( summary = "Repair a PDF file", description = - "This endpoint repairs a given PDF file by running qpdf command. The PDF is first saved to a temporary location, repaired, read back, and then returned as a response. Input:PDF Output:PDF Type:SISO") + "This endpoint repairs a given PDF file by running qpdf command. The PDF is" + + " first saved to a temporary location, repaired, read back, and then" + + " returned as a response. Input:PDF Output:PDF Type:SISO") public ResponseEntity repairPdf(@ModelAttribute PDFFile request) throws IOException, InterruptedException { MultipartFile inputFile = request.getFileInput(); diff --git a/src/main/java/stirling/software/SPDF/controller/api/misc/ShowJavascript.java b/src/main/java/stirling/software/SPDF/controller/api/misc/ShowJavascript.java index e38657cfe..ca5370b32 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/misc/ShowJavascript.java +++ b/src/main/java/stirling/software/SPDF/controller/api/misc/ShowJavascript.java @@ -20,7 +20,7 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import stirling.software.SPDF.model.api.PDFFile; -import stirling.software.SPDF.service.CustomPDDocumentFactory; +import stirling.software.SPDF.service.CustomPDFDocumentFactory; import stirling.software.SPDF.utils.WebResponseUtils; @RestController @@ -28,10 +28,10 @@ import stirling.software.SPDF.utils.WebResponseUtils; @Tag(name = "Misc", description = "Miscellaneous APIs") public class ShowJavascript { - private final CustomPDDocumentFactory pdfDocumentFactory; + private final CustomPDFDocumentFactory pdfDocumentFactory; @Autowired - public ShowJavascript(CustomPDDocumentFactory pdfDocumentFactory) { + public ShowJavascript(CustomPDFDocumentFactory pdfDocumentFactory) { this.pdfDocumentFactory = pdfDocumentFactory; } diff --git a/src/main/java/stirling/software/SPDF/controller/api/misc/StampController.java b/src/main/java/stirling/software/SPDF/controller/api/misc/StampController.java index 30a7540ee..e23635b82 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/misc/StampController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/misc/StampController.java @@ -38,7 +38,7 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import stirling.software.SPDF.model.api.misc.AddStampRequest; -import stirling.software.SPDF.service.CustomPDDocumentFactory; +import stirling.software.SPDF.service.CustomPDFDocumentFactory; import stirling.software.SPDF.utils.WebResponseUtils; @RestController @@ -46,10 +46,10 @@ import stirling.software.SPDF.utils.WebResponseUtils; @Tag(name = "Misc", description = "Miscellaneous APIs") public class StampController { - private final CustomPDDocumentFactory pdfDocumentFactory; + private final CustomPDFDocumentFactory pdfDocumentFactory; @Autowired - public StampController(CustomPDDocumentFactory pdfDocumentFactory) { + public StampController(CustomPDFDocumentFactory pdfDocumentFactory) { this.pdfDocumentFactory = pdfDocumentFactory; } @@ -57,7 +57,9 @@ public class StampController { @Operation( summary = "Add stamp to a PDF file", description = - "This endpoint adds a stamp to a given PDF file. Users can specify the stamp type (text or image), rotation, opacity, width spacer, and height spacer. Input:PDF Output:PDF Type:SISO") + "This endpoint adds a stamp to a given PDF file. Users can specify the stamp" + + " type (text or image), rotation, opacity, width spacer, and height" + + " spacer. Input:PDF Output:PDF Type:SISO") public ResponseEntity addStamp(@ModelAttribute AddStampRequest request) throws IOException, Exception { MultipartFile pdfFile = request.getFileInput(); diff --git a/src/main/java/stirling/software/SPDF/controller/api/security/CertSignController.java b/src/main/java/stirling/software/SPDF/controller/api/security/CertSignController.java index cad762062..4a8981142 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/security/CertSignController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/security/CertSignController.java @@ -68,7 +68,7 @@ import io.swagger.v3.oas.annotations.tags.Tag; import lombok.extern.slf4j.Slf4j; import stirling.software.SPDF.model.api.security.SignPDFWithCertRequest; -import stirling.software.SPDF.service.CustomPDDocumentFactory; +import stirling.software.SPDF.service.CustomPDFDocumentFactory; import stirling.software.SPDF.utils.WebResponseUtils; @RestController @@ -81,15 +81,15 @@ public class CertSignController { Security.addProvider(new BouncyCastleProvider()); } - private final CustomPDDocumentFactory pdfDocumentFactory; + private final CustomPDFDocumentFactory pdfDocumentFactory; @Autowired - public CertSignController(CustomPDDocumentFactory pdfDocumentFactory) { + public CertSignController(CustomPDFDocumentFactory pdfDocumentFactory) { this.pdfDocumentFactory = pdfDocumentFactory; } private static void sign( - CustomPDDocumentFactory pdfDocumentFactory, + CustomPDFDocumentFactory pdfDocumentFactory, MultipartFile input, OutputStream output, CreateSignature instance, diff --git a/src/main/java/stirling/software/SPDF/controller/api/security/GetInfoOnPDF.java b/src/main/java/stirling/software/SPDF/controller/api/security/GetInfoOnPDF.java index 026fd38a0..3452c3625 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/security/GetInfoOnPDF.java +++ b/src/main/java/stirling/software/SPDF/controller/api/security/GetInfoOnPDF.java @@ -62,7 +62,7 @@ import io.swagger.v3.oas.annotations.tags.Tag; import lombok.extern.slf4j.Slf4j; import stirling.software.SPDF.model.api.PDFFile; -import stirling.software.SPDF.service.CustomPDDocumentFactory; +import stirling.software.SPDF.service.CustomPDFDocumentFactory; import stirling.software.SPDF.utils.WebResponseUtils; @RestController @@ -73,10 +73,10 @@ public class GetInfoOnPDF { static ObjectMapper objectMapper = new ObjectMapper(); - private final CustomPDDocumentFactory pdfDocumentFactory; + private final CustomPDFDocumentFactory pdfDocumentFactory; @Autowired - public GetInfoOnPDF(CustomPDDocumentFactory pdfDocumentFactory) { + public GetInfoOnPDF(CustomPDFDocumentFactory pdfDocumentFactory) { this.pdfDocumentFactory = pdfDocumentFactory; } diff --git a/src/main/java/stirling/software/SPDF/controller/api/security/PasswordController.java b/src/main/java/stirling/software/SPDF/controller/api/security/PasswordController.java index 6a89a0710..386e938b9 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/security/PasswordController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/security/PasswordController.java @@ -19,7 +19,7 @@ import io.swagger.v3.oas.annotations.tags.Tag; import stirling.software.SPDF.model.api.security.AddPasswordRequest; import stirling.software.SPDF.model.api.security.PDFPasswordRequest; -import stirling.software.SPDF.service.CustomPDDocumentFactory; +import stirling.software.SPDF.service.CustomPDFDocumentFactory; import stirling.software.SPDF.utils.WebResponseUtils; @RestController @@ -27,10 +27,10 @@ import stirling.software.SPDF.utils.WebResponseUtils; @Tag(name = "Security", description = "Security APIs") public class PasswordController { - private final CustomPDDocumentFactory pdfDocumentFactory; + private final CustomPDFDocumentFactory pdfDocumentFactory; @Autowired - public PasswordController(CustomPDDocumentFactory pdfDocumentFactory) { + public PasswordController(CustomPDFDocumentFactory pdfDocumentFactory) { this.pdfDocumentFactory = pdfDocumentFactory; } @@ -38,8 +38,8 @@ public class PasswordController { @Operation( summary = "Remove password from a PDF file", description = - "This endpoint removes the password from a protected PDF file. Users need to provide the" - + " existing password. Input:PDF Output:PDF Type:SISO") + "This endpoint removes the password from a protected PDF file. Users need to" + + " provide the existing password. Input:PDF Output:PDF Type:SISO") public ResponseEntity removePassword(@ModelAttribute PDFPasswordRequest request) throws IOException { MultipartFile fileInput = request.getFileInput(); @@ -57,8 +57,9 @@ public class PasswordController { @Operation( summary = "Add password to a PDF file", description = - "This endpoint adds password protection to a PDF file. Users can specify a set of" - + " permissions that should be applied to the file. Input:PDF Output:PDF") + "This endpoint adds password protection to a PDF file. Users can specify a set" + + " of permissions that should be applied to the file. Input:PDF" + + " Output:PDF") public ResponseEntity addPassword(@ModelAttribute AddPasswordRequest request) throws IOException { MultipartFile fileInput = request.getFileInput(); diff --git a/src/main/java/stirling/software/SPDF/controller/api/security/RedactController.java b/src/main/java/stirling/software/SPDF/controller/api/security/RedactController.java index 5bffe68e1..deec98e4e 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/security/RedactController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/security/RedactController.java @@ -35,7 +35,7 @@ import stirling.software.SPDF.model.api.security.ManualRedactPdfRequest; import stirling.software.SPDF.model.api.security.RedactPdfRequest; import stirling.software.SPDF.model.api.security.RedactionArea; import stirling.software.SPDF.pdf.TextFinder; -import stirling.software.SPDF.service.CustomPDDocumentFactory; +import stirling.software.SPDF.service.CustomPDFDocumentFactory; import stirling.software.SPDF.utils.GeneralUtils; import stirling.software.SPDF.utils.PdfUtils; import stirling.software.SPDF.utils.WebResponseUtils; @@ -47,10 +47,10 @@ import stirling.software.SPDF.utils.propertyeditor.StringToArrayListPropertyEdit @Tag(name = "Security", description = "Security APIs") public class RedactController { - private final CustomPDDocumentFactory pdfDocumentFactory; + private final CustomPDFDocumentFactory pdfDocumentFactory; @Autowired - public RedactController(CustomPDDocumentFactory pdfDocumentFactory) { + public RedactController(CustomPDFDocumentFactory pdfDocumentFactory) { this.pdfDocumentFactory = pdfDocumentFactory; } @@ -64,7 +64,9 @@ public class RedactController { @Operation( summary = "Redacts areas and pages in a PDF document", description = - "This operation takes an input PDF file with a list of areas, page number(s)/range(s)/function(s) to redact. Input:PDF, Output:PDF, Type:SISO") + "This operation takes an input PDF file with a list of areas, page" + + " number(s)/range(s)/function(s) to redact. Input:PDF, Output:PDF," + + " Type:SISO") public ResponseEntity redactPDF(@ModelAttribute ManualRedactPdfRequest request) throws IOException { MultipartFile file = request.getFileInput(); @@ -196,8 +198,8 @@ public class RedactController { @Operation( summary = "Redacts listOfText in a PDF document", description = - "This operation takes an input PDF file and redacts the provided listOfText. Input:PDF," - + " Output:PDF, Type:SISO") + "This operation takes an input PDF file and redacts the provided listOfText." + + " Input:PDF, Output:PDF, Type:SISO") public ResponseEntity redactPdf(@ModelAttribute RedactPdfRequest request) throws Exception { MultipartFile file = request.getFileInput(); diff --git a/src/main/java/stirling/software/SPDF/controller/api/security/RemoveCertSignController.java b/src/main/java/stirling/software/SPDF/controller/api/security/RemoveCertSignController.java index 88ed9b13f..81c0bcfb1 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/security/RemoveCertSignController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/security/RemoveCertSignController.java @@ -21,7 +21,7 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import stirling.software.SPDF.model.api.PDFFile; -import stirling.software.SPDF.service.CustomPDDocumentFactory; +import stirling.software.SPDF.service.CustomPDFDocumentFactory; import stirling.software.SPDF.utils.WebResponseUtils; @RestController @@ -29,10 +29,10 @@ import stirling.software.SPDF.utils.WebResponseUtils; @Tag(name = "Security", description = "Security APIs") public class RemoveCertSignController { - private final CustomPDDocumentFactory pdfDocumentFactory; + private final CustomPDFDocumentFactory pdfDocumentFactory; @Autowired - public RemoveCertSignController(CustomPDDocumentFactory pdfDocumentFactory) { + public RemoveCertSignController(CustomPDFDocumentFactory pdfDocumentFactory) { this.pdfDocumentFactory = pdfDocumentFactory; } @@ -40,7 +40,8 @@ public class RemoveCertSignController { @Operation( summary = "Remove digital signature from PDF", description = - "This endpoint accepts a PDF file and returns the PDF file without the digital signature. Input:PDF, Output:PDF Type:SISO") + "This endpoint accepts a PDF file and returns the PDF file without the digital" + + " signature. Input:PDF, Output:PDF Type:SISO") public ResponseEntity removeCertSignPDF(@ModelAttribute PDFFile request) throws Exception { MultipartFile pdf = request.getFileInput(); diff --git a/src/main/java/stirling/software/SPDF/controller/api/security/SanitizeController.java b/src/main/java/stirling/software/SPDF/controller/api/security/SanitizeController.java index e075559e6..e1959fc88 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/security/SanitizeController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/security/SanitizeController.java @@ -25,7 +25,7 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import stirling.software.SPDF.model.api.security.SanitizePdfRequest; -import stirling.software.SPDF.service.CustomPDDocumentFactory; +import stirling.software.SPDF.service.CustomPDFDocumentFactory; import stirling.software.SPDF.utils.WebResponseUtils; @RestController @@ -33,10 +33,10 @@ import stirling.software.SPDF.utils.WebResponseUtils; @Tag(name = "Security", description = "Security APIs") public class SanitizeController { - private final CustomPDDocumentFactory pdfDocumentFactory; + private final CustomPDFDocumentFactory pdfDocumentFactory; @Autowired - public SanitizeController(CustomPDDocumentFactory pdfDocumentFactory) { + public SanitizeController(CustomPDFDocumentFactory pdfDocumentFactory) { this.pdfDocumentFactory = pdfDocumentFactory; } diff --git a/src/main/java/stirling/software/SPDF/controller/api/security/ValidateSignatureController.java b/src/main/java/stirling/software/SPDF/controller/api/security/ValidateSignatureController.java index 317c6424b..4b4da4bdd 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/security/ValidateSignatureController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/security/ValidateSignatureController.java @@ -35,19 +35,19 @@ import io.swagger.v3.oas.annotations.tags.Tag; import stirling.software.SPDF.model.api.security.SignatureValidationRequest; import stirling.software.SPDF.model.api.security.SignatureValidationResult; import stirling.software.SPDF.service.CertificateValidationService; -import stirling.software.SPDF.service.CustomPDDocumentFactory; +import stirling.software.SPDF.service.CustomPDFDocumentFactory; @RestController @RequestMapping("/api/v1/security") @Tag(name = "Security", description = "Security APIs") public class ValidateSignatureController { - private final CustomPDDocumentFactory pdfDocumentFactory; + private final CustomPDFDocumentFactory pdfDocumentFactory; private final CertificateValidationService certValidationService; @Autowired public ValidateSignatureController( - CustomPDDocumentFactory pdfDocumentFactory, + CustomPDFDocumentFactory pdfDocumentFactory, CertificateValidationService certValidationService) { this.pdfDocumentFactory = pdfDocumentFactory; this.certValidationService = certValidationService; @@ -56,7 +56,8 @@ public class ValidateSignatureController { @Operation( summary = "Validate PDF Digital Signature", description = - "Validates the digital signatures in a PDF file against default or custom certificates. Input:PDF Output:JSON Type:SISO") + "Validates the digital signatures in a PDF file against default or custom" + + " certificates. Input:PDF Output:JSON Type:SISO") @PostMapping(value = "/validate-signature") public ResponseEntity> validateSignature( @ModelAttribute SignatureValidationRequest request) throws IOException { diff --git a/src/main/java/stirling/software/SPDF/controller/api/security/WatermarkController.java b/src/main/java/stirling/software/SPDF/controller/api/security/WatermarkController.java index 3e1be8153..7ed8a5a04 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/security/WatermarkController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/security/WatermarkController.java @@ -36,7 +36,7 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import stirling.software.SPDF.model.api.security.AddWatermarkRequest; -import stirling.software.SPDF.service.CustomPDDocumentFactory; +import stirling.software.SPDF.service.CustomPDFDocumentFactory; import stirling.software.SPDF.utils.PdfUtils; import stirling.software.SPDF.utils.WebResponseUtils; @@ -45,10 +45,10 @@ import stirling.software.SPDF.utils.WebResponseUtils; @Tag(name = "Security", description = "Security APIs") public class WatermarkController { - private final CustomPDDocumentFactory pdfDocumentFactory; + private final CustomPDFDocumentFactory pdfDocumentFactory; @Autowired - public WatermarkController(CustomPDDocumentFactory pdfDocumentFactory) { + public WatermarkController(CustomPDFDocumentFactory pdfDocumentFactory) { this.pdfDocumentFactory = pdfDocumentFactory; } @@ -56,7 +56,9 @@ public class WatermarkController { @Operation( summary = "Add watermark to a PDF file", description = - "This endpoint adds a watermark to a given PDF file. Users can specify the watermark type (text or image), rotation, opacity, width spacer, and height spacer. Input:PDF Output:PDF Type:SISO") + "This endpoint adds a watermark to a given PDF file. Users can specify the" + + " watermark type (text or image), rotation, opacity, width spacer, and" + + " height spacer. Input:PDF Output:PDF Type:SISO") public ResponseEntity addWatermark(@ModelAttribute AddWatermarkRequest request) throws IOException, Exception { MultipartFile pdfFile = request.getFileInput(); diff --git a/src/main/java/stirling/software/SPDF/service/CustomPDDocumentFactory.java b/src/main/java/stirling/software/SPDF/service/CustomPDFDocumentFactory.java similarity index 95% rename from src/main/java/stirling/software/SPDF/service/CustomPDDocumentFactory.java rename to src/main/java/stirling/software/SPDF/service/CustomPDFDocumentFactory.java index 6963b522c..354324744 100644 --- a/src/main/java/stirling/software/SPDF/service/CustomPDDocumentFactory.java +++ b/src/main/java/stirling/software/SPDF/service/CustomPDFDocumentFactory.java @@ -29,7 +29,7 @@ import stirling.software.SPDF.model.api.PDFFile; */ @Component @Slf4j -public class CustomPDDocumentFactory { +public class CustomPDFDocumentFactory { private final PdfMetadataService pdfMetadataService; @@ -63,7 +63,7 @@ public class CustomPDDocumentFactory { // Counter for tracking temporary resources private static final AtomicLong tempCounter = new AtomicLong(0); - public CustomPDDocumentFactory(PdfMetadataService pdfMetadataService) { + public CustomPDFDocumentFactory(PdfMetadataService pdfMetadataService) { this.pdfMetadataService = pdfMetadataService; } @@ -82,6 +82,21 @@ public class CustomPDDocumentFactory { return loadAdaptively(file, fileSize); } + /** + * Main entry point for loading a PDF document from a Path. Automatically selects the most + * appropriate loading strategy. + */ + public PDDocument load(Path path) throws IOException { + if (path == null) { + throw new IllegalArgumentException("File cannot be null"); + } + + long fileSize = Files.size(path); + log.info("Loading PDF from file, size: {}MB", fileSize / (1024 * 1024)); + + return loadAdaptively(path.toFile(), fileSize); + } + /** Load a PDF from byte array with automatic optimization. */ public PDDocument load(byte[] input) throws IOException { if (input == null) { @@ -168,8 +183,8 @@ public class CustomPDDocumentFactory { private PDDocument loadAdaptively(Object source, long contentSize) throws IOException { // Get the appropriate caching strategy StreamCacheCreateFunction cacheFunction = getStreamCacheFunction(contentSize); - - //If small handle as bytes and remove original file + + // If small handle as bytes and remove original file if (contentSize <= SMALL_FILE_THRESHOLD && source instanceof File file) { source = Files.readAllBytes(file.toPath()); file.delete(); @@ -192,7 +207,7 @@ public class CustomPDDocumentFactory { throws IOException { // Get the appropriate caching strategy StreamCacheCreateFunction cacheFunction = getStreamCacheFunction(contentSize); - //If small handle as bytes and remove original file + // If small handle as bytes and remove original file if (contentSize <= SMALL_FILE_THRESHOLD && source instanceof File file) { source = Files.readAllBytes(file.toPath()); file.delete(); @@ -246,6 +261,7 @@ public class CustomPDDocumentFactory { removePassword(doc); } + private PDDocument loadFromFile(File file, long size, StreamCacheCreateFunction cache) throws IOException { return Loader.loadPDF(new DeletingRandomAccessFile(file), "", null, null, cache); diff --git a/src/main/java/stirling/software/SPDF/utils/PdfUtils.java b/src/main/java/stirling/software/SPDF/utils/PdfUtils.java index ad404660e..14c5c09d7 100644 --- a/src/main/java/stirling/software/SPDF/utils/PdfUtils.java +++ b/src/main/java/stirling/software/SPDF/utils/PdfUtils.java @@ -35,7 +35,7 @@ import io.github.pixee.security.Filenames; import lombok.extern.slf4j.Slf4j; -import stirling.software.SPDF.service.CustomPDDocumentFactory; +import stirling.software.SPDF.service.CustomPDFDocumentFactory; @Slf4j public class PdfUtils { @@ -127,7 +127,7 @@ public class PdfUtils { } public static byte[] convertFromPdf( - CustomPDDocumentFactory pdfDocumentFactory, + CustomPDFDocumentFactory pdfDocumentFactory, byte[] inputStream, String imageType, ImageType colorType, @@ -315,7 +315,7 @@ public class PdfUtils { String fitOption, boolean autoRotate, String colorType, - CustomPDDocumentFactory pdfDocumentFactory) + CustomPDFDocumentFactory pdfDocumentFactory) throws IOException { try (PDDocument doc = pdfDocumentFactory.createNewDocument()) { for (MultipartFile file : files) { @@ -405,7 +405,7 @@ public class PdfUtils { } public static byte[] overlayImage( - CustomPDDocumentFactory pdfDocumentFactory, + CustomPDFDocumentFactory pdfDocumentFactory, byte[] pdfBytes, byte[] imageBytes, float x, diff --git a/src/main/resources/messages_ar_AR.properties b/src/main/resources/messages_ar_AR.properties index a279ba794..037f398b2 100644 --- a/src/main/resources/messages_ar_AR.properties +++ b/src/main/resources/messages_ar_AR.properties @@ -860,7 +860,8 @@ sign.last=Last page sign.next=Next page sign.previous=Previous page sign.maintainRatio=Toggle maintain aspect ratio - +sign.undo=Undo +sign.redo=Redo #repair repair.title=إصلاح diff --git a/src/main/resources/messages_az_AZ.properties b/src/main/resources/messages_az_AZ.properties index ae59b7c53..e46bdb51a 100644 --- a/src/main/resources/messages_az_AZ.properties +++ b/src/main/resources/messages_az_AZ.properties @@ -860,7 +860,8 @@ sign.last=Son səhifə sign.next=Növbəti səhifə sign.previous=Əvvəlki səhifə sign.maintainRatio=Toggle maintain aspect ratio - +sign.undo=Undo +sign.redo=Redo #repair repair.title=Bərpa Et diff --git a/src/main/resources/messages_bg_BG.properties b/src/main/resources/messages_bg_BG.properties index 09af7cbbf..df91050b0 100644 --- a/src/main/resources/messages_bg_BG.properties +++ b/src/main/resources/messages_bg_BG.properties @@ -860,7 +860,8 @@ sign.last=Последна страница sign.next=Следваща страница sign.previous=Предишна стараница sign.maintainRatio=Превключване за поддържане на съотношението на страните - +sign.undo=Undo +sign.redo=Redo #repair repair.title=Поправи diff --git a/src/main/resources/messages_ca_CA.properties b/src/main/resources/messages_ca_CA.properties index a7da336de..7f0b266ed 100644 --- a/src/main/resources/messages_ca_CA.properties +++ b/src/main/resources/messages_ca_CA.properties @@ -860,7 +860,8 @@ sign.last=Last page sign.next=Next page sign.previous=Previous page sign.maintainRatio=Toggle maintain aspect ratio - +sign.undo=Undo +sign.redo=Redo #repair repair.title=Reparar diff --git a/src/main/resources/messages_cs_CZ.properties b/src/main/resources/messages_cs_CZ.properties index ae5076197..1a3970f57 100644 --- a/src/main/resources/messages_cs_CZ.properties +++ b/src/main/resources/messages_cs_CZ.properties @@ -860,7 +860,8 @@ sign.last=Poslední stránka sign.next=Další stránka sign.previous=Předchozí stránka sign.maintainRatio=Přepnout zachování poměru stran - +sign.undo=Undo +sign.redo=Redo #repair repair.title=Opravit diff --git a/src/main/resources/messages_da_DK.properties b/src/main/resources/messages_da_DK.properties index de2a26dd5..4bf0f03a1 100644 --- a/src/main/resources/messages_da_DK.properties +++ b/src/main/resources/messages_da_DK.properties @@ -860,7 +860,8 @@ sign.last=Last page sign.next=Next page sign.previous=Previous page sign.maintainRatio=Toggle maintain aspect ratio - +sign.undo=Undo +sign.redo=Redo #repair repair.title=Reparér diff --git a/src/main/resources/messages_de_DE.properties b/src/main/resources/messages_de_DE.properties index 401cff58f..6d73e5dca 100644 --- a/src/main/resources/messages_de_DE.properties +++ b/src/main/resources/messages_de_DE.properties @@ -860,7 +860,8 @@ sign.last=Letzte Seite sign.next=Nächste Seite sign.previous=Vorherige Seite sign.maintainRatio=Seitenverhältnis beibehalten ein-/ausschalten - +sign.undo=Undo +sign.redo=Redo #repair repair.title=Reparieren diff --git a/src/main/resources/messages_el_GR.properties b/src/main/resources/messages_el_GR.properties index 946585931..321bf7459 100644 --- a/src/main/resources/messages_el_GR.properties +++ b/src/main/resources/messages_el_GR.properties @@ -860,7 +860,8 @@ sign.last=Τελευταία σελίδα sign.next=Επόμενη σελίδα sign.previous=Προηγούμενη σελίδα sign.maintainRatio=Εναλλαγή διατήρησης αναλογίας διαστάσεων - +sign.undo=Undo +sign.redo=Redo #repair repair.title=Επιδιόρθωση diff --git a/src/main/resources/messages_en_GB.properties b/src/main/resources/messages_en_GB.properties index 176a83707..aab945371 100644 --- a/src/main/resources/messages_en_GB.properties +++ b/src/main/resources/messages_en_GB.properties @@ -860,7 +860,8 @@ sign.last=Last page sign.next=Next page sign.previous=Previous page sign.maintainRatio=Toggle maintain aspect ratio - +sign.undo=Undo +sign.redo=Redo #repair repair.title=Repair diff --git a/src/main/resources/messages_en_US.properties b/src/main/resources/messages_en_US.properties index 3a62c78e1..c5392d419 100644 --- a/src/main/resources/messages_en_US.properties +++ b/src/main/resources/messages_en_US.properties @@ -860,7 +860,8 @@ sign.last=Last page sign.next=Next page sign.previous=Previous page sign.maintainRatio=Toggle maintain aspect ratio - +sign.undo=Undo +sign.redo=Redo #repair repair.title=Repair diff --git a/src/main/resources/messages_es_ES.properties b/src/main/resources/messages_es_ES.properties index e02031243..2e0cbc5a3 100644 --- a/src/main/resources/messages_es_ES.properties +++ b/src/main/resources/messages_es_ES.properties @@ -860,7 +860,8 @@ sign.last=Última página sign.next=Siguiente página sign.previous=Página anterior sign.maintainRatio=Activar/desactivar la relación de aspecto - +sign.undo=Undo +sign.redo=Redo #repair repair.title=Reparar diff --git a/src/main/resources/messages_eu_ES.properties b/src/main/resources/messages_eu_ES.properties index 00647f829..e468adec6 100644 --- a/src/main/resources/messages_eu_ES.properties +++ b/src/main/resources/messages_eu_ES.properties @@ -860,7 +860,8 @@ sign.last=Last page sign.next=Next page sign.previous=Previous page sign.maintainRatio=Toggle maintain aspect ratio - +sign.undo=Undo +sign.redo=Redo #repair repair.title=Konpondu diff --git a/src/main/resources/messages_fa_IR.properties b/src/main/resources/messages_fa_IR.properties index 035d9f0dd..bb8af5449 100644 --- a/src/main/resources/messages_fa_IR.properties +++ b/src/main/resources/messages_fa_IR.properties @@ -860,7 +860,8 @@ sign.last=صفحه آخر sign.next=صفحه بعدی sign.previous=صفحه قبلی sign.maintainRatio=Toggle maintain aspect ratio - +sign.undo=Undo +sign.redo=Redo #repair repair.title=تعمیر diff --git a/src/main/resources/messages_fr_FR.properties b/src/main/resources/messages_fr_FR.properties index 268598181..ac6a78b27 100644 --- a/src/main/resources/messages_fr_FR.properties +++ b/src/main/resources/messages_fr_FR.properties @@ -860,7 +860,8 @@ sign.last=Dernière page sign.next=Page suivante sign.previous=Page précédente sign.maintainRatio=Conserver les proportions - +sign.undo=Undo +sign.redo=Redo #repair repair.title=Réparer diff --git a/src/main/resources/messages_ga_IE.properties b/src/main/resources/messages_ga_IE.properties index aef7de862..dc0346cdb 100644 --- a/src/main/resources/messages_ga_IE.properties +++ b/src/main/resources/messages_ga_IE.properties @@ -860,7 +860,8 @@ sign.last=An leathanach deiridh sign.next=An chéad leathanach eile sign.previous=Leathanach roimhe seo sign.maintainRatio=Scoránaigh, coinnigh an cóimheas gné - +sign.undo=Undo +sign.redo=Redo #repair repair.title=Deisiúchán diff --git a/src/main/resources/messages_hi_IN.properties b/src/main/resources/messages_hi_IN.properties index 5baa3a2c3..b9d2b7ccd 100644 --- a/src/main/resources/messages_hi_IN.properties +++ b/src/main/resources/messages_hi_IN.properties @@ -860,7 +860,8 @@ sign.last=अंतिम पृष्ठ sign.next=अगला पृष्ठ sign.previous=पिछला पृष्ठ sign.maintainRatio=आनुपातिक अनुपात बनाए रखें टॉगल करें - +sign.undo=Undo +sign.redo=Redo #repair repair.title=मरम्मत diff --git a/src/main/resources/messages_hr_HR.properties b/src/main/resources/messages_hr_HR.properties index 5751fc9bb..49b9da7bf 100644 --- a/src/main/resources/messages_hr_HR.properties +++ b/src/main/resources/messages_hr_HR.properties @@ -860,7 +860,8 @@ sign.last=Last page sign.next=Next page sign.previous=Previous page sign.maintainRatio=Toggle maintain aspect ratio - +sign.undo=Undo +sign.redo=Redo #repair repair.title=Popravi diff --git a/src/main/resources/messages_hu_HU.properties b/src/main/resources/messages_hu_HU.properties index ea365b0c8..a64e15ec0 100644 --- a/src/main/resources/messages_hu_HU.properties +++ b/src/main/resources/messages_hu_HU.properties @@ -860,7 +860,8 @@ sign.last=Utolsó oldal sign.next=Következő oldal sign.previous=Előző oldal sign.maintainRatio=Képarány fenntartása váltása - +sign.undo=Undo +sign.redo=Redo #repair repair.title=Javítás diff --git a/src/main/resources/messages_id_ID.properties b/src/main/resources/messages_id_ID.properties index 1348fb955..2c6e65b36 100644 --- a/src/main/resources/messages_id_ID.properties +++ b/src/main/resources/messages_id_ID.properties @@ -860,7 +860,8 @@ sign.last=Last page sign.next=Next page sign.previous=Previous page sign.maintainRatio=Toggle maintain aspect ratio - +sign.undo=Undo +sign.redo=Redo #repair repair.title=Perbaiki diff --git a/src/main/resources/messages_it_IT.properties b/src/main/resources/messages_it_IT.properties index a97228139..1ba2d0daf 100644 --- a/src/main/resources/messages_it_IT.properties +++ b/src/main/resources/messages_it_IT.properties @@ -860,7 +860,8 @@ sign.last=Ultima pagina sign.next=Prossima pagina sign.previous=Pagina precedente sign.maintainRatio=Attiva il mantenimento delle proporzioni - +sign.undo=Annulla +sign.redo=Rifare #repair repair.title=Ripara diff --git a/src/main/resources/messages_ja_JP.properties b/src/main/resources/messages_ja_JP.properties index d0b66ea60..79ad40c19 100644 --- a/src/main/resources/messages_ja_JP.properties +++ b/src/main/resources/messages_ja_JP.properties @@ -860,7 +860,8 @@ sign.last=最後のページ sign.next=次のページ sign.previous=前のページ sign.maintainRatio=アスペクト比を維持を切替え - +sign.undo=Undo +sign.redo=Redo #repair repair.title=修復 diff --git a/src/main/resources/messages_ko_KR.properties b/src/main/resources/messages_ko_KR.properties index 0b4368e96..00e8da564 100644 --- a/src/main/resources/messages_ko_KR.properties +++ b/src/main/resources/messages_ko_KR.properties @@ -860,7 +860,8 @@ sign.last=마지막 페이지 sign.next=다음 페이지 sign.previous=이전 페이지 sign.maintainRatio=종횡비 유지 토글 - +sign.undo=Undo +sign.redo=Redo #repair repair.title=복구 diff --git a/src/main/resources/messages_nl_NL.properties b/src/main/resources/messages_nl_NL.properties index 47428ac9c..21514896b 100644 --- a/src/main/resources/messages_nl_NL.properties +++ b/src/main/resources/messages_nl_NL.properties @@ -860,7 +860,8 @@ sign.last=Last page sign.next=Next page sign.previous=Previous page sign.maintainRatio=Toggle maintain aspect ratio - +sign.undo=Undo +sign.redo=Redo #repair repair.title=Repareren diff --git a/src/main/resources/messages_no_NB.properties b/src/main/resources/messages_no_NB.properties index d33daaa47..b2a22a183 100644 --- a/src/main/resources/messages_no_NB.properties +++ b/src/main/resources/messages_no_NB.properties @@ -860,7 +860,8 @@ sign.last=Last page sign.next=Next page sign.previous=Previous page sign.maintainRatio=Toggle maintain aspect ratio - +sign.undo=Undo +sign.redo=Redo #repair repair.title=Reparer diff --git a/src/main/resources/messages_pl_PL.properties b/src/main/resources/messages_pl_PL.properties index 7fec27796..506bec173 100644 --- a/src/main/resources/messages_pl_PL.properties +++ b/src/main/resources/messages_pl_PL.properties @@ -860,7 +860,8 @@ sign.last=Last page sign.next=Next page sign.previous=Previous page sign.maintainRatio=Toggle maintain aspect ratio - +sign.undo=Undo +sign.redo=Redo #repair repair.title=Napraw diff --git a/src/main/resources/messages_pt_BR.properties b/src/main/resources/messages_pt_BR.properties index 06bc23128..f66dc54a1 100644 --- a/src/main/resources/messages_pt_BR.properties +++ b/src/main/resources/messages_pt_BR.properties @@ -860,7 +860,8 @@ sign.last=Última página sign.next=Próxima página sign.previous=Página anterior sign.maintainRatio=Habilitar manter proporção - +sign.undo=Undo +sign.redo=Redo #repair repair.title=Reparar diff --git a/src/main/resources/messages_pt_PT.properties b/src/main/resources/messages_pt_PT.properties index 1f75a6e89..f83f36f65 100644 --- a/src/main/resources/messages_pt_PT.properties +++ b/src/main/resources/messages_pt_PT.properties @@ -860,7 +860,8 @@ sign.last=Última página sign.next=Próxima página sign.previous=Página anterior sign.maintainRatio=Alternar manter proporção - +sign.undo=Undo +sign.redo=Redo #repair repair.title=Reparar diff --git a/src/main/resources/messages_ro_RO.properties b/src/main/resources/messages_ro_RO.properties index 70ae27897..0685c95ac 100644 --- a/src/main/resources/messages_ro_RO.properties +++ b/src/main/resources/messages_ro_RO.properties @@ -860,7 +860,8 @@ sign.last=Last page sign.next=Next page sign.previous=Previous page sign.maintainRatio=Toggle maintain aspect ratio - +sign.undo=Undo +sign.redo=Redo #repair repair.title=Repară diff --git a/src/main/resources/messages_ru_RU.properties b/src/main/resources/messages_ru_RU.properties index fcb4413d5..1a9460bfc 100644 --- a/src/main/resources/messages_ru_RU.properties +++ b/src/main/resources/messages_ru_RU.properties @@ -860,7 +860,8 @@ sign.last=Последняя страница sign.next=Следующая страница sign.previous=Предыдущая страница sign.maintainRatio=Переключить сохранение пропорций - +sign.undo=Undo +sign.redo=Redo #repair repair.title=Восстановление diff --git a/src/main/resources/messages_sk_SK.properties b/src/main/resources/messages_sk_SK.properties index d7ea322ac..8e6dc085c 100644 --- a/src/main/resources/messages_sk_SK.properties +++ b/src/main/resources/messages_sk_SK.properties @@ -860,7 +860,8 @@ sign.last=Last page sign.next=Next page sign.previous=Previous page sign.maintainRatio=Toggle maintain aspect ratio - +sign.undo=Undo +sign.redo=Redo #repair repair.title=Opraviť diff --git a/src/main/resources/messages_sl_SI.properties b/src/main/resources/messages_sl_SI.properties index 870066c9c..b30b6db76 100644 --- a/src/main/resources/messages_sl_SI.properties +++ b/src/main/resources/messages_sl_SI.properties @@ -860,7 +860,8 @@ sign.last=Zadnja stran sign.next=Naslednja stran sign.previous=Prejšnja stran sign.maintainRatio=Preklopi ohranjanje razmerja stranic - +sign.undo=Undo +sign.redo=Redo #repair repair.title=Popravilo diff --git a/src/main/resources/messages_sr_LATN_RS.properties b/src/main/resources/messages_sr_LATN_RS.properties index e2db8ef6d..0a0cdfbb6 100644 --- a/src/main/resources/messages_sr_LATN_RS.properties +++ b/src/main/resources/messages_sr_LATN_RS.properties @@ -860,7 +860,8 @@ sign.last=Last page sign.next=Next page sign.previous=Previous page sign.maintainRatio=Toggle maintain aspect ratio - +sign.undo=Undo +sign.redo=Redo #repair repair.title=Popravi diff --git a/src/main/resources/messages_sv_SE.properties b/src/main/resources/messages_sv_SE.properties index 21d19c2e1..6a5d6d715 100644 --- a/src/main/resources/messages_sv_SE.properties +++ b/src/main/resources/messages_sv_SE.properties @@ -860,7 +860,8 @@ sign.last=Sista sidan sign.next=Nästa sida sign.previous=Föregående sida sign.maintainRatio=Toggle maintain aspect ratio - +sign.undo=Undo +sign.redo=Redo #repair repair.title=Reparera diff --git a/src/main/resources/messages_th_TH.properties b/src/main/resources/messages_th_TH.properties index 0c4d4ff71..372f3c38e 100644 --- a/src/main/resources/messages_th_TH.properties +++ b/src/main/resources/messages_th_TH.properties @@ -860,7 +860,8 @@ sign.last=Last page sign.next=Next page sign.previous=Previous page sign.maintainRatio=Toggle maintain aspect ratio - +sign.undo=Undo +sign.redo=Redo #repair repair.title=ซ่อมแซม diff --git a/src/main/resources/messages_tr_TR.properties b/src/main/resources/messages_tr_TR.properties index bee1c693a..adb9644df 100644 --- a/src/main/resources/messages_tr_TR.properties +++ b/src/main/resources/messages_tr_TR.properties @@ -860,7 +860,8 @@ sign.last=Last page sign.next=Next page sign.previous=Previous page sign.maintainRatio=Toggle maintain aspect ratio - +sign.undo=Undo +sign.redo=Redo #repair repair.title=Onar diff --git a/src/main/resources/messages_uk_UA.properties b/src/main/resources/messages_uk_UA.properties index f17f8a774..dcec7158a 100644 --- a/src/main/resources/messages_uk_UA.properties +++ b/src/main/resources/messages_uk_UA.properties @@ -860,7 +860,8 @@ sign.last=Остання сторінка sign.next=Наступна сторінка sign.previous=Попередня сторінка sign.maintainRatio=Переключити збереження пропорцій - +sign.undo=Undo +sign.redo=Redo #repair repair.title=Ремонт diff --git a/src/main/resources/messages_vi_VN.properties b/src/main/resources/messages_vi_VN.properties index 41daea0e3..8695969c6 100644 --- a/src/main/resources/messages_vi_VN.properties +++ b/src/main/resources/messages_vi_VN.properties @@ -860,7 +860,8 @@ sign.last=Last page sign.next=Next page sign.previous=Previous page sign.maintainRatio=Toggle maintain aspect ratio - +sign.undo=Undo +sign.redo=Redo #repair repair.title=Sửa chữa diff --git a/src/main/resources/messages_zh_BO.properties b/src/main/resources/messages_zh_BO.properties index 41dc4335d..8e768c306 100644 --- a/src/main/resources/messages_zh_BO.properties +++ b/src/main/resources/messages_zh_BO.properties @@ -860,7 +860,8 @@ sign.last=ཤོག་ངོས་མཐའ་མ། sign.next=ཤོག་ངོས་རྗེས་མ། sign.previous=ཤོག་ངོས་སྔོན་མ། sign.maintainRatio=བསྡུར་ཚད་རྒྱུན་འཁྱོངས་སྒོ་རྒྱག་པ། - +sign.undo=Undo +sign.redo=Redo #repair repair.title=བཟོ་བཅོས། diff --git a/src/main/resources/messages_zh_CN.properties b/src/main/resources/messages_zh_CN.properties index 77c839f65..af60eabc9 100644 --- a/src/main/resources/messages_zh_CN.properties +++ b/src/main/resources/messages_zh_CN.properties @@ -860,7 +860,8 @@ sign.last=末页 sign.next=下一页 sign.previous=上一页 sign.maintainRatio=切换保持长宽比 - +sign.undo=撤销 +sign.redo=重做 #repair repair.title=修复 diff --git a/src/main/resources/messages_zh_TW.properties b/src/main/resources/messages_zh_TW.properties index e6485e7bc..6a4bd2ee2 100644 --- a/src/main/resources/messages_zh_TW.properties +++ b/src/main/resources/messages_zh_TW.properties @@ -860,7 +860,8 @@ sign.last=最後一頁 sign.next=下一頁 sign.previous=上一頁 sign.maintainRatio=切換維持長寬比 - +sign.undo=撤销 +sign.redo=重做 #repair repair.title=修復 diff --git a/src/main/resources/static/3rdPartyLicenses.json b/src/main/resources/static/3rdPartyLicenses.json index 620eaeee6..2101765d9 100644 --- a/src/main/resources/static/3rdPartyLicenses.json +++ b/src/main/resources/static/3rdPartyLicenses.json @@ -530,13 +530,6 @@ "moduleLicense": "Apache-2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt" }, - { - "moduleName": "commons-logging:commons-logging", - "moduleUrl": "https://commons.apache.org/proper/commons-logging/", - "moduleVersion": "1.3.4", - "moduleLicense": "Apache-2.0", - "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt" - }, { "moduleName": "io.dropwizard.metrics:metrics-core", "moduleVersion": "4.2.25", @@ -560,7 +553,7 @@ { "moduleName": "io.micrometer:micrometer-core", "moduleUrl": "https://github.com/micrometer-metrics/micrometer", - "moduleVersion": "1.14.4", + "moduleVersion": "1.14.5", "moduleLicense": "The Apache Software License, Version 2.0", "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt" }, diff --git a/src/main/resources/static/js/sign/signature-canvas.js b/src/main/resources/static/js/sign/signature-canvas.js index 03052d9ce..bd06e8456 100644 --- a/src/main/resources/static/js/sign/signature-canvas.js +++ b/src/main/resources/static/js/sign/signature-canvas.js @@ -1,10 +1,48 @@ const signaturePadCanvas = document.getElementById('drawing-pad-canvas'); +const undoButton = document.getElementById("signature-undo-button"); +const redoButton = document.getElementById("signature-redo-button"); const signaturePad = new SignaturePad(signaturePadCanvas, { minWidth: 1, maxWidth: 2, penColor: 'black', }); +let undoData = []; + +signaturePad.addEventListener("endStroke", () => { + undoData = []; +}); + +window.addEventListener("keydown", (event) => { + switch (true) { + case event.key === "z" && event.ctrlKey: + undoButton.click(); + break; + case event.key === "y" && event.ctrlKey: + redoButton.click(); + break; + } +}); + +function undoDraw() { + const data = signaturePad.toData(); + + if (data && data.length > 0) { + const removed = data.pop(); + undoData.push(removed); + signaturePad.fromData(data); + } +} + +function redoDraw() { + + if (undoData.length > 0) { + const data = signaturePad.toData(); + data.push(undoData.pop()); + signaturePad.fromData(data); + } +} + function addDraggableFromPad() { if (signaturePad.isEmpty()) return; const startTime = Date.now(); diff --git a/src/main/resources/templates/fragments/common.html b/src/main/resources/templates/fragments/common.html index 28c7b0225..1e801d06f 100644 --- a/src/main/resources/templates/fragments/common.html +++ b/src/main/resources/templates/fragments/common.html @@ -34,11 +34,15 @@ + + + + diff --git a/src/main/resources/templates/sign.html b/src/main/resources/templates/sign.html index 605737457..b0c66c27e 100644 --- a/src/main/resources/templates/sign.html +++ b/src/main/resources/templates/sign.html @@ -56,6 +56,10 @@ th:text="#{sign.clear}"> + +
diff --git a/src/test/java/stirling/software/SPDF/SPDFApplicationTest.java b/src/test/java/stirling/software/SPDF/SPDFApplicationTest.java index 3b05e8761..c4dae34bf 100644 --- a/src/test/java/stirling/software/SPDF/SPDFApplicationTest.java +++ b/src/test/java/stirling/software/SPDF/SPDFApplicationTest.java @@ -1,11 +1,6 @@ package stirling.software.SPDF; -import static org.junit.jupiter.api.Assertions.*; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; +import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -16,22 +11,15 @@ import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.core.env.Environment; import stirling.software.SPDF.model.ApplicationProperties; -import static java.nio.file.Files.createDirectories; -import static java.nio.file.Files.createFile; -import static java.nio.file.Files.delete; -import static java.nio.file.Files.exists; @ExtendWith(MockitoExtension.class) public class SPDFApplicationTest { - @Mock - private Environment env; + @Mock private Environment env; - @Mock - private ApplicationProperties applicationProperties; + @Mock private ApplicationProperties applicationProperties; - @InjectMocks - private SPDFApplication sPDFApplication; + @InjectMocks private SPDFApplication sPDFApplication; @BeforeEach public void setUp() { @@ -48,5 +36,4 @@ public class SPDFApplicationTest { public void testGetStaticPort() { assertEquals("8080", SPDFApplication.getStaticPort()); } - } diff --git a/src/test/java/stirling/software/SPDF/config/security/CustomLogoutSuccessHandlerTest.java b/src/test/java/stirling/software/SPDF/config/security/CustomLogoutSuccessHandlerTest.java index 37a9f86e6..72dd95418 100644 --- a/src/test/java/stirling/software/SPDF/config/security/CustomLogoutSuccessHandlerTest.java +++ b/src/test/java/stirling/software/SPDF/config/security/CustomLogoutSuccessHandlerTest.java @@ -1,27 +1,29 @@ package stirling.software.SPDF.config.security; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + import java.io.IOException; + import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + import stirling.software.SPDF.model.ApplicationProperties; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class CustomLogoutSuccessHandlerTest { - @Mock - private ApplicationProperties applicationProperties; + @Mock private ApplicationProperties applicationProperties; - @InjectMocks - private CustomLogoutSuccessHandler customLogoutSuccessHandler; + @InjectMocks private CustomLogoutSuccessHandler customLogoutSuccessHandler; @Test void testSuccessfulLogout() throws IOException { @@ -44,7 +46,8 @@ class CustomLogoutSuccessHandlerTest { HttpServletResponse response = mock(HttpServletResponse.class); OAuth2AuthenticationToken oAuth2AuthenticationToken = mock(OAuth2AuthenticationToken.class); ApplicationProperties.Security security = mock(ApplicationProperties.Security.class); - ApplicationProperties.Security.OAUTH2 oauth = mock(ApplicationProperties.Security.OAUTH2.class); + ApplicationProperties.Security.OAUTH2 oauth = + mock(ApplicationProperties.Security.OAUTH2.class); when(response.isCommitted()).thenReturn(false); when(request.getParameter("oAuth2AuthenticationErrorWeb")).thenReturn(null); @@ -70,7 +73,8 @@ class CustomLogoutSuccessHandlerTest { HttpServletResponse response = mock(HttpServletResponse.class); OAuth2AuthenticationToken authentication = mock(OAuth2AuthenticationToken.class); ApplicationProperties.Security security = mock(ApplicationProperties.Security.class); - ApplicationProperties.Security.OAUTH2 oauth = mock(ApplicationProperties.Security.OAUTH2.class); + ApplicationProperties.Security.OAUTH2 oauth = + mock(ApplicationProperties.Security.OAUTH2.class); when(response.isCommitted()).thenReturn(false); when(request.getParameter("oAuth2AuthenticationErrorWeb")).thenReturn(null); @@ -100,7 +104,8 @@ class CustomLogoutSuccessHandlerTest { HttpServletResponse response = mock(HttpServletResponse.class); OAuth2AuthenticationToken authentication = mock(OAuth2AuthenticationToken.class); ApplicationProperties.Security security = mock(ApplicationProperties.Security.class); - ApplicationProperties.Security.OAUTH2 oauth = mock(ApplicationProperties.Security.OAUTH2.class); + ApplicationProperties.Security.OAUTH2 oauth = + mock(ApplicationProperties.Security.OAUTH2.class); when(response.isCommitted()).thenReturn(false); when(request.getParameter(error)).thenReturn("true"); @@ -125,7 +130,8 @@ class CustomLogoutSuccessHandlerTest { HttpServletResponse response = mock(HttpServletResponse.class); OAuth2AuthenticationToken authentication = mock(OAuth2AuthenticationToken.class); ApplicationProperties.Security security = mock(ApplicationProperties.Security.class); - ApplicationProperties.Security.OAUTH2 oauth = mock(ApplicationProperties.Security.OAUTH2.class); + ApplicationProperties.Security.OAUTH2 oauth = + mock(ApplicationProperties.Security.OAUTH2.class); when(response.isCommitted()).thenReturn(false); when(request.getParameter("oAuth2AuthenticationErrorWeb")).thenReturn(null); @@ -151,7 +157,8 @@ class CustomLogoutSuccessHandlerTest { HttpServletResponse response = mock(HttpServletResponse.class); OAuth2AuthenticationToken authentication = mock(OAuth2AuthenticationToken.class); ApplicationProperties.Security security = mock(ApplicationProperties.Security.class); - ApplicationProperties.Security.OAUTH2 oauth = mock(ApplicationProperties.Security.OAUTH2.class); + ApplicationProperties.Security.OAUTH2 oauth = + mock(ApplicationProperties.Security.OAUTH2.class); when(response.isCommitted()).thenReturn(false); when(request.getParameter("oAuth2AuthenticationErrorWeb")).thenReturn(null); @@ -179,7 +186,8 @@ class CustomLogoutSuccessHandlerTest { HttpServletResponse response = mock(HttpServletResponse.class); OAuth2AuthenticationToken authentication = mock(OAuth2AuthenticationToken.class); ApplicationProperties.Security security = mock(ApplicationProperties.Security.class); - ApplicationProperties.Security.OAUTH2 oauth = mock(ApplicationProperties.Security.OAUTH2.class); + ApplicationProperties.Security.OAUTH2 oauth = + mock(ApplicationProperties.Security.OAUTH2.class); when(response.isCommitted()).thenReturn(false); when(request.getParameter("oAuth2AuthenticationErrorWeb")).thenReturn(null); @@ -209,7 +217,8 @@ class CustomLogoutSuccessHandlerTest { HttpServletResponse response = mock(HttpServletResponse.class); OAuth2AuthenticationToken authentication = mock(OAuth2AuthenticationToken.class); ApplicationProperties.Security security = mock(ApplicationProperties.Security.class); - ApplicationProperties.Security.OAUTH2 oauth = mock(ApplicationProperties.Security.OAUTH2.class); + ApplicationProperties.Security.OAUTH2 oauth = + mock(ApplicationProperties.Security.OAUTH2.class); when(response.isCommitted()).thenReturn(false); when(request.getParameter("oAuth2AuthenticationErrorWeb")).thenReturn(null); @@ -240,7 +249,8 @@ class CustomLogoutSuccessHandlerTest { HttpServletResponse response = mock(HttpServletResponse.class); OAuth2AuthenticationToken authentication = mock(OAuth2AuthenticationToken.class); ApplicationProperties.Security security = mock(ApplicationProperties.Security.class); - ApplicationProperties.Security.OAUTH2 oauth = mock(ApplicationProperties.Security.OAUTH2.class); + ApplicationProperties.Security.OAUTH2 oauth = + mock(ApplicationProperties.Security.OAUTH2.class); when(response.isCommitted()).thenReturn(false); when(request.getParameter("oAuth2AuthenticationErrorWeb")).thenReturn(null); diff --git a/src/test/java/stirling/software/SPDF/config/security/database/DatabaseConfigTest.java b/src/test/java/stirling/software/SPDF/config/security/database/DatabaseConfigTest.java index 6d933d721..0118e8e32 100644 --- a/src/test/java/stirling/software/SPDF/config/security/database/DatabaseConfigTest.java +++ b/src/test/java/stirling/software/SPDF/config/security/database/DatabaseConfigTest.java @@ -1,6 +1,12 @@ package stirling.software.SPDF.config.security.database; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + import javax.sql.DataSource; + import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -8,18 +14,14 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; + import stirling.software.SPDF.model.ApplicationProperties; import stirling.software.SPDF.model.exception.UnsupportedProviderException; -import static org.junit.jupiter.api.Assertions.assertInstanceOf; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class DatabaseConfigTest { - @Mock - private ApplicationProperties applicationProperties; + @Mock private ApplicationProperties applicationProperties; private DatabaseConfig databaseConfig; diff --git a/src/test/java/stirling/software/SPDF/controller/api/RearrangePagesPDFControllerTest.java b/src/test/java/stirling/software/SPDF/controller/api/RearrangePagesPDFControllerTest.java index 6c6852866..d6085b6f2 100644 --- a/src/test/java/stirling/software/SPDF/controller/api/RearrangePagesPDFControllerTest.java +++ b/src/test/java/stirling/software/SPDF/controller/api/RearrangePagesPDFControllerTest.java @@ -1,30 +1,23 @@ package stirling.software.SPDF.controller.api; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.CsvSource; - -import java.util.Arrays; -import java.util.List; - -import static org.junit.jupiter.api.Assertions.*; - -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import stirling.software.SPDF.service.CustomPDDocumentFactory; - -import java.util.Arrays; -import java.util.List; - import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import java.util.Arrays; +import java.util.List; + import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import stirling.software.SPDF.service.CustomPDFDocumentFactory; class RearrangePagesPDFControllerTest { - @Mock - private CustomPDDocumentFactory mockPdfDocumentFactory; + @Mock private CustomPDFDocumentFactory mockPdfDocumentFactory; private RearrangePagesPDFController sut; @@ -34,9 +27,7 @@ class RearrangePagesPDFControllerTest { sut = new RearrangePagesPDFController(mockPdfDocumentFactory); } - /** - * Tests the behavior of the oddEvenMerge method when there are no pages in the document. - */ + /** Tests the behavior of the oddEvenMerge method when there are no pages in the document. */ @Test void oddEvenMerge_noPages() { int totalNumberOfPages = 0; @@ -61,7 +52,8 @@ class RearrangePagesPDFControllerTest { } /** - * Tests the behavior of the oddEvenMerge method when there are even total pages in the document. + * Tests the behavior of the oddEvenMerge method when there are even total pages in the + * document. */ @Test void oddEvenMerge_evenTotalPageNumber() { @@ -77,25 +69,28 @@ class RearrangePagesPDFControllerTest { * Tests the behavior of the oddEvenMerge method with multiple test cases of multiple pages. * * @param totalNumberOfPages The total number of pages in the document. - * @param expectedPageOrder The expected order of the pages after rearranging. + * @param expectedPageOrder The expected order of the pages after rearranging. */ @ParameterizedTest @CsvSource({ - "1, '0'", - "2, '0,1'", - "3, '0,2,1'", - "4, '0,2,1,3'", - "5, '0,3,1,4,2'", - "6, '0,3,1,4,2,5'", - "10, '0,5,1,6,2,7,3,8,4,9'", - "50, '0,25,1,26,2,27,3,28,4,29,5,30,6,31,7,32,8,33,9,34,10,35," + - "11,36,12,37,13,38,14,39,15,40,16,41,17,42,18,43,19,44,20,45,21,46," + - "22,47,23,48,24,49'" + "1, '0'", + "2, '0,1'", + "3, '0,2,1'", + "4, '0,2,1,3'", + "5, '0,3,1,4,2'", + "6, '0,3,1,4,2,5'", + "10, '0,5,1,6,2,7,3,8,4,9'", + "50, '0,25,1,26,2,27,3,28,4,29,5,30,6,31,7,32,8,33,9,34,10,35," + + "11,36,12,37,13,38,14,39,15,40,16,41,17,42,18,43,19,44,20,45,21,46," + + "22,47,23,48,24,49'" }) void oddEvenMerge_multi_test(int totalNumberOfPages, String expectedPageOrder) { List newPageOrder = sut.oddEvenMerge(totalNumberOfPages); assertNotNull(newPageOrder, "Returning null instead of page order list"); - assertEquals(Arrays.stream(expectedPageOrder.split(",")).map(Integer::parseInt).toList(), newPageOrder, "Page order doesn't match"); + assertEquals( + Arrays.stream(expectedPageOrder.split(",")).map(Integer::parseInt).toList(), + newPageOrder, + "Page order doesn't match"); } } diff --git a/src/test/java/stirling/software/SPDF/controller/api/RotationControllerTest.java b/src/test/java/stirling/software/SPDF/controller/api/RotationControllerTest.java new file mode 100644 index 000000000..ec84b0e4c --- /dev/null +++ b/src/test/java/stirling/software/SPDF/controller/api/RotationControllerTest.java @@ -0,0 +1,77 @@ +package stirling.software.SPDF.controller.api; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.io.IOException; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.pdfbox.pdmodel.PDPageTree; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.http.ResponseEntity; +import org.springframework.mock.web.MockMultipartFile; + +import stirling.software.SPDF.model.api.general.RotatePDFRequest; +import stirling.software.SPDF.service.CustomPDFDocumentFactory; + +@ExtendWith(MockitoExtension.class) +public class RotationControllerTest { + + @Mock private CustomPDFDocumentFactory pdfDocumentFactory; + + @InjectMocks private RotationController rotationController; + + @Test + public void testRotatePDF() throws IOException { + // Create a mock file + MockMultipartFile mockFile = + new MockMultipartFile("file", "test.pdf", "application/pdf", new byte[] {1, 2, 3}); + RotatePDFRequest request = new RotatePDFRequest(); + request.setFileInput(mockFile); + request.setAngle(90); + + PDDocument mockDocument = mock(PDDocument.class); + PDPageTree mockPages = mock(PDPageTree.class); + PDPage mockPage = mock(PDPage.class); + + when(pdfDocumentFactory.load(request)).thenReturn(mockDocument); + when(mockDocument.getPages()).thenReturn(mockPages); + when(mockPages.iterator()) + .thenReturn(java.util.Collections.singletonList(mockPage).iterator()); + when(mockPage.getRotation()).thenReturn(0); + + // Act + ResponseEntity response = rotationController.rotatePDF(request); + + // Assert + verify(mockPage).setRotation(90); + assertNotNull(response); + assertEquals(200, response.getStatusCode().value()); + } + + @Test + public void testRotatePDFInvalidAngle() throws IOException { + // Create a mock file + MockMultipartFile mockFile = + new MockMultipartFile("file", "test.pdf", "application/pdf", new byte[] {1, 2, 3}); + RotatePDFRequest request = new RotatePDFRequest(); + request.setFileInput(mockFile); + request.setAngle(45); // Invalid angle + + // Act & Assert: Controller direkt aufrufen und Exception erwarten + IllegalArgumentException exception = + assertThrows( + IllegalArgumentException.class, + () -> rotationController.rotatePDF(request)); + assertEquals("Angle must be a multiple of 90", exception.getMessage()); + } +} diff --git a/src/test/java/stirling/software/SPDF/controller/api/converters/ConvertWebsiteToPdfTest.java b/src/test/java/stirling/software/SPDF/controller/api/converters/ConvertWebsiteToPdfTest.java index 986ca55c2..952d765fc 100644 --- a/src/test/java/stirling/software/SPDF/controller/api/converters/ConvertWebsiteToPdfTest.java +++ b/src/test/java/stirling/software/SPDF/controller/api/converters/ConvertWebsiteToPdfTest.java @@ -1,5 +1,8 @@ package stirling.software.SPDF.controller.api.converters; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mock; @@ -7,20 +10,13 @@ import org.mockito.MockitoAnnotations; import stirling.software.SPDF.config.RuntimePathConfig; import stirling.software.SPDF.model.api.converters.UrlToPdfRequest; -import stirling.software.SPDF.service.CustomPDDocumentFactory; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; +import stirling.software.SPDF.service.CustomPDFDocumentFactory; public class ConvertWebsiteToPdfTest { + @Mock private CustomPDFDocumentFactory mockPdfDocumentFactory; - @Mock - private CustomPDDocumentFactory mockPdfDocumentFactory; - - @Mock - private RuntimePathConfig runtimePathConfig; - + @Mock private RuntimePathConfig runtimePathConfig; private ConvertWebsiteToPDF convertWebsiteToPDF; @@ -38,9 +34,12 @@ public class ConvertWebsiteToPdfTest { UrlToPdfRequest request = new UrlToPdfRequest(); request.setUrlInput(invalid_format_Url); // Act - IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, () -> { - convertWebsiteToPDF.urlToPdf(request); - }); + IllegalArgumentException thrown = + assertThrows( + IllegalArgumentException.class, + () -> { + convertWebsiteToPDF.urlToPdf(request); + }); // Assert assertEquals("Invalid URL format provided.", thrown.getMessage()); } @@ -53,9 +52,12 @@ public class ConvertWebsiteToPdfTest { UrlToPdfRequest request = new UrlToPdfRequest(); request.setUrlInput(unreachable_Url); // Act - IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, () -> { - convertWebsiteToPDF.urlToPdf(request); - }); + IllegalArgumentException thrown = + assertThrows( + IllegalArgumentException.class, + () -> { + convertWebsiteToPDF.urlToPdf(request); + }); // Assert assertEquals("URL is not reachable, please provide a valid URL.", thrown.getMessage()); } diff --git a/src/test/java/stirling/software/SPDF/utils/FileInfoTest.java b/src/test/java/stirling/software/SPDF/utils/FileInfoTest.java index e83a062f8..6192d1dea 100644 --- a/src/test/java/stirling/software/SPDF/utils/FileInfoTest.java +++ b/src/test/java/stirling/software/SPDF/utils/FileInfoTest.java @@ -1,31 +1,32 @@ package stirling.software.SPDF.utils; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.CsvSource; +import static org.junit.jupiter.api.Assertions.assertEquals; import java.time.LocalDateTime; -import static org.junit.jupiter.api.Assertions.assertEquals; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; public class FileInfoTest { @ParameterizedTest(name = "{index}: fileSize={0}") @CsvSource({ - "0, '0 Bytes'", - "1023, '1023 Bytes'", - "1024, '1.00 KB'", - "1048575, '1024.00 KB'", // Do we really want this as result? - "1048576, '1.00 MB'", - "1073741823, '1024.00 MB'", // Do we really want this as result? - "1073741824, '1.00 GB'" + "0, '0 Bytes'", + "1023, '1023 Bytes'", + "1024, '1.00 KB'", + "1048575, '1024.00 KB'", // Do we really want this as result? + "1048576, '1.00 MB'", + "1073741823, '1024.00 MB'", // Do we really want this as result? + "1073741824, '1.00 GB'" }) void testGetFormattedFileSize(long fileSize, String expectedFormattedSize) { - FileInfo fileInfo = new FileInfo( - "example.txt", - "/path/to/example.txt", - LocalDateTime.now(), - fileSize, - LocalDateTime.now().minusDays(1)); + FileInfo fileInfo = + new FileInfo( + "example.txt", + "/path/to/example.txt", + LocalDateTime.now(), + fileSize, + LocalDateTime.now().minusDays(1)); assertEquals(expectedFormattedSize, fileInfo.getFormattedFileSize()); } diff --git a/src/test/java/stirling/software/SPDF/utils/FileToPdfTest.java b/src/test/java/stirling/software/SPDF/utils/FileToPdfTest.java index 8edb1b871..38e2ec3b8 100644 --- a/src/test/java/stirling/software/SPDF/utils/FileToPdfTest.java +++ b/src/test/java/stirling/software/SPDF/utils/FileToPdfTest.java @@ -1,17 +1,20 @@ package stirling.software.SPDF.utils; -import org.junit.jupiter.api.Test; -import stirling.software.SPDF.model.api.converters.HTMLToPdfRequest; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; import java.io.IOException; -import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; + +import stirling.software.SPDF.model.api.converters.HTMLToPdfRequest; public class FileToPdfTest { /** - * Test the HTML to PDF conversion. - * This test expects an IOException when an empty HTML input is provided. + * Test the HTML to PDF conversion. This test expects an IOException when an empty HTML input is + * provided. */ @Test public void testConvertHtmlToPdf() { @@ -31,8 +34,8 @@ public class FileToPdfTest { } /** - * Test sanitizeZipFilename with null or empty input. - * It should return an empty string in these cases. + * Test sanitizeZipFilename with null or empty input. It should return an empty string in these + * cases. */ @Test public void testSanitizeZipFilename_NullOrEmpty() { @@ -41,8 +44,8 @@ public class FileToPdfTest { } /** - * Test sanitizeZipFilename to ensure it removes path traversal sequences. - * This includes removing both forward and backward slash sequences. + * Test sanitizeZipFilename to ensure it removes path traversal sequences. This includes + * removing both forward and backward slash sequences. */ @Test public void testSanitizeZipFilename_RemovesTraversalSequences() { @@ -58,9 +61,7 @@ public class FileToPdfTest { assertEquals(expected, FileToPdf.sanitizeZipFilename(input)); } - /** - * Test sanitizeZipFilename to ensure that it removes leading drive letters and slashes. - */ + /** Test sanitizeZipFilename to ensure that it removes leading drive letters and slashes. */ @Test public void testSanitizeZipFilename_RemovesLeadingDriveAndSlashes() { String input = "C:\\folder\\file.txt"; @@ -72,9 +73,7 @@ public class FileToPdfTest { assertEquals(expected, FileToPdf.sanitizeZipFilename(input)); } - /** - * Test sanitizeZipFilename to verify that safe filenames remain unchanged. - */ + /** Test sanitizeZipFilename to verify that safe filenames remain unchanged. */ @Test public void testSanitizeZipFilename_NoChangeForSafeNames() { String input = "folder/subfolder/file.txt"; diff --git a/src/test/java/stirling/software/SPDF/utils/GeneralUtilsTest.java b/src/test/java/stirling/software/SPDF/utils/GeneralUtilsTest.java index 18afcad62..72389130d 100644 --- a/src/test/java/stirling/software/SPDF/utils/GeneralUtilsTest.java +++ b/src/test/java/stirling/software/SPDF/utils/GeneralUtilsTest.java @@ -1,161 +1,157 @@ package stirling.software.SPDF.utils; -import org.junit.jupiter.api.Test; - -import java.util.List; - import static org.junit.jupiter.api.Assertions.assertEquals; +import java.util.List; + +import org.junit.jupiter.api.Test; public class GeneralUtilsTest { - @Test void testParsePageListWithAll() { - List result = GeneralUtils.parsePageList(new String[]{"all"}, 5, false); + List result = GeneralUtils.parsePageList(new String[] {"all"}, 5, false); assertEquals(List.of(0, 1, 2, 3, 4), result, "'All' keyword should return all pages."); } @Test void testParsePageListWithAllOneBased() { - List result = GeneralUtils.parsePageList(new String[]{"all"}, 5, true); + List result = GeneralUtils.parsePageList(new String[] {"all"}, 5, true); assertEquals(List.of(1, 2, 3, 4, 5), result, "'All' keyword should return all pages."); } @Test void nFunc() { - List result = GeneralUtils.parsePageList(new String[]{"n"}, 5, true); + List result = GeneralUtils.parsePageList(new String[] {"n"}, 5, true); assertEquals(List.of(1, 2, 3, 4, 5), result, "'n' keyword should return all pages."); } @Test void nFuncAdvanced() { - List result = GeneralUtils.parsePageList(new String[]{"4n"}, 9, true); - //skip 0 as not valid + List result = GeneralUtils.parsePageList(new String[] {"4n"}, 9, true); + // skip 0 as not valid assertEquals(List.of(4, 8), result, "'All' keyword should return all pages."); } @Test void nFuncAdvancedZero() { - List result = GeneralUtils.parsePageList(new String[]{"4n"}, 9, false); - //skip 0 as not valid + List result = GeneralUtils.parsePageList(new String[] {"4n"}, 9, false); + // skip 0 as not valid assertEquals(List.of(3, 7), result, "'All' keyword should return all pages."); } @Test void nFuncAdvanced2() { - List result = GeneralUtils.parsePageList(new String[]{"4n-1"}, 9, true); + List result = GeneralUtils.parsePageList(new String[] {"4n-1"}, 9, true); // skip -1 as not valid assertEquals(List.of(3, 7), result, "4n-1 should do (0-1), (4-1), (8-1)"); } @Test void nFuncAdvanced3() { - List result = GeneralUtils.parsePageList(new String[]{"4n+1"}, 9, true); + List result = GeneralUtils.parsePageList(new String[] {"4n+1"}, 9, true); assertEquals(List.of(5, 9), result, "'All' keyword should return all pages."); } @Test void nFunc_spaces() { - List result = GeneralUtils.parsePageList(new String[]{"n + 1"}, 9, true); + List result = GeneralUtils.parsePageList(new String[] {"n + 1"}, 9, true); assertEquals(List.of(2, 3, 4, 5, 6, 7, 8, 9), result); } @Test void nFunc_consecutive_Ns_nnn() { - List result = GeneralUtils.parsePageList(new String[]{"nnn"}, 9, true); + List result = GeneralUtils.parsePageList(new String[] {"nnn"}, 9, true); assertEquals(List.of(1, 8), result); } @Test void nFunc_consecutive_Ns_nn() { - List result = GeneralUtils.parsePageList(new String[]{"nn"}, 9, true); + List result = GeneralUtils.parsePageList(new String[] {"nn"}, 9, true); assertEquals(List.of(1, 4, 9), result); } @Test void nFunc_opening_closing_round_brackets() { - List result = GeneralUtils.parsePageList(new String[]{"(n-1)(n-2)"}, 9, true); + List result = GeneralUtils.parsePageList(new String[] {"(n-1)(n-2)"}, 9, true); assertEquals(List.of(2, 6), result); } @Test void nFunc_opening_round_brackets() { - List result = GeneralUtils.parsePageList(new String[]{"2(n-1)"}, 9, true); + List result = GeneralUtils.parsePageList(new String[] {"2(n-1)"}, 9, true); assertEquals(List.of(2, 4, 6, 8), result); } @Test void nFunc_opening_round_brackets_n() { - List result = GeneralUtils.parsePageList(new String[]{"n(n-1)"}, 9, true); + List result = GeneralUtils.parsePageList(new String[] {"n(n-1)"}, 9, true); assertEquals(List.of(2, 6), result); } @Test void nFunc_closing_round_brackets() { - List result = GeneralUtils.parsePageList(new String[]{"(n-1)2"}, 9, true); + List result = GeneralUtils.parsePageList(new String[] {"(n-1)2"}, 9, true); assertEquals(List.of(2, 4, 6, 8), result); } @Test void nFunc_closing_round_brackets_n() { - List result = GeneralUtils.parsePageList(new String[]{"(n-1)n"}, 9, true); + List result = GeneralUtils.parsePageList(new String[] {"(n-1)n"}, 9, true); assertEquals(List.of(2, 6), result); } @Test void nFunc_function_surrounded_with_brackets() { - List result = GeneralUtils.parsePageList(new String[]{"(n-1)"}, 9, true); + List result = GeneralUtils.parsePageList(new String[] {"(n-1)"}, 9, true); assertEquals(List.of(1, 2, 3, 4, 5, 6, 7, 8), result); } - @Test void nFuncAdvanced4() { - List result = GeneralUtils.parsePageList(new String[]{"3+2n"}, 9, true); + List result = GeneralUtils.parsePageList(new String[] {"3+2n"}, 9, true); assertEquals(List.of(5, 7, 9), result, "'All' keyword should return all pages."); } @Test void nFuncAdvancedZerobased() { - List result = GeneralUtils.parsePageList(new String[]{"4n"}, 9, false); + List result = GeneralUtils.parsePageList(new String[] {"4n"}, 9, false); assertEquals(List.of(3, 7), result, "'All' keyword should return all pages."); } @Test void nFuncAdvanced2Zerobased() { - List result = GeneralUtils.parsePageList(new String[]{"4n-1"}, 9, false); + List result = GeneralUtils.parsePageList(new String[] {"4n-1"}, 9, false); assertEquals(List.of(2, 6), result, "'All' keyword should return all pages."); } @Test void testParsePageListWithRangeOneBasedOutput() { - List result = GeneralUtils.parsePageList(new String[]{"1-3"}, 5, true); + List result = GeneralUtils.parsePageList(new String[] {"1-3"}, 5, true); assertEquals(List.of(1, 2, 3), result, "Range should be parsed correctly."); } @Test void testParsePageListWithRangeZeroBaseOutput() { - List result = GeneralUtils.parsePageList(new String[]{"1-3"}, 5, false); + List result = GeneralUtils.parsePageList(new String[] {"1-3"}, 5, false); assertEquals(List.of(0, 1, 2), result, "Range should be parsed correctly."); } - @Test void testParsePageListWithRangeOneBasedOutputFull() { - List result = GeneralUtils.parsePageList(new String[]{"1,3,7-8"}, 8, true); + List result = GeneralUtils.parsePageList(new String[] {"1,3,7-8"}, 8, true); assertEquals(List.of(1, 3, 7, 8), result, "Range should be parsed correctly."); } @Test void testParsePageListWithRangeOneBasedOutputFullOutOfRange() { - List result = GeneralUtils.parsePageList(new String[]{"1,3,7-8"}, 5, true); + List result = GeneralUtils.parsePageList(new String[] {"1,3,7-8"}, 5, true); assertEquals(List.of(1, 3), result, "Range should be parsed correctly."); } @Test void testParsePageListWithRangeZeroBaseOutputFull() { - List result = GeneralUtils.parsePageList(new String[]{"1,3,7-8"}, 8, false); + List result = GeneralUtils.parsePageList(new String[] {"1,3,7-8"}, 8, false); assertEquals(List.of(0, 2, 6, 7), result, "Range should be parsed correctly."); } } diff --git a/src/test/java/stirling/software/SPDF/utils/ImageProcessingUtilsTest.java b/src/test/java/stirling/software/SPDF/utils/ImageProcessingUtilsTest.java index af4bc22fb..dc4a94d7b 100644 --- a/src/test/java/stirling/software/SPDF/utils/ImageProcessingUtilsTest.java +++ b/src/test/java/stirling/software/SPDF/utils/ImageProcessingUtilsTest.java @@ -1,11 +1,13 @@ package stirling.software.SPDF.utils; -import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.awt.*; import java.awt.image.BufferedImage; -import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; public class ImageProcessingUtilsTest { @@ -14,7 +16,8 @@ public class ImageProcessingUtilsTest { BufferedImage sourceImage = new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB); fillImageWithColor(sourceImage, Color.RED); - BufferedImage convertedImage = ImageProcessingUtils.convertColorType(sourceImage, "greyscale"); + BufferedImage convertedImage = + ImageProcessingUtils.convertColorType(sourceImage, "greyscale"); assertNotNull(convertedImage); assertEquals(BufferedImage.TYPE_BYTE_GRAY, convertedImage.getType()); @@ -32,7 +35,8 @@ public class ImageProcessingUtilsTest { BufferedImage sourceImage = new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB); fillImageWithColor(sourceImage, Color.RED); - BufferedImage convertedImage = ImageProcessingUtils.convertColorType(sourceImage, "blackwhite"); + BufferedImage convertedImage = + ImageProcessingUtils.convertColorType(sourceImage, "blackwhite"); assertNotNull(convertedImage); assertEquals(BufferedImage.TYPE_BYTE_BINARY, convertedImage.getType()); @@ -49,7 +53,8 @@ public class ImageProcessingUtilsTest { BufferedImage sourceImage = new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB); fillImageWithColor(sourceImage, Color.RED); - BufferedImage convertedImage = ImageProcessingUtils.convertColorType(sourceImage, "fullcolor"); + BufferedImage convertedImage = + ImageProcessingUtils.convertColorType(sourceImage, "fullcolor"); assertNotNull(convertedImage); assertEquals(sourceImage, convertedImage); @@ -60,7 +65,8 @@ public class ImageProcessingUtilsTest { BufferedImage sourceImage = new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB); fillImageWithColor(sourceImage, Color.RED); - BufferedImage convertedImage = ImageProcessingUtils.convertColorType(sourceImage, "invalidtype"); + BufferedImage convertedImage = + ImageProcessingUtils.convertColorType(sourceImage, "invalidtype"); assertNotNull(convertedImage); assertEquals(sourceImage, convertedImage); diff --git a/src/test/java/stirling/software/SPDF/utils/PdfUtilsTest.java b/src/test/java/stirling/software/SPDF/utils/PdfUtilsTest.java index e57e92035..a03564ee9 100644 --- a/src/test/java/stirling/software/SPDF/utils/PdfUtilsTest.java +++ b/src/test/java/stirling/software/SPDF/utils/PdfUtilsTest.java @@ -1,5 +1,15 @@ package stirling.software.SPDF.utils; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.IOException; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + import org.apache.pdfbox.cos.COSName; import org.apache.pdfbox.pdmodel.PDPage; import org.apache.pdfbox.pdmodel.PDResources; @@ -8,13 +18,6 @@ import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject; import org.junit.jupiter.api.Test; import org.mockito.Mockito; -import java.io.IOException; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; - -import static org.junit.jupiter.api.Assertions.*; - public class PdfUtilsTest { @Test @@ -46,6 +49,4 @@ public class PdfUtilsTest { assertTrue(PdfUtils.hasImagesOnPage(page)); } - - } diff --git a/src/test/java/stirling/software/SPDF/utils/ProcessExecutorTest.java b/src/test/java/stirling/software/SPDF/utils/ProcessExecutorTest.java index 10910b125..871a1678c 100644 --- a/src/test/java/stirling/software/SPDF/utils/ProcessExecutorTest.java +++ b/src/test/java/stirling/software/SPDF/utils/ProcessExecutorTest.java @@ -1,13 +1,16 @@ package stirling.software.SPDF.utils; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.IOException; import java.util.ArrayList; import java.util.List; -import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; public class ProcessExecutorTest { @@ -27,7 +30,8 @@ public class ProcessExecutorTest { command.add("-version"); // Execute the command - ProcessExecutor.ProcessExecutorResult result = processExecutor.runCommandWithOutputHandling(command); + ProcessExecutor.ProcessExecutorResult result = + processExecutor.runCommandWithOutputHandling(command); // Check the exit code and output messages assertEquals(0, result.getRc()); @@ -41,15 +45,21 @@ public class ProcessExecutorTest { command.add("nonexistent-command"); // Execute the command and expect an IOException - IOException thrown = assertThrows(IOException.class, () -> { - processExecutor.runCommandWithOutputHandling(command); - }); + IOException thrown = + assertThrows( + IOException.class, + () -> { + processExecutor.runCommandWithOutputHandling(command); + }); // Log the actual error message System.out.println("Caught IOException: " + thrown.getMessage()); // Check the exception message to ensure it indicates the command was not found String errorMessage = thrown.getMessage(); - assertTrue(errorMessage.contains("error=2") || errorMessage.contains("No such file or directory"), "Unexpected error message: " + errorMessage); + assertTrue( + errorMessage.contains("error=2") + || errorMessage.contains("No such file or directory"), + "Unexpected error message: " + errorMessage); } } diff --git a/src/test/java/stirling/software/SPDF/utils/PropertyConfigsTest.java b/src/test/java/stirling/software/SPDF/utils/PropertyConfigsTest.java index 7909f1d9f..be0605735 100644 --- a/src/test/java/stirling/software/SPDF/utils/PropertyConfigsTest.java +++ b/src/test/java/stirling/software/SPDF/utils/PropertyConfigsTest.java @@ -1,11 +1,11 @@ package stirling.software.SPDF.utils; -import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.Arrays; import java.util.List; -import static org.junit.jupiter.api.Assertions.assertEquals; +import org.junit.jupiter.api.Test; public class PropertyConfigsTest { diff --git a/src/test/java/stirling/software/SPDF/utils/RequestUriUtilsTest.java b/src/test/java/stirling/software/SPDF/utils/RequestUriUtilsTest.java index 7b586f472..f18196030 100644 --- a/src/test/java/stirling/software/SPDF/utils/RequestUriUtilsTest.java +++ b/src/test/java/stirling/software/SPDF/utils/RequestUriUtilsTest.java @@ -1,10 +1,10 @@ package stirling.software.SPDF.utils; -import org.junit.jupiter.api.Test; - import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.Test; + public class RequestUriUtilsTest { @Test diff --git a/src/test/java/stirling/software/SPDF/utils/UrlUtilsTest.java b/src/test/java/stirling/software/SPDF/utils/UrlUtilsTest.java index c17c0e747..c6383accb 100644 --- a/src/test/java/stirling/software/SPDF/utils/UrlUtilsTest.java +++ b/src/test/java/stirling/software/SPDF/utils/UrlUtilsTest.java @@ -1,10 +1,11 @@ package stirling.software.SPDF.utils; -import jakarta.servlet.http.HttpServletRequest; +import static org.junit.jupiter.api.Assertions.assertEquals; + import org.junit.jupiter.api.Test; import org.mockito.Mockito; -import static org.junit.jupiter.api.Assertions.assertEquals; +import jakarta.servlet.http.HttpServletRequest; public class UrlUtilsTest { diff --git a/src/test/java/stirling/software/SPDF/utils/WebResponseUtilsTest.java b/src/test/java/stirling/software/SPDF/utils/WebResponseUtilsTest.java index 49ecab328..9175e7a1b 100644 --- a/src/test/java/stirling/software/SPDF/utils/WebResponseUtilsTest.java +++ b/src/test/java/stirling/software/SPDF/utils/WebResponseUtilsTest.java @@ -1,5 +1,12 @@ package stirling.software.SPDF.utils; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.fail; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + import org.apache.pdfbox.pdmodel.PDDocument; import org.junit.jupiter.api.Test; import org.springframework.http.HttpHeaders; @@ -8,11 +15,6 @@ import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.mock.web.MockMultipartFile; -import java.io.ByteArrayOutputStream; -import java.io.IOException; - -import static org.junit.jupiter.api.Assertions.*; - public class WebResponseUtilsTest { @Test @@ -22,7 +24,8 @@ public class WebResponseUtilsTest { baos.write("Sample PDF content".getBytes()); String docName = "sample.pdf"; - ResponseEntity responseEntity = WebResponseUtils.boasToWebResponse(baos, docName); + ResponseEntity responseEntity = + WebResponseUtils.boasToWebResponse(baos, docName); assertNotNull(responseEntity); assertEquals(HttpStatus.OK, responseEntity.getStatusCode()); @@ -32,7 +35,8 @@ public class WebResponseUtilsTest { assertNotNull(headers); assertEquals(MediaType.APPLICATION_PDF, headers.getContentType()); assertNotNull(headers.getContentDisposition()); - //assertEquals("attachment; filename=\"sample.pdf\"", headers.getContentDisposition().toString()); + // assertEquals("attachment; filename=\"sample.pdf\"", + // headers.getContentDisposition().toString()); } catch (IOException e) { fail("Exception thrown: " + e.getMessage()); @@ -43,9 +47,11 @@ public class WebResponseUtilsTest { public void testMultiPartFileToWebResponse() { try { byte[] fileContent = "Sample file content".getBytes(); - MockMultipartFile file = new MockMultipartFile("file", "sample.txt", "text/plain", fileContent); + MockMultipartFile file = + new MockMultipartFile("file", "sample.txt", "text/plain", fileContent); - ResponseEntity responseEntity = WebResponseUtils.multiPartFileToWebResponse(file); + ResponseEntity responseEntity = + WebResponseUtils.multiPartFileToWebResponse(file); assertNotNull(responseEntity); assertEquals(HttpStatus.OK, responseEntity.getStatusCode()); @@ -68,7 +74,8 @@ public class WebResponseUtilsTest { String docName = "sample.txt"; MediaType mediaType = MediaType.TEXT_PLAIN; - ResponseEntity responseEntity = WebResponseUtils.bytesToWebResponse(bytes, docName, mediaType); + ResponseEntity responseEntity = + WebResponseUtils.bytesToWebResponse(bytes, docName, mediaType); assertNotNull(responseEntity); assertEquals(HttpStatus.OK, responseEntity.getStatusCode()); @@ -79,7 +86,6 @@ public class WebResponseUtilsTest { assertEquals(MediaType.TEXT_PLAIN, headers.getContentType()); assertNotNull(headers.getContentDisposition()); - } catch (IOException e) { fail("Exception thrown: " + e.getMessage()); } @@ -92,7 +98,8 @@ public class WebResponseUtilsTest { document.addPage(new org.apache.pdfbox.pdmodel.PDPage()); String docName = "sample.pdf"; - ResponseEntity responseEntity = WebResponseUtils.pdfDocToWebResponse(document, docName); + ResponseEntity responseEntity = + WebResponseUtils.pdfDocToWebResponse(document, docName); assertNotNull(responseEntity); assertEquals(HttpStatus.OK, responseEntity.getStatusCode()); @@ -103,7 +110,6 @@ public class WebResponseUtilsTest { assertEquals(MediaType.APPLICATION_PDF, headers.getContentType()); assertNotNull(headers.getContentDisposition()); - } catch (IOException e) { fail("Exception thrown: " + e.getMessage()); } diff --git a/src/test/java/stirling/software/SPDF/utils/validation/ValidatorTest.java b/src/test/java/stirling/software/SPDF/utils/validation/ValidatorTest.java index 1e2b075ac..1ec8acd91 100644 --- a/src/test/java/stirling/software/SPDF/utils/validation/ValidatorTest.java +++ b/src/test/java/stirling/software/SPDF/utils/validation/ValidatorTest.java @@ -1,24 +1,25 @@ package stirling.software.SPDF.utils.validation; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.List; +import java.util.stream.Stream; + import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import org.mockito.junit.jupiter.MockitoExtension; + import stirling.software.SPDF.model.UsernameAttribute; import stirling.software.SPDF.model.provider.GitHubProvider; import stirling.software.SPDF.model.provider.GoogleProvider; -import stirling.software.SPDF.model.provider.KeycloakProvider; import stirling.software.SPDF.model.provider.Provider; -import java.util.List; -import java.util.stream.Stream; - -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - @ExtendWith(MockitoExtension.class) class ValidatorTest { @@ -41,14 +42,10 @@ class ValidatorTest { public static Stream providerParams() { Provider generic = null; - var google = new GoogleProvider(null, "clientSecret", List.of("scope"), UsernameAttribute.EMAIL); + var google = + new GoogleProvider(null, "clientSecret", List.of("scope"), UsernameAttribute.EMAIL); var github = new GitHubProvider("clientId", "", List.of("scope"), UsernameAttribute.LOGIN); - return Stream.of( - Arguments.of(generic), - Arguments.of(google), - Arguments.of(github) - ); + return Stream.of(Arguments.of(generic), Arguments.of(google), Arguments.of(github)); } - }