mirror of
https://github.com/Frooodle/Stirling-PDF.git
synced 2025-09-12 17:52:13 +02:00
Merge branch 'Stirling-Tools:main' into main
This commit is contained in:
commit
cc3bff2f52
3
.github/labeler-config.yml
vendored
3
.github/labeler-config.yml
vendored
@ -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'
|
||||
|
4
.github/workflows/swagger.yml
vendored
4
.github/workflows/swagger.yml
vendored
@ -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"
|
||||
|
6
.gitignore
vendored
6
.gitignore
vendored
@ -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
|
||||
|
@ -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 .
|
||||
|
12
README.md
12
README.md
@ -120,17 +120,17 @@ Stirling-PDF currently supports 39 languages!
|
||||
| Azerbaijani (Azərbaycan Dili) (az_AZ) |  |
|
||||
| Basque (Euskara) (eu_ES) |  |
|
||||
| Bulgarian (Български) (bg_BG) |  |
|
||||
| Catalan (Català) (ca_CA) |  |
|
||||
| Catalan (Català) (ca_CA) |  |
|
||||
| Croatian (Hrvatski) (hr_HR) |  |
|
||||
| Czech (Česky) (cs_CZ) |  |
|
||||
| Danish (Dansk) (da_DK) |  |
|
||||
| Dutch (Nederlands) (nl_NL) |  |
|
||||
| Dutch (Nederlands) (nl_NL) |  |
|
||||
| English (English) (en_GB) |  |
|
||||
| English (US) (en_US) |  |
|
||||
| French (Français) (fr_FR) |  |
|
||||
| German (Deutsch) (de_DE) |  |
|
||||
| Greek (Ελληνικά) (el_GR) |  |
|
||||
| Hindi (हिंदी) (hi_IN) |  |
|
||||
| Hindi (हिंदी) (hi_IN) |  |
|
||||
| Hungarian (Magyar) (hu_HU) |  |
|
||||
| Indonesian (Bahasa Indonesia) (id_ID) |  |
|
||||
| Irish (Gaeilge) (ga_IE) |  |
|
||||
@ -138,7 +138,7 @@ Stirling-PDF currently supports 39 languages!
|
||||
| Japanese (日本語) (ja_JP) |  |
|
||||
| Korean (한국어) (ko_KR) |  |
|
||||
| Norwegian (Norsk) (no_NB) |  |
|
||||
| Persian (فارسی) (fa_IR) |  |
|
||||
| Persian (فارسی) (fa_IR) |  |
|
||||
| Polish (Polski) (pl_PL) |  |
|
||||
| Portuguese (Português) (pt_PT) |  |
|
||||
| Portuguese Brazilian (Português) (pt_BR) |  |
|
||||
@ -146,7 +146,7 @@ Stirling-PDF currently supports 39 languages!
|
||||
| Russian (Русский) (ru_RU) |  |
|
||||
| Serbian Latin alphabet (Srpski) (sr_LATN_RS) |  |
|
||||
| Simplified Chinese (简体中文) (zh_CN) |  |
|
||||
| Slovakian (Slovensky) (sk_SK) |  |
|
||||
| Slovakian (Slovensky) (sk_SK) |  |
|
||||
| Slovenian (Slovenščina) (sl_SI) |  |
|
||||
| Spanish (Español) (es_ES) |  |
|
||||
| Swedish (Svenska) (sv_SE) |  |
|
||||
@ -154,7 +154,7 @@ Stirling-PDF currently supports 39 languages!
|
||||
| Tibetan (བོད་ཡིག་) (zh_BO) |  |
|
||||
| Traditional Chinese (繁體中文) (zh_TW) |  |
|
||||
| Turkish (Türkçe) (tr_TR) |  |
|
||||
| Ukrainian (Українська) (uk_UA) |  |
|
||||
| Ukrainian (Українська) (uk_UA) |  |
|
||||
| Vietnamese (Tiếng Việt) (vi_VN) |  |
|
||||
|
||||
|
||||
|
35
build.gradle
35
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")
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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<byte[]> cropPdf(@ModelAttribute CropPdfForm form) throws IOException {
|
||||
PDDocument sourceDocument = pdfDocumentFactory.load(form);
|
||||
|
||||
|
@ -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<byte[]> mergePdfs(@ModelAttribute MergePdfsRequest form)
|
||||
throws IOException {
|
||||
List<File> filesToDelete = new ArrayList<>(); // List of temporary files to delete
|
||||
|
@ -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<byte[]> mergeMultiplePagesIntoOne(
|
||||
@ModelAttribute MergeMultiplePagesRequest request) throws IOException {
|
||||
|
||||
|
@ -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<byte[]> removeImages(@ModelAttribute PDFFile file) throws IOException {
|
||||
// Load the PDF document
|
||||
PDDocument document = pdfDocumentFactory.load(file);
|
||||
|
@ -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<byte[]> overlayPdfs(@ModelAttribute OverlayPdfsRequest request)
|
||||
throws IOException {
|
||||
MultipartFile baseFile = request.getFileInput();
|
||||
|
@ -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<byte[]> 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<byte[]> rearrangePages(@ModelAttribute RearrangePagesRequest request)
|
||||
throws IOException {
|
||||
MultipartFile pdfFile = request.getFileInput();
|
||||
|
@ -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<byte[]> 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);
|
||||
|
||||
|
@ -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<byte[]> 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<String, PDRectangle> getSizeMap() {
|
||||
|
@ -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<byte[]> splitPdf(@ModelAttribute PDFWithPageNums request)
|
||||
throws IOException {
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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<byte[]> splitPdf(@ModelAttribute SplitPdfBySectionsRequest request)
|
||||
throws Exception {
|
||||
List<ByteArrayOutputStream> splitDocumentsBoas = new ArrayList<>();
|
||||
|
@ -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<byte[]> autoSplitPdf(@ModelAttribute SplitPdfBySizeOrCountRequest request)
|
||||
throws Exception {
|
||||
|
||||
|
@ -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<byte[]> pdfToSinglePage(@ModelAttribute PDFFile request)
|
||||
throws IOException {
|
||||
|
||||
|
@ -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<byte[]> HtmlToPdf(@ModelAttribute HTMLToPdfRequest request)
|
||||
throws Exception {
|
||||
MultipartFile fileInput = request.getFileInput();
|
||||
|
@ -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<byte[]> 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<byte[]> convertToPdf(@ModelAttribute ConvertToPdfRequest request)
|
||||
throws IOException {
|
||||
MultipartFile[] file = request.getFileInput();
|
||||
|
@ -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<byte[]> markdownToPdf(@ModelAttribute GeneralFile request)
|
||||
throws Exception {
|
||||
MultipartFile fileInput = request.getFileInput();
|
||||
|
@ -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<byte[]> processFileToPDF(@ModelAttribute GeneralFile request)
|
||||
throws Exception {
|
||||
MultipartFile inputFile = request.getFileInput();
|
||||
|
@ -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<byte[]> 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<byte[]> 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<byte[]> 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<byte[]> processPdfToXML(@ModelAttribute PDFFile request)
|
||||
throws Exception {
|
||||
MultipartFile inputFile = request.getFileInput();
|
||||
|
@ -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<byte[]> urlToPdf(@ModelAttribute UrlToPdfRequest request)
|
||||
throws IOException, InterruptedException {
|
||||
String URL = request.getUrlInput();
|
||||
|
@ -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<CsvEntry> csvEntries = new ArrayList<>();
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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<byte[]> extractHeader(@ModelAttribute ExtractHeaderRequest request)
|
||||
throws Exception {
|
||||
MultipartFile file = request.getFileInput();
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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<byte[]> removeBlankPages(@ModelAttribute RemoveBlankPagesRequest request)
|
||||
throws IOException, InterruptedException {
|
||||
MultipartFile inputFile = request.getFileInput();
|
||||
|
@ -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<String> processedImages = new HashSet<>();
|
||||
try (PDDocument doc = pdfDocumentFactory.load(pdfFile)) {
|
||||
|
||||
// Collect all unique images by content hash
|
||||
Map<String, List<ImageReference>> uniqueImages = new HashMap<>();
|
||||
Map<String, PDImageXObject> 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<String, List<ImageReference>> entry : uniqueImages.entrySet()) {
|
||||
String imageHash = entry.getKey();
|
||||
List<ImageReference> 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<ImageWriter> 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<ImageWriter> 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<ImageWriter> 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<byte[]> 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<Path> 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<String> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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<byte[]> extractImageScans(
|
||||
@RequestBody(
|
||||
description = "Form data containing file and extraction parameters",
|
||||
|
@ -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<byte[]> extractImages(@ModelAttribute PDFExtractImagesRequest request)
|
||||
throws IOException, InterruptedException, ExecutionException {
|
||||
MultipartFile file = request.getFileInput();
|
||||
|
@ -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<byte[]> flatten(@ModelAttribute FlattenRequest request) throws Exception {
|
||||
MultipartFile file = request.getFileInput();
|
||||
|
||||
|
@ -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<byte[]> metadata(@ModelAttribute MetadataRequest request)
|
||||
throws IOException {
|
||||
|
||||
|
@ -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<byte[]> processPdfWithOCR(
|
||||
@ModelAttribute ProcessPdfWithOcrRequest request)
|
||||
throws IOException, InterruptedException {
|
||||
|
@ -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<byte[]> overlayImage(@ModelAttribute OverlayImageRequest request) {
|
||||
MultipartFile pdfFile = request.getFileInput();
|
||||
MultipartFile imageFile = request.getImageFile();
|
||||
|
@ -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<byte[]> addPageNumbers(@ModelAttribute AddPageNumbersRequest request)
|
||||
throws IOException {
|
||||
|
||||
|
@ -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<byte[]> repairPdf(@ModelAttribute PDFFile request)
|
||||
throws IOException, InterruptedException {
|
||||
MultipartFile inputFile = request.getFileInput();
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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<byte[]> addStamp(@ModelAttribute AddStampRequest request)
|
||||
throws IOException, Exception {
|
||||
MultipartFile pdfFile = request.getFileInput();
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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<byte[]> 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<byte[]> addPassword(@ModelAttribute AddPasswordRequest request)
|
||||
throws IOException {
|
||||
MultipartFile fileInput = request.getFileInput();
|
||||
|
@ -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<byte[]> 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<byte[]> redactPdf(@ModelAttribute RedactPdfRequest request)
|
||||
throws Exception {
|
||||
MultipartFile file = request.getFileInput();
|
||||
|
@ -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<byte[]> removeCertSignPDF(@ModelAttribute PDFFile request)
|
||||
throws Exception {
|
||||
MultipartFile pdf = request.getFileInput();
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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<List<SignatureValidationResult>> validateSignature(
|
||||
@ModelAttribute SignatureValidationRequest request) throws IOException {
|
||||
|
@ -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<byte[]> addWatermark(@ModelAttribute AddWatermarkRequest request)
|
||||
throws IOException, Exception {
|
||||
MultipartFile pdfFile = request.getFileInput();
|
||||
|
@ -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);
|
@ -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,
|
||||
|
@ -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=إصلاح
|
||||
|
@ -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
|
||||
|
@ -860,7 +860,8 @@ sign.last=Последна страница
|
||||
sign.next=Следваща страница
|
||||
sign.previous=Предишна стараница
|
||||
sign.maintainRatio=Превключване за поддържане на съотношението на страните
|
||||
|
||||
sign.undo=Undo
|
||||
sign.redo=Redo
|
||||
|
||||
#repair
|
||||
repair.title=Поправи
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -860,7 +860,8 @@ sign.last=Τελευταία σελίδα
|
||||
sign.next=Επόμενη σελίδα
|
||||
sign.previous=Προηγούμενη σελίδα
|
||||
sign.maintainRatio=Εναλλαγή διατήρησης αναλογίας διαστάσεων
|
||||
|
||||
sign.undo=Undo
|
||||
sign.redo=Redo
|
||||
|
||||
#repair
|
||||
repair.title=Επιδιόρθωση
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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=تعمیر
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -860,7 +860,8 @@ sign.last=अंतिम पृष्ठ
|
||||
sign.next=अगला पृष्ठ
|
||||
sign.previous=पिछला पृष्ठ
|
||||
sign.maintainRatio=आनुपातिक अनुपात बनाए रखें टॉगल करें
|
||||
|
||||
sign.undo=Undo
|
||||
sign.redo=Redo
|
||||
|
||||
#repair
|
||||
repair.title=मरम्मत
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -860,7 +860,8 @@ sign.last=最後のページ
|
||||
sign.next=次のページ
|
||||
sign.previous=前のページ
|
||||
sign.maintainRatio=アスペクト比を維持を切替え
|
||||
|
||||
sign.undo=Undo
|
||||
sign.redo=Redo
|
||||
|
||||
#repair
|
||||
repair.title=修復
|
||||
|
@ -860,7 +860,8 @@ sign.last=마지막 페이지
|
||||
sign.next=다음 페이지
|
||||
sign.previous=이전 페이지
|
||||
sign.maintainRatio=종횡비 유지 토글
|
||||
|
||||
sign.undo=Undo
|
||||
sign.redo=Redo
|
||||
|
||||
#repair
|
||||
repair.title=복구
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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ă
|
||||
|
@ -860,7 +860,8 @@ sign.last=Последняя страница
|
||||
sign.next=Следующая страница
|
||||
sign.previous=Предыдущая страница
|
||||
sign.maintainRatio=Переключить сохранение пропорций
|
||||
|
||||
sign.undo=Undo
|
||||
sign.redo=Redo
|
||||
|
||||
#repair
|
||||
repair.title=Восстановление
|
||||
|
@ -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ť
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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=ซ่อมแซม
|
||||
|
@ -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
|
||||
|
@ -860,7 +860,8 @@ sign.last=Остання сторінка
|
||||
sign.next=Наступна сторінка
|
||||
sign.previous=Попередня сторінка
|
||||
sign.maintainRatio=Переключити збереження пропорцій
|
||||
|
||||
sign.undo=Undo
|
||||
sign.redo=Redo
|
||||
|
||||
#repair
|
||||
repair.title=Ремонт
|
||||
|
@ -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
|
||||
|
@ -860,7 +860,8 @@ sign.last=ཤོག་ངོས་མཐའ་མ།
|
||||
sign.next=ཤོག་ངོས་རྗེས་མ།
|
||||
sign.previous=ཤོག་ངོས་སྔོན་མ།
|
||||
sign.maintainRatio=བསྡུར་ཚད་རྒྱུན་འཁྱོངས་སྒོ་རྒྱག་པ།
|
||||
|
||||
sign.undo=Undo
|
||||
sign.redo=Redo
|
||||
|
||||
#repair
|
||||
repair.title=བཟོ་བཅོས།
|
||||
|
@ -860,7 +860,8 @@ sign.last=末页
|
||||
sign.next=下一页
|
||||
sign.previous=上一页
|
||||
sign.maintainRatio=切换保持长宽比
|
||||
|
||||
sign.undo=撤销
|
||||
sign.redo=重做
|
||||
|
||||
#repair
|
||||
repair.title=修复
|
||||
|
@ -860,7 +860,8 @@ sign.last=最後一頁
|
||||
sign.next=下一頁
|
||||
sign.previous=上一頁
|
||||
sign.maintainRatio=切換維持長寬比
|
||||
|
||||
sign.undo=撤销
|
||||
sign.redo=重做
|
||||
|
||||
#repair
|
||||
repair.title=修復
|
||||
|
@ -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"
|
||||
},
|
||||
|
@ -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();
|
||||
|
@ -34,11 +34,15 @@
|
||||
<!-- Bootstrap -->
|
||||
<script th:src="@{'/js/thirdParty/popper.min.js'}"></script>
|
||||
<script th:src="@{'/js/thirdParty/bootstrap.min.js'}"></script>
|
||||
|
||||
<link rel="stylesheet" th:href="@{'/css/bootstrap.min.css'}">
|
||||
|
||||
<!-- Bootstrap Icons -->
|
||||
<link rel="stylesheet" th:href="@{'/css/bootstrap-icons.min.css'}">
|
||||
|
||||
<!-- Pixel, doesn't collect any PII-->
|
||||
<img referrerpolicy="no-referrer-when-downgrade" src="https://pixel.stirlingpdf.com/a.png?x-pxid=4f5fa02f-a065-4efb-bb2c-24509a4b6b92" style="position: absolute; visibility: hidden;"/>
|
||||
|
||||
<!-- Custom -->
|
||||
<link rel="stylesheet" th:href="@{'/css/general.css'}">
|
||||
<link rel="stylesheet" th:href="@{'/css/theme/theme.css'}">
|
||||
|
@ -56,6 +56,10 @@
|
||||
th:text="#{sign.clear}"></button>
|
||||
<button id="save-signature" class="btn btn-outline-success mt-2" onclick="addDraggableFromPad()"
|
||||
th:text="#{sign.add}"></button>
|
||||
<button id="signature-undo-button" class="btn btn-outline-secondary mt-2" th:text="#{sign.undo}"
|
||||
onclick="undoDraw()"></button>
|
||||
<button id="signature-redo-button" class="btn btn-outline-secondary mt-2" th:text="#{sign.redo}"
|
||||
onclick="redoDraw()"></button>
|
||||
</div>
|
||||
|
||||
<div class="tab-container" th:title="#{sign.saved}" th:data-title="#{sign.saved}">
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user