Compare commits

..

No commits in common. "9b3e2c29a525093b60d14bc48bd877b014e80fda" and "dde6cc2d4940b55799271e1fa1f49f52779794f5" have entirely different histories.

85 changed files with 355 additions and 579 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -29,7 +29,6 @@ import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
import org.apache.pdfbox.rendering.ImageType;
import org.apache.pdfbox.rendering.PDFRenderer;
import org.apache.pdfbox.text.PDFTextStripper;
import org.springframework.http.MediaType;
import org.springframework.web.multipart.MultipartFile;
import io.github.pixee.security.Filenames;
@ -157,8 +156,7 @@ public class PdfUtils {
if (DPI > maxSafeDpi) {
throw ExceptionUtils.createIllegalArgumentException(
"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,
maxSafeDpi);
}
@ -198,9 +196,7 @@ public class PdfUtils {
.contains("Maximum size of image exceeded")) {
throw ExceptionUtils.createIllegalArgumentException(
"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,
DPI);
}
@ -245,10 +241,7 @@ public class PdfUtils {
.contains("Maximum size of image exceeded")) {
throw ExceptionUtils.createIllegalArgumentException(
"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,
DPI);
}
@ -289,9 +282,7 @@ public class PdfUtils {
.contains("Maximum size of image exceeded")) {
throw ExceptionUtils.createIllegalArgumentException(
"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,
DPI);
}
@ -324,8 +315,7 @@ public class PdfUtils {
&& e.getMessage().contains("Maximum size of image exceeded")) {
throw ExceptionUtils.createIllegalArgumentException(
"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,
DPI);
}
@ -376,9 +366,7 @@ public class PdfUtils {
&& e.getMessage().contains("Maximum size of image exceeded")) {
throw ExceptionUtils.createIllegalArgumentException(
"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);
}
throw e;
@ -447,7 +435,7 @@ public class PdfUtils {
ImageProcessingUtils.convertColorType(image, colorType);
// Use JPEGFactory if it's JPEG since JPEG is lossy
PDImageXObject pdImage =
(contentType != null && MediaType.IMAGE_JPEG_VALUE.equals(contentType))
(contentType != null && "image/jpeg".equals(contentType))
? JPEGFactory.createFromImage(doc, convertedImage)
: LosslessFactory.createFromImage(doc, convertedImage);
addImageToDocument(doc, pdImage, fitOption, autoRotate);

View File

@ -4,8 +4,6 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.springframework.http.HttpHeaders;
@ -13,13 +11,9 @@ import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import io.github.pixee.security.Filenames;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class WebResponseUtils {
public static ResponseEntity<byte[]> baosToWebResponse(
@ -70,59 +64,4 @@ public class WebResponseUtils {
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,7 +11,6 @@ import java.nio.file.Path;
import org.junit.jupiter.api.Test;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
@ -24,7 +23,7 @@ class ApplicationPropertiesSaml2HttpTest {
server.enqueue(
new MockResponse()
.setResponseCode(200)
.addHeader("Content-Type", MediaType.APPLICATION_XML_VALUE)
.addHeader("Content-Type", "application/xml")
.setBody("<EntityDescriptor/>"));
server.start();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -117,7 +117,7 @@ public class SplitPdfByChaptersController {
return bookmarks;
}
@PostMapping(value = "/split-pdf-by-chapters", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@PostMapping(value = "/split-pdf-by-chapters", consumes = "multipart/form-data")
@Operation(
summary = "Split PDFs by Chapters",
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.IOException;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipEntry;
@ -17,13 +17,13 @@ import org.apache.pdfbox.pdmodel.PDPageContentStream.AppendMode;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject;
import org.apache.pdfbox.util.Matrix;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import io.github.pixee.security.Filenames;
import io.swagger.v3.oas.annotations.Operation;
@ -33,9 +33,6 @@ import lombok.RequiredArgsConstructor;
import stirling.software.SPDF.model.api.SplitPdfBySectionsRequest;
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;
@RestController
@ -45,18 +42,16 @@ import stirling.software.common.util.WebResponseUtils;
public class SplitPdfBySectionsController {
private final CustomPDFDocumentFactory pdfDocumentFactory;
private final TempFileManager tempFileManager;
private final PDFService pdfService;
@PostMapping(value = "/split-pdf-by-sections", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@PostMapping(value = "/split-pdf-by-sections", consumes = "multipart/form-data")
@Operation(
summary = "Split PDF pages into smaller sections",
description =
"Split each page of a PDF into smaller sections based on the user's choice"
+ " (halves, thirds, quarters, etc.), both vertically and horizontally."
+ " Input:PDF Output:ZIP-PDF Type:SISO")
public ResponseEntity<StreamingResponseBody> splitPdf(
@ModelAttribute SplitPdfBySectionsRequest request) throws Exception {
public ResponseEntity<byte[]> splitPdf(@ModelAttribute SplitPdfBySectionsRequest request)
throws Exception {
List<ByteArrayOutputStream> splitDocumentsBoas = new ArrayList<>();
MultipartFile file = request.getFileInput();
@ -72,14 +67,10 @@ public class SplitPdfBySectionsController {
Filenames.toSimpleFileName(file.getOriginalFilename())
.replaceFirst("[.][^.]+$", "");
if (merge) {
TempFile tempFile = new TempFile(tempFileManager, ".pdf");
try (PDDocument merged = pdfService.mergeDocuments(splitDocuments);
OutputStream out = Files.newOutputStream(tempFile.getPath())) {
merged.save(out);
for (PDDocument d : splitDocuments) d.close();
sourceDocument.close();
}
return WebResponseUtils.pdfFileToWebResponse(tempFile, filename + "_split.pdf");
MergeController mergeController = new MergeController(pdfDocumentFactory);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
mergeController.mergeDocuments(splitDocuments).save(baos);
return WebResponseUtils.bytesToWebResponse(baos.toByteArray(), filename + "_split.pdf");
}
for (PDDocument doc : splitDocuments) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
@ -90,9 +81,10 @@ public class SplitPdfBySectionsController {
sourceDocument.close();
TempFile zipTempFile = new TempFile(tempFileManager, ".zip");
try (ZipOutputStream zipOut =
new ZipOutputStream(Files.newOutputStream(zipTempFile.getPath()))) {
Path zipFile = Files.createTempFile("split_documents", ".zip");
byte[] data;
try (ZipOutputStream zipOut = new ZipOutputStream(Files.newOutputStream(zipFile))) {
int pageNum = 1;
for (int i = 0; i < splitDocumentsBoas.size(); i++) {
ByteArrayOutputStream baos = splitDocumentsBoas.get(i);
@ -106,8 +98,15 @@ public class SplitPdfBySectionsController {
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(

View File

@ -28,8 +28,6 @@ import stirling.software.SPDF.model.api.general.SplitPdfBySizeOrCountRequest;
import stirling.software.common.service.CustomPDFDocumentFactory;
import stirling.software.common.util.ExceptionUtils;
import stirling.software.common.util.GeneralUtils;
import stirling.software.common.util.TempFile;
import stirling.software.common.util.TempFileManager;
import stirling.software.common.util.WebResponseUtils;
@RestController
@ -40,9 +38,8 @@ import stirling.software.common.util.WebResponseUtils;
public class SplitPdfBySizeController {
private final CustomPDFDocumentFactory pdfDocumentFactory;
private final TempFileManager tempFileManager;
@PostMapping(value = "/split-by-size-or-count", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@PostMapping(value = "/split-by-size-or-count", consumes = "multipart/form-data")
@Operation(
summary = "Auto split PDF pages into separate documents based on size or count",
description =
@ -57,68 +54,89 @@ public class SplitPdfBySizeController {
log.debug("Starting PDF split process with request: {}", request);
MultipartFile file = request.getFileInput();
Path zipFile = Files.createTempFile("split_documents", ".zip");
log.debug("Created temporary zip file: {}", zipFile);
String filename =
Filenames.toSimpleFileName(file.getOriginalFilename())
.replaceFirst("[.][^.]+$", "");
log.debug("Base filename for output: {}", filename);
try (TempFile zipTempFile = new TempFile(tempFileManager, ".zip")) {
Path zipFile = zipTempFile.getPath();
log.debug("Created temporary zip file: {}", zipFile);
try {
log.debug("Reading input file bytes");
byte[] pdfBytes = file.getBytes();
log.debug("Successfully read {} bytes from input file", pdfBytes.length);
byte[] data = null;
try {
log.debug("Reading input file bytes");
byte[] pdfBytes = file.getBytes();
log.debug("Successfully read {} bytes from input file", pdfBytes.length);
log.debug("Creating ZIP output stream");
try (ZipOutputStream zipOut = new ZipOutputStream(Files.newOutputStream(zipFile))) {
log.debug("Loading PDF document");
try (PDDocument sourceDocument = pdfDocumentFactory.load(pdfBytes)) {
log.debug(
"Successfully loaded PDF with {} pages",
sourceDocument.getNumberOfPages());
log.debug("Creating ZIP output stream");
try (ZipOutputStream zipOut = new ZipOutputStream(Files.newOutputStream(zipFile))) {
log.debug("Loading PDF document");
try (PDDocument sourceDocument = pdfDocumentFactory.load(pdfBytes)) {
log.debug(
"Successfully loaded PDF with {} pages",
sourceDocument.getNumberOfPages());
int type = request.getSplitType();
String value = request.getSplitValue();
log.debug("Split type: {}, Split value: {}", type, value);
int type = request.getSplitType();
String value = request.getSplitValue();
log.debug("Split type: {}, Split value: {}", type, value);
if (type == 0) {
log.debug("Processing split by size");
long maxBytes = GeneralUtils.convertSizeToBytes(value);
log.debug("Max bytes per document: {}", maxBytes);
handleSplitBySize(sourceDocument, maxBytes, zipOut, filename);
} else if (type == 1) {
log.debug("Processing split by page count");
int pageCount = Integer.parseInt(value);
log.debug("Pages per document: {}", pageCount);
handleSplitByPageCount(sourceDocument, pageCount, zipOut, filename);
} else if (type == 2) {
log.debug("Processing split by document count");
int documentCount = Integer.parseInt(value);
log.debug("Total number of documents: {}", documentCount);
handleSplitByDocCount(sourceDocument, documentCount, zipOut, filename);
} else {
log.error("Invalid split type: {}", type);
throw ExceptionUtils.createIllegalArgumentException(
"error.invalidArgument",
"Invalid argument: {0}",
"split type: " + type);
}
log.debug("PDF splitting completed successfully");
if (type == 0) {
log.debug("Processing split by size");
long maxBytes = GeneralUtils.convertSizeToBytes(value);
log.debug("Max bytes per document: {}", maxBytes);
handleSplitBySize(sourceDocument, maxBytes, zipOut, filename);
} else if (type == 1) {
log.debug("Processing split by page count");
int pageCount = Integer.parseInt(value);
log.debug("Pages per document: {}", pageCount);
handleSplitByPageCount(sourceDocument, pageCount, zipOut, filename);
} else if (type == 2) {
log.debug("Processing split by document count");
int documentCount = Integer.parseInt(value);
log.debug("Total number of documents: {}", documentCount);
handleSplitByDocCount(sourceDocument, documentCount, zipOut, filename);
} else {
log.error("Invalid split type: {}", type);
throw ExceptionUtils.createIllegalArgumentException(
"error.invalidArgument",
"Invalid argument: {0}",
"split type: " + type);
}
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);
} catch (Exception e) {
ExceptionUtils.logException("PDF splitting process", e);
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);
}
log.debug("Returning response with {} bytes of data", data.length);
return WebResponseUtils.bytesToWebResponse(
data, filename + ".zip", MediaType.APPLICATION_OCTET_STREAM);
} catch (Exception e) {
ExceptionUtils.logException("PDF splitting process", e);
throw e; // Re-throw to ensure proper error response
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(

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -24,7 +24,6 @@ import org.mockito.ArgumentMatchers;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.mock.web.MockMultipartFile;
@ -57,10 +56,7 @@ class EditTableOfContentsControllerTest {
void setUp() {
mockFile =
new MockMultipartFile(
"file",
"test.pdf",
MediaType.APPLICATION_PDF_VALUE,
"PDF content".getBytes());
"file", "test.pdf", "application/pdf", "PDF content".getBytes());
mockDocument = mock(PDDocument.class);
mockCatalog = mock(PDDocumentCatalog.class);
mockPages = mock(PDPageTree.class);

View File

@ -23,7 +23,6 @@ import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.web.multipart.MultipartFile;
@ -50,22 +49,13 @@ class MergeControllerTest {
void setUp() {
mockFile1 =
new MockMultipartFile(
"file1",
"document1.pdf",
MediaType.APPLICATION_PDF_VALUE,
"PDF content 1".getBytes());
"file1", "document1.pdf", "application/pdf", "PDF content 1".getBytes());
mockFile2 =
new MockMultipartFile(
"file2",
"document2.pdf",
MediaType.APPLICATION_PDF_VALUE,
"PDF content 2".getBytes());
"file2", "document2.pdf", "application/pdf", "PDF content 2".getBytes());
mockFile3 =
new MockMultipartFile(
"file3",
"chapter3.pdf",
MediaType.APPLICATION_PDF_VALUE,
"PDF content 3".getBytes());
"file3", "chapter3.pdf", "application/pdf", "PDF content 3".getBytes());
mockDocument = mock(PDDocument.class);
mockMergedDocument = mock(PDDocument.class);
@ -212,10 +202,7 @@ class MergeControllerTest {
// Given
MockMultipartFile fileWithoutExtension =
new MockMultipartFile(
"file",
"document_no_ext",
MediaType.APPLICATION_PDF_VALUE,
"PDF content".getBytes());
"file", "document_no_ext", "application/pdf", "PDF content".getBytes());
MultipartFile[] files = {fileWithoutExtension};
when(mockMergedDocument.getDocumentCatalog()).thenReturn(mockCatalog);

View File

@ -17,7 +17,6 @@ import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.mock.web.MockMultipartFile;
@ -35,8 +34,7 @@ public class RotationControllerTest {
public void testRotatePDF() throws IOException {
// Create a mock file
MockMultipartFile mockFile =
new MockMultipartFile(
"file", "test.pdf", MediaType.APPLICATION_PDF_VALUE, new byte[] {1, 2, 3});
new MockMultipartFile("file", "test.pdf", "application/pdf", new byte[] {1, 2, 3});
RotatePDFRequest request = new RotatePDFRequest();
request.setFileInput(mockFile);
request.setAngle(90);
@ -64,8 +62,7 @@ public class RotationControllerTest {
public void testRotatePDFInvalidAngle() throws IOException {
// Create a mock file
MockMultipartFile mockFile =
new MockMultipartFile(
"file", "test.pdf", MediaType.APPLICATION_PDF_VALUE, new byte[] {1, 2, 3});
new MockMultipartFile("file", "test.pdf", "application/pdf", new byte[] {1, 2, 3});
RotatePDFRequest request = new RotatePDFRequest();
request.setFileInput(mockFile);
request.setAngle(45); // Invalid angle

View File

@ -16,7 +16,6 @@ import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.web.multipart.MultipartFile;
@ -46,22 +45,13 @@ class AttachmentControllerTest {
void setUp() {
pdfFile =
new MockMultipartFile(
"fileInput",
"test.pdf",
MediaType.APPLICATION_PDF_VALUE,
"PDF content".getBytes());
"fileInput", "test.pdf", "application/pdf", "PDF content".getBytes());
attachment1 =
new MockMultipartFile(
"attachment1",
"file1.txt",
MediaType.TEXT_PLAIN_VALUE,
"File 1 content".getBytes());
"attachment1", "file1.txt", "text/plain", "File 1 content".getBytes());
attachment2 =
new MockMultipartFile(
"attachment2",
"file2.jpg",
MediaType.IMAGE_JPEG_VALUE,
"Image content".getBytes());
"attachment2", "file2.jpg", "image/jpeg", "Image content".getBytes());
request = new AddAttachmentRequest();
mockDocument = mock(PDDocument.class);
modifiedMockDocument = mock(PDDocument.class);

View File

@ -18,7 +18,6 @@ import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.web.multipart.MultipartFile;
@ -118,8 +117,7 @@ class CertSignControllerTest {
@Test
void testSignPdfWithPfx() throws Exception {
MockMultipartFile pdfFile =
new MockMultipartFile(
"fileInput", "test.pdf", MediaType.APPLICATION_PDF_VALUE, pdfBytes);
new MockMultipartFile("fileInput", "test.pdf", "application/pdf", pdfBytes);
MockMultipartFile pfxFile =
new MockMultipartFile("p12File", "test-cert.pfx", "application/x-pkcs12", pfxBytes);
@ -144,8 +142,7 @@ class CertSignControllerTest {
@Test
void testSignPdfWithPkcs12() throws Exception {
MockMultipartFile pdfFile =
new MockMultipartFile(
"fileInput", "test.pdf", MediaType.APPLICATION_PDF_VALUE, pdfBytes);
new MockMultipartFile("fileInput", "test.pdf", "application/pdf", pdfBytes);
MockMultipartFile p12File =
new MockMultipartFile("p12File", "test-cert.p12", "application/x-pkcs12", p12Bytes);
@ -170,8 +167,7 @@ class CertSignControllerTest {
@Test
void testSignPdfWithJks() throws Exception {
MockMultipartFile pdfFile =
new MockMultipartFile(
"fileInput", "test.pdf", MediaType.APPLICATION_PDF_VALUE, pdfBytes);
new MockMultipartFile("fileInput", "test.pdf", "application/pdf", pdfBytes);
MockMultipartFile jksFile =
new MockMultipartFile(
"jksFile", "test-cert.jks", "application/octet-stream", jksBytes);
@ -197,8 +193,7 @@ class CertSignControllerTest {
@Test
void testSignPdfWithPem() throws Exception {
MockMultipartFile pdfFile =
new MockMultipartFile(
"fileInput", "test.pdf", MediaType.APPLICATION_PDF_VALUE, pdfBytes);
new MockMultipartFile("fileInput", "test.pdf", "application/pdf", pdfBytes);
MockMultipartFile keyFile =
new MockMultipartFile(
"privateKeyFile", "test-key.pem", "application/x-pem-file", pemKeyBytes);
@ -228,8 +223,7 @@ class CertSignControllerTest {
@Test
void testSignPdfWithCrt() throws Exception {
MockMultipartFile pdfFile =
new MockMultipartFile(
"fileInput", "test.pdf", MediaType.APPLICATION_PDF_VALUE, pdfBytes);
new MockMultipartFile("fileInput", "test.pdf", "application/pdf", pdfBytes);
MockMultipartFile keyFile =
new MockMultipartFile(
"privateKeyFile", "test-key.key", "application/x-pem-file", keyBytes);
@ -259,8 +253,7 @@ class CertSignControllerTest {
@Test
void testSignPdfWithCer() throws Exception {
MockMultipartFile pdfFile =
new MockMultipartFile(
"fileInput", "test.pdf", MediaType.APPLICATION_PDF_VALUE, pdfBytes);
new MockMultipartFile("fileInput", "test.pdf", "application/pdf", pdfBytes);
MockMultipartFile keyFile =
new MockMultipartFile(
"privateKeyFile", "test-key.key", "application/x-pem-file", keyBytes);
@ -290,8 +283,7 @@ class CertSignControllerTest {
@Test
void testSignPdfWithDer() throws Exception {
MockMultipartFile pdfFile =
new MockMultipartFile(
"fileInput", "test.pdf", MediaType.APPLICATION_PDF_VALUE, pdfBytes);
new MockMultipartFile("fileInput", "test.pdf", "application/pdf", pdfBytes);
MockMultipartFile keyFile =
new MockMultipartFile(
"privateKeyFile", "test-key.key", "application/x-pem-file", keyBytes);

View File

@ -42,7 +42,6 @@ import org.mockito.junit.jupiter.MockitoSettings;
import org.mockito.quality.Strictness;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.mock.web.MockMultipartFile;
@ -131,10 +130,7 @@ class RedactControllerTest {
void setUp() throws IOException {
mockPdfFile =
new MockMultipartFile(
"fileInput",
"test.pdf",
MediaType.APPLICATION_PDF_VALUE,
createSimplePdfContent());
"fileInput", "test.pdf", "application/pdf", createSimplePdfContent());
// Mock PDF document and related objects
mockDocument = mock(PDDocument.class);
@ -636,7 +632,7 @@ class RedactControllerTest {
new MockMultipartFile(
"fileInput",
"malformed.pdf",
MediaType.APPLICATION_PDF_VALUE,
"application/pdf",
"Not a real PDF content".getBytes());
RedactPdfRequest request = new RedactPdfRequest();

View File

@ -11,7 +11,6 @@ import java.util.List;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.http.MediaType;
import org.springframework.web.multipart.MultipartFile;
class AttachmentServiceTest {
@ -55,13 +54,13 @@ class AttachmentServiceTest {
when(attachment1.getInputStream())
.thenReturn(new ByteArrayInputStream("PDF content".getBytes()));
when(attachment1.getSize()).thenReturn(15L);
when(attachment1.getContentType()).thenReturn(MediaType.APPLICATION_PDF_VALUE);
when(attachment1.getContentType()).thenReturn("application/pdf");
when(attachment2.getOriginalFilename()).thenReturn("image.jpg");
when(attachment2.getInputStream())
.thenReturn(new ByteArrayInputStream("Image content".getBytes()));
when(attachment2.getSize()).thenReturn(20L);
when(attachment2.getContentType()).thenReturn(MediaType.IMAGE_JPEG_VALUE);
when(attachment2.getContentType()).thenReturn("image/jpeg");
PDDocument result = attachmentService.addAttachment(document, attachments);

View File

@ -1,7 +1,6 @@
package stirling.software.common.controller;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.*;
import java.util.Map;
@ -12,7 +11,6 @@ import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.mock.web.MockHttpSession;
@ -128,7 +126,7 @@ class JobControllerTest {
String jobId = "test-job-id";
String fileId = "file-id";
String originalFileName = "test.pdf";
String contentType = MediaType.APPLICATION_PDF_VALUE;
String contentType = "application/pdf";
byte[] fileContent = "Test file content".getBytes();
JobResult mockResult = new JobResult();
@ -208,7 +206,7 @@ class JobControllerTest {
String jobId = "test-job-id";
String fileId = "file-id";
String originalFileName = "test.pdf";
String contentType = MediaType.APPLICATION_PDF_VALUE;
String contentType = "application/pdf";
JobResult mockResult = new JobResult();
mockResult.setJobId(jobId);

View File

@ -13,7 +13,6 @@ import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.MDC;
import org.springframework.http.MediaType;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.context.request.RequestContextHolder;
@ -113,8 +112,8 @@ public class AuditUtils {
&& req.getContentType() != null) {
String contentType = req.getContentType();
if (contentType.contains(MediaType.APPLICATION_FORM_URLENCODED_VALUE)
|| contentType.contains(MediaType.MULTIPART_FORM_DATA_VALUE)) {
if (contentType.contains("application/x-www-form-urlencoded")
|| contentType.contains("multipart/form-data")) {
Map<String, String[]> params = new HashMap<>(req.getParameterMap());
// Remove CSRF token from logged parameters

View File

@ -71,12 +71,9 @@ public class CustomLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
authentication.getClass().getSimpleName());
getRedirectStrategy().sendRedirect(request, response, LOGOUT_PATH);
}
} else if (jwtService != null) {
String token = jwtService.extractToken(request);
if (token != null && !token.isBlank()) {
jwtService.clearToken(response);
getRedirectStrategy().sendRedirect(request, response, LOGOUT_PATH);
}
} else if (!jwtService.extractToken(request).isBlank()) {
jwtService.clearToken(response);
getRedirectStrategy().sendRedirect(request, response, LOGOUT_PATH);
} else {
// Redirect to login page after logout
String path = checkForErrors(request);
@ -168,8 +165,7 @@ public class CustomLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
log.info("Redirecting to Keycloak logout URL: {}", logoutUrl);
} else {
log.info(
"No redirect URL for {} available. Redirecting to default logout URL:"
+ " {}",
"No redirect URL for {} available. Redirecting to default logout URL: {}",
registrationId,
logoutUrl);
}

View File

@ -44,7 +44,7 @@ public class DatabaseController {
@Operation(
summary = "Import a database backup file",
description = "Uploads and imports a database backup SQL file.")
@PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE, value = "import-database")
@PostMapping(consumes = "multipart/form-data", value = "import-database")
public String importDatabase(
@Parameter(description = "SQL file to import", required = true)
@RequestParam("fileInput")

View File

@ -2,7 +2,6 @@ package stirling.software.proprietary.security.controller.api;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.mail.MailSendException;
import org.springframework.web.bind.annotation.ModelAttribute;
@ -43,7 +42,7 @@ public class EmailController {
* attachment.
* @return ResponseEntity with success or error message.
*/
@PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE, value = "/send-email")
@PostMapping(consumes = "multipart/form-data", value = "/send-email")
@Operation(
summary = "Send an email with an attachment",
description =