Compare commits

..

8 Commits

Author SHA1 Message Date
stirlingbot[bot]
81d0e2cb5f
📁 pre-commit
Signed-off-by: stirlingbot[bot] <stirlingbot[bot]@users.noreply.github.com>
2025-09-05 18:46:11 +00:00
Anthony Stirling
5e72dce0de
login_fix (#4402)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-09-05 19:42:47 +01:00
Anthony Stirling
6d03ab27d4
Bump version from 1.3.0 to 1.3.1 (#4395) 2025-09-05 13:39:15 +01:00
stirlingbot[bot]
830b346945
🌐 Sync Translations + Update README Progress Table (#4386)
Co-authored-by: stirlingbot[bot] <195170888+stirlingbot[bot]@users.noreply.github.com>
2025-09-05 13:24:55 +01:00
Ludy
e5da63554e
fix: add missing MediaType import in SplitPdfBySectionsController (#4393) 2025-09-05 12:31:52 +01:00
Ludy
9b3e2c29a5
perf(core): Stream responses and unify temp file lifecycle across controllers (#4330) 2025-09-05 11:27:28 +01:00
Ludy
9a39aff19f
refactor: standardize MIME handling via Spring MediaType (#4389) 2025-09-05 11:08:24 +01:00
Ludy
f14955a019
fix(security): prevent NPE on logout when JWT service is unavailable (#4390) 2025-09-05 10:59:24 +01:00
127 changed files with 670 additions and 359 deletions

View File

@ -122,13 +122,13 @@ Stirling-PDF currently supports 40 languages!
| Bulgarian (Български) (bg_BG) | ![68%](https://geps.dev/progress/68) | | Bulgarian (Български) (bg_BG) | ![68%](https://geps.dev/progress/68) |
| Catalan (Català) (ca_CA) | ![67%](https://geps.dev/progress/67) | | Catalan (Català) (ca_CA) | ![67%](https://geps.dev/progress/67) |
| Croatian (Hrvatski) (hr_HR) | ![60%](https://geps.dev/progress/60) | | Croatian (Hrvatski) (hr_HR) | ![60%](https://geps.dev/progress/60) |
| Czech (Česky) (cs_CZ) | ![70%](https://geps.dev/progress/70) | | Czech (Česky) (cs_CZ) | ![69%](https://geps.dev/progress/69) |
| Danish (Dansk) (da_DK) | ![61%](https://geps.dev/progress/61) | | Danish (Dansk) (da_DK) | ![61%](https://geps.dev/progress/61) |
| Dutch (Nederlands) (nl_NL) | ![60%](https://geps.dev/progress/60) | | Dutch (Nederlands) (nl_NL) | ![60%](https://geps.dev/progress/60) |
| English (English) (en_GB) | ![100%](https://geps.dev/progress/100) | | English (English) (en_GB) | ![100%](https://geps.dev/progress/100) |
| English (US) (en_US) | ![100%](https://geps.dev/progress/100) | | English (US) (en_US) | ![100%](https://geps.dev/progress/100) |
| French (Français) (fr_FR) | ![88%](https://geps.dev/progress/88) | | French (Français) (fr_FR) | ![88%](https://geps.dev/progress/88) |
| German (Deutsch) (de_DE) | ![98%](https://geps.dev/progress/98) | | German (Deutsch) (de_DE) | ![97%](https://geps.dev/progress/97) |
| Greek (Ελληνικά) (el_GR) | ![67%](https://geps.dev/progress/67) | | Greek (Ελληνικά) (el_GR) | ![67%](https://geps.dev/progress/67) |
| Hindi (हिंदी) (hi_IN) | ![67%](https://geps.dev/progress/67) | | Hindi (हिंदी) (hi_IN) | ![67%](https://geps.dev/progress/67) |
| Hungarian (Magyar) (hu_HU) | ![99%](https://geps.dev/progress/99) | | Hungarian (Magyar) (hu_HU) | ![99%](https://geps.dev/progress/99) |
@ -144,7 +144,7 @@ Stirling-PDF currently supports 40 languages!
| Portuguese Brazilian (Português) (pt_BR) | ![76%](https://geps.dev/progress/76) | | Portuguese Brazilian (Português) (pt_BR) | ![76%](https://geps.dev/progress/76) |
| Romanian (Română) (ro_RO) | ![57%](https://geps.dev/progress/57) | | Romanian (Română) (ro_RO) | ![57%](https://geps.dev/progress/57) |
| Russian (Русский) (ru_RU) | ![88%](https://geps.dev/progress/88) | | Russian (Русский) (ru_RU) | ![88%](https://geps.dev/progress/88) |
| Serbian Latin alphabet (Srpski) (sr_LATN_RS) | ![95%](https://geps.dev/progress/95) | | Serbian Latin alphabet (Srpski) (sr_LATN_RS) | ![94%](https://geps.dev/progress/94) |
| Simplified Chinese (简体中文) (zh_CN) | ![93%](https://geps.dev/progress/93) | | Simplified Chinese (简体中文) (zh_CN) | ![93%](https://geps.dev/progress/93) |
| Slovakian (Slovensky) (sk_SK) | ![51%](https://geps.dev/progress/51) | | Slovakian (Slovensky) (sk_SK) | ![51%](https://geps.dev/progress/51) |
| Slovenian (Slovenščina) (sl_SI) | ![71%](https://geps.dev/progress/71) | | Slovenian (Slovenščina) (sl_SI) | ![71%](https://geps.dev/progress/71) |

View File

@ -3,6 +3,7 @@ package stirling.software.common.annotations;
import java.lang.annotation.*; import java.lang.annotation.*;
import org.springframework.core.annotation.AliasFor; import org.springframework.core.annotation.AliasFor;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestMethod;
@ -37,7 +38,7 @@ public @interface AutoJobPostMapping {
/** MIME types this endpoint accepts. Defaults to {@code multipart/form-data}. */ /** MIME types this endpoint accepts. Defaults to {@code multipart/form-data}. */
@AliasFor(annotation = RequestMapping.class, attribute = "consumes") @AliasFor(annotation = RequestMapping.class, attribute = "consumes")
String[] consumes() default {"multipart/form-data"}; String[] consumes() default {MediaType.MULTIPART_FORM_DATA_VALUE};
/** /**
* Maximum execution time in milliseconds before the job is aborted. A negative value means "use * Maximum execution time in milliseconds before the job is aborted. A negative value means "use

View File

@ -1,5 +1,6 @@
package stirling.software.common.model.api; package stirling.software.common.model.api;
import org.springframework.http.MediaType;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
@ -14,7 +15,7 @@ import lombok.NoArgsConstructor;
public class PDFFile { public class PDFFile {
@Schema( @Schema(
description = "The input PDF file", description = "The input PDF file",
contentMediaType = "application/pdf", contentMediaType = MediaType.APPLICATION_PDF_VALUE,
format = "binary") format = "binary")
private MultipartFile fileInput; private MultipartFile fileInput;

View File

@ -227,7 +227,8 @@ public class JobExecutorService {
if (result instanceof byte[]) { if (result instanceof byte[]) {
// Store byte array directly to disk to avoid double memory consumption // Store byte array directly to disk to avoid double memory consumption
String fileId = fileStorage.storeBytes((byte[]) result, "result.pdf"); String fileId = fileStorage.storeBytes((byte[]) result, "result.pdf");
taskManager.setFileResult(jobId, fileId, "result.pdf", "application/pdf"); taskManager.setFileResult(
jobId, fileId, "result.pdf", MediaType.APPLICATION_PDF_VALUE);
log.debug("Stored byte[] result with fileId: {}", fileId); log.debug("Stored byte[] result with fileId: {}", fileId);
// Let the byte array get collected naturally in the next GC cycle // Let the byte array get collected naturally in the next GC cycle
@ -239,7 +240,7 @@ public class JobExecutorService {
if (body instanceof byte[]) { if (body instanceof byte[]) {
// Extract filename from content-disposition header if available // Extract filename from content-disposition header if available
String filename = "result.pdf"; String filename = "result.pdf";
String contentType = "application/pdf"; String contentType = MediaType.APPLICATION_PDF_VALUE;
if (response.getHeaders().getContentDisposition() != null) { if (response.getHeaders().getContentDisposition() != null) {
String disposition = String disposition =
@ -276,7 +277,7 @@ public class JobExecutorService {
if (fileId != null && !fileId.isEmpty()) { if (fileId != null && !fileId.isEmpty()) {
// Try to get filename and content type // Try to get filename and content type
String filename = "result.pdf"; String filename = "result.pdf";
String contentType = "application/pdf"; String contentType = MediaType.APPLICATION_PDF_VALUE;
try { try {
java.lang.reflect.Method getOriginalFileName = java.lang.reflect.Method getOriginalFileName =
@ -317,8 +318,7 @@ public class JobExecutorService {
// Store generic result // Store generic result
taskManager.setResult(jobId, body); taskManager.setResult(jobId, body);
} }
} else if (result instanceof MultipartFile) { } else if (result instanceof MultipartFile file) {
MultipartFile file = (MultipartFile) result;
String fileId = fileStorage.storeFile(file); String fileId = fileStorage.storeFile(file);
taskManager.setFileResult( taskManager.setFileResult(
jobId, fileId, file.getOriginalFilename(), file.getContentType()); jobId, fileId, file.getOriginalFilename(), file.getContentType());
@ -335,7 +335,7 @@ public class JobExecutorService {
if (fileId != null && !fileId.isEmpty()) { if (fileId != null && !fileId.isEmpty()) {
// Try to get filename and content type // Try to get filename and content type
String filename = "result.pdf"; String filename = "result.pdf";
String contentType = "application/pdf"; String contentType = MediaType.APPLICATION_PDF_VALUE;
try { try {
java.lang.reflect.Method getOriginalFileName = java.lang.reflect.Method getOriginalFileName =
@ -398,9 +398,8 @@ public class JobExecutorService {
HttpHeaders.CONTENT_DISPOSITION, HttpHeaders.CONTENT_DISPOSITION,
"form-data; name=\"attachment\"; filename=\"result.pdf\"") "form-data; name=\"attachment\"; filename=\"result.pdf\"")
.body(result); .body(result);
} else if (result instanceof MultipartFile) { } else if (result instanceof MultipartFile file) {
// Return MultipartFile content // Return MultipartFile content
MultipartFile file = (MultipartFile) result;
return ResponseEntity.ok() return ResponseEntity.ok()
.contentType(MediaType.parseMediaType(file.getContentType())) .contentType(MediaType.parseMediaType(file.getContentType()))
.header( .header(

View File

@ -12,6 +12,8 @@ import java.util.List;
import java.util.Properties; import java.util.Properties;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import org.springframework.http.MediaType;
import lombok.Data; import lombok.Data;
import lombok.experimental.UtilityClass; import lombok.experimental.UtilityClass;
@ -28,8 +30,8 @@ public class EmlParser {
Pattern.compile("=\\?([^?]+)\\?([BbQq])\\?([^?]*)\\?="); Pattern.compile("=\\?([^?]+)\\?([BbQq])\\?([^?]*)\\?=");
private static final String DISPOSITION_ATTACHMENT = "attachment"; private static final String DISPOSITION_ATTACHMENT = "attachment";
private static final String TEXT_PLAIN = "text/plain"; private static final String TEXT_PLAIN = MediaType.TEXT_PLAIN_VALUE;
private static final String TEXT_HTML = "text/html"; private static final String TEXT_HTML = MediaType.TEXT_HTML_VALUE;
private static final String MULTIPART_PREFIX = "multipart/"; private static final String MULTIPART_PREFIX = "multipart/";
private static final String HEADER_CONTENT_TYPE = "content-type:"; private static final String HEADER_CONTENT_TYPE = "content-type:";
@ -69,12 +71,12 @@ public class EmlParser {
if (isJakartaMailAvailable()) { if (isJakartaMailAvailable()) {
return extractEmailContentAdvanced(emlBytes, request, customHtmlSanitizer); return extractEmailContentAdvanced(emlBytes, request, customHtmlSanitizer);
} else { } else {
return extractEmailContentBasic(emlBytes, request, customHtmlSanitizer); return extractEmailContentBasic(emlBytes, customHtmlSanitizer);
} }
} }
private static EmailContent extractEmailContentBasic( private static EmailContent extractEmailContentBasic(
byte[] emlBytes, EmlToPdfRequest request, CustomHtmlSanitizer customHtmlSanitizer) { byte[] emlBytes, CustomHtmlSanitizer customHtmlSanitizer) {
String emlContent = new String(emlBytes, StandardCharsets.UTF_8); String emlContent = new String(emlBytes, StandardCharsets.UTF_8);
EmailContent content = new EmailContent(); EmailContent content = new EmailContent();
@ -121,7 +123,7 @@ public class EmlParser {
return extractFromMimeMessage(message, request, customHtmlSanitizer); return extractFromMimeMessage(message, request, customHtmlSanitizer);
} catch (ReflectiveOperationException e) { } catch (ReflectiveOperationException e) {
return extractEmailContentBasic(emlBytes, request, customHtmlSanitizer); return extractEmailContentBasic(emlBytes, customHtmlSanitizer);
} }
} }

View File

@ -8,6 +8,8 @@ import java.util.Map;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import org.springframework.http.MediaType;
import lombok.experimental.UtilityClass; import lombok.experimental.UtilityClass;
import stirling.software.common.model.api.converters.EmlToPdfRequest; import stirling.software.common.model.api.converters.EmlToPdfRequest;
@ -33,10 +35,10 @@ public class EmlProcessingUtils {
// MIME type detection // MIME type detection
private static final Map<String, String> EXTENSION_TO_MIME_TYPE = private static final Map<String, String> EXTENSION_TO_MIME_TYPE =
Map.of( Map.of(
".png", "image/png", ".png", MediaType.IMAGE_PNG_VALUE,
".jpg", "image/jpeg", ".jpg", MediaType.IMAGE_JPEG_VALUE,
".jpeg", "image/jpeg", ".jpeg", MediaType.IMAGE_JPEG_VALUE,
".gif", "image/gif", ".gif", MediaType.IMAGE_GIF_VALUE,
".bmp", "image/bmp", ".bmp", "image/bmp",
".webp", "image/webp", ".webp", "image/webp",
".svg", "image/svg+xml", ".svg", "image/svg+xml",
@ -81,8 +83,8 @@ public class EmlProcessingUtils {
|| lowerContent.contains("bcc:"); || lowerContent.contains("bcc:");
boolean hasMimeStructure = boolean hasMimeStructure =
lowerContent.contains("multipart/") lowerContent.contains("multipart/")
|| lowerContent.contains("text/plain") || lowerContent.contains(MediaType.TEXT_PLAIN_VALUE)
|| lowerContent.contains("text/html") || lowerContent.contains(MediaType.TEXT_HTML_VALUE)
|| lowerContent.contains("boundary="); || lowerContent.contains("boundary=");
int headerCount = 0; int headerCount = 0;
@ -464,7 +466,7 @@ public class EmlProcessingUtils {
} }
} }
return "image/png"; return MediaType.IMAGE_PNG_VALUE; // Default MIME type
} }
public static String decodeUrlEncoded(String encoded) { public static String decodeUrlEncoded(String encoded) {

View File

@ -0,0 +1,35 @@
package stirling.software.common.util;
import java.io.IOException;
import java.util.List;
import org.apache.pdfbox.multipdf.PDFMergerUtility;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.springframework.stereotype.Service;
import lombok.RequiredArgsConstructor;
import stirling.software.common.service.CustomPDFDocumentFactory;
@Service
@RequiredArgsConstructor
public class PDFService {
private final CustomPDFDocumentFactory pdfDocumentFactory;
/*
* Merge multiple PDF documents into a single PDF document
*
* @param documents List of PDDocument to be merged
* @return Merged PDDocument
* @throws IOException If an error occurs during merging
*/
public PDDocument mergeDocuments(List<PDDocument> documents) throws IOException {
PDDocument merged = pdfDocumentFactory.createNewDocument();
PDFMergerUtility merger = new PDFMergerUtility();
for (PDDocument doc : documents) {
merger.appendDocument(merged, doc);
}
return merged;
}
}

View File

@ -36,7 +36,7 @@ public class PDFToFile {
public ResponseEntity<byte[]> processPdfToMarkdown(MultipartFile inputFile) public ResponseEntity<byte[]> processPdfToMarkdown(MultipartFile inputFile)
throws IOException, InterruptedException { throws IOException, InterruptedException {
if (!"application/pdf".equals(inputFile.getContentType())) { if (!MediaType.APPLICATION_PDF_VALUE.equals(inputFile.getContentType())) {
return new ResponseEntity<>(HttpStatus.BAD_REQUEST); return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
} }
@ -153,7 +153,7 @@ public class PDFToFile {
public ResponseEntity<byte[]> processPdfToHtml(MultipartFile inputFile) public ResponseEntity<byte[]> processPdfToHtml(MultipartFile inputFile)
throws IOException, InterruptedException { throws IOException, InterruptedException {
if (!"application/pdf".equals(inputFile.getContentType())) { if (!MediaType.APPLICATION_PDF_VALUE.equals(inputFile.getContentType())) {
return new ResponseEntity<>(HttpStatus.BAD_REQUEST); return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
} }
@ -223,7 +223,7 @@ public class PDFToFile {
MultipartFile inputFile, String outputFormat, String libreOfficeFilter) MultipartFile inputFile, String outputFormat, String libreOfficeFilter)
throws IOException, InterruptedException { throws IOException, InterruptedException {
if (!"application/pdf".equals(inputFile.getContentType())) { if (!MediaType.APPLICATION_PDF_VALUE.equals(inputFile.getContentType())) {
return new ResponseEntity<>(HttpStatus.BAD_REQUEST); return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
} }

View File

@ -37,6 +37,7 @@ import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceStream;
import org.apache.pdfbox.text.PDFTextStripper; import org.apache.pdfbox.text.PDFTextStripper;
import org.apache.pdfbox.text.TextPosition; import org.apache.pdfbox.text.TextPosition;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.springframework.http.MediaType;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import lombok.Data; import lombok.Data;
@ -118,7 +119,7 @@ public class PdfAttachmentHandler {
public String getContentType() { public String getContentType() {
return attachment.getContentType() != null return attachment.getContentType() != null
? attachment.getContentType() ? attachment.getContentType()
: "application/octet-stream"; : MediaType.APPLICATION_OCTET_STREAM_VALUE;
} }
@Override @Override

View File

@ -29,6 +29,7 @@ import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
import org.apache.pdfbox.rendering.ImageType; import org.apache.pdfbox.rendering.ImageType;
import org.apache.pdfbox.rendering.PDFRenderer; import org.apache.pdfbox.rendering.PDFRenderer;
import org.apache.pdfbox.text.PDFTextStripper; import org.apache.pdfbox.text.PDFTextStripper;
import org.springframework.http.MediaType;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import io.github.pixee.security.Filenames; import io.github.pixee.security.Filenames;
@ -156,7 +157,8 @@ public class PdfUtils {
if (DPI > maxSafeDpi) { if (DPI > maxSafeDpi) {
throw ExceptionUtils.createIllegalArgumentException( throw ExceptionUtils.createIllegalArgumentException(
"error.dpiExceedsLimit", "error.dpiExceedsLimit",
"DPI value {0} exceeds maximum safe limit of {1}. High DPI values can cause memory issues and crashes. Please use a lower DPI value.", "DPI value {0} exceeds maximum safe limit of {1}. High DPI values can cause"
+ " memory issues and crashes. Please use a lower DPI value.",
DPI, DPI,
maxSafeDpi); maxSafeDpi);
} }
@ -196,7 +198,9 @@ public class PdfUtils {
.contains("Maximum size of image exceeded")) { .contains("Maximum size of image exceeded")) {
throw ExceptionUtils.createIllegalArgumentException( throw ExceptionUtils.createIllegalArgumentException(
"error.pageTooBigForDpi", "error.pageTooBigForDpi",
"PDF page {0} is too large to render at {1} DPI. Please try a lower DPI value (recommended: 150 or less).", "PDF page {0} is too large to render at {1} DPI. Please"
+ " try a lower DPI value (recommended: 150 or"
+ " less).",
i + 1, i + 1,
DPI); DPI);
} }
@ -241,7 +245,10 @@ public class PdfUtils {
.contains("Maximum size of image exceeded")) { .contains("Maximum size of image exceeded")) {
throw ExceptionUtils.createIllegalArgumentException( throw ExceptionUtils.createIllegalArgumentException(
"error.pageTooBigExceedsArray", "error.pageTooBigExceedsArray",
"PDF page {0} is too large to render at {1} DPI. The resulting image would exceed Java's maximum array size. Please try a lower DPI value (recommended: 150 or less).", "PDF page {0} is too large to render at {1} DPI. The"
+ " resulting image would exceed Java's maximum"
+ " array size. Please try a lower DPI value"
+ " (recommended: 150 or less).",
i + 1, i + 1,
DPI); DPI);
} }
@ -282,7 +289,9 @@ public class PdfUtils {
.contains("Maximum size of image exceeded")) { .contains("Maximum size of image exceeded")) {
throw ExceptionUtils.createIllegalArgumentException( throw ExceptionUtils.createIllegalArgumentException(
"error.pageTooBigForDpi", "error.pageTooBigForDpi",
"PDF page {0} is too large to render at {1} DPI. Please try a lower DPI value (recommended: 150 or less).", "PDF page {0} is too large to render at {1} DPI. Please"
+ " try a lower DPI value (recommended: 150 or"
+ " less).",
i + 1, i + 1,
DPI); DPI);
} }
@ -315,7 +324,8 @@ public class PdfUtils {
&& e.getMessage().contains("Maximum size of image exceeded")) { && e.getMessage().contains("Maximum size of image exceeded")) {
throw ExceptionUtils.createIllegalArgumentException( throw ExceptionUtils.createIllegalArgumentException(
"error.pageTooBigForDpi", "error.pageTooBigForDpi",
"PDF page {0} is too large to render at {1} DPI. Please try a lower DPI value (recommended: 150 or less).", "PDF page {0} is too large to render at {1} DPI. Please try"
+ " a lower DPI value (recommended: 150 or less).",
i + 1, i + 1,
DPI); DPI);
} }
@ -366,7 +376,9 @@ public class PdfUtils {
&& e.getMessage().contains("Maximum size of image exceeded")) { && e.getMessage().contains("Maximum size of image exceeded")) {
throw ExceptionUtils.createIllegalArgumentException( throw ExceptionUtils.createIllegalArgumentException(
"error.pageTooBigFor300Dpi", "error.pageTooBigFor300Dpi",
"PDF page {0} is too large to render at 300 DPI. The resulting image would exceed Java's maximum array size. Please use a lower DPI value for PDF-to-image conversion.", "PDF page {0} is too large to render at 300 DPI. The resulting image"
+ " would exceed Java's maximum array size. Please use a lower DPI"
+ " value for PDF-to-image conversion.",
page + 1); page + 1);
} }
throw e; throw e;
@ -435,7 +447,7 @@ public class PdfUtils {
ImageProcessingUtils.convertColorType(image, colorType); ImageProcessingUtils.convertColorType(image, colorType);
// Use JPEGFactory if it's JPEG since JPEG is lossy // Use JPEGFactory if it's JPEG since JPEG is lossy
PDImageXObject pdImage = PDImageXObject pdImage =
(contentType != null && "image/jpeg".equals(contentType)) (contentType != null && MediaType.IMAGE_JPEG_VALUE.equals(contentType))
? JPEGFactory.createFromImage(doc, convertedImage) ? JPEGFactory.createFromImage(doc, convertedImage)
: LosslessFactory.createFromImage(doc, convertedImage); : LosslessFactory.createFromImage(doc, convertedImage);
addImageToDocument(doc, pdImage, fitOption, autoRotate); addImageToDocument(doc, pdImage, fitOption, autoRotate);

View File

@ -4,6 +4,8 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.net.URLEncoder; import java.net.URLEncoder;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocument;
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpHeaders;
@ -11,9 +13,13 @@ import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import io.github.pixee.security.Filenames; import io.github.pixee.security.Filenames;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class WebResponseUtils { public class WebResponseUtils {
public static ResponseEntity<byte[]> baosToWebResponse( public static ResponseEntity<byte[]> baosToWebResponse(
@ -64,4 +70,59 @@ public class WebResponseUtils {
return baosToWebResponse(baos, docName); return baosToWebResponse(baos, docName);
} }
/**
* Convert a File to a web response (PDF default).
*
* @param outputTempFile The temporary file to be sent as a response.
* @param docName The name of the document.
* @return A ResponseEntity containing the file as a resource.
*/
public static ResponseEntity<StreamingResponseBody> pdfFileToWebResponse(
TempFile outputTempFile, String docName) throws IOException {
return fileToWebResponse(outputTempFile, docName, MediaType.APPLICATION_PDF);
}
/**
* Convert a File to a web response (ZIP default).
*
* @param outputTempFile The temporary file to be sent as a response.
* @param docName The name of the document.
* @return A ResponseEntity containing the file as a resource.
*/
public static ResponseEntity<StreamingResponseBody> zipFileToWebResponse(
TempFile outputTempFile, String docName) throws IOException {
return fileToWebResponse(outputTempFile, docName, MediaType.APPLICATION_OCTET_STREAM);
}
/**
* Convert a File to a web response with explicit media type (e.g., ZIP).
*
* @param outputTempFile The temporary file to be sent as a response.
* @param docName The name of the document.
* @param mediaType The content type to set on the response.
* @return A ResponseEntity containing the file as a resource.
*/
public static ResponseEntity<StreamingResponseBody> fileToWebResponse(
TempFile outputTempFile, String docName, MediaType mediaType) throws IOException {
Path path = outputTempFile.getFile().toPath().normalize();
long len = Files.size(path);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(mediaType);
headers.setContentLength(len);
headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + docName + "\"");
StreamingResponseBody body =
os -> {
try (os) {
Files.copy(path, os);
os.flush();
} finally {
outputTempFile.close();
}
};
return new ResponseEntity<>(body, headers, HttpStatus.OK);
}
} }

View File

@ -11,6 +11,7 @@ import java.nio.file.Path;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.core.io.FileSystemResource; import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer; import okhttp3.mockwebserver.MockWebServer;
@ -23,7 +24,7 @@ class ApplicationPropertiesSaml2HttpTest {
server.enqueue( server.enqueue(
new MockResponse() new MockResponse()
.setResponseCode(200) .setResponseCode(200)
.addHeader("Content-Type", "application/xml") .addHeader("Content-Type", MediaType.APPLICATION_XML_VALUE)
.setBody("<EntityDescriptor/>")); .setBody("<EntityDescriptor/>"));
server.start(); server.start();

View File

@ -1,12 +1,12 @@
package stirling.software.common.service; package stirling.software.common.service;
import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*; import static org.mockito.Mockito.*;
import static stirling.software.common.service.SpyPDFDocumentFactory.*; import static stirling.software.common.service.SpyPDFDocumentFactory.*;
import java.io.*; import java.io.*;
import java.nio.file.*; import java.nio.file.*;
import java.nio.file.Files;
import java.util.Arrays; import java.util.Arrays;
import org.apache.pdfbox.Loader; import org.apache.pdfbox.Loader;
@ -18,9 +18,11 @@ import org.junit.jupiter.api.parallel.Execution;
import org.junit.jupiter.api.parallel.ExecutionMode; import org.junit.jupiter.api.parallel.ExecutionMode;
import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource; import org.junit.jupiter.params.provider.CsvSource;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockMultipartFile; import org.springframework.mock.web.MockMultipartFile;
import stirling.software.common.model.api.PDFFile; import stirling.software.common.model.api.PDFFile;
import stirling.software.common.service.SpyPDFDocumentFactory.StrategyType;
@TestInstance(TestInstance.Lifecycle.PER_CLASS) @TestInstance(TestInstance.Lifecycle.PER_CLASS)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class) @TestMethodOrder(MethodOrderer.OrderAnnotation.class)
@ -73,7 +75,7 @@ class CustomPDFDocumentFactoryTest {
void testStrategy_MultipartFile(int sizeMB, StrategyType expected) throws IOException { void testStrategy_MultipartFile(int sizeMB, StrategyType expected) throws IOException {
byte[] inflated = inflatePdf(basePdfBytes, sizeMB); byte[] inflated = inflatePdf(basePdfBytes, sizeMB);
MockMultipartFile multipart = MockMultipartFile multipart =
new MockMultipartFile("file", "doc.pdf", "application/pdf", inflated); new MockMultipartFile("file", "doc.pdf", MediaType.APPLICATION_PDF_VALUE, inflated);
try (PDDocument doc = factory.load(multipart)) { try (PDDocument doc = factory.load(multipart)) {
Assertions.assertEquals(expected, factory.lastStrategyUsed); Assertions.assertEquals(expected, factory.lastStrategyUsed);
} }
@ -84,7 +86,7 @@ class CustomPDFDocumentFactoryTest {
void testStrategy_PDFFile(int sizeMB, StrategyType expected) throws IOException { void testStrategy_PDFFile(int sizeMB, StrategyType expected) throws IOException {
byte[] inflated = inflatePdf(basePdfBytes, sizeMB); byte[] inflated = inflatePdf(basePdfBytes, sizeMB);
MockMultipartFile multipart = MockMultipartFile multipart =
new MockMultipartFile("file", "doc.pdf", "application/pdf", inflated); new MockMultipartFile("file", "doc.pdf", MediaType.APPLICATION_PDF_VALUE, inflated);
PDFFile pdfFile = new PDFFile(); PDFFile pdfFile = new PDFFile();
pdfFile.setFileInput(multipart); pdfFile.setFileInput(multipart);
try (PDDocument doc = factory.load(pdfFile)) { try (PDDocument doc = factory.load(pdfFile)) {

View File

@ -2,6 +2,8 @@ package stirling.software.common.service;
import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.AdditionalAnswers.*; import static org.mockito.AdditionalAnswers.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.*; import static org.mockito.Mockito.*;
import java.io.IOException; import java.io.IOException;
@ -15,6 +17,7 @@ import org.junit.jupiter.api.io.TempDir;
import org.mockito.InjectMocks; import org.mockito.InjectMocks;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.MockitoAnnotations; import org.mockito.MockitoAnnotations;
import org.springframework.http.MediaType;
import org.springframework.test.util.ReflectionTestUtils; import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
@ -36,7 +39,7 @@ class FileStorageTest {
// Create a mock MultipartFile // Create a mock MultipartFile
mockFile = mock(MultipartFile.class); mockFile = mock(MultipartFile.class);
when(mockFile.getOriginalFilename()).thenReturn("test.pdf"); when(mockFile.getOriginalFilename()).thenReturn("test.pdf");
when(mockFile.getContentType()).thenReturn("application/pdf"); when(mockFile.getContentType()).thenReturn(MediaType.APPLICATION_PDF_VALUE);
} }
@Test @Test

View File

@ -13,6 +13,7 @@ import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks; import org.mockito.InjectMocks;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.MockitoAnnotations; import org.mockito.MockitoAnnotations;
import org.springframework.http.MediaType;
import org.springframework.test.util.ReflectionTestUtils; import org.springframework.test.util.ReflectionTestUtils;
import stirling.software.common.model.job.JobResult; import stirling.software.common.model.job.JobResult;
@ -77,7 +78,7 @@ class TaskManagerTest {
taskManager.createTask(jobId); taskManager.createTask(jobId);
String fileId = "file-id"; String fileId = "file-id";
String originalFileName = "test.pdf"; String originalFileName = "test.pdf";
String contentType = "application/pdf"; String contentType = MediaType.APPLICATION_PDF_VALUE;
long fileSize = 1024L; long fileSize = 1024L;
// Mock the fileStorage.getFileSize() call // Mock the fileStorage.getFileSize() call
@ -185,7 +186,8 @@ class TaskManagerTest {
// 2. Create completed successful job with file // 2. Create completed successful job with file
String successFileJobId = "success-file-job"; String successFileJobId = "success-file-job";
taskManager.createTask(successFileJobId); taskManager.createTask(successFileJobId);
taskManager.setFileResult(successFileJobId, "file-id", "test.pdf", "application/pdf"); taskManager.setFileResult(
successFileJobId, "file-id", "test.pdf", MediaType.APPLICATION_PDF_VALUE);
// 3. Create completed successful job without file // 3. Create completed successful job without file
String successJobId = "success-job"; String successJobId = "success-job";
@ -235,7 +237,7 @@ class TaskManagerTest {
ResultFile.builder() ResultFile.builder()
.fileId("file-id") .fileId("file-id")
.fileName("test.pdf") .fileName("test.pdf")
.contentType("application/pdf") .contentType(MediaType.APPLICATION_PDF_VALUE)
.fileSize(1024L) .fileSize(1024L)
.build(); .build();
ReflectionTestUtils.setField(oldJob, "resultFiles", java.util.List.of(resultFile)); ReflectionTestUtils.setField(oldJob, "resultFiles", java.util.List.of(resultFile));

View File

@ -25,6 +25,7 @@ import org.mockito.Mock;
import org.mockito.MockedStatic; import org.mockito.MockedStatic;
import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.mock.web.MockMultipartFile; import org.springframework.mock.web.MockMultipartFile;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
@ -57,7 +58,10 @@ class PDFToFileTest {
// Prepare // Prepare
MultipartFile nonPdfFile = MultipartFile nonPdfFile =
new MockMultipartFile( new MockMultipartFile(
"file", "test.txt", "text/plain", "This is not a PDF".getBytes()); "file",
"test.txt",
MediaType.TEXT_PLAIN_VALUE,
"This is not a PDF".getBytes());
// Execute // Execute
ResponseEntity<byte[]> response = pdfToFile.processPdfToMarkdown(nonPdfFile); ResponseEntity<byte[]> response = pdfToFile.processPdfToMarkdown(nonPdfFile);
@ -71,7 +75,10 @@ class PDFToFileTest {
// Prepare // Prepare
MultipartFile nonPdfFile = MultipartFile nonPdfFile =
new MockMultipartFile( new MockMultipartFile(
"file", "test.txt", "text/plain", "This is not a PDF".getBytes()); "file",
"test.txt",
MediaType.TEXT_PLAIN_VALUE,
"This is not a PDF".getBytes());
// Execute // Execute
ResponseEntity<byte[]> response = pdfToFile.processPdfToHtml(nonPdfFile); ResponseEntity<byte[]> response = pdfToFile.processPdfToHtml(nonPdfFile);
@ -86,7 +93,10 @@ class PDFToFileTest {
// Prepare // Prepare
MultipartFile nonPdfFile = MultipartFile nonPdfFile =
new MockMultipartFile( new MockMultipartFile(
"file", "test.txt", "text/plain", "This is not a PDF".getBytes()); "file",
"test.txt",
MediaType.TEXT_PLAIN_VALUE,
"This is not a PDF".getBytes());
// Execute // Execute
ResponseEntity<byte[]> response = ResponseEntity<byte[]> response =
@ -102,7 +112,10 @@ class PDFToFileTest {
// Prepare // Prepare
MultipartFile pdfFile = MultipartFile pdfFile =
new MockMultipartFile( new MockMultipartFile(
"file", "test.pdf", "application/pdf", "Fake PDF content".getBytes()); "file",
"test.pdf",
MediaType.APPLICATION_PDF_VALUE,
"Fake PDF content".getBytes());
// Execute with invalid format // Execute with invalid format
ResponseEntity<byte[]> response = ResponseEntity<byte[]> response =
@ -120,7 +133,10 @@ class PDFToFileTest {
// Create a mock PDF file // Create a mock PDF file
MultipartFile pdfFile = MultipartFile pdfFile =
new MockMultipartFile( new MockMultipartFile(
"file", "test.pdf", "application/pdf", "Fake PDF content".getBytes()); "file",
"test.pdf",
MediaType.APPLICATION_PDF_VALUE,
"Fake PDF content".getBytes());
// Create a mock HTML output file // Create a mock HTML output file
Path htmlOutputFile = tempDir.resolve("test.html"); Path htmlOutputFile = tempDir.resolve("test.html");
@ -168,7 +184,7 @@ class PDFToFileTest {
new MockMultipartFile( new MockMultipartFile(
"file", "file",
"multipage.pdf", "multipage.pdf",
"application/pdf", MediaType.APPLICATION_PDF_VALUE,
"Fake PDF content".getBytes()); "Fake PDF content".getBytes());
// Setup ProcessExecutor mock // Setup ProcessExecutor mock
@ -245,7 +261,10 @@ class PDFToFileTest {
// Create a mock PDF file // Create a mock PDF file
MultipartFile pdfFile = MultipartFile pdfFile =
new MockMultipartFile( new MockMultipartFile(
"file", "test.pdf", "application/pdf", "Fake PDF content".getBytes()); "file",
"test.pdf",
MediaType.APPLICATION_PDF_VALUE,
"Fake PDF content".getBytes());
// Setup ProcessExecutor mock // Setup ProcessExecutor mock
mockedStaticProcessExecutor mockedStaticProcessExecutor
@ -324,7 +343,7 @@ class PDFToFileTest {
new MockMultipartFile( new MockMultipartFile(
"file", "file",
"document.pdf", "document.pdf",
"application/pdf", MediaType.APPLICATION_PDF_VALUE,
"Fake PDF content".getBytes()); "Fake PDF content".getBytes());
// Setup ProcessExecutor mock // Setup ProcessExecutor mock
@ -386,7 +405,7 @@ class PDFToFileTest {
new MockMultipartFile( new MockMultipartFile(
"file", "file",
"document.pdf", "document.pdf",
"application/pdf", MediaType.APPLICATION_PDF_VALUE,
"Fake PDF content".getBytes()); "Fake PDF content".getBytes());
// Setup ProcessExecutor mock // Setup ProcessExecutor mock
@ -472,7 +491,7 @@ class PDFToFileTest {
new MockMultipartFile( new MockMultipartFile(
"file", "file",
"document.pdf", "document.pdf",
"application/pdf", MediaType.APPLICATION_PDF_VALUE,
"Fake PDF content".getBytes()); "Fake PDF content".getBytes());
// Setup ProcessExecutor mock // Setup ProcessExecutor mock
@ -531,7 +550,10 @@ class PDFToFileTest {
// Create a mock PDF file with no filename // Create a mock PDF file with no filename
MultipartFile pdfFile = MultipartFile pdfFile =
new MockMultipartFile( new MockMultipartFile(
"file", "", "application/pdf", "Fake PDF content".getBytes()); "file",
"",
MediaType.APPLICATION_PDF_VALUE,
"Fake PDF content".getBytes());
// Setup ProcessExecutor mock // Setup ProcessExecutor mock
mockedStaticProcessExecutor mockedStaticProcessExecutor

View File

@ -48,7 +48,8 @@ public class WebResponseUtilsTest {
try { try {
byte[] fileContent = "Sample file content".getBytes(); byte[] fileContent = "Sample file content".getBytes();
MockMultipartFile file = MockMultipartFile file =
new MockMultipartFile("file", "sample.txt", "text/plain", fileContent); new MockMultipartFile(
"file", "sample.txt", MediaType.TEXT_PLAIN_VALUE, fileContent);
ResponseEntity<byte[]> responseEntity = ResponseEntity<byte[]> responseEntity =
WebResponseUtils.multiPartFileToWebResponse(file); WebResponseUtils.multiPartFileToWebResponse(file);

View File

@ -8,6 +8,7 @@ import java.lang.reflect.Method;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockMultipartFile; import org.springframework.mock.web.MockMultipartFile;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
@ -24,7 +25,10 @@ class CustomColorReplaceStrategyTest {
// Create a mock file // Create a mock file
mockFile = mockFile =
new MockMultipartFile( new MockMultipartFile(
"file", "test.pdf", "application/pdf", "test pdf content".getBytes()); "file",
"test.pdf",
MediaType.APPLICATION_PDF_VALUE,
"test pdf content".getBytes());
// Initialize strategy with custom colors // Initialize strategy with custom colors
strategy = strategy =

View File

@ -24,6 +24,7 @@ import org.apache.pdfbox.pdmodel.graphics.color.PDDeviceRGB;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.core.io.InputStreamResource; import org.springframework.core.io.InputStreamResource;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockMultipartFile; import org.springframework.mock.web.MockMultipartFile;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
@ -38,7 +39,9 @@ class InvertFullColorStrategyTest {
void setUp() throws Exception { void setUp() throws Exception {
// Create a simple PDF document for testing // Create a simple PDF document for testing
byte[] pdfBytes = createSimplePdfWithRectangle(); byte[] pdfBytes = createSimplePdfWithRectangle();
mockPdfFile = new MockMultipartFile("file", "test.pdf", "application/pdf", pdfBytes); mockPdfFile =
new MockMultipartFile(
"file", "test.pdf", MediaType.APPLICATION_PDF_VALUE, pdfBytes);
// Create the strategy instance // Create the strategy instance
strategy = new InvertFullColorStrategy(mockPdfFile, ReplaceAndInvert.FULL_INVERSION); strategy = new InvertFullColorStrategy(mockPdfFile, ReplaceAndInvert.FULL_INVERSION);

View File

@ -7,6 +7,7 @@ import java.io.IOException;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.core.io.InputStreamResource; import org.springframework.core.io.InputStreamResource;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockMultipartFile; import org.springframework.mock.web.MockMultipartFile;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
@ -35,7 +36,10 @@ class ReplaceAndInvertColorStrategyTest {
// Arrange // Arrange
MultipartFile mockFile = MultipartFile mockFile =
new MockMultipartFile( new MockMultipartFile(
"file", "test.pdf", "application/pdf", "test content".getBytes()); "file",
"test.pdf",
MediaType.APPLICATION_PDF_VALUE,
"test content".getBytes());
ReplaceAndInvert replaceAndInvert = ReplaceAndInvert.CUSTOM_COLOR; ReplaceAndInvert replaceAndInvert = ReplaceAndInvert.CUSTOM_COLOR;
// Act // Act
@ -56,7 +60,7 @@ class ReplaceAndInvertColorStrategyTest {
// Arrange // Arrange
byte[] content = "test pdf content".getBytes(); byte[] content = "test pdf content".getBytes();
MultipartFile mockFile = MultipartFile mockFile =
new MockMultipartFile("file", "test.pdf", "application/pdf", content); new MockMultipartFile("file", "test.pdf", MediaType.APPLICATION_PDF_VALUE, content);
ReplaceAndInvert replaceAndInvert = ReplaceAndInvert.CUSTOM_COLOR; ReplaceAndInvert replaceAndInvert = ReplaceAndInvert.CUSTOM_COLOR;
ReplaceAndInvertColorStrategy strategy = ReplaceAndInvertColorStrategy strategy =
@ -74,10 +78,16 @@ class ReplaceAndInvertColorStrategyTest {
// Arrange // Arrange
MultipartFile mockFile1 = MultipartFile mockFile1 =
new MockMultipartFile( new MockMultipartFile(
"file1", "test1.pdf", "application/pdf", "content1".getBytes()); "file1",
"test1.pdf",
MediaType.APPLICATION_PDF_VALUE,
"content1".getBytes());
MultipartFile mockFile2 = MultipartFile mockFile2 =
new MockMultipartFile( new MockMultipartFile(
"file2", "test2.pdf", "application/pdf", "content2".getBytes()); "file2",
"test2.pdf",
MediaType.APPLICATION_PDF_VALUE,
"content2".getBytes());
// Act // Act
ReplaceAndInvertColorStrategy strategy = ReplaceAndInvertColorStrategy strategy =

View File

@ -11,6 +11,7 @@ import org.apache.pdfbox.pdmodel.PDPageTree;
import org.apache.pdfbox.pdmodel.encryption.PDEncryption; import org.apache.pdfbox.pdmodel.encryption.PDEncryption;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotation; import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotation;
import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm; import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
@ -29,7 +30,7 @@ public class AnalysisController {
private final CustomPDFDocumentFactory pdfDocumentFactory; private final CustomPDFDocumentFactory pdfDocumentFactory;
@PostMapping(value = "/page-count", consumes = "multipart/form-data") @PostMapping(value = "/page-count", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@Operation( @Operation(
summary = "Get PDF page count", summary = "Get PDF page count",
description = "Returns total number of pages in PDF. Input:PDF Output:JSON Type:SISO") description = "Returns total number of pages in PDF. Input:PDF Output:JSON Type:SISO")
@ -39,7 +40,7 @@ public class AnalysisController {
} }
} }
@PostMapping(value = "/basic-info", consumes = "multipart/form-data") @PostMapping(value = "/basic-info", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@Operation( @Operation(
summary = "Get basic PDF information", summary = "Get basic PDF information",
description = "Returns page count, version, file size. Input:PDF Output:JSON Type:SISO") description = "Returns page count, version, file size. Input:PDF Output:JSON Type:SISO")
@ -53,7 +54,7 @@ public class AnalysisController {
} }
} }
@PostMapping(value = "/document-properties", consumes = "multipart/form-data") @PostMapping(value = "/document-properties", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@Operation( @Operation(
summary = "Get PDF document properties", summary = "Get PDF document properties",
description = "Returns title, author, subject, etc. Input:PDF Output:JSON Type:SISO") description = "Returns title, author, subject, etc. Input:PDF Output:JSON Type:SISO")
@ -76,7 +77,7 @@ public class AnalysisController {
} }
} }
@PostMapping(value = "/page-dimensions", consumes = "multipart/form-data") @PostMapping(value = "/page-dimensions", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@Operation( @Operation(
summary = "Get page dimensions for all pages", summary = "Get page dimensions for all pages",
description = "Returns width and height of each page. Input:PDF Output:JSON Type:SISO") description = "Returns width and height of each page. Input:PDF Output:JSON Type:SISO")
@ -96,7 +97,7 @@ public class AnalysisController {
} }
} }
@PostMapping(value = "/form-fields", consumes = "multipart/form-data") @PostMapping(value = "/form-fields", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@Operation( @Operation(
summary = "Get form field information", summary = "Get form field information",
description = description =
@ -119,7 +120,7 @@ public class AnalysisController {
} }
} }
@PostMapping(value = "/annotation-info", consumes = "multipart/form-data") @PostMapping(value = "/annotation-info", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@Operation( @Operation(
summary = "Get annotation information", summary = "Get annotation information",
description = "Returns count and types of annotations. Input:PDF Output:JSON Type:SISO") description = "Returns count and types of annotations. Input:PDF Output:JSON Type:SISO")
@ -143,7 +144,7 @@ public class AnalysisController {
} }
} }
@PostMapping(value = "/font-info", consumes = "multipart/form-data") @PostMapping(value = "/font-info", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@Operation( @Operation(
summary = "Get font information", summary = "Get font information",
description = description =
@ -165,7 +166,7 @@ public class AnalysisController {
} }
} }
@PostMapping(value = "/security-info", consumes = "multipart/form-data") @PostMapping(value = "/security-info", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@Operation( @Operation(
summary = "Get security information", summary = "Get security information",
description = description =

View File

@ -10,6 +10,7 @@ import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.PDPageContentStream.AppendMode; import org.apache.pdfbox.pdmodel.PDPageContentStream.AppendMode;
import org.apache.pdfbox.pdmodel.common.PDRectangle; import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject; import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
@ -33,7 +34,7 @@ public class CropController {
private final CustomPDFDocumentFactory pdfDocumentFactory; private final CustomPDFDocumentFactory pdfDocumentFactory;
@PostMapping(value = "/crop", consumes = "multipart/form-data") @PostMapping(value = "/crop", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@Operation( @Operation(
summary = "Crops a PDF document", summary = "Crops a PDF document",
description = description =

View File

@ -46,7 +46,7 @@ public class EditTableOfContentsController {
private final CustomPDFDocumentFactory pdfDocumentFactory; private final CustomPDFDocumentFactory pdfDocumentFactory;
private final ObjectMapper objectMapper; private final ObjectMapper objectMapper;
@PostMapping(value = "/extract-bookmarks", consumes = "multipart/form-data") @PostMapping(value = "/extract-bookmarks", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@Operation( @Operation(
summary = "Extract PDF Bookmarks", summary = "Extract PDF Bookmarks",
description = "Extracts bookmarks/table of contents from a PDF document as JSON.") description = "Extracts bookmarks/table of contents from a PDF document as JSON.")
@ -154,7 +154,7 @@ public class EditTableOfContentsController {
return bookmark; return bookmark;
} }
@PostMapping(value = "/edit-table-of-contents", consumes = "multipart/form-data") @PostMapping(value = "/edit-table-of-contents", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@Operation( @Operation(
summary = "Edit Table of Contents", summary = "Edit Table of Contents",
description = "Add or edit bookmarks/table of contents in a PDF document.") description = "Add or edit bookmarks/table of contents in a PDF document.")

View File

@ -1,6 +1,5 @@
package stirling.software.SPDF.controller.api; package stirling.software.SPDF.controller.api;
import java.io.ByteArrayOutputStream;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
@ -20,12 +19,14 @@ import org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline.PDOutlin
import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm; import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm;
import org.apache.pdfbox.pdmodel.interactive.form.PDField; import org.apache.pdfbox.pdmodel.interactive.form.PDField;
import org.apache.pdfbox.pdmodel.interactive.form.PDSignatureField; import org.apache.pdfbox.pdmodel.interactive.form.PDSignatureField;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
@ -36,8 +37,9 @@ import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.model.api.general.MergePdfsRequest; import stirling.software.SPDF.model.api.general.MergePdfsRequest;
import stirling.software.common.service.CustomPDFDocumentFactory; import stirling.software.common.service.CustomPDFDocumentFactory;
import stirling.software.common.util.ExceptionUtils; import stirling.software.common.util.ExceptionUtils;
import stirling.software.common.util.GeneralUtils;
import stirling.software.common.util.PdfErrorUtils; import stirling.software.common.util.PdfErrorUtils;
import stirling.software.common.util.TempFile;
import stirling.software.common.util.TempFileManager;
import stirling.software.common.util.WebResponseUtils; import stirling.software.common.util.WebResponseUtils;
@RestController @RestController
@ -48,6 +50,7 @@ import stirling.software.common.util.WebResponseUtils;
public class MergeController { public class MergeController {
private final CustomPDFDocumentFactory pdfDocumentFactory; private final CustomPDFDocumentFactory pdfDocumentFactory;
private final TempFileManager tempFileManager;
// Merges a list of PDDocument objects into a single PDDocument // Merges a list of PDDocument objects into a single PDDocument
public PDDocument mergeDocuments(List<PDDocument> documents) throws IOException { public PDDocument mergeDocuments(List<PDDocument> documents) throws IOException {
@ -62,11 +65,10 @@ public class MergeController {
// Returns a comparator for sorting MultipartFile arrays based on the given sort type // Returns a comparator for sorting MultipartFile arrays based on the given sort type
private Comparator<MultipartFile> getSortComparator(String sortType) { private Comparator<MultipartFile> getSortComparator(String sortType) {
switch (sortType) { return switch (sortType) {
case "byFileName": case "byFileName" -> Comparator.comparing(MultipartFile::getOriginalFilename);
return Comparator.comparing(MultipartFile::getOriginalFilename); case "byDateModified" ->
case "byDateModified": (file1, file2) -> {
return (file1, file2) -> {
try { try {
BasicFileAttributes attr1 = BasicFileAttributes attr1 =
Files.readAttributes( Files.readAttributes(
@ -81,8 +83,8 @@ public class MergeController {
return 0; // If there's an error, treat them as equal return 0; // If there's an error, treat them as equal
} }
}; };
case "byDateCreated": case "byDateCreated" ->
return (file1, file2) -> { (file1, file2) -> {
try { try {
BasicFileAttributes attr1 = BasicFileAttributes attr1 =
Files.readAttributes( Files.readAttributes(
@ -97,8 +99,8 @@ public class MergeController {
return 0; // If there's an error, treat them as equal return 0; // If there's an error, treat them as equal
} }
}; };
case "byPDFTitle": case "byPDFTitle" ->
return (file1, file2) -> { (file1, file2) -> {
try (PDDocument doc1 = pdfDocumentFactory.load(file1); try (PDDocument doc1 = pdfDocumentFactory.load(file1);
PDDocument doc2 = pdfDocumentFactory.load(file2)) { PDDocument doc2 = pdfDocumentFactory.load(file2)) {
String title1 = doc1.getDocumentInformation().getTitle(); String title1 = doc1.getDocumentInformation().getTitle();
@ -108,10 +110,9 @@ public class MergeController {
return 0; return 0;
} }
}; };
case "orderProvided": case "orderProvided" -> (file1, file2) -> 0; // Default is the order provided
default: default -> (file1, file2) -> 0; // Default is the order provided
return (file1, file2) -> 0; // Default is the order provided };
}
} }
// Adds a table of contents to the merged document using filenames as chapter titles // Adds a table of contents to the merged document using filenames as chapter titles
@ -154,17 +155,18 @@ public class MergeController {
} }
} }
@PostMapping(consumes = "multipart/form-data", value = "/merge-pdfs") @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE, value = "/merge-pdfs")
@Operation( @Operation(
summary = "Merge multiple PDF files into one", summary = "Merge multiple PDF files into one",
description = description =
"This endpoint merges multiple PDF files into a single PDF file. The merged" "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" + " file will contain all pages from the input files in the order they were"
+ " provided. Input:PDF Output:PDF Type:MISO") + " provided. Input:PDF Output:PDF Type:MISO")
public ResponseEntity<byte[]> mergePdfs(@ModelAttribute MergePdfsRequest request) public ResponseEntity<StreamingResponseBody> mergePdfs(@ModelAttribute MergePdfsRequest request)
throws IOException { throws IOException {
List<File> filesToDelete = new ArrayList<>(); // List of temporary files to delete List<File> filesToDelete = new ArrayList<>(); // List of temporary files to delete
File mergedTempFile = null; TempFile mergedTempFile = null;
TempFile outputTempFile = null;
PDDocument mergedDocument = null; PDDocument mergedDocument = null;
boolean removeCertSign = Boolean.TRUE.equals(request.getRemoveCertSign()); boolean removeCertSign = Boolean.TRUE.equals(request.getRemoveCertSign());
@ -182,14 +184,14 @@ public class MergeController {
for (MultipartFile multipartFile : files) { for (MultipartFile multipartFile : files) {
totalSize += multipartFile.getSize(); totalSize += multipartFile.getSize();
File tempFile = File tempFile =
GeneralUtils.convertMultipartFileToFile( tempFileManager.convertMultipartFileToFile(
multipartFile); // Convert MultipartFile to File multipartFile); // Convert MultipartFile to File
filesToDelete.add(tempFile); // Add temp file to the list for later deletion filesToDelete.add(tempFile); // Add temp file to the list for later deletion
mergerUtility.addSource(tempFile); // Add source file to the merger utility mergerUtility.addSource(tempFile); // Add source file to the merger utility
} }
mergedTempFile = Files.createTempFile("merged-", ".pdf").toFile(); mergedTempFile = new TempFile(tempFileManager, ".pdf");
mergerUtility.setDestinationFileName(mergedTempFile.getAbsolutePath()); mergerUtility.setDestinationFileName(mergedTempFile.getFile().getAbsolutePath());
try { try {
mergerUtility.mergeDocuments( mergerUtility.mergeDocuments(
@ -204,7 +206,7 @@ public class MergeController {
} }
// Load the merged PDF document // Load the merged PDF document
mergedDocument = pdfDocumentFactory.load(mergedTempFile); mergedDocument = pdfDocumentFactory.load(mergedTempFile.getFile());
// Remove signatures if removeCertSign is true // Remove signatures if removeCertSign is true
if (removeCertSign) { if (removeCertSign) {
@ -213,7 +215,7 @@ public class MergeController {
if (acroForm != null) { if (acroForm != null) {
List<PDField> fieldsToRemove = List<PDField> fieldsToRemove =
acroForm.getFields().stream() acroForm.getFields().stream()
.filter(field -> field instanceof PDSignatureField) .filter(PDSignatureField.class::isInstance)
.toList(); .toList();
if (!fieldsToRemove.isEmpty()) { if (!fieldsToRemove.isEmpty()) {
@ -229,16 +231,15 @@ public class MergeController {
addTableOfContents(mergedDocument, files); addTableOfContents(mergedDocument, files);
} }
// Save the modified document to a new ByteArrayOutputStream // Save the modified document to a temporary file
ByteArrayOutputStream baos = new ByteArrayOutputStream(); outputTempFile = new TempFile(tempFileManager, ".pdf");
mergedDocument.save(baos); mergedDocument.save(outputTempFile.getFile());
String mergedFileName = String mergedFileName =
files[0].getOriginalFilename().replaceFirst("[.][^.]+$", "") files[0].getOriginalFilename().replaceFirst("[.][^.]+$", "")
+ "_merged_unsigned.pdf"; + "_merged_unsigned.pdf";
return WebResponseUtils.baosToWebResponse( return WebResponseUtils.pdfFileToWebResponse(
baos, mergedFileName); // Return the modified PDF outputTempFile, mergedFileName); // Return the modified PDF as stream
} catch (Exception ex) { } catch (Exception ex) {
if (ex instanceof IOException && PdfErrorUtils.isCorruptedPdfError((IOException) ex)) { if (ex instanceof IOException && PdfErrorUtils.isCorruptedPdfError((IOException) ex)) {
log.warn("Corrupted PDF detected in merge pdf process: {}", ex.getMessage()); log.warn("Corrupted PDF detected in merge pdf process: {}", ex.getMessage());
@ -251,12 +252,10 @@ public class MergeController {
mergedDocument.close(); // Close the merged document mergedDocument.close(); // Close the merged document
} }
for (File file : filesToDelete) { for (File file : filesToDelete) {
if (file != null) { tempFileManager.deleteTempFile(file); // Delete temporary files
Files.deleteIfExists(file.toPath()); // Delete temporary files
}
} }
if (mergedTempFile != null) { if (mergedTempFile != null) {
Files.deleteIfExists(mergedTempFile.toPath()); mergedTempFile.close();
} }
} }
} }

View File

@ -11,6 +11,7 @@ import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.common.PDRectangle; import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject; import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject;
import org.apache.pdfbox.util.Matrix; import org.apache.pdfbox.util.Matrix;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
@ -36,7 +37,7 @@ public class MultiPageLayoutController {
private final CustomPDFDocumentFactory pdfDocumentFactory; private final CustomPDFDocumentFactory pdfDocumentFactory;
@PostMapping(value = "/multi-page-layout", consumes = "multipart/form-data") @PostMapping(value = "/multi-page-layout", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@Operation( @Operation(
summary = "Merge multiple pages of a PDF document into a single page", summary = "Merge multiple pages of a PDF document into a single page",
description = description =

View File

@ -4,6 +4,7 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocument;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
@ -46,7 +47,7 @@ public class PdfImageRemovalController {
* content type and filename. * content type and filename.
* @throws IOException If an error occurs while processing the PDF file. * @throws IOException If an error occurs while processing the PDF file.
*/ */
@PostMapping(consumes = "multipart/form-data", value = "/remove-image-pdf") @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE, value = "/remove-image-pdf")
@Operation( @Operation(
summary = "Remove images from file to reduce the file size.", summary = "Remove images from file to reduce the file size.",
description = description =

View File

@ -39,7 +39,7 @@ public class PdfOverlayController {
private final CustomPDFDocumentFactory pdfDocumentFactory; private final CustomPDFDocumentFactory pdfDocumentFactory;
@PostMapping(value = "/overlay-pdfs", consumes = "multipart/form-data") @PostMapping(value = "/overlay-pdfs", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@Operation( @Operation(
summary = "Overlay PDF files in various modes", summary = "Overlay PDF files in various modes",
description = description =

View File

@ -7,6 +7,7 @@ import java.util.List;
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage; import org.apache.pdfbox.pdmodel.PDPage;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
@ -38,7 +39,7 @@ public class RearrangePagesPDFController {
private final CustomPDFDocumentFactory pdfDocumentFactory; private final CustomPDFDocumentFactory pdfDocumentFactory;
@PostMapping(consumes = "multipart/form-data", value = "/remove-pages") @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE, value = "/remove-pages")
@Operation( @Operation(
summary = "Remove pages from a PDF file", summary = "Remove pages from a PDF file",
description = description =
@ -237,7 +238,7 @@ public class RearrangePagesPDFController {
} }
} }
@PostMapping(consumes = "multipart/form-data", value = "/rearrange-pages") @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE, value = "/rearrange-pages")
@Operation( @Operation(
summary = "Rearrange pages in a PDF file", summary = "Rearrange pages in a PDF file",
description = description =

View File

@ -5,6 +5,7 @@ import java.io.IOException;
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage; import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageTree; import org.apache.pdfbox.pdmodel.PDPageTree;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
@ -31,7 +32,7 @@ public class RotationController {
private final CustomPDFDocumentFactory pdfDocumentFactory; private final CustomPDFDocumentFactory pdfDocumentFactory;
@PostMapping(consumes = "multipart/form-data", value = "/rotate-pdf") @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE, value = "/rotate-pdf")
@Operation( @Operation(
summary = "Rotate a PDF file", summary = "Rotate a PDF file",
description = description =

View File

@ -12,6 +12,7 @@ import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.common.PDRectangle; import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject; import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject;
import org.apache.pdfbox.util.Matrix; import org.apache.pdfbox.util.Matrix;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
@ -38,7 +39,7 @@ public class ScalePagesController {
private final CustomPDFDocumentFactory pdfDocumentFactory; private final CustomPDFDocumentFactory pdfDocumentFactory;
@PostMapping(value = "/scale-pages", consumes = "multipart/form-data") @PostMapping(value = "/scale-pages", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@Operation( @Operation(
summary = "Change the size of a PDF page/document", summary = "Change the size of a PDF page/document",
description = description =

View File

@ -30,6 +30,8 @@ import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.model.api.PDFWithPageNums; import stirling.software.SPDF.model.api.PDFWithPageNums;
import stirling.software.common.service.CustomPDFDocumentFactory; import stirling.software.common.service.CustomPDFDocumentFactory;
import stirling.software.common.util.ExceptionUtils; import stirling.software.common.util.ExceptionUtils;
import stirling.software.common.util.TempFile;
import stirling.software.common.util.TempFileManager;
import stirling.software.common.util.WebResponseUtils; import stirling.software.common.util.WebResponseUtils;
@RestController @RestController
@ -40,8 +42,9 @@ import stirling.software.common.util.WebResponseUtils;
public class SplitPDFController { public class SplitPDFController {
private final CustomPDFDocumentFactory pdfDocumentFactory; private final CustomPDFDocumentFactory pdfDocumentFactory;
private final TempFileManager tempFileManager;
@PostMapping(consumes = "multipart/form-data", value = "/split-pages") @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE, value = "/split-pages")
@Operation( @Operation(
summary = "Split a PDF file into separate documents", summary = "Split a PDF file into separate documents",
description = description =
@ -55,8 +58,11 @@ public class SplitPDFController {
PDDocument document = null; PDDocument document = null;
Path zipFile = null; Path zipFile = null;
List<ByteArrayOutputStream> splitDocumentsBoas = new ArrayList<>(); List<ByteArrayOutputStream> splitDocumentsBoas = new ArrayList<>();
String filename;
TempFile outputTempFile = null;
try { try {
outputTempFile = new TempFile(tempFileManager, ".zip");
MultipartFile file = request.getFileInput(); MultipartFile file = request.getFileInput();
String pages = request.getPageNumbers(); String pages = request.getPageNumbers();
@ -105,12 +111,11 @@ public class SplitPDFController {
// closing the original document // closing the original document
document.close(); document.close();
zipFile = Files.createTempFile("split_documents", ".zip"); filename =
String filename =
Filenames.toSimpleFileName(file.getOriginalFilename()) Filenames.toSimpleFileName(file.getOriginalFilename())
.replaceFirst("[.][^.]+$", ""); .replaceFirst("[.][^.]+$", "");
try (ZipOutputStream zipOut = new ZipOutputStream(Files.newOutputStream(zipFile))) { try (ZipOutputStream zipOut =
new ZipOutputStream(Files.newOutputStream(outputTempFile.getPath()))) {
// loop through the split documents and write them to the zip file // loop through the split documents and write them to the zip file
for (int i = 0; i < splitDocumentsBoas.size(); i++) { for (int i = 0; i < splitDocumentsBoas.size(); i++) {
String fileName = filename + "_" + (i + 1) + ".pdf"; String fileName = filename + "_" + (i + 1) + ".pdf";
@ -125,19 +130,13 @@ public class SplitPDFController {
log.debug("Wrote split document {} to zip file", fileName); log.debug("Wrote split document {} to zip file", fileName);
} }
} catch (Exception e) {
log.error("Failed writing to zip", e);
throw e;
} }
log.debug(
log.debug("Successfully created zip file with split documents: {}", zipFile.toString()); "Successfully created zip file with split documents: {}",
byte[] data = Files.readAllBytes(zipFile); outputTempFile.getPath());
Files.deleteIfExists(zipFile); byte[] data = Files.readAllBytes(outputTempFile.getPath());
// return the Resource in the response
return WebResponseUtils.bytesToWebResponse( return WebResponseUtils.bytesToWebResponse(
data, filename + ".zip", MediaType.APPLICATION_OCTET_STREAM); data, filename + ".zip", MediaType.APPLICATION_OCTET_STREAM);
} finally { } finally {
try { try {
// Close the main document // Close the main document
@ -152,9 +151,9 @@ public class SplitPDFController {
} }
} }
// Delete temporary zip file // Close the output temporary file
if (zipFile != null) { if (outputTempFile != null) {
Files.deleteIfExists(zipFile); outputTempFile.close();
} }
} catch (Exception e) { } catch (Exception e) {
log.error("Error while cleaning up resources", e); log.error("Error while cleaning up resources", e);

View File

@ -117,7 +117,7 @@ public class SplitPdfByChaptersController {
return bookmarks; return bookmarks;
} }
@PostMapping(value = "/split-pdf-by-chapters", consumes = "multipart/form-data") @PostMapping(value = "/split-pdf-by-chapters", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@Operation( @Operation(
summary = "Split PDFs by Chapters", summary = "Split PDFs by Chapters",
description = "Splits a PDF into chapters and returns a ZIP file.") description = "Splits a PDF into chapters and returns a ZIP file.")

View File

@ -2,8 +2,8 @@ package stirling.software.SPDF.controller.api;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
@ -24,6 +24,7 @@ import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import io.github.pixee.security.Filenames; import io.github.pixee.security.Filenames;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
@ -33,6 +34,9 @@ import lombok.RequiredArgsConstructor;
import stirling.software.SPDF.model.api.SplitPdfBySectionsRequest; import stirling.software.SPDF.model.api.SplitPdfBySectionsRequest;
import stirling.software.common.service.CustomPDFDocumentFactory; import stirling.software.common.service.CustomPDFDocumentFactory;
import stirling.software.common.util.PDFService;
import stirling.software.common.util.TempFile;
import stirling.software.common.util.TempFileManager;
import stirling.software.common.util.WebResponseUtils; import stirling.software.common.util.WebResponseUtils;
@RestController @RestController
@ -42,16 +46,18 @@ import stirling.software.common.util.WebResponseUtils;
public class SplitPdfBySectionsController { public class SplitPdfBySectionsController {
private final CustomPDFDocumentFactory pdfDocumentFactory; private final CustomPDFDocumentFactory pdfDocumentFactory;
private final TempFileManager tempFileManager;
private final PDFService pdfService;
@PostMapping(value = "/split-pdf-by-sections", consumes = "multipart/form-data") @PostMapping(value = "/split-pdf-by-sections", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@Operation( @Operation(
summary = "Split PDF pages into smaller sections", summary = "Split PDF pages into smaller sections",
description = description =
"Split each page of a PDF into smaller sections based on the user's choice" "Split each page of a PDF into smaller sections based on the user's choice"
+ " (halves, thirds, quarters, etc.), both vertically and horizontally." + " (halves, thirds, quarters, etc.), both vertically and horizontally."
+ " Input:PDF Output:ZIP-PDF Type:SISO") + " Input:PDF Output:ZIP-PDF Type:SISO")
public ResponseEntity<byte[]> splitPdf(@ModelAttribute SplitPdfBySectionsRequest request) public ResponseEntity<StreamingResponseBody> splitPdf(
throws Exception { @ModelAttribute SplitPdfBySectionsRequest request) throws Exception {
List<ByteArrayOutputStream> splitDocumentsBoas = new ArrayList<>(); List<ByteArrayOutputStream> splitDocumentsBoas = new ArrayList<>();
MultipartFile file = request.getFileInput(); MultipartFile file = request.getFileInput();
@ -67,10 +73,14 @@ public class SplitPdfBySectionsController {
Filenames.toSimpleFileName(file.getOriginalFilename()) Filenames.toSimpleFileName(file.getOriginalFilename())
.replaceFirst("[.][^.]+$", ""); .replaceFirst("[.][^.]+$", "");
if (merge) { if (merge) {
MergeController mergeController = new MergeController(pdfDocumentFactory); TempFile tempFile = new TempFile(tempFileManager, ".pdf");
ByteArrayOutputStream baos = new ByteArrayOutputStream(); try (PDDocument merged = pdfService.mergeDocuments(splitDocuments);
mergeController.mergeDocuments(splitDocuments).save(baos); OutputStream out = Files.newOutputStream(tempFile.getPath())) {
return WebResponseUtils.bytesToWebResponse(baos.toByteArray(), filename + "_split.pdf"); merged.save(out);
for (PDDocument d : splitDocuments) d.close();
sourceDocument.close();
}
return WebResponseUtils.pdfFileToWebResponse(tempFile, filename + "_split.pdf");
} }
for (PDDocument doc : splitDocuments) { for (PDDocument doc : splitDocuments) {
ByteArrayOutputStream baos = new ByteArrayOutputStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream();
@ -81,10 +91,9 @@ public class SplitPdfBySectionsController {
sourceDocument.close(); sourceDocument.close();
Path zipFile = Files.createTempFile("split_documents", ".zip"); TempFile zipTempFile = new TempFile(tempFileManager, ".zip");
byte[] data; try (ZipOutputStream zipOut =
new ZipOutputStream(Files.newOutputStream(zipTempFile.getPath()))) {
try (ZipOutputStream zipOut = new ZipOutputStream(Files.newOutputStream(zipFile))) {
int pageNum = 1; int pageNum = 1;
for (int i = 0; i < splitDocumentsBoas.size(); i++) { for (int i = 0; i < splitDocumentsBoas.size(); i++) {
ByteArrayOutputStream baos = splitDocumentsBoas.get(i); ByteArrayOutputStream baos = splitDocumentsBoas.get(i);
@ -98,15 +107,8 @@ public class SplitPdfBySectionsController {
if (sectionNum == horiz * verti) pageNum++; if (sectionNum == horiz * verti) pageNum++;
} }
zipOut.finish();
data = Files.readAllBytes(zipFile);
return WebResponseUtils.bytesToWebResponse(
data, filename + "_split.zip", MediaType.APPLICATION_OCTET_STREAM);
} finally {
Files.deleteIfExists(zipFile);
} }
return WebResponseUtils.zipFileToWebResponse(zipTempFile, filename + "_split.zip");
} }
public List<PDDocument> splitPdfPages( public List<PDDocument> splitPdfPages(

View File

@ -28,6 +28,8 @@ import stirling.software.SPDF.model.api.general.SplitPdfBySizeOrCountRequest;
import stirling.software.common.service.CustomPDFDocumentFactory; import stirling.software.common.service.CustomPDFDocumentFactory;
import stirling.software.common.util.ExceptionUtils; import stirling.software.common.util.ExceptionUtils;
import stirling.software.common.util.GeneralUtils; import stirling.software.common.util.GeneralUtils;
import stirling.software.common.util.TempFile;
import stirling.software.common.util.TempFileManager;
import stirling.software.common.util.WebResponseUtils; import stirling.software.common.util.WebResponseUtils;
@RestController @RestController
@ -38,8 +40,9 @@ import stirling.software.common.util.WebResponseUtils;
public class SplitPdfBySizeController { public class SplitPdfBySizeController {
private final CustomPDFDocumentFactory pdfDocumentFactory; private final CustomPDFDocumentFactory pdfDocumentFactory;
private final TempFileManager tempFileManager;
@PostMapping(value = "/split-by-size-or-count", consumes = "multipart/form-data") @PostMapping(value = "/split-by-size-or-count", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@Operation( @Operation(
summary = "Auto split PDF pages into separate documents based on size or count", summary = "Auto split PDF pages into separate documents based on size or count",
description = description =
@ -54,15 +57,14 @@ public class SplitPdfBySizeController {
log.debug("Starting PDF split process with request: {}", request); log.debug("Starting PDF split process with request: {}", request);
MultipartFile file = request.getFileInput(); MultipartFile file = request.getFileInput();
Path zipFile = Files.createTempFile("split_documents", ".zip");
log.debug("Created temporary zip file: {}", zipFile);
String filename = String filename =
Filenames.toSimpleFileName(file.getOriginalFilename()) Filenames.toSimpleFileName(file.getOriginalFilename())
.replaceFirst("[.][^.]+$", ""); .replaceFirst("[.][^.]+$", "");
log.debug("Base filename for output: {}", filename); log.debug("Base filename for output: {}", filename);
byte[] data = null; try (TempFile zipTempFile = new TempFile(tempFileManager, ".zip")) {
Path zipFile = zipTempFile.getPath();
log.debug("Created temporary zip file: {}", zipFile);
try { try {
log.debug("Reading input file bytes"); log.debug("Reading input file bytes");
byte[] pdfBytes = file.getBytes(); byte[] pdfBytes = file.getBytes();
@ -102,41 +104,21 @@ public class SplitPdfBySizeController {
"Invalid argument: {0}", "Invalid argument: {0}",
"split type: " + type); "split type: " + type);
} }
log.debug("PDF splitting completed successfully"); log.debug("PDF splitting completed successfully");
} catch (Exception e) {
ExceptionUtils.logException("PDF document loading or processing", e);
throw e;
} }
} catch (IOException e) {
log.error("Error creating or writing to ZIP file", e);
throw e;
} }
byte[] data = Files.readAllBytes(zipFile);
log.debug("Successfully read {} bytes from ZIP file", data.length);
log.debug("Returning response with {} bytes of data", data.length);
return WebResponseUtils.bytesToWebResponse(
data, filename + ".zip", MediaType.APPLICATION_OCTET_STREAM);
} catch (Exception e) { } catch (Exception e) {
ExceptionUtils.logException("PDF splitting process", e); ExceptionUtils.logException("PDF splitting process", e);
throw e; // Re-throw to ensure proper error response throw e; // Re-throw to ensure proper error response
} finally {
try {
log.debug("Reading ZIP file data");
data = Files.readAllBytes(zipFile);
log.debug("Successfully read {} bytes from ZIP file", data.length);
} catch (IOException e) {
log.error("Error reading ZIP file data", e);
}
try {
log.debug("Deleting temporary ZIP file");
boolean deleted = Files.deleteIfExists(zipFile);
log.debug("Temporary ZIP file deleted: {}", deleted);
} catch (IOException e) {
log.error("Error deleting temporary ZIP file", e);
} }
} }
log.debug("Returning response with {} bytes of data", data != null ? data.length : 0);
return WebResponseUtils.bytesToWebResponse(
data, filename + ".zip", MediaType.APPLICATION_OCTET_STREAM);
} }
private void handleSplitBySize( private void handleSplitBySize(

View File

@ -10,6 +10,7 @@ import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream; import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.common.PDRectangle; import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject; import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
@ -33,7 +34,7 @@ public class ToSinglePageController {
private final CustomPDFDocumentFactory pdfDocumentFactory; private final CustomPDFDocumentFactory pdfDocumentFactory;
@PostMapping(consumes = "multipart/form-data", value = "/pdf-to-single-page") @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE, value = "/pdf-to-single-page")
@Operation( @Operation(
summary = "Convert a multi-page PDF into a single long page PDF", summary = "Convert a multi-page PDF into a single long page PDF",
description = description =

View File

@ -40,7 +40,7 @@ public class ConvertEmlToPDF {
private final TempFileManager tempFileManager; private final TempFileManager tempFileManager;
private final CustomHtmlSanitizer customHtmlSanitizer; private final CustomHtmlSanitizer customHtmlSanitizer;
@PostMapping(consumes = "multipart/form-data", value = "/eml/pdf") @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE, value = "/eml/pdf")
@Operation( @Operation(
summary = "Convert EML to PDF", summary = "Convert EML to PDF",
description = description =

View File

@ -1,5 +1,6 @@
package stirling.software.SPDF.controller.api.converters; package stirling.software.SPDF.controller.api.converters;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
@ -36,7 +37,7 @@ public class ConvertHtmlToPDF {
private final CustomHtmlSanitizer customHtmlSanitizer; private final CustomHtmlSanitizer customHtmlSanitizer;
@PostMapping(consumes = "multipart/form-data", value = "/html/pdf") @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE, value = "/html/pdf")
@Operation( @Operation(
summary = "Convert an HTML or ZIP (containing HTML and CSS) to PDF", summary = "Convert an HTML or ZIP (containing HTML and CSS) to PDF",
description = description =

View File

@ -51,7 +51,7 @@ public class ConvertImgPDFController {
private final CustomPDFDocumentFactory pdfDocumentFactory; private final CustomPDFDocumentFactory pdfDocumentFactory;
@PostMapping(consumes = "multipart/form-data", value = "/pdf/img") @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE, value = "/pdf/img")
@Operation( @Operation(
summary = "Convert PDF to image(s)", summary = "Convert PDF to image(s)",
description = description =
@ -213,7 +213,7 @@ public class ConvertImgPDFController {
} }
} }
@PostMapping(consumes = "multipart/form-data", value = "/img/pdf") @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE, value = "/img/pdf")
@Operation( @Operation(
summary = "Convert images to a PDF file", summary = "Convert images to a PDF file",
description = description =
@ -244,7 +244,7 @@ public class ConvertImgPDFController {
private String getMediaType(String imageFormat) { private String getMediaType(String imageFormat) {
String mimeType = URLConnection.guessContentTypeFromName("." + imageFormat); String mimeType = URLConnection.guessContentTypeFromName("." + imageFormat);
return "null".equals(mimeType) ? "application/octet-stream" : mimeType; return "null".equals(mimeType) ? MediaType.APPLICATION_OCTET_STREAM_VALUE : mimeType;
} }
/** /**

View File

@ -10,6 +10,7 @@ import org.commonmark.node.Node;
import org.commonmark.parser.Parser; import org.commonmark.parser.Parser;
import org.commonmark.renderer.html.AttributeProvider; import org.commonmark.renderer.html.AttributeProvider;
import org.commonmark.renderer.html.HtmlRenderer; import org.commonmark.renderer.html.HtmlRenderer;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
@ -45,7 +46,7 @@ public class ConvertMarkdownToPdf {
private final CustomHtmlSanitizer customHtmlSanitizer; private final CustomHtmlSanitizer customHtmlSanitizer;
@PostMapping(consumes = "multipart/form-data", value = "/markdown/pdf") @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE, value = "/markdown/pdf")
@Operation( @Operation(
summary = "Convert a Markdown file to PDF", summary = "Convert a Markdown file to PDF",
description = description =

View File

@ -12,6 +12,7 @@ import java.util.List;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.FilenameUtils;
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocument;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
@ -171,7 +172,7 @@ public class ConvertOfficeController {
return fileExtension.matches(extensionPattern); return fileExtension.matches(extensionPattern);
} }
@PostMapping(consumes = "multipart/form-data", value = "/file/pdf") @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE, value = "/file/pdf")
@Operation( @Operation(
summary = "Convert a file to a PDF using LibreOffice", summary = "Convert a file to a PDF using LibreOffice",
description = description =

View File

@ -1,5 +1,6 @@
package stirling.software.SPDF.controller.api.converters; package stirling.software.SPDF.controller.api.converters;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
@ -18,7 +19,7 @@ import stirling.software.common.util.PDFToFile;
@RequestMapping("/api/v1/convert") @RequestMapping("/api/v1/convert")
public class ConvertPDFToHtml { public class ConvertPDFToHtml {
@PostMapping(consumes = "multipart/form-data", value = "/pdf/html") @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE, value = "/pdf/html")
@Operation( @Operation(
summary = "Convert PDF to HTML", summary = "Convert PDF to HTML",
description = description =

View File

@ -34,7 +34,7 @@ public class ConvertPDFToOffice {
private final CustomPDFDocumentFactory pdfDocumentFactory; private final CustomPDFDocumentFactory pdfDocumentFactory;
@PostMapping(consumes = "multipart/form-data", value = "/pdf/presentation") @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE, value = "/pdf/presentation")
@Operation( @Operation(
summary = "Convert PDF to Presentation format", summary = "Convert PDF to Presentation format",
description = description =
@ -49,7 +49,7 @@ public class ConvertPDFToOffice {
return pdfToFile.processPdfToOfficeFormat(inputFile, outputFormat, "impress_pdf_import"); return pdfToFile.processPdfToOfficeFormat(inputFile, outputFormat, "impress_pdf_import");
} }
@PostMapping(consumes = "multipart/form-data", value = "/pdf/text") @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE, value = "/pdf/text")
@Operation( @Operation(
summary = "Convert PDF to Text or RTF format", summary = "Convert PDF to Text or RTF format",
description = description =
@ -77,7 +77,7 @@ public class ConvertPDFToOffice {
} }
} }
@PostMapping(consumes = "multipart/form-data", value = "/pdf/word") @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE, value = "/pdf/word")
@Operation( @Operation(
summary = "Convert PDF to Word document", summary = "Convert PDF to Word document",
description = description =
@ -91,7 +91,7 @@ public class ConvertPDFToOffice {
return pdfToFile.processPdfToOfficeFormat(inputFile, outputFormat, "writer_pdf_import"); return pdfToFile.processPdfToOfficeFormat(inputFile, outputFormat, "writer_pdf_import");
} }
@PostMapping(consumes = "multipart/form-data", value = "/pdf/xml") @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE, value = "/pdf/xml")
@Operation( @Operation(
summary = "Convert PDF to XML", summary = "Convert PDF to XML",
description = description =

View File

@ -78,7 +78,7 @@ import stirling.software.common.util.WebResponseUtils;
@Tag(name = "Convert", description = "Convert APIs") @Tag(name = "Convert", description = "Convert APIs")
public class ConvertPDFToPDFA { public class ConvertPDFToPDFA {
@PostMapping(consumes = "multipart/form-data", value = "/pdf/pdfa") @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE, value = "/pdf/pdfa")
@Operation( @Operation(
summary = "Convert a PDF to a PDF/A", summary = "Convert a PDF to a PDF/A",
description = description =
@ -89,7 +89,7 @@ public class ConvertPDFToPDFA {
String outputFormat = request.getOutputFormat(); String outputFormat = request.getOutputFormat();
// Validate input file type // Validate input file type
if (!"application/pdf".equals(inputFile.getContentType())) { if (!MediaType.APPLICATION_PDF_VALUE.equals(inputFile.getContentType())) {
log.error("Invalid input file type: {}", inputFile.getContentType()); log.error("Invalid input file type: {}", inputFile.getContentType());
throw ExceptionUtils.createPdfFileRequiredException(); throw ExceptionUtils.createPdfFileRequiredException();
} }

View File

@ -9,6 +9,7 @@ import java.util.List;
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocument;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
@ -43,7 +44,7 @@ public class ConvertWebsiteToPDF {
private final RuntimePathConfig runtimePathConfig; private final RuntimePathConfig runtimePathConfig;
private final ApplicationProperties applicationProperties; private final ApplicationProperties applicationProperties;
@PostMapping(consumes = "multipart/form-data", value = "/url/pdf") @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE, value = "/url/pdf")
@Operation( @Operation(
summary = "Convert a URL to a PDF", summary = "Convert a URL to a PDF",
description = description =

View File

@ -46,7 +46,7 @@ public class ExtractCSVController {
private final CustomPDFDocumentFactory pdfDocumentFactory; private final CustomPDFDocumentFactory pdfDocumentFactory;
@PostMapping(value = "/pdf/csv", consumes = "multipart/form-data") @PostMapping(value = "/pdf/csv", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@Operation( @Operation(
summary = "Extracts a CSV document from a PDF", summary = "Extracts a CSV document from a PDF",
description = description =

View File

@ -5,6 +5,7 @@ import java.io.IOException;
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage; import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.common.PDRectangle; import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
@ -37,7 +38,7 @@ public class FilterController {
private final CustomPDFDocumentFactory pdfDocumentFactory; private final CustomPDFDocumentFactory pdfDocumentFactory;
@PostMapping(consumes = "multipart/form-data", value = "/filter-contains-text") @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE, value = "/filter-contains-text")
@Operation( @Operation(
summary = "Checks if a PDF contains set text, returns true if does", summary = "Checks if a PDF contains set text, returns true if does",
description = "Input:PDF Output:Boolean Type:SISO") description = "Input:PDF Output:Boolean Type:SISO")
@ -55,7 +56,7 @@ public class FilterController {
} }
// TODO // TODO
@PostMapping(consumes = "multipart/form-data", value = "/filter-contains-image") @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE, value = "/filter-contains-image")
@Operation( @Operation(
summary = "Checks if a PDF contains an image", summary = "Checks if a PDF contains an image",
description = "Input:PDF Output:Boolean Type:SISO") description = "Input:PDF Output:Boolean Type:SISO")
@ -71,7 +72,7 @@ public class FilterController {
return null; return null;
} }
@PostMapping(consumes = "multipart/form-data", value = "/filter-page-count") @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE, value = "/filter-page-count")
@Operation( @Operation(
summary = "Checks if a PDF is greater, less or equal to a setPageCount", summary = "Checks if a PDF is greater, less or equal to a setPageCount",
description = "Input:PDF Output:Boolean Type:SISO") description = "Input:PDF Output:Boolean Type:SISO")
@ -104,7 +105,7 @@ public class FilterController {
return null; return null;
} }
@PostMapping(consumes = "multipart/form-data", value = "/filter-page-size") @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE, value = "/filter-page-size")
@Operation( @Operation(
summary = "Checks if a PDF is of a certain size", summary = "Checks if a PDF is of a certain size",
description = "Input:PDF Output:Boolean Type:SISO") description = "Input:PDF Output:Boolean Type:SISO")
@ -147,7 +148,7 @@ public class FilterController {
return null; return null;
} }
@PostMapping(consumes = "multipart/form-data", value = "/filter-file-size") @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE, value = "/filter-file-size")
@Operation( @Operation(
summary = "Checks if a PDF is a set file size", summary = "Checks if a PDF is a set file size",
description = "Input:PDF Output:Boolean Type:SISO") description = "Input:PDF Output:Boolean Type:SISO")
@ -180,7 +181,7 @@ public class FilterController {
return null; return null;
} }
@PostMapping(consumes = "multipart/form-data", value = "/filter-page-rotation") @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE, value = "/filter-page-rotation")
@Operation( @Operation(
summary = "Checks if a PDF is of a certain rotation", summary = "Checks if a PDF is of a certain rotation",
description = "Input:PDF Output:Boolean Type:SISO") description = "Input:PDF Output:Boolean Type:SISO")

View File

@ -4,6 +4,7 @@ import java.io.IOException;
import java.util.List; import java.util.List;
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocument;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
@ -34,7 +35,7 @@ public class AttachmentController {
private final AttachmentServiceInterface pdfAttachmentService; private final AttachmentServiceInterface pdfAttachmentService;
@PostMapping(consumes = "multipart/form-data", value = "/add-attachments") @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE, value = "/add-attachments")
@Operation( @Operation(
summary = "Add attachments to PDF", summary = "Add attachments to PDF",
description = description =

View File

@ -8,6 +8,7 @@ import java.util.List;
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.text.PDFTextStripper; import org.apache.pdfbox.text.PDFTextStripper;
import org.apache.pdfbox.text.TextPosition; import org.apache.pdfbox.text.TextPosition;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
@ -38,7 +39,7 @@ public class AutoRenameController {
private final CustomPDFDocumentFactory pdfDocumentFactory; private final CustomPDFDocumentFactory pdfDocumentFactory;
@PostMapping(consumes = "multipart/form-data", value = "/auto-rename") @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE, value = "/auto-rename")
@Operation( @Operation(
summary = "Extract header from PDF file", summary = "Extract header from PDF file",
description = description =

View File

@ -6,7 +6,6 @@ import java.awt.image.DataBufferInt;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
@ -36,6 +35,8 @@ import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.model.api.misc.AutoSplitPdfRequest; import stirling.software.SPDF.model.api.misc.AutoSplitPdfRequest;
import stirling.software.common.service.CustomPDFDocumentFactory; import stirling.software.common.service.CustomPDFDocumentFactory;
import stirling.software.common.util.TempFile;
import stirling.software.common.util.TempFileManager;
import stirling.software.common.util.WebResponseUtils; import stirling.software.common.util.WebResponseUtils;
@RestController @RestController
@ -53,6 +54,7 @@ public class AutoSplitPdfController {
"https://stirlingpdf.com")); "https://stirlingpdf.com"));
private final CustomPDFDocumentFactory pdfDocumentFactory; private final CustomPDFDocumentFactory pdfDocumentFactory;
private final TempFileManager tempFileManager;
private static String decodeQRCode(BufferedImage bufferedImage) { private static String decodeQRCode(BufferedImage bufferedImage) {
LuminanceSource source; LuminanceSource source;
@ -102,7 +104,7 @@ public class AutoSplitPdfController {
} }
} }
@PostMapping(value = "/auto-split-pdf", consumes = "multipart/form-data") @PostMapping(value = "/auto-split-pdf", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@Operation( @Operation(
summary = "Auto split PDF pages into separate documents", summary = "Auto split PDF pages into separate documents",
description = description =
@ -117,10 +119,10 @@ public class AutoSplitPdfController {
PDDocument document = null; PDDocument document = null;
List<PDDocument> splitDocuments = new ArrayList<>(); List<PDDocument> splitDocuments = new ArrayList<>();
Path zipFile = null; TempFile outputTempFile = null;
byte[] data = null;
try { try {
outputTempFile = new TempFile(tempFileManager, ".zip");
document = pdfDocumentFactory.load(file.getInputStream()); document = pdfDocumentFactory.load(file.getInputStream());
PDFRenderer pdfRenderer = new PDFRenderer(document); PDFRenderer pdfRenderer = new PDFRenderer(document);
pdfRenderer.setSubsamplingAllowed(true); pdfRenderer.setSubsamplingAllowed(true);
@ -152,12 +154,12 @@ public class AutoSplitPdfController {
// Remove split documents that have no pages // Remove split documents that have no pages
splitDocuments.removeIf(pdDocument -> pdDocument.getNumberOfPages() == 0); splitDocuments.removeIf(pdDocument -> pdDocument.getNumberOfPages() == 0);
zipFile = Files.createTempFile("split_documents", ".zip");
String filename = String filename =
Filenames.toSimpleFileName(file.getOriginalFilename()) Filenames.toSimpleFileName(file.getOriginalFilename())
.replaceFirst("[.][^.]+$", ""); .replaceFirst("[.][^.]+$", "");
try (ZipOutputStream zipOut = new ZipOutputStream(Files.newOutputStream(zipFile))) { try (ZipOutputStream zipOut =
new ZipOutputStream(Files.newOutputStream(outputTempFile.getPath()))) {
for (int i = 0; i < splitDocuments.size(); i++) { for (int i = 0; i < splitDocuments.size(); i++) {
String fileName = filename + "_" + (i + 1) + ".pdf"; String fileName = filename + "_" + (i + 1) + ".pdf";
PDDocument splitDocument = splitDocuments.get(i); PDDocument splitDocument = splitDocuments.get(i);
@ -173,10 +175,10 @@ public class AutoSplitPdfController {
} }
} }
data = Files.readAllBytes(zipFile); byte[] data = Files.readAllBytes(outputTempFile.getPath());
return WebResponseUtils.bytesToWebResponse( return WebResponseUtils.bytesToWebResponse(
data, filename + ".zip", MediaType.APPLICATION_OCTET_STREAM); data, filename + ".zip", MediaType.APPLICATION_OCTET_STREAM);
} catch (Exception e) { } catch (Exception e) {
log.error("Error in auto split", e); log.error("Error in auto split", e);
throw e; throw e;
@ -198,12 +200,8 @@ public class AutoSplitPdfController {
} }
} }
if (zipFile != null) { if (outputTempFile != null) {
try { outputTempFile.close();
Files.deleteIfExists(zipFile);
} catch (IOException e) {
log.error("Error deleting temporary zip file", e);
}
} }
} }
} }

View File

@ -69,7 +69,7 @@ public class BlankPageController {
return whitePixelPercentage >= whitePercent; return whitePixelPercentage >= whitePercent;
} }
@PostMapping(consumes = "multipart/form-data", value = "/remove-blanks") @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE, value = "/remove-blanks")
@Operation( @Operation(
summary = "Remove blank pages from a PDF file", summary = "Remove blank pages from a PDF file",
description = description =

View File

@ -32,6 +32,7 @@ import org.apache.pdfbox.pdmodel.PDResources;
import org.apache.pdfbox.pdmodel.graphics.PDXObject; import org.apache.pdfbox.pdmodel.graphics.PDXObject;
import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject; import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject;
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject; import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
@ -658,7 +659,7 @@ public class CompressController {
}; };
} }
@PostMapping(consumes = "multipart/form-data", value = "/compress-pdf") @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE, value = "/compress-pdf")
@Operation( @Operation(
summary = "Optimize PDF file", summary = "Optimize PDF file",
description = description =

View File

@ -38,7 +38,7 @@ public class DecompressPdfController {
private final CustomPDFDocumentFactory pdfDocumentFactory; private final CustomPDFDocumentFactory pdfDocumentFactory;
@PostMapping(value = "/decompress-pdf", consumes = "multipart/form-data") @PostMapping(value = "/decompress-pdf", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@Operation( @Operation(
summary = "Decompress PDF streams", summary = "Decompress PDF streams",
description = "Fully decompresses all PDF streams including text content") description = "Fully decompresses all PDF streams including text content")

View File

@ -50,7 +50,7 @@ public class ExtractImageScansController {
private final CustomPDFDocumentFactory pdfDocumentFactory; private final CustomPDFDocumentFactory pdfDocumentFactory;
@PostMapping(consumes = "multipart/form-data", value = "/extract-image-scans") @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE, value = "/extract-image-scans")
@Operation( @Operation(
summary = "Extract image scans from an input file", summary = "Extract image scans from an input file",
description = description =

View File

@ -54,7 +54,7 @@ public class ExtractImagesController {
private final CustomPDFDocumentFactory pdfDocumentFactory; private final CustomPDFDocumentFactory pdfDocumentFactory;
@PostMapping(consumes = "multipart/form-data", value = "/extract-images") @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE, value = "/extract-images")
@Operation( @Operation(
summary = "Extract images from a PDF file", summary = "Extract images from a PDF file",
description = description =

View File

@ -11,6 +11,7 @@ import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm; import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm;
import org.apache.pdfbox.rendering.ImageType; import org.apache.pdfbox.rendering.ImageType;
import org.apache.pdfbox.rendering.PDFRenderer; import org.apache.pdfbox.rendering.PDFRenderer;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
@ -38,7 +39,7 @@ public class FlattenController {
private final CustomPDFDocumentFactory pdfDocumentFactory; private final CustomPDFDocumentFactory pdfDocumentFactory;
@PostMapping(consumes = "multipart/form-data", value = "/flatten") @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE, value = "/flatten")
@Operation( @Operation(
summary = "Flatten PDF form fields or full page", summary = "Flatten PDF form fields or full page",
description = description =

View File

@ -10,6 +10,7 @@ import java.util.Map.Entry;
import org.apache.pdfbox.cos.COSName; import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDDocumentInformation; import org.apache.pdfbox.pdmodel.PDDocumentInformation;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.WebDataBinder; import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
@ -51,7 +52,7 @@ public class MetadataController {
binder.registerCustomEditor(Map.class, "allRequestParams", new StringToMapPropertyEditor()); binder.registerCustomEditor(Map.class, "allRequestParams", new StringToMapPropertyEditor());
} }
@PostMapping(consumes = "multipart/form-data", value = "/update-metadata") @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE, value = "/update-metadata")
@Operation( @Operation(
summary = "Update metadata of a PDF file", summary = "Update metadata of a PDF file",
description = description =

View File

@ -76,7 +76,7 @@ public class OCRController {
.toList(); .toList();
} }
@PostMapping(consumes = "multipart/form-data", value = "/ocr-pdf") @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE, value = "/ocr-pdf")
@Operation( @Operation(
summary = "Process a PDF file with OCR", summary = "Process a PDF file with OCR",
description = description =

View File

@ -3,6 +3,7 @@ package stirling.software.SPDF.controller.api.misc;
import java.io.IOException; import java.io.IOException;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
@ -31,7 +32,7 @@ public class OverlayImageController {
private final CustomPDFDocumentFactory pdfDocumentFactory; private final CustomPDFDocumentFactory pdfDocumentFactory;
@PostMapping(consumes = "multipart/form-data", value = "/add-image") @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE, value = "/add-image")
@Operation( @Operation(
summary = "Overlay image onto a PDF file", summary = "Overlay image onto a PDF file",
description = description =

View File

@ -39,7 +39,7 @@ public class PageNumbersController {
private final CustomPDFDocumentFactory pdfDocumentFactory; private final CustomPDFDocumentFactory pdfDocumentFactory;
@PostMapping(value = "/add-page-numbers", consumes = "multipart/form-data") @PostMapping(value = "/add-page-numbers", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@Operation( @Operation(
summary = "Add page numbers to a PDF document", summary = "Add page numbers to a PDF document",
description = description =

View File

@ -18,6 +18,7 @@ import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.printing.PDFPageable; import org.apache.pdfbox.printing.PDFPageable;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
@ -37,7 +38,7 @@ import stirling.software.SPDF.model.api.misc.PrintFileRequest;
public class PrintFileController { public class PrintFileController {
// TODO // TODO
// @PostMapping(value = "/print-file", consumes = "multipart/form-data") // @PostMapping(value = "/print-file", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
// @Operation( // @Operation(
// summary = "Prints PDF/Image file to a set printer", // summary = "Prints PDF/Image file to a set printer",
// description = // description =
@ -69,7 +70,7 @@ public class PrintFileController {
log.info("Selected Printer: " + selectedService.getName()); log.info("Selected Printer: " + selectedService.getName());
if ("application/pdf".equals(contentType)) { if (MediaType.APPLICATION_PDF_VALUE.equals(contentType)) {
PDDocument document = Loader.loadPDF(file.getBytes()); PDDocument document = Loader.loadPDF(file.getBytes());
PrinterJob job = PrinterJob.getPrinterJob(); PrinterJob job = PrinterJob.getPrinterJob();
job.setPrintService(selectedService); job.setPrintService(selectedService);

View File

@ -4,6 +4,7 @@ import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
@ -46,7 +47,7 @@ public class RepairController {
return endpointConfiguration.isGroupEnabled("qpdf"); return endpointConfiguration.isGroupEnabled("qpdf");
} }
@PostMapping(consumes = "multipart/form-data", value = "/repair") @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE, value = "/repair")
@Operation( @Operation(
summary = "Repair a PDF file", summary = "Repair a PDF file",
description = description =

View File

@ -27,7 +27,7 @@ public class ReplaceAndInvertColorController {
private final ReplaceAndInvertColorService replaceAndInvertColorService; private final ReplaceAndInvertColorService replaceAndInvertColorService;
@PostMapping(consumes = "multipart/form-data", value = "/replace-invert-pdf") @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE, value = "/replace-invert-pdf")
@Operation( @Operation(
summary = "Replace-Invert Color PDF", summary = "Replace-Invert Color PDF",
description = description =

View File

@ -52,7 +52,7 @@ public class ScannerEffectController {
private static final int MAX_IMAGE_HEIGHT = 8192; private static final int MAX_IMAGE_HEIGHT = 8192;
private static final long MAX_IMAGE_PIXELS = 16_777_216; // 4096x4096 private static final long MAX_IMAGE_PIXELS = 16_777_216; // 4096x4096
@PostMapping(value = "/scanner-effect", consumes = "multipart/form-data") @PostMapping(value = "/scanner-effect", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@Operation( @Operation(
summary = "Apply scanner effect to PDF", summary = "Apply scanner effect to PDF",
description = description =

View File

@ -32,7 +32,7 @@ public class ShowJavascript {
private final CustomPDFDocumentFactory pdfDocumentFactory; private final CustomPDFDocumentFactory pdfDocumentFactory;
@PostMapping(consumes = "multipart/form-data", value = "/show-javascript") @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE, value = "/show-javascript")
@Operation( @Operation(
summary = "Grabs all JS from a PDF and returns a single JS file with all code", summary = "Grabs all JS from a PDF and returns a single JS file with all code",
description = "desc. Input:PDF Output:JS Type:SISO") description = "desc. Input:PDF Output:JS Type:SISO")

View File

@ -25,6 +25,7 @@ import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
import org.apache.pdfbox.pdmodel.graphics.state.PDExtendedGraphicsState; import org.apache.pdfbox.pdmodel.graphics.state.PDExtendedGraphicsState;
import org.apache.pdfbox.util.Matrix; import org.apache.pdfbox.util.Matrix;
import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.ClassPathResource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.WebDataBinder; import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder; import org.springframework.web.bind.annotation.InitBinder;
@ -72,7 +73,7 @@ public class StampController {
}); });
} }
@PostMapping(consumes = "multipart/form-data", value = "/add-stamp") @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE, value = "/add-stamp")
@Operation( @Operation(
summary = "Add stamp to a PDF file", summary = "Add stamp to a PDF file",
description = description =

View File

@ -10,6 +10,7 @@ import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.common.PDStream; import org.apache.pdfbox.pdmodel.common.PDStream;
import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm; import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm;
import org.apache.pdfbox.pdmodel.interactive.form.PDField; import org.apache.pdfbox.pdmodel.interactive.form.PDField;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
@ -37,7 +38,7 @@ public class UnlockPDFFormsController {
this.pdfDocumentFactory = pdfDocumentFactory; this.pdfDocumentFactory = pdfDocumentFactory;
} }
@PostMapping(consumes = "multipart/form-data", value = "/unlock-pdf-forms") @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE, value = "/unlock-pdf-forms")
@Operation( @Operation(
summary = "Remove read-only property from form fields", summary = "Remove read-only property from form fields",
description = description =

View File

@ -188,7 +188,7 @@ public class GetInfoOnPDF {
return false; return false;
} }
@PostMapping(consumes = "multipart/form-data", value = "/get-info-on-pdf") @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE, value = "/get-info-on-pdf")
@Operation(summary = "Summary here", description = "desc. Input:PDF Output:JSON Type:SISO") @Operation(summary = "Summary here", description = "desc. Input:PDF Output:JSON Type:SISO")
public ResponseEntity<byte[]> getPdfInfo(@ModelAttribute PDFFile request) throws IOException { public ResponseEntity<byte[]> getPdfInfo(@ModelAttribute PDFFile request) throws IOException {
MultipartFile inputFile = request.getFileInput(); MultipartFile inputFile = request.getFileInput();

View File

@ -5,6 +5,7 @@ import java.io.IOException;
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.encryption.AccessPermission; import org.apache.pdfbox.pdmodel.encryption.AccessPermission;
import org.apache.pdfbox.pdmodel.encryption.StandardProtectionPolicy; import org.apache.pdfbox.pdmodel.encryption.StandardProtectionPolicy;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
@ -32,7 +33,7 @@ public class PasswordController {
private final CustomPDFDocumentFactory pdfDocumentFactory; private final CustomPDFDocumentFactory pdfDocumentFactory;
@PostMapping(consumes = "multipart/form-data", value = "/remove-password") @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE, value = "/remove-password")
@Operation( @Operation(
summary = "Remove password from a PDF file", summary = "Remove password from a PDF file",
description = description =
@ -58,7 +59,7 @@ public class PasswordController {
} }
} }
@PostMapping(consumes = "multipart/form-data", value = "/add-password") @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE, value = "/add-password")
@Operation( @Operation(
summary = "Add password to a PDF file", summary = "Add password to a PDF file",
description = description =

View File

@ -34,6 +34,7 @@ import org.apache.pdfbox.pdmodel.common.PDStream;
import org.apache.pdfbox.pdmodel.font.PDFont; import org.apache.pdfbox.pdmodel.font.PDFont;
import org.apache.pdfbox.pdmodel.graphics.PDXObject; import org.apache.pdfbox.pdmodel.graphics.PDXObject;
import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject; import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.WebDataBinder; import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder; import org.springframework.web.bind.annotation.InitBinder;
@ -97,7 +98,7 @@ public class RedactController {
List.class, "redactions", new StringToArrayListPropertyEditor()); List.class, "redactions", new StringToArrayListPropertyEditor());
} }
@PostMapping(value = "/redact", consumes = "multipart/form-data") @PostMapping(value = "/redact", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@Operation( @Operation(
summary = "Redact PDF manually", summary = "Redact PDF manually",
description = description =
@ -494,7 +495,7 @@ public class RedactController {
return pageNumbers; return pageNumbers;
} }
@PostMapping(value = "/auto-redact", consumes = "multipart/form-data") @PostMapping(value = "/auto-redact", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@Operation( @Operation(
summary = "Redact PDF automatically", summary = "Redact PDF automatically",
description = description =

View File

@ -7,6 +7,7 @@ import org.apache.pdfbox.pdmodel.PDDocumentCatalog;
import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm; import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm;
import org.apache.pdfbox.pdmodel.interactive.form.PDField; import org.apache.pdfbox.pdmodel.interactive.form.PDField;
import org.apache.pdfbox.pdmodel.interactive.form.PDSignatureField; import org.apache.pdfbox.pdmodel.interactive.form.PDSignatureField;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
@ -32,7 +33,7 @@ public class RemoveCertSignController {
private final CustomPDFDocumentFactory pdfDocumentFactory; private final CustomPDFDocumentFactory pdfDocumentFactory;
@PostMapping(consumes = "multipart/form-data", value = "/remove-cert-sign") @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE, value = "/remove-cert-sign")
@Operation( @Operation(
summary = "Remove digital signature from PDF", summary = "Remove digital signature from PDF",
description = description =

View File

@ -21,6 +21,7 @@ import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationLink;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationWidget; import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationWidget;
import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm; import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm;
import org.apache.pdfbox.pdmodel.interactive.form.PDField; import org.apache.pdfbox.pdmodel.interactive.form.PDField;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
@ -46,7 +47,7 @@ public class SanitizeController {
private final CustomPDFDocumentFactory pdfDocumentFactory; private final CustomPDFDocumentFactory pdfDocumentFactory;
@PostMapping(consumes = "multipart/form-data", value = "/sanitize-pdf") @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE, value = "/sanitize-pdf")
@Operation( @Operation(
summary = "Sanitize a PDF file", summary = "Sanitize a PDF file",
description = description =

View File

@ -24,6 +24,7 @@ import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
import org.apache.pdfbox.pdmodel.graphics.state.PDExtendedGraphicsState; import org.apache.pdfbox.pdmodel.graphics.state.PDExtendedGraphicsState;
import org.apache.pdfbox.util.Matrix; import org.apache.pdfbox.util.Matrix;
import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.ClassPathResource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.WebDataBinder; import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder; import org.springframework.web.bind.annotation.InitBinder;
@ -64,7 +65,7 @@ public class WatermarkController {
}); });
} }
@PostMapping(consumes = "multipart/form-data", value = "/add-watermark") @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE, value = "/add-watermark")
@Operation( @Operation(
summary = "Add watermark to a PDF file", summary = "Add watermark to a PDF file",
description = description =

View File

@ -1,5 +1,6 @@
package stirling.software.SPDF.model.api.converters; package stirling.software.SPDF.model.api.converters;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
@ -18,7 +19,7 @@ import stirling.software.common.util.PDFToFile;
@RequestMapping("/api/v1/convert") @RequestMapping("/api/v1/convert")
public class ConvertPDFToMarkdown { public class ConvertPDFToMarkdown {
@PostMapping(consumes = "multipart/form-data", value = "/pdf/markdown") @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE, value = "/pdf/markdown")
@Operation( @Operation(
summary = "Convert PDF to Markdown", summary = "Convert PDF to Markdown",
description = description =

View File

@ -260,7 +260,7 @@ public class JobController {
"fileName", "fileName",
"unknown", "unknown",
"contentType", "contentType",
"application/octet-stream", MediaType.APPLICATION_OCTET_STREAM_VALUE,
"fileSize", "fileSize",
fileSize)); fileSize));
} }
@ -295,7 +295,9 @@ public class JobController {
String fileName = resultFile != null ? resultFile.getFileName() : "download"; String fileName = resultFile != null ? resultFile.getFileName() : "download";
String contentType = String contentType =
resultFile != null ? resultFile.getContentType() : "application/octet-stream"; resultFile != null
? resultFile.getContentType()
: MediaType.APPLICATION_OCTET_STREAM_VALUE;
return ResponseEntity.ok() return ResponseEntity.ok()
.header("Content-Type", contentType) .header("Content-Type", contentType)

View File

@ -137,6 +137,7 @@ lang.yor=Yoruba
addPageNumbers.fontSize=حجم الخط addPageNumbers.fontSize=حجم الخط
addPageNumbers.fontName=اسم الخط addPageNumbers.fontName=اسم الخط
addPageNumbers.fontColor=Font Colour
pdfPrompt=اختر PDF pdfPrompt=اختر PDF
multiPdfPrompt=اختر ملفات PDF (2+) multiPdfPrompt=اختر ملفات PDF (2+)
multiPdfDropPrompt=حدد (أو اسحب وأفلت) جميع ملفات PDF التي تحتاجها multiPdfDropPrompt=حدد (أو اسحب وأفلت) جميع ملفات PDF التي تحتاجها

View File

@ -137,6 +137,7 @@ lang.yor=Yoruba
addPageNumbers.fontSize=Şrift Ölçüsü addPageNumbers.fontSize=Şrift Ölçüsü
addPageNumbers.fontName=Şrift Adı addPageNumbers.fontName=Şrift Adı
addPageNumbers.fontColor=Font Colour
pdfPrompt=PDF(lər)i Seç pdfPrompt=PDF(lər)i Seç
multiPdfPrompt=PDFləri Seç (2+) multiPdfPrompt=PDFləri Seç (2+)
multiPdfDropPrompt=Ehtiyacınız olan bütün PDFləri seçin (və ya sürükləyib buraxın) multiPdfDropPrompt=Ehtiyacınız olan bütün PDFləri seçin (və ya sürükləyib buraxın)

View File

@ -137,6 +137,7 @@ lang.yor=Yoruba
addPageNumbers.fontSize=Размер на шрифт addPageNumbers.fontSize=Размер на шрифт
addPageNumbers.fontName=Име на шрифт addPageNumbers.fontName=Име на шрифт
addPageNumbers.fontColor=Font Colour
pdfPrompt=Изберете PDF(и) pdfPrompt=Изберете PDF(и)
multiPdfPrompt=Изберете PDF (2+) multiPdfPrompt=Изберете PDF (2+)
multiPdfDropPrompt=Изберете (или плъзнете и пуснете) всички PDF файлове, от които се нуждаете multiPdfDropPrompt=Изберете (или плъзнете и пуснете) всички PDF файлове, от които се нуждаете

View File

@ -137,6 +137,7 @@ lang.yor=Yoruba
addPageNumbers.fontSize=ཡིག་གཟུགས་ཆེ་ཆུང་ addPageNumbers.fontSize=ཡིག་གཟུགས་ཆེ་ཆུང་
addPageNumbers.fontName=ཡིག་གཟུགས་མིང་ addPageNumbers.fontName=ཡིག་གཟུགས་མིང་
addPageNumbers.fontColor=Font Colour
pdfPrompt=PDF འདེམས་རོགས། pdfPrompt=PDF འདེམས་རོགས།
multiPdfPrompt=PDF གཉིས་ཡན་འདེམས་རོགས། multiPdfPrompt=PDF གཉིས་ཡན་འདེམས་རོགས།
multiPdfDropPrompt=དགོས་མཁོ་འདི་ PDF ཡིག་ཆ་ཚང་མ་འདེམས་པའམ་འཐེན་རོགས། multiPdfDropPrompt=དགོས་མཁོ་འདི་ PDF ཡིག་ཆ་ཚང་མ་འདེམས་པའམ་འཐེན་རོགས།

View File

@ -137,6 +137,7 @@ lang.yor=Yoruba
addPageNumbers.fontSize=Mida del tipus de lletra addPageNumbers.fontSize=Mida del tipus de lletra
addPageNumbers.fontName=Nom del tipus de lletra addPageNumbers.fontName=Nom del tipus de lletra
addPageNumbers.fontColor=Font Colour
pdfPrompt=Selecciona PDF(s) pdfPrompt=Selecciona PDF(s)
multiPdfPrompt=Selecciona PDFs (2+) multiPdfPrompt=Selecciona PDFs (2+)
multiPdfDropPrompt=Selecciona (o arrossega) els documents PDF multiPdfDropPrompt=Selecciona (o arrossega) els documents PDF

View File

@ -137,6 +137,7 @@ lang.yor=Yoruba
addPageNumbers.fontSize=Velikost písma addPageNumbers.fontSize=Velikost písma
addPageNumbers.fontName=Název písma addPageNumbers.fontName=Název písma
addPageNumbers.fontColor=Font Colour
pdfPrompt=Vyberte PDF soubor(y) pdfPrompt=Vyberte PDF soubor(y)
multiPdfPrompt=Vyberte PDF soubory (2+) multiPdfPrompt=Vyberte PDF soubory (2+)
multiPdfDropPrompt=Vyberte (nebo přetáhněte) všechny požadované PDF soubory multiPdfDropPrompt=Vyberte (nebo přetáhněte) všechny požadované PDF soubory

View File

@ -137,6 +137,7 @@ lang.yor=Yoruba
addPageNumbers.fontSize=Skriftstørrelse addPageNumbers.fontSize=Skriftstørrelse
addPageNumbers.fontName=Skriftnavn addPageNumbers.fontName=Skriftnavn
addPageNumbers.fontColor=Font Colour
pdfPrompt=Vælg PDF-fil(er) pdfPrompt=Vælg PDF-fil(er)
multiPdfPrompt=Vælg PDF-filerne (2+) multiPdfPrompt=Vælg PDF-filerne (2+)
multiPdfDropPrompt=Vælg (eller drag & drop) alle PDF-filerne du skal bruge multiPdfDropPrompt=Vælg (eller drag & drop) alle PDF-filerne du skal bruge

View File

@ -137,6 +137,7 @@ lang.yor=Yoruba
addPageNumbers.fontSize=Schriftgröße addPageNumbers.fontSize=Schriftgröße
addPageNumbers.fontName=Schriftart addPageNumbers.fontName=Schriftart
addPageNumbers.fontColor=Font Colour
pdfPrompt=PDF(s) auswählen pdfPrompt=PDF(s) auswählen
multiPdfPrompt=PDFs auswählen (2+) multiPdfPrompt=PDFs auswählen (2+)
multiPdfDropPrompt=Wählen Sie alle gewünschten PDFs aus (oder ziehen Sie sie per Drag & Drop hierhin) multiPdfDropPrompt=Wählen Sie alle gewünschten PDFs aus (oder ziehen Sie sie per Drag & Drop hierhin)

View File

@ -137,6 +137,7 @@ lang.yor=Yoruba
addPageNumbers.fontSize=Μέγεθος γραμματοσειράς addPageNumbers.fontSize=Μέγεθος γραμματοσειράς
addPageNumbers.fontName=Όνομα γραμματοσειράς addPageNumbers.fontName=Όνομα γραμματοσειράς
addPageNumbers.fontColor=Font Colour
pdfPrompt=Επιλέξτε PDF(s) pdfPrompt=Επιλέξτε PDF(s)
multiPdfPrompt=Επιλέξτε PDFs (2+) multiPdfPrompt=Επιλέξτε PDFs (2+)
multiPdfDropPrompt=Επιλέξτε (ή σύρετε & αφήστε) όλα τα PDF που χρειάζεστε multiPdfDropPrompt=Επιλέξτε (ή σύρετε & αφήστε) όλα τα PDF που χρειάζεστε

View File

@ -137,6 +137,7 @@ lang.yor=Yoruba
addPageNumbers.fontSize=Font Size addPageNumbers.fontSize=Font Size
addPageNumbers.fontName=Font Name addPageNumbers.fontName=Font Name
addPageNumbers.fontColor=Font Colour
pdfPrompt=Select PDF(s) pdfPrompt=Select PDF(s)
multiPdfPrompt=Select PDFs (2+) multiPdfPrompt=Select PDFs (2+)
multiPdfDropPrompt=Select (or drag & drop) all PDFs you require multiPdfDropPrompt=Select (or drag & drop) all PDFs you require

View File

@ -137,6 +137,7 @@ lang.yor=Yoruba
addPageNumbers.fontSize=Tamaño de Letra addPageNumbers.fontSize=Tamaño de Letra
addPageNumbers.fontName=Nombre de Letra addPageNumbers.fontName=Nombre de Letra
addPageNumbers.fontColor=Font Colour
pdfPrompt=Seleccionar PDF(s) pdfPrompt=Seleccionar PDF(s)
multiPdfPrompt=Seleccionar PDFs (2+) multiPdfPrompt=Seleccionar PDFs (2+)
multiPdfDropPrompt=Seleccione (o arrastre y suelte) todos los PDFs que quiera multiPdfDropPrompt=Seleccione (o arrastre y suelte) todos los PDFs que quiera

View File

@ -137,6 +137,7 @@ lang.yor=Yoruba
addPageNumbers.fontSize=Font Size addPageNumbers.fontSize=Font Size
addPageNumbers.fontName=Font Name addPageNumbers.fontName=Font Name
addPageNumbers.fontColor=Font Colour
pdfPrompt=Hautatu PDFa(k) pdfPrompt=Hautatu PDFa(k)
multiPdfPrompt=Hautatu PDFak (2+) multiPdfPrompt=Hautatu PDFak (2+)
multiPdfDropPrompt=Hautatu (edo arrastatu eta jaregin) nahi dituzun PDFak multiPdfDropPrompt=Hautatu (edo arrastatu eta jaregin) nahi dituzun PDFak

View File

@ -137,6 +137,7 @@ lang.yor=Yoruba
addPageNumbers.fontSize=اندازه فونت addPageNumbers.fontSize=اندازه فونت
addPageNumbers.fontName=نام فونت addPageNumbers.fontName=نام فونت
addPageNumbers.fontColor=Font Colour
pdfPrompt=انتخاب فایل(های) PDF pdfPrompt=انتخاب فایل(های) PDF
multiPdfPrompt=انتخاب فایل‌های PDF (دو یا بیشتر) multiPdfPrompt=انتخاب فایل‌های PDF (دو یا بیشتر)
multiPdfDropPrompt=انتخاب (یا کشیدن و رها کردن) تمام فایل‌های PDF مورد نیاز multiPdfDropPrompt=انتخاب (یا کشیدن و رها کردن) تمام فایل‌های PDF مورد نیاز

View File

@ -137,6 +137,7 @@ lang.yor=Yoruba
addPageNumbers.fontSize=Taille de Police addPageNumbers.fontSize=Taille de Police
addPageNumbers.fontName=Nom de la Police addPageNumbers.fontName=Nom de la Police
addPageNumbers.fontColor=Font Colour
pdfPrompt=Sélectionnez le(s) PDF pdfPrompt=Sélectionnez le(s) PDF
multiPdfPrompt=Sélectionnez les PDF multiPdfPrompt=Sélectionnez les PDF
multiPdfDropPrompt=Sélectionnez (ou glissez-déposez) tous les PDF dont vous avez besoin multiPdfDropPrompt=Sélectionnez (ou glissez-déposez) tous les PDF dont vous avez besoin

View File

@ -137,6 +137,7 @@ lang.yor=Yoruba
addPageNumbers.fontSize=Méid an Chló addPageNumbers.fontSize=Méid an Chló
addPageNumbers.fontName=Ainm Cló addPageNumbers.fontName=Ainm Cló
addPageNumbers.fontColor=Font Colour
pdfPrompt=Roghnaigh PDF(anna) pdfPrompt=Roghnaigh PDF(anna)
multiPdfPrompt=Roghnaigh PDFs (2+) multiPdfPrompt=Roghnaigh PDFs (2+)
multiPdfDropPrompt=Roghnaigh (nó tarraing & scaoil) gach PDF atá uait multiPdfDropPrompt=Roghnaigh (nó tarraing & scaoil) gach PDF atá uait

View File

@ -137,6 +137,7 @@ lang.yor=Yoruba
addPageNumbers.fontSize=फ़ॉन्ट आकार addPageNumbers.fontSize=फ़ॉन्ट आकार
addPageNumbers.fontName=फ़ॉन्ट नाम addPageNumbers.fontName=फ़ॉन्ट नाम
addPageNumbers.fontColor=Font Colour
pdfPrompt=पीडीएफ फ़ाइल(ें) चुनें pdfPrompt=पीडीएफ फ़ाइल(ें) चुनें
multiPdfPrompt=पीडीएफ फ़ाइलें चुनें (2+) multiPdfPrompt=पीडीएफ फ़ाइलें चुनें (2+)
multiPdfDropPrompt=आवश्यक सभी पीडीएफ फ़ाइलों को चुनें (या खींच कर छोड़ें) multiPdfDropPrompt=आवश्यक सभी पीडीएफ फ़ाइलों को चुनें (या खींच कर छोड़ें)

View File

@ -137,6 +137,7 @@ lang.yor=Yoruba
addPageNumbers.fontSize=Veličina pisma addPageNumbers.fontSize=Veličina pisma
addPageNumbers.fontName=Ime pisma addPageNumbers.fontName=Ime pisma
addPageNumbers.fontColor=Font Colour
pdfPrompt=Odaberi PDF(ove) pdfPrompt=Odaberi PDF(ove)
multiPdfPrompt=Odaberi PDF-ove (2+) multiPdfPrompt=Odaberi PDF-ove (2+)
multiPdfDropPrompt=Odaberi (ili povuci i ispusti) sve potrebne PDF-ove multiPdfDropPrompt=Odaberi (ili povuci i ispusti) sve potrebne PDF-ove

View File

@ -137,6 +137,7 @@ lang.yor=joruba
addPageNumbers.fontSize=Betűméret addPageNumbers.fontSize=Betűméret
addPageNumbers.fontName=Betűtípus addPageNumbers.fontName=Betűtípus
addPageNumbers.fontColor=Font Colour
pdfPrompt=PDF-fájl kiválasztása pdfPrompt=PDF-fájl kiválasztása
multiPdfPrompt=PDF-fájlok kiválasztása (2+) multiPdfPrompt=PDF-fájlok kiválasztása (2+)
multiPdfDropPrompt=Válassza ki (vagy húzza ide) az összes szükséges PDF-fájlt multiPdfDropPrompt=Válassza ki (vagy húzza ide) az összes szükséges PDF-fájlt

View File

@ -137,6 +137,7 @@ lang.yor=Yoruba
addPageNumbers.fontSize=Ukuran Fonta addPageNumbers.fontSize=Ukuran Fonta
addPageNumbers.fontName=Nama Fonta addPageNumbers.fontName=Nama Fonta
addPageNumbers.fontColor=Font Colour
pdfPrompt=Pilih PDF pdfPrompt=Pilih PDF
multiPdfPrompt=Pilih PDF (2+) multiPdfPrompt=Pilih PDF (2+)
multiPdfDropPrompt=Pilih (atau seret & letakkan)) semua PDF yang Anda butuhkan multiPdfDropPrompt=Pilih (atau seret & letakkan)) semua PDF yang Anda butuhkan

View File

@ -137,6 +137,7 @@ lang.yor=Yoruba
addPageNumbers.fontSize=Dimensione del font addPageNumbers.fontSize=Dimensione del font
addPageNumbers.fontName=Nome del font addPageNumbers.fontName=Nome del font
addPageNumbers.fontColor=Font Colour
pdfPrompt=Scegli PDF pdfPrompt=Scegli PDF
multiPdfPrompt=Scegli 2 o più PDF multiPdfPrompt=Scegli 2 o più PDF
multiPdfDropPrompt=Scegli (o trascina e rilascia) uno o più PDF multiPdfDropPrompt=Scegli (o trascina e rilascia) uno o più PDF

View File

@ -137,6 +137,7 @@ lang.yor=ヨルバ語
addPageNumbers.fontSize=フォントサイズ addPageNumbers.fontSize=フォントサイズ
addPageNumbers.fontName=フォント名 addPageNumbers.fontName=フォント名
addPageNumbers.fontColor=Font Colour
pdfPrompt=PDFを選択 pdfPrompt=PDFを選択
multiPdfPrompt=PDFを選択2つ以上 multiPdfPrompt=PDFを選択2つ以上
multiPdfDropPrompt=PDFを選択又はドラッグ&ドロップ) multiPdfDropPrompt=PDFを選択又はドラッグ&ドロップ)

View File

@ -137,6 +137,7 @@ lang.yor=Yoruba
addPageNumbers.fontSize=글꼴 크기 addPageNumbers.fontSize=글꼴 크기
addPageNumbers.fontName=글꼴 이름 addPageNumbers.fontName=글꼴 이름
addPageNumbers.fontColor=Font Colour
pdfPrompt=PDF 선택 pdfPrompt=PDF 선택
multiPdfPrompt=PDF 선택 (2개 이상) multiPdfPrompt=PDF 선택 (2개 이상)
multiPdfDropPrompt=필요한 모든 PDF를 선택(또는 끌어다 놓기)하세요 multiPdfDropPrompt=필요한 모든 PDF를 선택(또는 끌어다 놓기)하세요

View File

@ -137,6 +137,7 @@ lang.yor=Yoruba
addPageNumbers.fontSize=അക്ഷര വലുപ്പം addPageNumbers.fontSize=അക്ഷര വലുപ്പം
addPageNumbers.fontName=അക്ഷരത്തിന്റെ പേര് addPageNumbers.fontName=അക്ഷരത്തിന്റെ പേര്
addPageNumbers.fontColor=Font Colour
pdfPrompt=PDF(കൾ) തിരഞ്ഞെടുക്കുക pdfPrompt=PDF(കൾ) തിരഞ്ഞെടുക്കുക
multiPdfPrompt=PDF-കൾ തിരഞ്ഞെടുക്കുക (2+) multiPdfPrompt=PDF-കൾ തിരഞ്ഞെടുക്കുക (2+)
multiPdfDropPrompt=നിങ്ങൾക്ക് ആവശ്യമുള്ള എല്ലാ PDF-കളും തിരഞ്ഞെടുക്കുക (അല്ലെങ്കിൽ വലിച്ചിടുക) multiPdfDropPrompt=നിങ്ങൾക്ക് ആവശ്യമുള്ള എല്ലാ PDF-കളും തിരഞ്ഞെടുക്കുക (അല്ലെങ്കിൽ വലിച്ചിടുക)

View File

@ -137,6 +137,7 @@ lang.yor=Yoruba
addPageNumbers.fontSize=Lettertypegrootte addPageNumbers.fontSize=Lettertypegrootte
addPageNumbers.fontName=Lettertypenaam addPageNumbers.fontName=Lettertypenaam
addPageNumbers.fontColor=Font Colour
pdfPrompt=Selecteer PDF('s) pdfPrompt=Selecteer PDF('s)
multiPdfPrompt=Selecteer PDF's (2+) multiPdfPrompt=Selecteer PDF's (2+)
multiPdfDropPrompt=Selecteer (of sleep & zet neer) alle PDF's die je nodig hebt multiPdfDropPrompt=Selecteer (of sleep & zet neer) alle PDF's die je nodig hebt

View File

@ -137,6 +137,7 @@ lang.yor=Yoruba
addPageNumbers.fontSize=Skriftstørrelse addPageNumbers.fontSize=Skriftstørrelse
addPageNumbers.fontName=Skrifttype addPageNumbers.fontName=Skrifttype
addPageNumbers.fontColor=Font Colour
pdfPrompt=Velg PDF(er) pdfPrompt=Velg PDF(er)
multiPdfPrompt=Velg PDF-filer (2+) multiPdfPrompt=Velg PDF-filer (2+)
multiPdfDropPrompt=Velg (eller dra og slipp) alle PDF-ene du trenger multiPdfDropPrompt=Velg (eller dra og slipp) alle PDF-ene du trenger

View File

@ -137,6 +137,7 @@ lang.yor=Yoruba
addPageNumbers.fontSize=Rozmiar Czcionki addPageNumbers.fontSize=Rozmiar Czcionki
addPageNumbers.fontName=Nazwa Czcionki addPageNumbers.fontName=Nazwa Czcionki
addPageNumbers.fontColor=Font Colour
pdfPrompt=Wybierz PDF pdfPrompt=Wybierz PDF
multiPdfPrompt=Wybierz PDF (2+) multiPdfPrompt=Wybierz PDF (2+)
multiPdfDropPrompt=Wybierz (lub przeciągnij i puść) wszystkie dokumenty PDF multiPdfDropPrompt=Wybierz (lub przeciągnij i puść) wszystkie dokumenty PDF

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