Migrate stream to resource for stability (#6160)

This commit is contained in:
Anthony Stirling
2026-04-23 15:56:31 +01:00
committed by GitHub
parent c294e9b2cb
commit 177c776658
109 changed files with 1390 additions and 855 deletions
@@ -10,6 +10,7 @@ import java.nio.file.Path;
import java.util.UUID;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
@@ -157,13 +158,48 @@ public class FileStorage {
success = true;
} finally {
if (!success) {
Files.deleteIfExists(filePath);
try {
Files.deleteIfExists(filePath);
} catch (IOException cleanupEx) {
log.warn(
"Failed to clean up partial file {} after store failure",
filePath,
cleanupEx);
}
}
}
log.debug("Stored StreamingResponseBody with ID: {}", fileId);
return fileId;
}
/**
* Persist a {@link Resource} body to disk, returning the generated file ID. Used by the async
* job pipeline to capture {@code ResponseEntity<Resource>} results produced by controllers.
*/
public String storeFromResource(Resource resource, String originalName) throws IOException {
String fileId = generateFileId();
Path filePath = getFilePath(fileId);
Files.createDirectories(filePath.getParent());
boolean success = false;
try (InputStream in = resource.getInputStream()) {
Files.copy(in, filePath);
success = true;
} finally {
if (!success) {
try {
Files.deleteIfExists(filePath);
} catch (IOException cleanupEx) {
log.warn(
"Failed to clean up partial file {} after store failure",
filePath,
cleanupEx);
}
}
}
log.debug("Stored Resource with ID: {}", fileId);
return fileId;
}
/**
* Delete a file by its ID
*
@@ -11,6 +11,7 @@ import java.util.function.Supplier;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
@@ -321,6 +322,13 @@ public class JobExecutorService {
log.debug(
"Stored ResponseEntity<StreamingResponseBody> result with fileId: {}",
fileId);
} else if (body instanceof Resource resource) {
String filename = extractResponseFilename(response);
String contentType = extractResponseContentType(response);
String fileId = fileStorage.storeFromResource(resource, filename);
taskManager.setFileResult(jobId, fileId, filename, contentType);
log.debug("Stored ResponseEntity<Resource> result with fileId: {}", fileId);
} else {
// Check if the response body contains a fileId
if (body != null && body.toString().contains("fileId")) {
@@ -16,11 +16,11 @@ import java.util.zip.ZipOutputStream;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.springframework.core.io.Resource;
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 com.vladsch.flexmark.html2md.converter.FlexmarkHtmlConverter;
import com.vladsch.flexmark.util.data.MutableDataSet;
@@ -49,7 +49,7 @@ public class PDFToFile {
this.runtimePathConfig = runtimePathConfig;
}
public ResponseEntity<StreamingResponseBody> processPdfToMarkdown(MultipartFile inputFile)
public ResponseEntity<Resource> processPdfToMarkdown(MultipartFile inputFile)
throws IOException, InterruptedException {
if (!MediaType.APPLICATION_PDF_VALUE.equals(inputFile.getContentType())) {
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
@@ -169,7 +169,7 @@ public class PDFToFile {
return PATTERN.matcher(markdown).replaceAll("$1(images/$2)");
}
public ResponseEntity<StreamingResponseBody> processPdfToHtml(MultipartFile inputFile)
public ResponseEntity<Resource> processPdfToHtml(MultipartFile inputFile)
throws IOException, InterruptedException {
if (!MediaType.APPLICATION_PDF_VALUE.equals(inputFile.getContentType())) {
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
@@ -232,7 +232,7 @@ public class PDFToFile {
finalOut, fileName, MediaType.APPLICATION_OCTET_STREAM);
}
public ResponseEntity<StreamingResponseBody> processPdfToOfficeFormat(
public ResponseEntity<Resource> processPdfToOfficeFormat(
MultipartFile inputFile, String outputFormat, String libreOfficeFilter)
throws IOException, InterruptedException {
@@ -1,19 +1,22 @@
package stirling.software.common.util;
import java.io.ByteArrayOutputStream;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
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.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
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;
@@ -49,12 +52,7 @@ public class WebResponseUtils {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(mediaType);
headers.setContentLength(bytes.length);
String encodedDocName =
RegexPatternUtils.getInstance()
.getPlusSignPattern()
.matcher(URLEncoder.encode(docName, StandardCharsets.UTF_8))
.replaceAll("%20");
headers.setContentDispositionFormData("attachment", encodedDocName);
headers.setContentDispositionFormData("attachment", encodeAttachmentName(docName));
return new ResponseEntity<>(bytes, headers, HttpStatus.OK);
}
@@ -73,7 +71,17 @@ public class WebResponseUtils {
return baosToWebResponse(baos, docName);
}
public static ResponseEntity<StreamingResponseBody> pdfDocToWebResponse(
/**
* Save a {@link PDDocument} to a managed temp file and return it as a file-backed {@code
* ResponseEntity<Resource>}.
*
* <p>The returned {@link Resource} owns the supplied {@link TempFile} — the file is deleted
* when Spring closes the response {@link InputStream} after writing the body. This is a
* synchronous equivalent of the previous {@code StreamingResponseBody} pattern and avoids the
* async-dispatch hazards (response-committed races, filter incompatibility, silent write
* failures) that {@code StreamingResponseBody} introduced.
*/
public static ResponseEntity<Resource> pdfDocToWebResponse(
PDDocument document, String docName, TempFileManager tempFileManager)
throws IOException {
TempFile tempFile = tempFileManager.createManagedTempFile(".pdf");
@@ -87,38 +95,49 @@ public class WebResponseUtils {
}
/**
* Convert a File to a web response (PDF default).
* Convert a {@link TempFile} holding a PDF into a web response.
*
* <p>The temp file is deleted when Spring closes the response body stream.
*
* @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.
* @return A ResponseEntity whose body streams the file, deleting it on close.
*/
public static ResponseEntity<StreamingResponseBody> pdfFileToWebResponse(
public static ResponseEntity<Resource> pdfFileToWebResponse(
TempFile outputTempFile, String docName) throws IOException {
return fileToWebResponse(outputTempFile, docName, MediaType.APPLICATION_PDF);
}
/**
* Convert a File to a web response (ZIP default).
* Convert a {@link TempFile} holding a ZIP archive into a web response.
*
* <p>The temp file is deleted when Spring closes the response body stream.
*
* @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.
* @return A ResponseEntity whose body streams the file, deleting it on close.
*/
public static ResponseEntity<StreamingResponseBody> zipFileToWebResponse(
public static ResponseEntity<Resource> 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).
* Convert a {@link TempFile} into a web response with an explicit media type.
*
* <p>The returned {@link ResponseEntity} carries a {@link ManagedTempFileResource} as its body.
* Spring's {@code ResourceHttpMessageConverter} calls {@link Resource#getInputStream()} once
* and closes the returned stream after writing — at which point the underlying {@link TempFile}
* is deleted. Everything runs synchronously on the request thread, so write failures propagate
* through normal Spring error handling and are logged, rather than silently truncating the
* response.
*
* @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.
* @return A ResponseEntity whose body streams the file, deleting it on close.
*/
public static ResponseEntity<StreamingResponseBody> fileToWebResponse(
public static ResponseEntity<Resource> fileToWebResponse(
TempFile outputTempFile, String docName, MediaType mediaType) throws IOException {
try {
@@ -127,23 +146,9 @@ public class WebResponseUtils {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(mediaType);
headers.setContentLength(len);
String encodedDocName =
RegexPatternUtils.getInstance()
.getPlusSignPattern()
.matcher(URLEncoder.encode(docName, StandardCharsets.UTF_8))
.replaceAll("%20");
headers.setContentDispositionFormData("attachment", encodedDocName);
StreamingResponseBody body =
os -> {
try (os) {
Files.copy(path, os);
os.flush();
} finally {
outputTempFile.close();
}
};
headers.setContentDispositionFormData("attachment", encodeAttachmentName(docName));
Resource body = new ManagedTempFileResource(outputTempFile);
return new ResponseEntity<>(body, headers, HttpStatus.OK);
} catch (IOException | RuntimeException e) {
try {
@@ -154,4 +159,115 @@ public class WebResponseUtils {
throw e;
}
}
private static String encodeAttachmentName(String docName) {
return RegexPatternUtils.getInstance()
.getPlusSignPattern()
.matcher(URLEncoder.encode(docName, StandardCharsets.UTF_8))
.replaceAll("%20");
}
/**
* {@link Resource} backed by a {@link TempFile}. The underlying temp file is deleted when the
* response {@code InputStream} is closed — i.e. after Spring has finished writing the body. Any
* {@link IOException} during the copy is logged via {@link ClosingInputStream} and propagates
* through Spring's normal error path. Since response headers are committed before the body
* transfer begins, a mid-body failure manifests as a server-side log entry plus an aborted
* connection rather than a silently-truncated success — which is the behaviour this class was
* added to restore.
*
* <p><b>Single-use contract:</b> {@link #getInputStream()} is intended to be called once by
* Spring's {@code ResourceHttpMessageConverter} on the normal write path. After the returned
* stream is closed the backing temp file is deleted, so subsequent {@code getInputStream()}
* calls will either see a deleted file (tests that mock {@link TempFile#close()} are an
* exception) or fail at read time. Callers that need to re-read the body must copy it first.
*/
public static final class ManagedTempFileResource extends FileSystemResource {
private final TempFile tempFile;
public ManagedTempFileResource(TempFile tempFile) {
super(tempFile.getFile());
this.tempFile = tempFile;
}
@Override
public InputStream getInputStream() throws IOException {
InputStream source;
try {
source = super.getInputStream();
} catch (IOException e) {
// Opening the input stream already failed; make sure we don't leak the temp file.
try {
tempFile.close();
} catch (Exception closeEx) {
e.addSuppressed(closeEx);
}
throw e;
}
return new ClosingInputStream(source, tempFile);
}
}
/**
* Stream wrapper that deletes its backing {@link TempFile} on close. Logs — but does not
* swallow — any IOException that happens while reading the body, so upstream handlers can
* surface the failure to the client.
*/
private static final class ClosingInputStream extends FilterInputStream {
private final TempFile tempFile;
private boolean closed;
ClosingInputStream(InputStream delegate, TempFile tempFile) {
super(delegate);
this.tempFile = tempFile;
}
@Override
public int read() throws IOException {
try {
return super.read();
} catch (IOException e) {
log.error(
"Failed to read temp response body {} while streaming to client",
tempFile.getAbsolutePath(),
e);
throw e;
}
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
try {
return super.read(b, off, len);
} catch (IOException e) {
log.error(
"Failed to read temp response body {} while streaming to client",
tempFile.getAbsolutePath(),
e);
throw e;
}
}
@Override
public void close() throws IOException {
if (closed) {
return;
}
closed = true;
try {
super.close();
} finally {
try {
tempFile.close();
} catch (Exception closeEx) {
log.warn(
"Failed to clean up temp file {} after streaming response",
tempFile.getAbsolutePath(),
closeEx);
}
}
}
}
}
@@ -4,8 +4,11 @@ import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.stream.Stream;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -13,6 +16,8 @@ import org.junit.jupiter.api.io.TempDir;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.web.multipart.MultipartFile;
@@ -185,4 +190,76 @@ class FileStorageTest {
// Assert
assertFalse(result);
}
@Test
void storeFromResource_happyPath_persistsContent() throws IOException {
// Arrange
byte[] payload = "resource-body-bytes".getBytes(StandardCharsets.UTF_8);
Resource resource = new ByteArrayResource(payload);
// Act
String fileId = fileStorage.storeFromResource(resource, "whatever.pdf");
// Assert
assertNotNull(fileId);
assertTrue(Files.exists(tempDir.resolve(fileId)));
assertArrayEquals(payload, fileStorage.retrieveBytes(fileId));
}
@Test
void storeFromResource_failureCleansUpPartialFile() throws IOException {
// Arrange: a Resource whose getInputStream returns a stream that throws after
// emitting a few bytes. The finally block in storeFromResource should remove
// the partial file on disk.
byte[] head = "partial".getBytes(StandardCharsets.UTF_8);
Resource flakyResource =
new ByteArrayResource(head) {
@Override
public InputStream getInputStream() {
return new InputStream() {
private int position = 0;
@Override
public int read() throws IOException {
if (position < head.length) {
return head[position++] & 0xFF;
}
throw new IOException("simulated mid-copy read failure");
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
if (position >= head.length) {
throw new IOException("simulated mid-copy read failure");
}
int toCopy = Math.min(len, head.length - position);
System.arraycopy(head, position, b, off, toCopy);
position += toCopy;
return toCopy;
}
};
}
};
// Snapshot dir contents before the call so we can detect any lingering file.
long filesBefore;
try (Stream<Path> s = Files.list(tempDir)) {
filesBefore = s.count();
}
// Act + Assert: IOException must propagate out — not be swallowed.
assertThrows(
IOException.class, () -> fileStorage.storeFromResource(flakyResource, "n.pdf"));
// Assert: no partial file lingers under the storage directory — the finally
// branch's deleteIfExists must have cleaned it up.
long filesAfter;
try (Stream<Path> s = Files.list(tempDir)) {
filesAfter = s.count();
}
assertEquals(
filesBefore,
filesAfter,
"partial file must be cleaned up by storeFromResource finally block");
}
}
@@ -8,9 +8,11 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeoutException;
@@ -24,7 +26,12 @@ import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
import org.springframework.http.ContentDisposition;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import jakarta.servlet.http.HttpServletRequest;
@@ -180,6 +187,46 @@ class JobExecutorServiceTest {
verify(spy).runJobGeneric(eq(false), any(Supplier.class), eq(customTimeout));
}
@Test
void shouldPersistResponseEntityResourceBodyViaFileStorage() throws Exception {
// Given: an async job whose result is a ResponseEntity<Resource> — the new
// branch added by the stream-to-Resource migration. The executor must route
// the body through FileStorage.storeFromResource and then record the result
// via TaskManager.setFileResult with the filename/content-type extracted
// from the response headers.
byte[] payload = "resource-bytes".getBytes(StandardCharsets.UTF_8);
Resource resource = new ByteArrayResource(payload);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_PDF);
headers.setContentDisposition(
ContentDisposition.formData().name("attachment").filename("result.pdf").build());
Supplier<Object> work = () -> new ResponseEntity<>(resource, headers, HttpStatus.OK);
when(fileStorage.storeFromResource(any(Resource.class), anyString()))
.thenReturn("stored-file-id");
// When: run the job asynchronously — processJobResult runs on the executor.
ResponseEntity<?> response = jobExecutorService.runJobGeneric(true, work);
// Then: the immediate return must be the JobResponse envelope.
assertEquals(HttpStatus.OK, response.getStatusCode());
assertInstanceOf(JobResponse.class, response.getBody());
// Wait for async processing and verify the Resource branch was taken —
// FileStorage.storeFromResource was invoked with the same Resource instance,
// and TaskManager.setFileResult recorded the extracted filename + content-type.
verify(fileStorage, timeout(5000)).storeFromResource(eq(resource), eq("result.pdf"));
verify(taskManager, timeout(5000))
.setFileResult(
anyString(),
eq("stored-file-id"),
eq("result.pdf"),
eq(MediaType.APPLICATION_PDF_VALUE));
verify(taskManager, timeout(5000)).setComplete(anyString());
}
@Test
void shouldHandleTimeout() throws Exception {
// Given
@@ -25,12 +25,12 @@ import org.junit.jupiter.api.io.TempDir;
import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.core.io.Resource;
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;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import io.github.pixee.security.ZipSecurity;
@@ -83,9 +83,11 @@ class PDFToFileTest {
pdfToFile = new PDFToFile(mockTempFileManager, mockRuntimePathConfig);
}
private static byte[] drain(ResponseEntity<StreamingResponseBody> response) throws IOException {
private static byte[] drain(ResponseEntity<Resource> response) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
response.getBody().writeTo(baos);
try (java.io.InputStream __in = response.getBody().getInputStream()) {
__in.transferTo(baos);
}
return baos.toByteArray();
}
@@ -100,7 +102,7 @@ class PDFToFileTest {
"This is not a PDF".getBytes());
// Execute
ResponseEntity<StreamingResponseBody> response = pdfToFile.processPdfToMarkdown(nonPdfFile);
ResponseEntity<Resource> response = pdfToFile.processPdfToMarkdown(nonPdfFile);
// Verify
assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode());
@@ -117,7 +119,7 @@ class PDFToFileTest {
"This is not a PDF".getBytes());
// Execute
ResponseEntity<StreamingResponseBody> response = pdfToFile.processPdfToHtml(nonPdfFile);
ResponseEntity<Resource> response = pdfToFile.processPdfToHtml(nonPdfFile);
// Verify
assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode());
@@ -135,7 +137,7 @@ class PDFToFileTest {
"This is not a PDF".getBytes());
// Execute
ResponseEntity<StreamingResponseBody> response =
ResponseEntity<Resource> response =
pdfToFile.processPdfToOfficeFormat(nonPdfFile, "docx", "draw_pdf_import");
// Verify
@@ -154,7 +156,7 @@ class PDFToFileTest {
"Fake PDF content".getBytes());
// Execute with invalid format
ResponseEntity<StreamingResponseBody> response =
ResponseEntity<Resource> response =
pdfToFile.processPdfToOfficeFormat(pdfFile, "invalid_format", "draw_pdf_import");
// Verify
@@ -205,8 +207,7 @@ class PDFToFileTest {
});
// Execute the method
ResponseEntity<StreamingResponseBody> response =
pdfToFile.processPdfToMarkdown(pdfFile);
ResponseEntity<Resource> response = pdfToFile.processPdfToMarkdown(pdfFile);
// Verify - should now return a ZIP file instead of plain markdown
assertEquals(HttpStatus.OK, response.getStatusCode());
@@ -298,8 +299,7 @@ class PDFToFileTest {
});
// Execute the method
ResponseEntity<StreamingResponseBody> response =
pdfToFile.processPdfToMarkdown(pdfFile);
ResponseEntity<Resource> response = pdfToFile.processPdfToMarkdown(pdfFile);
// Verify
assertEquals(HttpStatus.OK, response.getStatusCode());
@@ -377,7 +377,7 @@ class PDFToFileTest {
});
// Execute the method
ResponseEntity<StreamingResponseBody> response = pdfToFile.processPdfToHtml(pdfFile);
ResponseEntity<Resource> response = pdfToFile.processPdfToHtml(pdfFile);
// Verify
assertEquals(HttpStatus.OK, response.getStatusCode());
@@ -463,7 +463,7 @@ class PDFToFileTest {
});
// Execute the method with docx format
ResponseEntity<StreamingResponseBody> response =
ResponseEntity<Resource> response =
pdfToFile.processPdfToOfficeFormat(pdfFile, "docx", "draw_pdf_import");
// Verify
@@ -535,7 +535,7 @@ class PDFToFileTest {
});
// Execute the method with ODP format
ResponseEntity<StreamingResponseBody> response =
ResponseEntity<Resource> response =
pdfToFile.processPdfToOfficeFormat(pdfFile, "odp", "draw_pdf_import");
// Verify
@@ -620,7 +620,7 @@ class PDFToFileTest {
});
// Execute the method with text format
ResponseEntity<StreamingResponseBody> response =
ResponseEntity<Resource> response =
pdfToFile.processPdfToOfficeFormat(pdfFile, "txt:Text", "draw_pdf_import");
// Verify
@@ -679,7 +679,7 @@ class PDFToFileTest {
});
// Execute the method
ResponseEntity<StreamingResponseBody> response =
ResponseEntity<Resource> response =
pdfToFile.processPdfToOfficeFormat(pdfFile, "docx", "draw_pdf_import");
// Verify
@@ -726,7 +726,7 @@ class PDFToFileTest {
return mockExecutorResult;
});
ResponseEntity<StreamingResponseBody> response =
ResponseEntity<Resource> response =
pdfToFileWithUno.processPdfToOfficeFormat(pdfFile, "docx", "writer_pdf_import");
assertEquals(HttpStatus.OK, response.getStatusCode());
@@ -790,7 +790,7 @@ class PDFToFileTest {
return mockExecutorResult;
});
ResponseEntity<StreamingResponseBody> response =
ResponseEntity<Resource> response =
pdfToFileWithUno.processPdfToOfficeFormat(pdfFile, "docx", "writer_pdf_import");
assertEquals(HttpStatus.OK, response.getStatusCode());
@@ -3,17 +3,39 @@ package stirling.software.common.util;
import static org.junit.jupiter.api.Assertions.*;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.concurrent.atomic.AtomicBoolean;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import stirling.software.common.model.ApplicationProperties;
class WebResponseUtilsTest {
@TempDir Path tempDir;
private TempFileManager tempFileManager;
@BeforeEach
void setUpTempFileManager() {
TempFileRegistry registry = new TempFileRegistry();
ApplicationProperties applicationProperties = new ApplicationProperties();
applicationProperties.getSystem().getTempFileManagement().setBaseTmpDir(tempDir.toString());
applicationProperties.getSystem().getTempFileManagement().setPrefix("wru-test-");
tempFileManager = new TempFileManager(registry, applicationProperties);
}
@Test
void testBytesToWebResponse_defaultMediaType() throws IOException {
byte[] data = "test content".getBytes(StandardCharsets.UTF_8);
@@ -91,4 +113,87 @@ class WebResponseUtilsTest {
assertEquals(HttpStatus.OK, response.getStatusCode());
assertEquals(0, response.getHeaders().getContentLength());
}
@Test
void managedTempFileResource_happyPathDeletesBackingFile() throws IOException {
// Create a real managed temp file on disk and populate it.
TempFile tempFile = tempFileManager.createManagedTempFile(".pdf");
byte[] payload = "fully-readable-pdf-body".getBytes(StandardCharsets.UTF_8);
Files.write(tempFile.getPath(), payload);
File backing = tempFile.getFile();
assertTrue(backing.exists(), "precondition: backing file should exist");
WebResponseUtils.ManagedTempFileResource resource =
new WebResponseUtils.ManagedTempFileResource(tempFile);
byte[] readBack;
try (InputStream in = resource.getInputStream()) {
readBack = in.readAllBytes();
}
assertArrayEquals(payload, readBack, "stream should deliver the original bytes");
assertFalse(
backing.exists(),
"backing temp file must be deleted once the response stream is closed");
}
@Test
void closingInputStream_propagatesReadFailure() throws IOException {
// ManagedTempFileResource is final, so we can't swap in a mock underlying stream.
// Instead: open the resource's stream, close the inner FileInputStream early
// (by closing the outer stream once), then confirm reading the now-closed stream
// throws — exercising ClosingInputStream's read() catch/log/rethrow path. Finally
// confirm the temp file is still cleaned up on close().
TempFile tempFile = tempFileManager.createManagedTempFile(".pdf");
Files.write(tempFile.getPath(), "some-bytes".getBytes(StandardCharsets.UTF_8));
File backing = tempFile.getFile();
assertTrue(backing.exists());
WebResponseUtils.ManagedTempFileResource resource =
new WebResponseUtils.ManagedTempFileResource(tempFile);
InputStream in = resource.getInputStream();
// Pre-close the underlying stream to guarantee read() throws.
in.close();
// After close, the temp file has been deleted already.
assertFalse(
backing.exists(),
"backing file is deleted on first close — precondition for read assertion");
// Any attempt to read from the already-closed stream must throw IOException
// (not silently return -1). This exercises the ClosingInputStream.read() path
// that logs and rethrows.
IOException ioex = assertThrows(IOException.class, in::read);
assertNotNull(ioex);
// close() is idempotent — a second call must not throw.
assertDoesNotThrow(in::close);
}
@Test
void managedTempFileResource_openFailureCleansUp() throws IOException {
// Arrange: build a TempFile whose backing file is deleted out from under it so
// super.getInputStream() throws on open. Instrument close() with an AtomicBoolean
// hook to confirm the cleanup path ran.
AtomicBoolean closed = new AtomicBoolean(false);
TempFile spying =
new TempFile(tempFileManager, ".pdf") {
@Override
public void close() {
closed.set(true);
super.close();
}
};
assertTrue(
spying.getFile().delete(),
"precondition: delete backing so super.getInputStream() fails");
WebResponseUtils.ManagedTempFileResource resource =
new WebResponseUtils.ManagedTempFileResource(spying);
assertThrows(IOException.class, resource::getInputStream);
assertTrue(
closed.get(),
"tempFile.close() must run when super.getInputStream() fails on open");
}
}
@@ -12,13 +12,13 @@ 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.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute;
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;
@@ -51,7 +51,7 @@ public class BookletImpositionController {
"This operation combines page reordering for booklet printing with multi-page layout. "
+ "It rearranges pages in the correct order for booklet printing and places multiple pages "
+ "on each sheet for proper folding and binding. Input:PDF Output:PDF Type:SISO")
public ResponseEntity<StreamingResponseBody> createBookletImposition(
public ResponseEntity<Resource> createBookletImposition(
@ModelAttribute BookletImpositionRequest request) throws IOException {
MultipartFile file = request.getFileInput();
@@ -12,10 +12,10 @@ 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.rendering.PDFRenderer;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import io.swagger.v3.oas.annotations.Operation;
@@ -132,7 +132,7 @@ public class CropController {
description =
"This operation takes an input PDF file and crops it according to the given"
+ " coordinates. Input:PDF Output:PDF Type:SISO")
public ResponseEntity<StreamingResponseBody> cropPdf(@ModelAttribute CropPdfForm request)
public ResponseEntity<Resource> cropPdf(@ModelAttribute CropPdfForm request)
throws IOException {
if (request.isAutoCrop()) {
return cropWithAutomaticDetection(request);
@@ -153,8 +153,8 @@ public class CropController {
}
}
private ResponseEntity<StreamingResponseBody> cropWithAutomaticDetection(
@ModelAttribute CropPdfForm request) throws IOException {
private ResponseEntity<Resource> cropWithAutomaticDetection(@ModelAttribute CropPdfForm request)
throws IOException {
try (PDDocument sourceDocument = pdfDocumentFactory.load(request)) {
try (PDDocument newDocument =
@@ -207,8 +207,8 @@ public class CropController {
}
}
private ResponseEntity<StreamingResponseBody> cropWithPDFBox(
@ModelAttribute CropPdfForm request) throws IOException {
private ResponseEntity<Resource> cropWithPDFBox(@ModelAttribute CropPdfForm request)
throws IOException {
try (PDDocument sourceDocument = pdfDocumentFactory.load(request)) {
try (PDDocument newDocument =
@@ -263,8 +263,8 @@ public class CropController {
}
}
private ResponseEntity<StreamingResponseBody> cropWithGhostscript(
@ModelAttribute CropPdfForm request) throws IOException {
private ResponseEntity<Resource> cropWithGhostscript(@ModelAttribute CropPdfForm request)
throws IOException {
TempFile tempInputFile = null;
TempFile tempOutputFile = null;
@@ -301,7 +301,7 @@ public class CropController {
processExecutor.runCommandWithOutputHandling(command);
TempFile out = tempOutputFile;
tempOutputFile = null; // ownership transferred to StreamingResponseBody
tempOutputFile = null; // ownership transferred to response Resource
return WebResponseUtils.pdfFileToWebResponse(
out,
GeneralUtils.generateFilename(
@@ -9,11 +9,11 @@ import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline.PDDocumentOutline;
import org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline.PDOutlineItem;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import io.swagger.v3.oas.annotations.Operation;
@@ -151,7 +151,7 @@ public class EditTableOfContentsController {
@Operation(
summary = "Edit Table of Contents",
description = "Add or edit bookmarks/table of contents in a PDF document.")
public ResponseEntity<StreamingResponseBody> editTableOfContents(
public ResponseEntity<Resource> editTableOfContents(
@ModelAttribute EditTableOfContentsRequest request) throws Exception {
MultipartFile file = request.getFileInput();
@@ -23,12 +23,12 @@ import org.apache.pdfbox.pdmodel.interactive.form.PDSignatureField;
import org.apache.xmpbox.XMPMetadata;
import org.apache.xmpbox.schema.XMPBasicSchema;
import org.apache.xmpbox.xml.DomXmpParser;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import io.swagger.v3.oas.annotations.Operation;
@@ -279,7 +279,7 @@ public class MergeController {
"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(
public ResponseEntity<Resource> mergePdfs(
@ModelAttribute MergePdfsRequest request,
@RequestParam(value = "fileOrder", required = false) String fileOrder)
throws IOException {
@@ -10,11 +10,11 @@ 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.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import io.swagger.v3.oas.annotations.Operation;
@@ -47,7 +47,7 @@ public class MultiPageLayoutController {
description =
"This operation takes an input PDF file and the number of pages to merge into a"
+ " single sheet in the output PDF file. Input:PDF Output:PDF Type:SISO")
public ResponseEntity<StreamingResponseBody> mergeMultiplePagesIntoOne(
public ResponseEntity<Resource> mergeMultiplePagesIntoOne(
@ModelAttribute MergeMultiplePagesRequest request) throws IOException {
int MAX_PAGES = 100000;
@@ -11,11 +11,11 @@ import java.util.Map;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.multipdf.Overlay;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import io.swagger.v3.oas.annotations.Operation;
@@ -46,8 +46,8 @@ public class PdfOverlayController {
description =
"Overlay PDF files onto a base PDF with different modes: Sequential,"
+ " Interleaved, or Fixed Repeat. Input:PDF Output:PDF Type:MIMO")
public ResponseEntity<StreamingResponseBody> overlayPdfs(
@ModelAttribute OverlayPdfsRequest request) throws IOException {
public ResponseEntity<Resource> overlayPdfs(@ModelAttribute OverlayPdfsRequest request)
throws IOException {
MultipartFile baseFile = request.getFileInput();
int overlayPos = request.getOverlayPosition();
@@ -89,7 +89,7 @@ public class PdfOverlayController {
baseFile.getOriginalFilename(), "_overlayed.pdf");
TempFile out = tempOut;
tempOut = null; // ownership transferred to StreamingResponseBody
tempOut = null; // ownership transferred to response Resource
return WebResponseUtils.pdfFileToWebResponse(out, outputFilename);
}
} catch (Exception e) {
@@ -14,10 +14,10 @@ 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.core.io.Resource;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import io.swagger.v3.oas.annotations.Operation;
@@ -52,7 +52,7 @@ public class PosterPdfController {
+ "suitable for printing on standard paper sizes (e.g., A4, Letter). "
+ "Divides each page into a grid of smaller pages using Apache PDFBox. "
+ "Input: PDF Output: ZIP-PDF Type: SISO")
public ResponseEntity<StreamingResponseBody> posterPdf(@ModelAttribute PosterPdfRequest request)
public ResponseEntity<Resource> posterPdf(@ModelAttribute PosterPdfRequest request)
throws Exception {
log.debug("Starting PDF poster split process with request: {}", request);
@@ -8,11 +8,11 @@ import java.util.Locale;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import io.swagger.v3.oas.annotations.Operation;
@@ -47,8 +47,8 @@ public class RearrangePagesPDFController {
"This endpoint removes specified pages from a given PDF file. Users can provide"
+ " a comma-separated list of page numbers or ranges to delete. Input:PDF"
+ " Output:PDF Type:SISO")
public ResponseEntity<StreamingResponseBody> deletePages(
@ModelAttribute PDFWithPageNums request) throws IOException {
public ResponseEntity<Resource> deletePages(@ModelAttribute PDFWithPageNums request)
throws IOException {
MultipartFile pdfFile = request.getFileInput();
String pagesToDelete = request.getPageNumbers();
@@ -228,8 +228,8 @@ public class RearrangePagesPDFController {
+ " order or custom mode. Users can provide a page order as a"
+ " comma-separated list of page numbers or page ranges, or a custom mode."
+ " Input:PDF Output:PDF")
public ResponseEntity<StreamingResponseBody> rearrangePages(
@ModelAttribute RearrangePagesRequest request) throws IOException {
public ResponseEntity<Resource> rearrangePages(@ModelAttribute RearrangePagesRequest request)
throws IOException {
MultipartFile pdfFile = request.getFileInput();
String pageOrder = request.getPageNumbers();
String sortType = request.getCustomMode();
@@ -5,11 +5,11 @@ 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.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import io.swagger.v3.oas.annotations.Operation;
@@ -39,7 +39,7 @@ public class RotationController {
description =
"This endpoint rotates a given PDF file by a specified angle. The angle must be"
+ " a multiple of 90. Input:PDF Output:PDF Type:SISO")
public ResponseEntity<StreamingResponseBody> rotatePDF(@ModelAttribute RotatePDFRequest request)
public ResponseEntity<Resource> rotatePDF(@ModelAttribute RotatePDFRequest request)
throws IOException {
MultipartFile pdfFile = request.getFileInput();
Integer angle = request.getAngle();
@@ -11,11 +11,11 @@ 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.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import io.swagger.v3.oas.annotations.Operation;
@@ -120,8 +120,8 @@ public class ScalePagesController {
description =
"This operation takes an input PDF file and the size to scale the pages to in"
+ " the output PDF file. Input:PDF Output:PDF Type:SISO")
public ResponseEntity<StreamingResponseBody> scalePages(
@ModelAttribute ScalePagesRequest request) throws IOException {
public ResponseEntity<Resource> scalePages(@ModelAttribute ScalePagesRequest request)
throws IOException {
MultipartFile file = request.getFileInput();
String targetPDRectangle = request.getPageSize();
float scaleFactor = request.getScaleFactor();
@@ -9,11 +9,11 @@ import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import io.swagger.v3.oas.annotations.Operation;
@@ -48,7 +48,7 @@ public class SplitPDFController {
+ " specified page numbers or ranges. Users can specify pages using"
+ " individual numbers, ranges, or 'all' for every page. Input:PDF"
+ " Output:PDF Type:SIMO")
public ResponseEntity<StreamingResponseBody> splitPdf(@ModelAttribute SplitPagesRequest request)
public ResponseEntity<Resource> splitPdf(@ModelAttribute SplitPagesRequest request)
throws IOException {
MultipartFile file = request.getFileInput();
@@ -11,11 +11,11 @@ import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline.PDDocumentOutline;
import org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline.PDOutlineItem;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import io.swagger.v3.oas.annotations.Operation;
@@ -127,8 +127,8 @@ public class SplitPdfByChaptersController {
description =
"Splits a PDF into chapters and returns a ZIP file. Input:PDF Output:ZIP-PDF"
+ " Type:SISO")
public ResponseEntity<StreamingResponseBody> splitPdf(
@ModelAttribute SplitPdfByChaptersRequest request) throws Exception {
public ResponseEntity<Resource> splitPdf(@ModelAttribute SplitPdfByChaptersRequest request)
throws Exception {
MultipartFile file = request.getFileInput();
boolean includeMetadata = Boolean.TRUE.equals(request.getIncludeMetadata());
@@ -15,11 +15,11 @@ 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.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import io.swagger.v3.oas.annotations.Operation;
@@ -59,7 +59,7 @@ public class SplitPdfBySectionsController {
+ " which page to split, and how to split"
+ " ( halves, thirds, quarters, etc.), both vertically and horizontally."
+ " Input:PDF Output:ZIP-PDF Type:SISO")
public ResponseEntity<StreamingResponseBody> splitPdf(
public ResponseEntity<Resource> splitPdf(
@Valid @ModelAttribute SplitPdfBySectionsRequest request) throws Exception {
MultipartFile file = request.getFileInput();
String pageNumbers = request.getPageNumbers();
@@ -8,11 +8,11 @@ import java.util.zip.ZipOutputStream;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import io.swagger.v3.oas.annotations.Operation;
@@ -51,7 +51,7 @@ public class SplitPdfBySizeController {
+ " if 10MB and each page is 1MB and you enter 2MB then 5 docs each 2MB"
+ " (rounded so that it accepts 1.9MB but not 2.1MB) Input:PDF"
+ " Output:ZIP-PDF Type:SISO")
public ResponseEntity<StreamingResponseBody> autoSplitPdf(
public ResponseEntity<Resource> autoSplitPdf(
@ModelAttribute SplitPdfBySizeOrCountRequest request) throws Exception {
log.debug("Starting PDF split process with request: {}", request);
@@ -8,10 +8,10 @@ import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import io.swagger.v3.oas.annotations.Operation;
@@ -44,7 +44,7 @@ public class ToSinglePageController {
+ " document. The width of the single page will be same as the input's"
+ " width, but the height will be the sum of all the pages' heights."
+ " Input:PDF Output:PDF Type:SISO")
public ResponseEntity<StreamingResponseBody> pdfToSinglePage(@ModelAttribute PDFFile request)
public ResponseEntity<Resource> pdfToSinglePage(@ModelAttribute PDFFile request)
throws IOException {
// Load the source document
@@ -12,11 +12,11 @@ import java.util.Set;
import org.apache.commons.io.FilenameUtils;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute;
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;
@@ -62,7 +62,7 @@ public class ConvertEbookToPDFController {
description =
"This endpoint converts common eBook formats (EPUB, MOBI, AZW3, FB2, TXT, DOCX)"
+ " to PDF using Calibre. Input:BOOK Output:PDF Type:SISO")
public ResponseEntity<StreamingResponseBody> convertEbookToPdf(
public ResponseEntity<Resource> convertEbookToPdf(
@ModelAttribute ConvertEbookToPdfRequest request) throws Exception {
if (!isCalibreEnabled()) {
throw new IllegalStateException("Calibre support is disabled");
@@ -162,7 +162,7 @@ public class ConvertEbookToPDFController {
document.save(tempOut.getFile());
}
}
ResponseEntity<StreamingResponseBody> response =
ResponseEntity<Resource> response =
WebResponseUtils.pdfFileToWebResponse(tempOut, outputFilename);
tempOut = null;
return response;
@@ -6,12 +6,13 @@ import java.nio.file.Files;
import java.util.Locale;
import org.jetbrains.annotations.NotNull;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
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.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import org.springframework.web.util.HtmlUtils;
import io.github.pixee.security.Filenames;
@@ -51,8 +52,7 @@ public class ConvertEmlToPDF {
+ " with extensive customization options. Features include font settings,"
+ " image constraints, display modes, attachment handling, and HTML debug"
+ " output. Input: EML or MSG file, Output: PDF or HTML file. Type: SISO")
public ResponseEntity<StreamingResponseBody> convertEmlToPdf(
@ModelAttribute EmlToPdfRequest request) {
public ResponseEntity<Resource> convertEmlToPdf(@ModelAttribute EmlToPdfRequest request) {
MultipartFile inputFile = request.getFileInput();
String originalFilename = inputFile.getOriginalFilename();
@@ -159,14 +159,11 @@ public class ConvertEmlToPDF {
}
}
private ResponseEntity<StreamingResponseBody> errorResponse(HttpStatus status, String message) {
private ResponseEntity<Resource> errorResponse(HttpStatus status, String message) {
byte[] body = message.getBytes(StandardCharsets.UTF_8);
StreamingResponseBody streaming =
os -> {
os.write(body);
os.flush();
};
return ResponseEntity.status(status).body(streaming);
return ResponseEntity.status(status)
.contentLength(body.length)
.body(new ByteArrayResource(body));
}
private static @NotNull String buildErrorMessage(Exception e, String originalFilename) {
@@ -2,11 +2,11 @@ package stirling.software.SPDF.controller.api.converters;
import java.nio.file.Files;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute;
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;
@@ -40,7 +40,7 @@ public class ConvertHtmlToPDF {
description =
"This endpoint takes an HTML or ZIP file input and converts it to a PDF format."
+ " Input:HTML Output:PDF Type:SISO")
public ResponseEntity<StreamingResponseBody> HtmlToPdf(@ModelAttribute HTMLToPdfRequest request)
public ResponseEntity<Resource> HtmlToPdf(@ModelAttribute HTMLToPdfRequest request)
throws Exception {
MultipartFile fileInput = request.getFileInput();
@@ -18,11 +18,11 @@ import org.apache.commons.io.FileUtils;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.rendering.ImageType;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import io.swagger.v3.oas.annotations.Operation;
@@ -274,8 +274,8 @@ public class ConvertImgPDFController {
description =
"This endpoint converts a CBZ (ZIP) comic book archive to a PDF file. "
+ "Input:CBZ Output:PDF Type:SISO")
public ResponseEntity<StreamingResponseBody> convertCbzToPdf(
@ModelAttribute ConvertCbzToPdfRequest request) throws IOException {
public ResponseEntity<Resource> convertCbzToPdf(@ModelAttribute ConvertCbzToPdfRequest request)
throws IOException {
MultipartFile file = request.getFileInput();
boolean optimizeForEbook = request.isOptimizeForEbook();
@@ -300,8 +300,8 @@ public class ConvertImgPDFController {
description =
"This endpoint converts a PDF file to a CBZ (ZIP) comic book archive. "
+ "Input:PDF Output:CBZ Type:SISO")
public ResponseEntity<StreamingResponseBody> convertPdfToCbz(
@ModelAttribute ConvertPdfToCbzRequest request) throws IOException {
public ResponseEntity<Resource> convertPdfToCbz(@ModelAttribute ConvertPdfToCbzRequest request)
throws IOException {
MultipartFile file = request.getFileInput();
int dpi = request.getDpi();
@@ -11,11 +11,11 @@ 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.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute;
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;
@@ -48,8 +48,8 @@ public class ConvertMarkdownToPdf {
description =
"This endpoint takes a Markdown file or ZIP (containing Markdown + images) input, converts it to HTML, and then to"
+ " PDF format. Input:MARKDOWN Output:PDF Type:SISO")
public ResponseEntity<StreamingResponseBody> markdownToPdf(
@ModelAttribute GeneralFile generalFile) throws Exception {
public ResponseEntity<Resource> markdownToPdf(@ModelAttribute GeneralFile generalFile)
throws Exception {
MultipartFile fileInput = generalFile.getFileInput();
if (fileInput == null) {
@@ -13,11 +13,11 @@ import java.util.Locale;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute;
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;
@@ -206,8 +206,8 @@ public class ConvertOfficeController {
description =
"This endpoint converts a given file to a PDF using LibreOffice API Input:ANY"
+ " Output:PDF Type:SISO")
public ResponseEntity<StreamingResponseBody> processFileToPDF(
@ModelAttribute GeneralFile generalFile) throws Exception {
public ResponseEntity<Resource> processFileToPDF(@ModelAttribute GeneralFile generalFile)
throws Exception {
MultipartFile inputFile = generalFile.getFileInput();
// unused but can start server instance if startup time is to long
// LibreOfficeListener.getInstance().start();
@@ -223,7 +223,7 @@ public class ConvertOfficeController {
String filename =
GeneralUtils.generateFilename(
inputFile.getOriginalFilename(), "_convertedToPDF.pdf");
ResponseEntity<StreamingResponseBody> response =
ResponseEntity<Resource> response =
WebResponseUtils.pdfFileToWebResponse(tempOut, filename);
tempOut = null;
return response;
@@ -9,11 +9,11 @@ import java.util.ArrayList;
import java.util.List;
import org.apache.commons.io.FilenameUtils;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute;
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;
@@ -87,7 +87,7 @@ public class ConvertPDFToEpubController {
description =
"Convert a PDF file to a high-quality EPUB or AZW3 ebook using Calibre. Input:PDF"
+ " Output:EPUB/AZW3 Type:SISO")
public ResponseEntity<StreamingResponseBody> convertPdfToEpub(
public ResponseEntity<Resource> convertPdfToEpub(
@ModelAttribute ConvertPdfToEpubRequest request) throws Exception {
if (!endpointConfiguration.isGroupEnabled(CALIBRE_GROUP)) {
@@ -12,10 +12,10 @@ import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.util.WorkbookUtil;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import io.swagger.v3.oas.annotations.Operation;
@@ -51,7 +51,7 @@ public class ConvertPDFToExcelController {
description =
"Extracts tabular data from each page of a PDF and writes it into an Excel"
+ " workbook, one sheet per table. Input:PDF Output:XLSX Type:SISO")
public ResponseEntity<StreamingResponseBody> pdfToExcel(@ModelAttribute PDFWithPageNums request)
public ResponseEntity<Resource> pdfToExcel(@ModelAttribute PDFWithPageNums request)
throws Exception {
String baseName =
GeneralUtils.removeExtension(request.getFileInput().getOriginalFilename());
@@ -1,10 +1,10 @@
package stirling.software.SPDF.controller.api.converters;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import io.swagger.v3.oas.annotations.Operation;
@@ -29,7 +29,7 @@ public class ConvertPDFToHtml {
summary = "Convert PDF to HTML",
description =
"This endpoint converts a PDF file to HTML format. Input:PDF Output:HTML Type:SISO")
public ResponseEntity<StreamingResponseBody> processPdfToHTML(@ModelAttribute PDFFile file)
public ResponseEntity<Resource> processPdfToHTML(@ModelAttribute PDFFile file)
throws Exception {
MultipartFile inputFile = file.getFileInput();
PDFToFile pdfToFile = new PDFToFile(tempFileManager, runtimePathConfig);
@@ -6,11 +6,11 @@ import java.nio.file.Files;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.text.PDFTextStripper;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import io.swagger.v3.oas.annotations.Operation;
@@ -44,7 +44,7 @@ public class ConvertPDFToOffice {
description =
"This endpoint converts a given PDF file to a Presentation format. Input:PDF"
+ " Output:PPT Type:SISO")
public ResponseEntity<StreamingResponseBody> processPdfToPresentation(
public ResponseEntity<Resource> processPdfToPresentation(
@ModelAttribute PdfToPresentationRequest request)
throws IOException, InterruptedException {
MultipartFile inputFile = request.getFileInput();
@@ -59,7 +59,7 @@ public class ConvertPDFToOffice {
description =
"This endpoint converts a given PDF file to Text or RTF format. Input:PDF"
+ " Output:TXT Type:SISO")
public ResponseEntity<StreamingResponseBody> processPdfToRTForTXT(
public ResponseEntity<Resource> processPdfToRTForTXT(
@ModelAttribute PdfToTextOrRTFRequest request)
throws IOException, InterruptedException {
MultipartFile inputFile = request.getFileInput();
@@ -89,8 +89,8 @@ public class ConvertPDFToOffice {
description =
"This endpoint converts a given PDF file to a Word document format. Input:PDF"
+ " Output:WORD Type:SISO")
public ResponseEntity<StreamingResponseBody> processPdfToWord(
@ModelAttribute PdfToWordRequest request) throws IOException, InterruptedException {
public ResponseEntity<Resource> processPdfToWord(@ModelAttribute PdfToWordRequest request)
throws IOException, InterruptedException {
MultipartFile inputFile = request.getFileInput();
String outputFormat = request.getOutputFormat();
PDFToFile pdfToFile = new PDFToFile(tempFileManager, runtimePathConfig);
@@ -103,8 +103,7 @@ public class ConvertPDFToOffice {
description =
"This endpoint converts a PDF file to an XML file. Input:PDF Output:XML"
+ " Type:SISO")
public ResponseEntity<StreamingResponseBody> processPdfToXML(@ModelAttribute PDFFile file)
throws Exception {
public ResponseEntity<Resource> processPdfToXML(@ModelAttribute PDFFile file) throws Exception {
MultipartFile inputFile = file.getFileInput();
PDFToFile pdfToFile = new PDFToFile(tempFileManager, runtimePathConfig);
@@ -71,13 +71,13 @@ import org.apache.xmpbox.schema.PDFAIdentificationSchema;
import org.apache.xmpbox.schema.XMPBasicSchema;
import org.apache.xmpbox.xml.DomXmpParser;
import org.apache.xmpbox.xml.XmpSerializer;
import org.springframework.core.io.Resource;
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.multipart.MultipartFile;
import org.springframework.web.server.ResponseStatusException;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import io.github.pixee.security.Filenames;
import io.swagger.v3.oas.annotations.Operation;
@@ -577,7 +577,7 @@ public class ConvertPDFToPDFA {
summary = "Convert a PDF to a PDF/A or PDF/X",
description =
"This endpoint converts a PDF file to a PDF/A or PDF/X file using Ghostscript (preferred) or PDFBox/LibreOffice (fallback). PDF/A is a format designed for long-term archiving, while PDF/X is optimized for print production. Input:PDF Output:PDF Type:SISO")
public ResponseEntity<StreamingResponseBody> pdfToPdfA(@ModelAttribute PdfToPdfARequest request)
public ResponseEntity<Resource> pdfToPdfA(@ModelAttribute PdfToPdfARequest request)
throws Exception {
MultipartFile inputFile = request.getFileInput();
String outputFormat = request.getOutputFormat();
@@ -613,7 +613,7 @@ public class ConvertPDFToPDFA {
return missing;
}
private ResponseEntity<StreamingResponseBody> handlePdfXConversion(
private ResponseEntity<Resource> handlePdfXConversion(
MultipartFile inputFile, String outputFormat) throws Exception {
PdfXProfile profile = PdfXProfile.fromRequest(outputFormat);
@@ -1806,7 +1806,7 @@ public class ConvertPDFToPDFA {
return Files.readAllBytes(outputPdf);
}
private ResponseEntity<StreamingResponseBody> handlePdfAConversion(
private ResponseEntity<Resource> handlePdfAConversion(
MultipartFile inputFile, String outputFormat, boolean strict) throws Exception {
PdfaProfile profile = PdfaProfile.fromRequest(outputFormat);
@@ -1,12 +1,16 @@
package stirling.software.SPDF.controller.api.converters;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Optional;
import java.util.UUID;
import java.util.regex.Pattern;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
@@ -15,7 +19,6 @@ import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
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;
@@ -56,7 +59,7 @@ public class ConvertPdfJsonController {
summary = "Convert PDF to Text Editor Format",
description =
"Extracts PDF text, fonts, and metadata into an editable JSON structure for the text editor tool. Input:PDF Output:JSON Type:SISO")
public ResponseEntity<StreamingResponseBody> convertPdfToJson(
public ResponseEntity<Resource> convertPdfToJson(
@ModelAttribute PDFFile request,
@RequestParam(value = "lightweight", defaultValue = "false") boolean lightweight)
throws Exception {
@@ -65,10 +68,6 @@ public class ConvertPdfJsonController {
throw ExceptionUtils.createNullArgumentException("fileInput");
}
// TODO: Refactor PdfJsonConversionService to write directly to an OutputStream
// instead of returning byte[], avoiding the intermediate heap allocation + temp file write
byte[] jsonBytes = pdfJsonConversionService.convertPdfToJson(inputFile, lightweight);
logJsonResponse("pdf/text-editor", jsonBytes);
String originalName = inputFile.getOriginalFilename();
String baseName =
(originalName != null && !originalName.isBlank())
@@ -78,13 +77,19 @@ public class ConvertPdfJsonController {
: "document";
String docName = baseName + ".json";
TempFile tempOut = tempFileManager.createManagedTempFile(".json");
try (OutputStream os = Files.newOutputStream(tempOut.getPath())) {
pdfJsonConversionService.convertPdfToJson(inputFile, lightweight, os);
} catch (Exception e) {
tempOut.close();
throw e;
}
try {
Files.write(tempOut.getPath(), jsonBytes);
logJsonResponse("pdf/text-editor", tempOut.getPath());
return WebResponseUtils.fileToWebResponse(tempOut, docName, MediaType.APPLICATION_JSON);
} catch (Exception e) {
tempOut.close();
throw e;
}
return WebResponseUtils.fileToWebResponse(tempOut, docName, MediaType.APPLICATION_JSON);
}
@AutoJobPostMapping(consumes = "multipart/form-data", value = "/text-editor/pdf")
@@ -93,14 +98,13 @@ public class ConvertPdfJsonController {
summary = "Convert Text Editor Format to PDF",
description =
"Rebuilds a PDF from the editable JSON structure generated by the text editor tool. Input:JSON Output:PDF Type:SISO")
public ResponseEntity<StreamingResponseBody> convertJsonToPdf(
@ModelAttribute GeneralFile request) throws Exception {
public ResponseEntity<Resource> convertJsonToPdf(@ModelAttribute GeneralFile request)
throws Exception {
MultipartFile jsonFile = request.getFileInput();
if (jsonFile == null) {
throw ExceptionUtils.createNullArgumentException("fileInput");
}
byte[] pdfBytes = pdfJsonConversionService.convertJsonToPdf(jsonFile);
String originalName = jsonFile.getOriginalFilename();
String baseName =
(originalName != null && !originalName.isBlank())
@@ -110,8 +114,8 @@ public class ConvertPdfJsonController {
: "document";
String docName = baseName.endsWith(".pdf") ? baseName : baseName + ".pdf";
TempFile tempOut = tempFileManager.createManagedTempFile(".pdf");
try {
Files.write(tempOut.getPath(), pdfBytes);
try (OutputStream os = Files.newOutputStream(tempOut.getPath())) {
pdfJsonConversionService.convertJsonToPdf(jsonFile, os);
} catch (Exception e) {
tempOut.close();
throw e;
@@ -126,7 +130,7 @@ public class ConvertPdfJsonController {
"Extracts document metadata, fonts, and page dimensions for the text editor tool. Caches the document for"
+ " subsequent page requests. Returns a server-generated jobId scoped to the"
+ " authenticated user. Input:PDF Output:JSON Type:SISO")
public ResponseEntity<StreamingResponseBody> extractPdfMetadata(@ModelAttribute PDFFile request)
public ResponseEntity<Resource> extractPdfMetadata(@ModelAttribute PDFFile request)
throws Exception {
MultipartFile inputFile = request.getFileInput();
if (inputFile == null) {
@@ -139,30 +143,24 @@ public class ConvertPdfJsonController {
log.debug("Extracting metadata for PDF, assigned jobId: {}", scopedJobKey);
byte[] jsonBytes =
pdfJsonConversionService.extractDocumentMetadata(inputFile, scopedJobKey);
logJsonResponse("pdf/text-editor/metadata", jsonBytes);
TempFile tempOut = tempFileManager.createManagedTempFile(".json");
try {
Files.write(tempOut.getPath(), jsonBytes);
try (OutputStream os = Files.newOutputStream(tempOut.getPath())) {
pdfJsonConversionService.extractDocumentMetadata(inputFile, scopedJobKey, os);
} catch (Exception e) {
tempOut.close();
throw e;
}
return ResponseEntity.ok()
.header("X-Job-Id", scopedJobKey)
.contentType(MediaType.APPLICATION_JSON)
.contentLength(java.nio.file.Files.size(tempOut.getPath()))
.body(
os -> {
try (os) {
Files.copy(tempOut.getPath(), os);
os.flush();
} finally {
tempOut.close();
}
});
try {
logJsonResponse("pdf/text-editor/metadata", tempOut.getPath());
return ResponseEntity.ok()
.header("X-Job-Id", scopedJobKey)
.contentType(MediaType.APPLICATION_JSON)
.contentLength(Files.size(tempOut.getPath()))
.body(new WebResponseUtils.ManagedTempFileResource(tempOut));
} catch (IOException | RuntimeException e) {
tempOut.close();
throw e;
}
}
@AutoJobPostMapping(
@@ -175,7 +173,7 @@ public class ConvertPdfJsonController {
"Applies edits for the specified pages of a cached PDF and returns an updated PDF."
+ " Requires the PDF to have been previously cached via the text editor metadata endpoint."
+ " The jobId must be obtained from the metadata extraction endpoint.")
public ResponseEntity<StreamingResponseBody> exportPartialPdf(
public ResponseEntity<Resource> exportPartialPdf(
@PathVariable String jobId,
@RequestBody PdfJsonDocument document,
@RequestParam(value = "filename", required = false) String filename)
@@ -215,22 +213,26 @@ public class ConvertPdfJsonController {
"Retrieves a single page's content from a previously cached PDF document for the text editor tool."
+ " Requires prior call to /pdf/text-editor/metadata. The jobId must belong to the"
+ " authenticated user. Output:JSON")
public ResponseEntity<StreamingResponseBody> extractSinglePage(
public ResponseEntity<Resource> extractSinglePage(
@PathVariable String jobId, @PathVariable int pageNumber) throws Exception {
validateJobAccess(jobId);
byte[] jsonBytes = pdfJsonConversionService.extractSinglePage(jobId, pageNumber);
logJsonResponse("pdf/text-editor/page", jsonBytes);
String docName = "page_" + pageNumber + ".json";
TempFile tempOut = tempFileManager.createManagedTempFile(".json");
try (OutputStream os = Files.newOutputStream(tempOut.getPath())) {
pdfJsonConversionService.extractSinglePage(jobId, pageNumber, os);
} catch (Exception e) {
tempOut.close();
throw e;
}
try {
Files.write(tempOut.getPath(), jsonBytes);
logJsonResponse("pdf/text-editor/page", tempOut.getPath());
return WebResponseUtils.fileToWebResponse(tempOut, docName, MediaType.APPLICATION_JSON);
} catch (Exception e) {
tempOut.close();
throw e;
}
return WebResponseUtils.fileToWebResponse(tempOut, docName, MediaType.APPLICATION_JSON);
}
@GetMapping(value = "/pdf/text-editor/fonts/{jobId}/{pageNumber}")
@@ -240,22 +242,26 @@ public class ConvertPdfJsonController {
"Retrieves the font payloads used by a single page from a previously cached PDF document."
+ " Requires prior call to /pdf/text-editor/metadata. The jobId must belong to the"
+ " authenticated user. Output:JSON")
public ResponseEntity<StreamingResponseBody> extractPageFonts(
public ResponseEntity<Resource> extractPageFonts(
@PathVariable String jobId, @PathVariable int pageNumber) throws Exception {
validateJobAccess(jobId);
byte[] jsonBytes = pdfJsonConversionService.extractPageFonts(jobId, pageNumber);
logJsonResponse("pdf/text-editor/fonts/page", jsonBytes);
String docName = "page_fonts_" + pageNumber + ".json";
TempFile tempOut = tempFileManager.createManagedTempFile(".json");
try (OutputStream os = Files.newOutputStream(tempOut.getPath())) {
pdfJsonConversionService.extractPageFonts(jobId, pageNumber, os);
} catch (Exception e) {
tempOut.close();
throw e;
}
try {
Files.write(tempOut.getPath(), jsonBytes);
logJsonResponse("pdf/text-editor/fonts/page", tempOut.getPath());
return WebResponseUtils.fileToWebResponse(tempOut, docName, MediaType.APPLICATION_JSON);
} catch (Exception e) {
tempOut.close();
throw e;
}
return WebResponseUtils.fileToWebResponse(tempOut, docName, MediaType.APPLICATION_JSON);
}
@AutoJobPostMapping(
@@ -282,13 +288,34 @@ public class ConvertPdfJsonController {
return baseJobId;
}
private void logJsonResponse(String label, byte[] jsonBytes) {
if (jsonBytes == null) {
log.warn("Returning {} JSON response: null bytes", label);
private void logJsonResponse(String label, Path jsonPath) {
if (jsonPath == null) {
log.warn("Returning {} JSON response: null path", label);
return;
}
if (log.isDebugEnabled()) {
boolean debugEnabled = log.isDebugEnabled();
boolean dumpEnabled = isPdfJsonDebugDumpEnabled();
boolean repeatScanEnabled = isPdfJsonRepeatScanEnabled();
// Reading the full file back from disk is only worth it for these diagnostic paths.
// The happy path (no debug flags) returns here immediately no extra IO.
if (!debugEnabled && !dumpEnabled && !repeatScanEnabled) {
return;
}
byte[] jsonBytes;
try {
jsonBytes = Files.readAllBytes(jsonPath);
} catch (IOException ex) {
log.warn(
"Failed to read PDF JSON ({}) for diagnostic logging: {}",
label,
ex.getMessage());
return;
}
if (debugEnabled) {
int length = jsonBytes.length;
boolean endsWithJson =
length > 0 && (jsonBytes[length - 1] == '}' || jsonBytes[length - 1] == ']');
@@ -309,24 +336,23 @@ public class ConvertPdfJsonController {
tail);
}
if (isPdfJsonDebugDumpEnabled()) {
if (dumpEnabled) {
try {
String tmpDir = System.getProperty("java.io.tmpdir");
String customDir = System.getenv("SPDF_PDFJSON_DUMP_DIR");
java.nio.file.Path dumpDir =
Path dumpDir =
customDir != null && !customDir.isBlank()
? java.nio.file.Path.of(customDir)
: java.nio.file.Path.of(tmpDir);
java.nio.file.Path dumpPath =
java.nio.file.Files.createTempFile(dumpDir, "pdfjson_", ".json");
java.nio.file.Files.write(dumpPath, jsonBytes);
? Path.of(customDir)
: Path.of(tmpDir);
Path dumpPath = Files.createTempFile(dumpDir, "pdfjson_", ".json");
Files.copy(jsonPath, dumpPath, java.nio.file.StandardCopyOption.REPLACE_EXISTING);
log.debug("PDF JSON debug dump ({}): {}", label, dumpPath);
} catch (Exception ex) {
log.warn("Failed to write PDF JSON debug dump ({}): {}", label, ex.getMessage());
}
}
if (isPdfJsonRepeatScanEnabled()) {
if (repeatScanEnabled) {
logRepeatedJsonStrings(label, jsonBytes);
}
}
@@ -9,12 +9,13 @@ import java.util.Locale;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
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.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import io.github.pixee.security.Filenames;
import io.swagger.v3.oas.annotations.Operation;
@@ -54,8 +55,7 @@ public class ConvertSvgToPDF {
+ "SVG dimensions (width/height) determine the PDF page size; defaults to A4 if not specified. "
+ "SVG content is sanitized to prevent XSS attacks. "
+ "Input: SVG file(s), Output: PDF file(s) or ZIP. Type: MIMO")
public ResponseEntity<StreamingResponseBody> convertSvgToPdf(
@ModelAttribute SvgToPdfRequest request) {
public ResponseEntity<Resource> convertSvgToPdf(@ModelAttribute SvgToPdfRequest request) {
MultipartFile[] inputFiles = request.getFileInput();
boolean combineIntoSinglePdf = Boolean.TRUE.equals(request.getCombineIntoSinglePdf());
@@ -95,10 +95,7 @@ public class ConvertSvgToPDF {
filenames.add(Filenames.toSimpleFileName(originalFilename));
} catch (IOException e) {
log.error(
"SVG sanitization/reading failed for {}: {}",
originalFilename,
e.getMessage());
log.error("SVG sanitization/reading failed for {}", originalFilename, e);
}
}
@@ -121,17 +118,14 @@ public class ConvertSvgToPDF {
}
}
private ResponseEntity<StreamingResponseBody> errorResponse(HttpStatus status, String message) {
private ResponseEntity<Resource> errorResponse(HttpStatus status, String message) {
byte[] body = message.getBytes(StandardCharsets.UTF_8);
StreamingResponseBody streaming =
os -> {
os.write(body);
os.flush();
};
return ResponseEntity.status(status).body(streaming);
return ResponseEntity.status(status)
.contentLength(body.length)
.body(new ByteArrayResource(body));
}
private ResponseEntity<StreamingResponseBody> handleCombinedConversion(
private ResponseEntity<Resource> handleCombinedConversion(
List<byte[]> sanitizedSvgs, List<String> filenames) {
try {
log.info("Combining {} SVG files into single PDF", sanitizedSvgs.size());
@@ -169,7 +163,7 @@ public class ConvertSvgToPDF {
}
}
private ResponseEntity<StreamingResponseBody> handleSeparateConversion(
private ResponseEntity<Resource> handleSeparateConversion(
List<byte[]> sanitizedSvgs, List<String> filenames) {
List<ConvertedPdf> convertedPdfs = new ArrayList<>();
@@ -10,10 +10,10 @@ import java.util.Locale;
import java.util.Set;
import org.apache.commons.io.FilenameUtils;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import io.swagger.v3.oas.annotations.Operation;
@@ -51,7 +51,7 @@ public class PdfVectorExportController {
description =
"Converts PostScript vector inputs (PS, EPS, EPSF) to PDF using Ghostscript."
+ " Input:PS/EPS Output:PDF Type:SISO")
public ResponseEntity<StreamingResponseBody> convertGhostscriptInputsToPdf(
public ResponseEntity<Resource> convertGhostscriptInputsToPdf(
@Valid @ModelAttribute PdfVectorExportRequest request) throws Exception {
String originalName =
@@ -98,7 +98,7 @@ public class PdfVectorExportController {
description =
"Converts PDF to Ghostscript vector formats (EPS, PS, PCL, or XPS)."
+ " Input:PDF Output:VECTOR Type:SISO")
public ResponseEntity<StreamingResponseBody> convertPdfToVector(
public ResponseEntity<Resource> convertPdfToVector(
@Valid @ModelAttribute PdfVectorExportRequest request) throws Exception {
String originalName =
@@ -5,11 +5,11 @@ 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.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute;
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;
@@ -56,8 +56,8 @@ public class FilterController {
description = "PDF did not pass filter",
content = @Content())
})
public ResponseEntity<StreamingResponseBody> containsText(
@ModelAttribute ContainsTextRequest request) throws IOException, InterruptedException {
public ResponseEntity<Resource> containsText(@ModelAttribute ContainsTextRequest request)
throws IOException, InterruptedException {
MultipartFile inputFile = request.getFileInput();
String text = request.getText();
String pageNumber = request.getPageNumbers();
@@ -89,8 +89,8 @@ public class FilterController {
description = "PDF did not pass filter",
content = @Content())
})
public ResponseEntity<StreamingResponseBody> containsImage(
@ModelAttribute PDFWithPageNums request) throws IOException, InterruptedException {
public ResponseEntity<Resource> containsImage(@ModelAttribute PDFWithPageNums request)
throws IOException, InterruptedException {
MultipartFile inputFile = request.getFileInput();
String pageNumber = request.getPageNumbers();
@@ -10,6 +10,7 @@ import java.util.Map;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
@@ -18,7 +19,6 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import com.opencsv.CSVWriter;
@@ -63,7 +63,7 @@ public class FormFillController {
private final ObjectMapper objectMapper;
private final TempFileManager tempFileManager;
private ResponseEntity<StreamingResponseBody> saveDocument(PDDocument document, String baseName)
private ResponseEntity<Resource> saveDocument(PDDocument document, String baseName)
throws IOException {
return WebResponseUtils.pdfDocToWebResponse(document, baseName + ".pdf", tempFileManager);
}
@@ -262,7 +262,7 @@ public class FormFillController {
summary = "Modify existing form fields",
description =
"Updates existing fields in the provided PDF and returns the updated file")
public ResponseEntity<StreamingResponseBody> modifyFields(
public ResponseEntity<Resource> modifyFields(
@Parameter(
description = "The input PDF file",
required = true,
@@ -293,7 +293,7 @@ public class FormFillController {
@Operation(
summary = "Delete form fields",
description = "Removes the specified fields from the PDF and returns the updated file")
public ResponseEntity<StreamingResponseBody> deleteFields(
public ResponseEntity<Resource> deleteFields(
@Parameter(
description = "The input PDF file",
required = true,
@@ -329,7 +329,7 @@ public class FormFillController {
description =
"Populates the supplied PDF form using values from the provided JSON payload"
+ " and returns the filled PDF")
public ResponseEntity<StreamingResponseBody> fillForm(
public ResponseEntity<Resource> fillForm(
@Parameter(
description = "The input PDF file",
required = true,
@@ -356,7 +356,7 @@ public class FormFillController {
document -> FormUtils.applyFieldValues(document, values, flatten, true));
}
private ResponseEntity<StreamingResponseBody> processSingleFile(
private ResponseEntity<Resource> processSingleFile(
MultipartFile file, String suffix, DocumentProcessor processor) throws IOException {
requirePdf(file);
@@ -6,11 +6,11 @@ import java.util.List;
import java.util.Optional;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute;
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;
@@ -54,8 +54,8 @@ public class AttachmentController {
summary = "Add attachments to PDF",
description =
"This endpoint adds attachments to a PDF. Input:PDF, Output:PDF Type:MISO")
public ResponseEntity<StreamingResponseBody> addAttachments(
@ModelAttribute AddAttachmentRequest request) throws Exception {
public ResponseEntity<Resource> addAttachments(@ModelAttribute AddAttachmentRequest request)
throws Exception {
MultipartFile fileInput = request.getFileInput();
List<MultipartFile> attachments = request.getAttachments();
boolean convertToPdfA3b = request.isConvertToPdfA3b();
@@ -143,7 +143,7 @@ public class AttachmentController {
description =
"This endpoint extracts all embedded attachments from a PDF into a ZIP archive."
+ " Input:PDF Output:ZIP Type:SISO")
public ResponseEntity<StreamingResponseBody> extractAttachments(
public ResponseEntity<Resource> extractAttachments(
@ModelAttribute ExtractAttachmentsRequest request) throws IOException {
try (PDDocument document = pdfDocumentFactory.load(request, true)) {
Optional<byte[]> extracted = pdfAttachmentService.extractAttachments(document);
@@ -195,7 +195,7 @@ public class AttachmentController {
summary = "Rename attachment in PDF",
description =
"This endpoint renames an embedded attachment in a PDF. Input:PDF Output:PDF Type:MISO")
public ResponseEntity<StreamingResponseBody> renameAttachment(
public ResponseEntity<Resource> renameAttachment(
@ModelAttribute RenameAttachmentRequest request) throws Exception {
MultipartFile fileInput = request.getFileInput();
String attachmentName = request.getAttachmentName();
@@ -230,7 +230,7 @@ public class AttachmentController {
summary = "Delete attachment from PDF",
description =
"This endpoint deletes an embedded attachment from a PDF. Input:PDF Output:PDF Type:MISO")
public ResponseEntity<StreamingResponseBody> deleteAttachment(
public ResponseEntity<Resource> deleteAttachment(
@ModelAttribute DeleteAttachmentRequest request) throws Exception {
MultipartFile fileInput = request.getFileInput();
String attachmentName = request.getAttachmentName();
@@ -8,11 +8,11 @@ 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.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute;
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;
@@ -45,8 +45,8 @@ public class AutoRenameController {
description =
"This endpoint accepts a PDF file and attempts to extract its title or header"
+ " based on heuristics. Input:PDF Output:PDF Type:SISO")
public ResponseEntity<StreamingResponseBody> extractHeader(
@ModelAttribute ExtractHeaderRequest request) throws Exception {
public ResponseEntity<Resource> extractHeader(@ModelAttribute ExtractHeaderRequest request)
throws Exception {
MultipartFile file = request.getFileInput();
boolean useFirstTextAsFallback = Boolean.TRUE.equals(request.getUseFirstTextAsFallback());
@@ -18,11 +18,11 @@ import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
import org.apache.pdfbox.rendering.PDFRenderer;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import com.google.zxing.*;
import com.google.zxing.common.GlobalHistogramBinarizer;
@@ -276,8 +276,8 @@ public class AutoSplitPdfController {
+ " splits the document at the QR code boundaries. The output is a zip"
+ " file containing each separate PDF document. Input:PDF Output:ZIP-PDF"
+ " Type:SISO")
public ResponseEntity<StreamingResponseBody> autoSplitPdf(
@ModelAttribute AutoSplitPdfRequest request) throws IOException {
public ResponseEntity<Resource> autoSplitPdf(@ModelAttribute AutoSplitPdfRequest request)
throws IOException {
MultipartFile file = request.getFileInput();
boolean duplexMode = Boolean.TRUE.equals(request.getDuplexMode());
@@ -15,12 +15,12 @@ import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageTree;
import org.apache.pdfbox.rendering.PDFRenderer;
import org.apache.pdfbox.text.PDFTextStripper;
import org.springframework.core.io.Resource;
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.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import io.github.pixee.security.Filenames;
import io.swagger.v3.oas.annotations.Operation;
@@ -88,7 +88,7 @@ public class BlankPageController {
"This endpoint removes blank pages from a given PDF file. Users can specify the"
+ " threshold and white percentage to tune the detection of blank pages."
+ " Input:PDF Output:PDF Type:SISO")
public ResponseEntity<StreamingResponseBody> removeBlankPages(
public ResponseEntity<Resource> removeBlankPages(
@ModelAttribute RemoveBlankPagesRequest request)
throws IOException, InterruptedException {
MultipartFile inputFile = request.getFileInput();
@@ -32,13 +32,13 @@ import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject;
import org.apache.pdfbox.pdmodel.graphics.image.PDImage;
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
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.multipart.MultipartFile;
import org.springframework.web.server.ResponseStatusException;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import io.swagger.v3.oas.annotations.Operation;
@@ -928,8 +928,8 @@ public class CompressController {
description =
"This endpoint accepts a PDF file and optimizes it based on the provided"
+ " parameters. Input:PDF Output:PDF Type:SISO")
public ResponseEntity<StreamingResponseBody> optimizePdf(
@ModelAttribute OptimizePdfRequest request) throws Exception {
public ResponseEntity<Resource> optimizePdf(@ModelAttribute OptimizePdfRequest request)
throws Exception {
MultipartFile inputFile = request.getFileInput();
// Validate input file
@@ -9,11 +9,11 @@ import org.apache.pdfbox.cos.*;
import org.apache.pdfbox.io.IOUtils;
import org.apache.pdfbox.pdfwriter.compress.CompressParameters;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import io.swagger.v3.oas.annotations.Operation;
@@ -42,7 +42,7 @@ public class DecompressPdfController {
@Operation(
summary = "Decompress PDF streams",
description = "Fully decompresses all PDF streams including text content")
public ResponseEntity<StreamingResponseBody> decompressPdf(@ModelAttribute PDFFile request)
public ResponseEntity<Resource> decompressPdf(@ModelAttribute PDFFile request)
throws IOException {
MultipartFile file = request.getFileInput();
@@ -17,11 +17,11 @@ import javax.imageio.ImageIO;
import org.apache.commons.io.FileUtils;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.rendering.PDFRenderer;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import io.swagger.v3.oas.annotations.Operation;
@@ -65,7 +65,7 @@ public class ExtractImageScansController {
+ " parameters. Users can specify angle threshold, tolerance, minimum area,"
+ " minimum contour area, and border size. Input:PDF Output:IMAGE/ZIP"
+ " Type:SIMO")
public ResponseEntity<StreamingResponseBody> extractImageScans(
public ResponseEntity<Resource> extractImageScans(
@ModelAttribute ExtractImageScansRequest request)
throws IOException, InterruptedException {
MultipartFile inputFile = request.getFileInput();
@@ -198,7 +198,7 @@ public class ExtractImageScansController {
}
}
ResponseEntity<StreamingResponseBody> response =
ResponseEntity<Resource> response =
WebResponseUtils.zipFileToWebResponse(finalOutput, outputZipFilename);
finalOutputOwnershipTransferred = true;
return response;
@@ -215,7 +215,7 @@ public class ExtractImageScansController {
out.write(imageBytes);
}
ResponseEntity<StreamingResponseBody> response =
ResponseEntity<Resource> response =
WebResponseUtils.fileToWebResponse(
finalOutput,
GeneralUtils.generateFilename(fileName, ".png"),
@@ -19,11 +19,11 @@ import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import io.swagger.v3.oas.annotations.Operation;
@@ -57,8 +57,8 @@ public class ExtractImagesController {
"This endpoint extracts images from a given PDF file and returns them in a zip"
+ " file. Users can specify the output image format. Input:PDF"
+ " Output:IMAGE/ZIP Type:SIMO")
public ResponseEntity<StreamingResponseBody> extractImages(
@ModelAttribute PDFExtractImagesRequest request) throws IOException {
public ResponseEntity<Resource> extractImages(@ModelAttribute PDFExtractImagesRequest request)
throws IOException {
MultipartFile file = request.getFileInput();
String imageFormat = request.getFormat();
@@ -11,11 +11,11 @@ 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.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute;
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;
@@ -49,7 +49,7 @@ public class FlattenController {
description =
"Flattening just PDF form fields or converting each page to images to make text"
+ " unselectable. Input:PDF, Output:PDF. Type:SISO")
public ResponseEntity<StreamingResponseBody> flatten(@ModelAttribute FlattenRequest request)
public ResponseEntity<Resource> flatten(@ModelAttribute FlattenRequest request)
throws Exception {
MultipartFile file = request.getFileInput();
@@ -8,12 +8,12 @@ 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.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.*;
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;
@@ -64,7 +64,7 @@ public class MetadataController {
"This endpoint allows you to update the metadata of a given PDF file. You can"
+ " add, modify, or delete standard and custom metadata fields. Input:PDF"
+ " Output:PDF Type:SISO")
public ResponseEntity<StreamingResponseBody> metadata(@ModelAttribute MetadataRequest request)
public ResponseEntity<Resource> metadata(@ModelAttribute MetadataRequest request)
throws IOException {
// Extract PDF file from the request object
@@ -21,11 +21,11 @@ import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.rendering.PDFRenderer;
import org.apache.pdfbox.text.PDFTextStripper;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute;
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;
@@ -90,7 +90,7 @@ public class OCRController {
+ " specify languages, sidecar, deskew, clean, cleanFinal, ocrType, ocrRenderType,"
+ " and removeImagesAfter options. Uses OCRmyPDF if available, falls back to"
+ " Tesseract. Input:PDF Output:PDF Type:SI-Conditional")
public ResponseEntity<StreamingResponseBody> processPdfWithOCR(
public ResponseEntity<Resource> processPdfWithOCR(
@ModelAttribute ProcessPdfWithOcrRequest request)
throws IOException, InterruptedException {
MultipartFile inputFile = request.getFileInput();
@@ -194,13 +194,13 @@ public class OCRController {
// The intermediate PDF temp file is no longer needed; only the zip is streamed.
tempOutputFile.close();
pdfOwnershipTransferred = true;
ResponseEntity<StreamingResponseBody> response =
ResponseEntity<Resource> response =
WebResponseUtils.fileToWebResponse(
tempZipFile, outputZipFilename, MediaType.APPLICATION_OCTET_STREAM);
zipOwnershipTransferred = true;
return response;
} else {
ResponseEntity<StreamingResponseBody> response =
ResponseEntity<Resource> response =
WebResponseUtils.pdfFileToWebResponse(tempOutputFile, outputFilename);
pdfOwnershipTransferred = true;
return response;
@@ -6,12 +6,12 @@ import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
import org.springframework.core.io.Resource;
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.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import io.swagger.v3.oas.annotations.Operation;
@@ -45,8 +45,7 @@ public class OverlayImageController {
+ "SVG files are rendered as vector graphics for crisp output at any resolution. "
+ "The image can be overlaid on every page of the PDF if specified. "
+ "Input:PDF/IMAGE/SVG Output:PDF Type:SISO")
public ResponseEntity<StreamingResponseBody> overlayImage(
@ModelAttribute OverlayImageRequest request) {
public ResponseEntity<Resource> overlayImage(@ModelAttribute OverlayImageRequest request) {
MultipartFile pdfFile = request.getFileInput();
MultipartFile imageFile = request.getImageFile();
float x = request.getX();
@@ -11,11 +11,11 @@ import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.font.PDType1Font;
import org.apache.pdfbox.pdmodel.font.Standard14Fonts;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute;
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;
@@ -46,8 +46,8 @@ public class PageNumbersController {
description =
"This operation takes an input PDF file and adds page numbers to it. Input:PDF"
+ " Output:PDF Type:SISO")
public ResponseEntity<StreamingResponseBody> addPageNumbers(
@ModelAttribute AddPageNumbersRequest request) throws IOException {
public ResponseEntity<Resource> addPageNumbers(@ModelAttribute AddPageNumbersRequest request)
throws IOException {
MultipartFile file = request.getFileInput();
String customMargin = request.getCustomMargin();
@@ -12,11 +12,11 @@ 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.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import io.swagger.v3.oas.annotations.Operation;
@@ -47,7 +47,7 @@ public class RemoveImagesController {
description =
"This endpoint removes all embedded images from a PDF file and returns the"
+ " modified document. Input:PDF Output:PDF Type:SISO")
public ResponseEntity<StreamingResponseBody> removeImages(@ModelAttribute PDFFile request)
public ResponseEntity<Resource> removeImages(@ModelAttribute PDFFile request)
throws IOException {
MultipartFile inputFile = request.getFileInput();
@@ -4,11 +4,11 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import io.swagger.v3.oas.annotations.Operation;
@@ -54,7 +54,7 @@ public class RepairController {
"This endpoint repairs a given PDF file by running Ghostscript (primary), qpdf (fallback), or PDFBox (if no external tools available). The PDF is"
+ " first saved to a temporary location, repaired, read back, and then"
+ " returned as a response. Input:PDF Output:PDF Type:SISO")
public ResponseEntity<StreamingResponseBody> repairPdf(@ModelAttribute PDFFile file)
public ResponseEntity<Resource> repairPdf(@ModelAttribute PDFFile file)
throws IOException, InterruptedException {
MultipartFile inputFile = file.getFileInput();
@@ -6,10 +6,10 @@ import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import org.springframework.core.io.InputStreamResource;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import io.swagger.v3.oas.annotations.Operation;
@@ -39,7 +39,7 @@ public class ReplaceAndInvertColorController {
description =
"This endpoint accepts a PDF file and provides options to invert all colors, replace"
+ " text and background colors, or convert to CMYK color space for printing. Input:PDF Output:PDF Type:SISO")
public ResponseEntity<StreamingResponseBody> replaceAndInvertColor(
public ResponseEntity<Resource> replaceAndInvertColor(
@ModelAttribute ReplaceAndInvertColorRequest request) throws IOException {
InputStreamResource resource =
@@ -30,11 +30,11 @@ import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.graphics.image.LosslessFactory;
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
import org.apache.pdfbox.rendering.PDFRenderer;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import io.swagger.v3.oas.annotations.Operation;
@@ -564,7 +564,7 @@ public class ScannerEffectController {
summary = "Apply scanner effect to PDF",
description =
"Applies various effects to simulate a scanned document, including rotation, noise, and edge softening. Input:PDF Output:PDF Type:SISO")
public ResponseEntity<StreamingResponseBody> scannerEffect(
public ResponseEntity<Resource> scannerEffect(
@Valid @ModelAttribute ScannerEffectRequest request) throws IOException {
MultipartFile file = request.getFileInput();
@@ -7,11 +7,11 @@ import java.util.Map;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.common.PDNameTreeNode;
import org.apache.pdfbox.pdmodel.interactive.action.PDActionJavaScript;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute;
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;
@@ -39,8 +39,7 @@ public class ShowJavascript {
@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")
public ResponseEntity<StreamingResponseBody> extractHeader(@ModelAttribute PDFFile file)
throws Exception {
public ResponseEntity<Resource> extractHeader(@ModelAttribute PDFFile file) throws Exception {
MultipartFile inputFile = file.getFileInput();
StringBuilder script = new StringBuilder();
boolean foundScript = false;
@@ -31,13 +31,13 @@ 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.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import io.swagger.v3.oas.annotations.Operation;
@@ -93,7 +93,7 @@ public class StampController {
"This endpoint adds a stamp to a given PDF file. Users can specify the stamp"
+ " type (text or image), rotation, opacity, width spacer, and height"
+ " spacer. Input:PDF Output:PDF Type:SISO")
public ResponseEntity<StreamingResponseBody> addStamp(@ModelAttribute AddStampRequest request)
public ResponseEntity<Resource> addStamp(@ModelAttribute AddStampRequest request)
throws IOException, Exception {
MultipartFile pdfFile = request.getFileInput();
String pdfFileName = pdfFile.getOriginalFilename();
@@ -10,10 +10,10 @@ 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.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import io.github.pixee.security.Filenames;
import io.swagger.v3.oas.annotations.Operation;
@@ -49,7 +49,7 @@ public class UnlockPDFFormsController {
description =
"Removing read-only property from form fields making them fillable"
+ "Input:PDF, Output:PDF. Type:SISO")
public ResponseEntity<StreamingResponseBody> unlockPDFForms(@ModelAttribute PDFFile file) {
public ResponseEntity<Resource> unlockPDFForms(@ModelAttribute PDFFile file) {
try (PDDocument document = pdfDocumentFactory.load(file)) {
PDAcroForm acroForm = document.getDocumentCatalog().getAcroForm();
@@ -13,7 +13,6 @@ import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import io.swagger.v3.oas.annotations.Operation;
@@ -58,8 +57,8 @@ public class PipelineController {
"This endpoint processes multiple PDF files through a configurable pipeline of operations. "
+ "Users provide files and a JSON configuration defining the sequence of operations to perform. "
+ "Input:PDF Output:PDF/ZIP Type:MIMO")
public ResponseEntity<StreamingResponseBody> handleData(
@ModelAttribute HandleDataRequest request) throws DatabindException, JacksonException {
public ResponseEntity<Resource> handleData(@ModelAttribute HandleDataRequest request)
throws DatabindException, JacksonException {
MultipartFile[] files = request.getFileInput();
String jsonString = request.getJson();
if (files == null) {
@@ -55,6 +55,7 @@ import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo;
import org.bouncycastle.pkcs.PKCSException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.WebDataBinder;
@@ -64,7 +65,6 @@ import org.springframework.web.bind.annotation.ModelAttribute;
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.micrometer.common.util.StringUtils;
import io.swagger.v3.oas.annotations.Operation;
@@ -168,8 +168,8 @@ public class CertSignController {
"This endpoint accepts a PDF file, a digital certificate and related"
+ " information to sign the PDF. It then returns the digitally signed PDF"
+ " file. Input:PDF Output:PDF Type:SISO")
public ResponseEntity<StreamingResponseBody> signPDFWithCert(
@ModelAttribute SignPDFWithCertRequest request) throws Exception {
public ResponseEntity<Resource> signPDFWithCert(@ModelAttribute SignPDFWithCertRequest request)
throws Exception {
MultipartFile pdf = request.getFileInput();
String certType = request.getCertType();
MultipartFile privateKeyFile = request.getPrivateKeyFile();
@@ -5,11 +5,11 @@ 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.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import io.swagger.v3.oas.annotations.Operation;
@@ -40,8 +40,8 @@ public class PasswordController {
description =
"This endpoint removes the password from a protected PDF file. Users need to"
+ " provide the existing password. Input:PDF Output:PDF Type:SISO")
public ResponseEntity<StreamingResponseBody> removePassword(
@ModelAttribute PDFPasswordRequest request) throws IOException {
public ResponseEntity<Resource> removePassword(@ModelAttribute PDFPasswordRequest request)
throws IOException {
MultipartFile fileInput = request.getFileInput();
String password = request.getPassword();
@@ -70,8 +70,8 @@ public class PasswordController {
"This endpoint adds password protection to a PDF file. Users can specify a set"
+ " of permissions that should be applied to the file. Input:PDF"
+ " Output:PDF")
public ResponseEntity<StreamingResponseBody> addPassword(
@ModelAttribute AddPasswordRequest request) throws IOException {
public ResponseEntity<Resource> addPassword(@ModelAttribute AddPasswordRequest request)
throws IOException {
MultipartFile fileInput = request.getFileInput();
String ownerPassword = request.getOwnerPassword();
String password = request.getPassword();
@@ -33,13 +33,13 @@ 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.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
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;
@@ -108,8 +108,8 @@ public class RedactController {
"This endpoint redacts content from a PDF file based on manually specified areas. "
+ "Users can specify areas to redact and optionally convert the PDF to an image. "
+ "Input:PDF Output:PDF Type:SISO")
public ResponseEntity<StreamingResponseBody> redactPDF(
@ModelAttribute ManualRedactPdfRequest request) throws IOException {
public ResponseEntity<Resource> redactPDF(@ModelAttribute ManualRedactPdfRequest request)
throws IOException {
MultipartFile file = request.getFileInput();
List<RedactionArea> redactionAreas = request.getRedactions();
@@ -501,8 +501,7 @@ public class RedactController {
"This endpoint automatically redacts text from a PDF file based on specified patterns. "
+ "Users can provide text patterns to redact, with options for regex and whole word matching. "
+ "Input:PDF Output:PDF Type:SISO")
public ResponseEntity<StreamingResponseBody> redactPdf(
@ModelAttribute RedactPdfRequest request) {
public ResponseEntity<Resource> redactPdf(@ModelAttribute RedactPdfRequest request) {
String[] listOfText = request.getListOfText().split("\n");
boolean useRegex = Boolean.TRUE.equals(request.getUseRegex());
boolean wholeWordSearchBool = Boolean.TRUE.equals(request.getWholeWordSearch());
@@ -7,11 +7,11 @@ 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.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import io.swagger.v3.oas.annotations.Operation;
@@ -40,7 +40,7 @@ public class RemoveCertSignController {
description =
"This endpoint accepts a PDF file and returns the PDF file without the digital"
+ " signature. Input:PDF, Output:PDF Type:SISO")
public ResponseEntity<StreamingResponseBody> removeCertSignPDF(@ModelAttribute PDFFile request)
public ResponseEntity<Resource> removeCertSignPDF(@ModelAttribute PDFFile request)
throws Exception {
MultipartFile pdf = request.getFileInput();
@@ -24,11 +24,11 @@ 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.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import io.swagger.v3.oas.annotations.Operation;
@@ -59,8 +59,8 @@ public class SanitizeController {
description =
"This endpoint processes a PDF file and removes specific elements based on the"
+ " provided options. Input:PDF Output:PDF Type:SISO")
public ResponseEntity<StreamingResponseBody> sanitizePDF(
@ModelAttribute SanitizePdfRequest request) throws IOException {
public ResponseEntity<Resource> sanitizePDF(@ModelAttribute SanitizePdfRequest request)
throws IOException {
MultipartFile inputFile = request.getFileInput();
boolean removeJavaScript = Boolean.TRUE.equals(request.getRemoveJavaScript());
boolean removeEmbeddedFiles = Boolean.TRUE.equals(request.getRemoveEmbeddedFiles());
@@ -28,11 +28,11 @@ import org.bouncycastle.tsp.TimeStampRequest;
import org.bouncycastle.tsp.TimeStampRequestGenerator;
import org.bouncycastle.tsp.TimeStampResponse;
import org.bouncycastle.tsp.TimeStampToken;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import io.swagger.v3.oas.annotations.Operation;
@@ -87,8 +87,8 @@ public class TimestampController {
+ " document timestamp into the PDF. Only a SHA-256 hash of the"
+ " document is sent to the TSA — the PDF itself never leaves the"
+ " server. Input:PDF Output:PDF Type:SISO")
public ResponseEntity<StreamingResponseBody> timestampPdf(
@ModelAttribute TimestampPdfRequest request) throws Exception {
public ResponseEntity<Resource> timestampPdf(@ModelAttribute TimestampPdfRequest request)
throws Exception {
MultipartFile inputFile = request.getFileInput();
ApplicationProperties.Security.Timestamp tsConfig =
applicationProperties.getSecurity().getTimestamp();
@@ -24,13 +24,13 @@ 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.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import io.swagger.v3.oas.annotations.Operation;
@@ -76,8 +76,8 @@ public class WatermarkController {
"This endpoint adds a watermark to a given PDF file. Users can specify the"
+ " watermark type (text or image), rotation, opacity, width spacer, and"
+ " height spacer. Input:PDF Output:PDF Type:SISO")
public ResponseEntity<StreamingResponseBody> addWatermark(
@Valid @ModelAttribute AddWatermarkRequest request) throws IOException, Exception {
public ResponseEntity<Resource> addWatermark(@Valid @ModelAttribute AddWatermarkRequest request)
throws IOException, Exception {
MultipartFile pdfFile = request.getFileInput();
String pdfFileName = pdfFile.getOriginalFilename();
if (pdfFileName != null && (pdfFileName.contains("..") || pdfFileName.startsWith("/"))) {
@@ -1,10 +1,10 @@
package stirling.software.SPDF.model.api.converters;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import io.swagger.v3.oas.annotations.Operation;
@@ -29,7 +29,7 @@ public class ConvertPDFToMarkdown {
summary = "Convert PDF to Markdown",
description =
"This endpoint converts a PDF file to Markdown format. Input:PDF Output:Markdown Type:SISO")
public ResponseEntity<StreamingResponseBody> processPdfToMarkdown(@ModelAttribute PDFFile file)
public ResponseEntity<Resource> processPdfToMarkdown(@ModelAttribute PDFFile file)
throws Exception {
MultipartFile inputFile = file.getFileInput();
PDFToFile pdfToFile = new PDFToFile(tempFileManager);
@@ -248,24 +248,28 @@ public class PdfJsonConversionService {
}
}
public byte[] convertPdfToJson(MultipartFile file) throws IOException {
return convertPdfToJson(file, null, false);
public void convertPdfToJson(MultipartFile file, OutputStream out) throws IOException {
convertPdfToJson(file, null, false, out);
}
public byte[] convertPdfToJson(MultipartFile file, boolean lightweight) throws IOException {
return convertPdfToJson(file, null, lightweight);
}
public byte[] convertPdfToJson(
MultipartFile file, Consumer<PdfJsonConversionProgress> progressCallback)
public void convertPdfToJson(MultipartFile file, boolean lightweight, OutputStream out)
throws IOException {
return convertPdfToJson(file, progressCallback, false);
convertPdfToJson(file, null, lightweight, out);
}
public byte[] convertPdfToJson(
public void convertPdfToJson(
MultipartFile file,
Consumer<PdfJsonConversionProgress> progressCallback,
boolean lightweight)
OutputStream out)
throws IOException {
convertPdfToJson(file, progressCallback, false, out);
}
public void convertPdfToJson(
MultipartFile file,
Consumer<PdfJsonConversionProgress> progressCallback,
boolean lightweight,
OutputStream out)
throws IOException {
if (file == null) {
throw ExceptionUtils.createNullArgumentException("fileInput");
@@ -593,7 +597,9 @@ public class PdfJsonConversionService {
pdfJson.getPages().size());
}
byte[] result = objectMapper.writeValueAsBytes(pdfJson);
// Stream JSON directly to the caller's OutputStream to avoid allocating the
// entire document on the heap before writing it out.
objectMapper.writeValue(out, pdfJson);
progress.accept(PdfJsonConversionProgress.complete());
// Clear Type3 cache entries immediately for non-cached conversions
@@ -602,15 +608,13 @@ public class PdfJsonConversionService {
if (!useLazyImages) {
clearType3CacheEntriesForJob(jobId);
}
return result;
}
} finally {
closeQuietly(normalizedFile);
}
}
public byte[] convertJsonToPdf(MultipartFile file) throws IOException {
public void convertJsonToPdf(MultipartFile file, OutputStream out) throws IOException {
if (file == null) {
throw ExceptionUtils.createNullArgumentException("fileInput");
}
@@ -804,15 +808,11 @@ public class PdfJsonConversionService {
log.info("JSON->PDF conversion complete: {} pages", pages.size());
}
try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
document.save(baos);
byte[] result = baos.toByteArray();
// Stream the PDF directly to the caller's OutputStream PDFBox writes incrementally.
document.save(out);
// Clear Type3 cache entries for this conversion
clearType3CacheEntriesForJob(syntheticJobId);
return result;
}
// Clear Type3 cache entries for this conversion
clearType3CacheEntriesForJob(syntheticJobId);
}
}
@@ -6075,7 +6075,8 @@ public class PdfJsonConversionService {
* Extracts document metadata, fonts, and page dimensions without page content. Caches the PDF
* bytes for subsequent page requests.
*/
public byte[] extractDocumentMetadata(MultipartFile file, String jobId) throws IOException {
public void extractDocumentMetadata(MultipartFile file, String jobId, OutputStream out)
throws IOException {
if (file == null) {
throw ExceptionUtils.createNullArgumentException("fileInput");
}
@@ -6170,12 +6171,13 @@ public class PdfJsonConversionService {
progress.accept(
PdfJsonConversionProgress.of(100, "complete", "Metadata extraction complete"));
return objectMapper.writeValueAsBytes(docMetadata);
objectMapper.writeValue(out, docMetadata);
}
}
/** Extracts a single page from cached PDF bytes. Re-loads the PDF for each request. */
public byte[] extractSinglePage(String jobId, int pageNumber) throws IOException {
public void extractSinglePage(String jobId, int pageNumber, OutputStream out)
throws IOException {
CachedPdfDocument cached = getCachedDocument(jobId);
if (cached == null) {
throw new stirling.software.SPDF.exception.CacheUnavailableException(
@@ -6326,11 +6328,12 @@ public class PdfJsonConversionService {
pageModel.getAnnotations().size(),
jobId);
return objectMapper.writeValueAsBytes(pageModel);
objectMapper.writeValue(out, pageModel);
}
}
public byte[] extractPageFonts(String jobId, int pageNumber) throws IOException {
public void extractPageFonts(String jobId, int pageNumber, OutputStream out)
throws IOException {
CachedPdfDocument cached = getCachedDocument(jobId);
if (cached == null) {
throw new stirling.software.SPDF.exception.CacheUnavailableException(
@@ -6347,7 +6350,8 @@ public class PdfJsonConversionService {
Map<PDFont, String> pageMap =
pageFontResources != null ? pageFontResources.get(pageNumber) : null;
if (pageMap == null || pageMap.isEmpty()) {
return objectMapper.writeValueAsBytes(Collections.emptyList());
objectMapper.writeValue(out, Collections.emptyList());
return;
}
Map<String, PdfJsonFont> cachedFonts = cached.getFonts();
@@ -6375,7 +6379,7 @@ public class PdfJsonConversionService {
pageFonts.sort(
Comparator.comparing(
PdfJsonFont::getUid, Comparator.nullsLast(Comparator.naturalOrder())));
return objectMapper.writeValueAsBytes(pageFonts);
objectMapper.writeValue(out, pageFonts);
}
public byte[] exportUpdatedPages(String jobId, PdfJsonDocument updates) throws IOException {
@@ -1,6 +1,7 @@
package stirling.software.SPDF.service.pdfjson;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
@@ -81,14 +82,15 @@ public class PdfLazyLoadingService {
* @param jobId The job ID for caching
* @param fonts Font map (will be populated)
* @param pageFontResources Page font resources map (will be populated)
* @return Serialized metadata JSON
* @param out The output stream to write the serialized metadata JSON to
* @throws IOException If extraction fails
*/
public byte[] extractDocumentMetadata(
public void extractDocumentMetadata(
MultipartFile file,
String jobId,
Map<String, PdfJsonFont> fonts,
Map<Integer, Map<PDFont, String>> pageFontResources)
Map<Integer, Map<PDFont, String>> pageFontResources,
OutputStream out)
throws IOException {
if (file == null) {
throw ExceptionUtils.createNullArgumentException("fileInput");
@@ -160,7 +162,7 @@ public class PdfLazyLoadingService {
progress.accept(
PdfJsonConversionProgress.of(100, "complete", "Metadata extraction complete"));
return objectMapper.writeValueAsBytes(docMetadata);
objectMapper.writeValue(out, docMetadata);
}
}
@@ -174,10 +176,10 @@ public class PdfLazyLoadingService {
* @param filterImageXObjectsFromResources Function to filter image XObjects
* @param extractText Function to extract text elements for the page
* @param extractAnnotations Function to extract annotations for the page
* @return Serialized page JSON
* @param out The output stream to write the serialized page JSON to
* @throws IOException If extraction fails
*/
public byte[] extractSinglePage(
public void extractSinglePage(
String jobId,
int pageNumber,
java.util.function.Function<COSBase, PdfJsonCosValue> serializeCosValue,
@@ -186,7 +188,8 @@ public class PdfLazyLoadingService {
java.util.function.BiFunction<PDDocument, Integer, List<PdfJsonTextElement>>
extractText,
java.util.function.BiFunction<PDDocument, Integer, List<PdfJsonAnnotation>>
extractAnnotations)
extractAnnotations,
OutputStream out)
throws IOException {
CachedPdfDocument cached = documentCache.get(jobId);
if (cached == null) {
@@ -238,7 +241,7 @@ public class PdfLazyLoadingService {
pageModel.getAnnotations().size(),
jobId);
return objectMapper.writeValueAsBytes(pageModel);
objectMapper.writeValue(out, pageModel);
}
}
@@ -20,11 +20,12 @@ import org.junit.jupiter.api.io.TempDir;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
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.servlet.mvc.method.annotation.StreamingResponseBody;
import stirling.software.SPDF.model.api.general.BookletImpositionRequest;
import stirling.software.common.service.CustomPDFDocumentFactory;
@@ -33,14 +34,15 @@ import stirling.software.common.util.TempFileManager;
@ExtendWith(MockitoExtension.class)
class BookletImpositionControllerTest {
private static ResponseEntity<StreamingResponseBody> streamingOk(byte[] bytes) {
return ResponseEntity.ok(out -> out.write(bytes));
private static ResponseEntity<Resource> streamingOk(byte[] bytes) {
return ResponseEntity.ok(new ByteArrayResource(bytes));
}
private static byte[] drainBody(ResponseEntity<StreamingResponseBody> response)
throws java.io.IOException {
private static byte[] drainBody(ResponseEntity<Resource> response) throws java.io.IOException {
java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
response.getBody().writeTo(baos);
try (java.io.InputStream __in = response.getBody().getInputStream()) {
__in.transferTo(baos);
}
return baos.toByteArray();
}
@@ -94,8 +96,7 @@ class BookletImpositionControllerTest {
when(pdfDocumentFactory.load(file)).thenReturn(sourceDoc);
when(pdfDocumentFactory.createNewDocumentBasedOnOldDocument(sourceDoc)).thenReturn(newDoc);
ResponseEntity<StreamingResponseBody> response =
controller.createBookletImposition(request);
ResponseEntity<Resource> response = controller.createBookletImposition(request);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(drainBody(response)).isNotEmpty();
@@ -126,8 +127,7 @@ class BookletImpositionControllerTest {
when(pdfDocumentFactory.load(file)).thenReturn(sourceDoc);
when(pdfDocumentFactory.createNewDocumentBasedOnOldDocument(sourceDoc)).thenReturn(newDoc);
ResponseEntity<StreamingResponseBody> response =
controller.createBookletImposition(request);
ResponseEntity<Resource> response = controller.createBookletImposition(request);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(drainBody(response)).isNotEmpty();
@@ -144,8 +144,7 @@ class BookletImpositionControllerTest {
when(pdfDocumentFactory.load(file)).thenReturn(sourceDoc);
when(pdfDocumentFactory.createNewDocumentBasedOnOldDocument(sourceDoc)).thenReturn(newDoc);
ResponseEntity<StreamingResponseBody> response =
controller.createBookletImposition(request);
ResponseEntity<Resource> response = controller.createBookletImposition(request);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
}
@@ -162,8 +161,7 @@ class BookletImpositionControllerTest {
when(pdfDocumentFactory.load(file)).thenReturn(sourceDoc);
when(pdfDocumentFactory.createNewDocumentBasedOnOldDocument(sourceDoc)).thenReturn(newDoc);
ResponseEntity<StreamingResponseBody> response =
controller.createBookletImposition(request);
ResponseEntity<Resource> response = controller.createBookletImposition(request);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
}
@@ -180,8 +178,7 @@ class BookletImpositionControllerTest {
when(pdfDocumentFactory.load(file)).thenReturn(sourceDoc);
when(pdfDocumentFactory.createNewDocumentBasedOnOldDocument(sourceDoc)).thenReturn(newDoc);
ResponseEntity<StreamingResponseBody> response =
controller.createBookletImposition(request);
ResponseEntity<Resource> response = controller.createBookletImposition(request);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
}
@@ -198,8 +195,7 @@ class BookletImpositionControllerTest {
when(pdfDocumentFactory.load(file)).thenReturn(sourceDoc);
when(pdfDocumentFactory.createNewDocumentBasedOnOldDocument(sourceDoc)).thenReturn(newDoc);
ResponseEntity<StreamingResponseBody> response =
controller.createBookletImposition(request);
ResponseEntity<Resource> response = controller.createBookletImposition(request);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
}
@@ -216,8 +212,7 @@ class BookletImpositionControllerTest {
when(pdfDocumentFactory.load(file)).thenReturn(sourceDoc);
when(pdfDocumentFactory.createNewDocumentBasedOnOldDocument(sourceDoc)).thenReturn(newDoc);
ResponseEntity<StreamingResponseBody> response =
controller.createBookletImposition(request);
ResponseEntity<Resource> response = controller.createBookletImposition(request);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
}
@@ -232,8 +227,7 @@ class BookletImpositionControllerTest {
when(pdfDocumentFactory.load(file)).thenReturn(sourceDoc);
when(pdfDocumentFactory.createNewDocumentBasedOnOldDocument(sourceDoc)).thenReturn(newDoc);
ResponseEntity<StreamingResponseBody> response =
controller.createBookletImposition(request);
ResponseEntity<Resource> response = controller.createBookletImposition(request);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
}
@@ -261,8 +255,7 @@ class BookletImpositionControllerTest {
when(pdfDocumentFactory.load(file)).thenReturn(sourceDoc);
when(pdfDocumentFactory.createNewDocumentBasedOnOldDocument(sourceDoc)).thenReturn(newDoc);
ResponseEntity<StreamingResponseBody> response =
controller.createBookletImposition(request);
ResponseEntity<Resource> response = controller.createBookletImposition(request);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
}
@@ -27,11 +27,12 @@ import org.junit.jupiter.params.provider.ValueSource;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
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.servlet.mvc.method.annotation.StreamingResponseBody;
import stirling.software.SPDF.model.api.general.CropPdfForm;
import stirling.software.common.service.CustomPDFDocumentFactory;
@@ -41,14 +42,15 @@ import stirling.software.common.util.TempFileManager;
@ExtendWith(MockitoExtension.class)
@DisplayName("CropController Tests")
class CropControllerTest {
private static ResponseEntity<StreamingResponseBody> streamingOk(byte[] bytes) {
return ResponseEntity.ok(out -> out.write(bytes));
private static ResponseEntity<Resource> streamingOk(byte[] bytes) {
return ResponseEntity.ok(new ByteArrayResource(bytes));
}
private static byte[] drainBody(ResponseEntity<StreamingResponseBody> response)
throws java.io.IOException {
private static byte[] drainBody(ResponseEntity<Resource> response) throws java.io.IOException {
java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
response.getBody().writeTo(baos);
try (java.io.InputStream __in = response.getBody().getInputStream()) {
__in.transferTo(baos);
}
return baos.toByteArray();
}
@@ -205,7 +207,7 @@ class CropControllerTest {
when(pdfDocumentFactory.createNewDocumentBasedOnOldDocument(mockDocument))
.thenReturn(newDocument);
ResponseEntity<StreamingResponseBody> response = cropController.cropPdf(request);
ResponseEntity<Resource> response = cropController.cropPdf(request);
assertThat(response)
.isNotNull()
@@ -242,7 +244,7 @@ class CropControllerTest {
when(pdfDocumentFactory.createNewDocumentBasedOnOldDocument(mockDocument))
.thenReturn(newDocument);
ResponseEntity<StreamingResponseBody> response = cropController.cropPdf(request);
ResponseEntity<Resource> response = cropController.cropPdf(request);
assertThat(response).isNotNull();
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
@@ -282,7 +284,7 @@ class CropControllerTest {
when(pdfDocumentFactory.createNewDocumentBasedOnOldDocument(sourceDoc))
.thenReturn(newDoc);
ResponseEntity<StreamingResponseBody> response = cropController.cropPdf(request);
ResponseEntity<Resource> response = cropController.cropPdf(request);
assertThat(response).isNotNull();
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
@@ -313,7 +315,7 @@ class CropControllerTest {
when(pdfDocumentFactory.createNewDocumentBasedOnOldDocument(sourceDoc))
.thenReturn(newDoc);
ResponseEntity<StreamingResponseBody> response = cropController.cropPdf(request);
ResponseEntity<Resource> response = cropController.cropPdf(request);
assertThat(response).isNotNull();
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
@@ -676,7 +678,7 @@ class CropControllerTest {
when(pdfDocumentFactory.createNewDocumentBasedOnOldDocument(mockDocument))
.thenReturn(newDocument);
ResponseEntity<StreamingResponseBody> response = cropController.cropPdf(request);
ResponseEntity<Resource> response = cropController.cropPdf(request);
assertThat(response).isNotNull();
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
@@ -704,7 +706,7 @@ class CropControllerTest {
when(pdfDocumentFactory.createNewDocumentBasedOnOldDocument(mockDocument))
.thenReturn(newDocument);
ResponseEntity<StreamingResponseBody> response = cropController.cropPdf(request);
ResponseEntity<Resource> response = cropController.cropPdf(request);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
verify(mockDocument, times(1)).close();
@@ -25,10 +25,10 @@ import org.mockito.ArgumentMatchers;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import stirling.software.SPDF.controller.api.EditTableOfContentsController.BookmarkItem;
import stirling.software.SPDF.model.api.EditTableOfContentsRequest;
@@ -255,7 +255,7 @@ class EditTableOfContentsControllerTest {
.save(any(File.class));
// When
ResponseEntity<StreamingResponseBody> result =
ResponseEntity<Resource> result =
editTableOfContentsController.editTableOfContents(request);
// Then
@@ -319,7 +319,7 @@ class EditTableOfContentsControllerTest {
.save(any(File.class));
// When
ResponseEntity<StreamingResponseBody> result =
ResponseEntity<Resource> result =
editTableOfContentsController.editTableOfContents(request);
// Then
@@ -373,7 +373,7 @@ class EditTableOfContentsControllerTest {
.save(any(File.class));
// When
ResponseEntity<StreamingResponseBody> result =
ResponseEntity<Resource> result =
editTableOfContentsController.editTableOfContents(request);
// Then
@@ -18,11 +18,12 @@ import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
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.servlet.mvc.method.annotation.StreamingResponseBody;
import stirling.software.SPDF.model.api.general.MergeMultiplePagesRequest;
import stirling.software.common.service.CustomPDFDocumentFactory;
@@ -31,14 +32,15 @@ import stirling.software.common.util.TempFileManager;
@ExtendWith(MockitoExtension.class)
class MultiPageLayoutControllerTest {
private static ResponseEntity<StreamingResponseBody> streamingOk(byte[] bytes) {
return ResponseEntity.ok(out -> out.write(bytes));
private static ResponseEntity<Resource> streamingOk(byte[] bytes) {
return ResponseEntity.ok(new ByteArrayResource(bytes));
}
private static byte[] drainBody(ResponseEntity<StreamingResponseBody> response)
throws java.io.IOException {
private static byte[] drainBody(ResponseEntity<Resource> response) throws java.io.IOException {
java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
response.getBody().writeTo(baos);
try (java.io.InputStream __in = response.getBody().getInputStream()) {
__in.transferTo(baos);
}
return baos.toByteArray();
}
@@ -97,7 +99,7 @@ class MultiPageLayoutControllerTest {
req.setAddBorder(Boolean.FALSE);
req.setFileInput(fileWithExt);
ResponseEntity<StreamingResponseBody> resp = controller.mergeMultiplePagesIntoOne(req);
ResponseEntity<Resource> resp = controller.mergeMultiplePagesIntoOne(req);
Assertions.assertEquals(HttpStatus.OK, resp.getStatusCode());
Assertions.assertEquals(MediaType.APPLICATION_PDF, resp.getHeaders().getContentType());
Assertions.assertNotNull(resp.getBody());
@@ -122,7 +124,7 @@ class MultiPageLayoutControllerTest {
req.setAddBorder(Boolean.TRUE);
req.setFileInput(fileWithExt);
ResponseEntity<StreamingResponseBody> resp = controller.mergeMultiplePagesIntoOne(req);
ResponseEntity<Resource> resp = controller.mergeMultiplePagesIntoOne(req);
Assertions.assertEquals(HttpStatus.OK, resp.getStatusCode());
Assertions.assertEquals(MediaType.APPLICATION_PDF, resp.getHeaders().getContentType());
Assertions.assertNotNull(resp.getBody());
@@ -145,7 +147,7 @@ class MultiPageLayoutControllerTest {
req.setAddBorder(Boolean.TRUE);
req.setFileInput(fileNoExt);
ResponseEntity<StreamingResponseBody> resp = controller.mergeMultiplePagesIntoOne(req);
ResponseEntity<Resource> resp = controller.mergeMultiplePagesIntoOne(req);
Assertions.assertEquals(
"name_multi_page_layout.pdf",
resp.getHeaders().getContentDisposition().getFilename());
@@ -25,12 +25,13 @@ import org.junit.jupiter.api.io.TempDir;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
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;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import stirling.software.SPDF.model.api.general.OverlayPdfsRequest;
import stirling.software.common.service.CustomPDFDocumentFactory;
@@ -39,14 +40,15 @@ import stirling.software.common.util.TempFileManager;
@ExtendWith(MockitoExtension.class)
class PdfOverlayControllerTest {
private static ResponseEntity<StreamingResponseBody> streamingOk(byte[] bytes) {
return ResponseEntity.ok(out -> out.write(bytes));
private static ResponseEntity<Resource> streamingOk(byte[] bytes) {
return ResponseEntity.ok(new ByteArrayResource(bytes));
}
private static byte[] drainBody(ResponseEntity<StreamingResponseBody> response)
throws java.io.IOException {
private static byte[] drainBody(ResponseEntity<Resource> response) throws java.io.IOException {
java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
response.getBody().writeTo(baos);
try (java.io.InputStream __in = response.getBody().getInputStream()) {
__in.transferTo(baos);
}
return baos.toByteArray();
}
@@ -107,7 +109,7 @@ class PdfOverlayControllerTest {
when(pdfDocumentFactory.load(any(MultipartFile.class)))
.thenAnswer(inv -> Loader.loadPDF(((MultipartFile) inv.getArgument(0)).getBytes()));
ResponseEntity<StreamingResponseBody> response = controller.overlayPdfs(request);
ResponseEntity<Resource> response = controller.overlayPdfs(request);
assertNotNull(response);
assertEquals(HttpStatus.OK, response.getStatusCode());
@@ -141,7 +143,7 @@ class PdfOverlayControllerTest {
when(pdfDocumentFactory.load(any(MultipartFile.class)))
.thenAnswer(inv -> Loader.loadPDF(((MultipartFile) inv.getArgument(0)).getBytes()));
ResponseEntity<StreamingResponseBody> response = controller.overlayPdfs(request);
ResponseEntity<Resource> response = controller.overlayPdfs(request);
assertNotNull(response);
assertEquals(HttpStatus.OK, response.getStatusCode());
@@ -173,7 +175,7 @@ class PdfOverlayControllerTest {
when(pdfDocumentFactory.load(any(MultipartFile.class)))
.thenAnswer(inv -> Loader.loadPDF(((MultipartFile) inv.getArgument(0)).getBytes()));
ResponseEntity<StreamingResponseBody> response = controller.overlayPdfs(request);
ResponseEntity<Resource> response = controller.overlayPdfs(request);
assertNotNull(response);
assertEquals(HttpStatus.OK, response.getStatusCode());
@@ -204,7 +206,7 @@ class PdfOverlayControllerTest {
when(pdfDocumentFactory.load(any(MultipartFile.class)))
.thenAnswer(inv -> Loader.loadPDF(((MultipartFile) inv.getArgument(0)).getBytes()));
ResponseEntity<StreamingResponseBody> response = controller.overlayPdfs(request);
ResponseEntity<Resource> response = controller.overlayPdfs(request);
assertNotNull(response);
assertEquals(HttpStatus.OK, response.getStatusCode());
@@ -294,7 +296,7 @@ class PdfOverlayControllerTest {
when(pdfDocumentFactory.load(any(MultipartFile.class)))
.thenAnswer(inv -> Loader.loadPDF(((MultipartFile) inv.getArgument(0)).getBytes()));
ResponseEntity<StreamingResponseBody> response = controller.overlayPdfs(request);
ResponseEntity<Resource> response = controller.overlayPdfs(request);
assertNotNull(response);
assertEquals(HttpStatus.OK, response.getStatusCode());
@@ -327,7 +329,7 @@ class PdfOverlayControllerTest {
when(pdfDocumentFactory.load(any(MultipartFile.class)))
.thenAnswer(inv -> Loader.loadPDF(((MultipartFile) inv.getArgument(0)).getBytes()));
ResponseEntity<StreamingResponseBody> response = controller.overlayPdfs(request);
ResponseEntity<Resource> response = controller.overlayPdfs(request);
assertNotNull(response);
assertEquals(HttpStatus.OK, response.getStatusCode());
@@ -16,10 +16,10 @@ import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import stirling.software.SPDF.model.api.PDFWithPageNums;
import stirling.software.SPDF.model.api.general.RearrangePagesRequest;
@@ -67,7 +67,7 @@ class RearrangePagesPDFControllerTest {
when(pdfDocumentFactory.load(file)).thenReturn(mockDoc);
when(mockDoc.getNumberOfPages()).thenReturn(5);
ResponseEntity<StreamingResponseBody> response = controller.deletePages(request);
ResponseEntity<Resource> response = controller.deletePages(request);
assertNotNull(response);
assertEquals(200, response.getStatusCode().value());
@@ -97,7 +97,7 @@ class RearrangePagesPDFControllerTest {
when(pdfDocumentFactory.createNewDocumentBasedOnOldDocument(mockDoc))
.thenReturn(mockNewDoc);
ResponseEntity<StreamingResponseBody> response = controller.rearrangePages(request);
ResponseEntity<Resource> response = controller.rearrangePages(request);
assertNotNull(response);
assertEquals(200, response.getStatusCode().value());
@@ -127,7 +127,7 @@ class RearrangePagesPDFControllerTest {
when(pdfDocumentFactory.createNewDocumentBasedOnOldDocument(mockDoc))
.thenReturn(mockNewDoc);
ResponseEntity<StreamingResponseBody> response = controller.rearrangePages(request);
ResponseEntity<Resource> response = controller.rearrangePages(request);
assertNotNull(response);
verify(mockNewDoc).addPage(page1);
@@ -155,7 +155,7 @@ class RearrangePagesPDFControllerTest {
when(pdfDocumentFactory.createNewDocumentBasedOnOldDocument(mockDoc))
.thenReturn(mockNewDoc);
ResponseEntity<StreamingResponseBody> response = controller.rearrangePages(request);
ResponseEntity<Resource> response = controller.rearrangePages(request);
assertNotNull(response);
verify(mockNewDoc).addPage(page0);
@@ -181,7 +181,7 @@ class RearrangePagesPDFControllerTest {
when(pdfDocumentFactory.createNewDocumentBasedOnOldDocument(mockDoc))
.thenReturn(mockNewDoc);
ResponseEntity<StreamingResponseBody> response = controller.rearrangePages(request);
ResponseEntity<Resource> response = controller.rearrangePages(request);
assertNotNull(response);
assertEquals(200, response.getStatusCode().value());
@@ -208,7 +208,7 @@ class RearrangePagesPDFControllerTest {
when(pdfDocumentFactory.createNewDocumentBasedOnOldDocument(mockDoc))
.thenReturn(mockNewDoc);
ResponseEntity<StreamingResponseBody> response = controller.rearrangePages(request);
ResponseEntity<Resource> response = controller.rearrangePages(request);
assertNotNull(response);
assertEquals(200, response.getStatusCode().value());
@@ -232,7 +232,7 @@ class RearrangePagesPDFControllerTest {
when(pdfDocumentFactory.createNewDocumentBasedOnOldDocument(mockDoc))
.thenReturn(mockNewDoc);
ResponseEntity<StreamingResponseBody> response = controller.rearrangePages(request);
ResponseEntity<Resource> response = controller.rearrangePages(request);
assertNotNull(response);
assertEquals(200, response.getStatusCode().value());
@@ -256,7 +256,7 @@ class RearrangePagesPDFControllerTest {
when(pdfDocumentFactory.createNewDocumentBasedOnOldDocument(mockDoc))
.thenReturn(mockNewDoc);
ResponseEntity<StreamingResponseBody> response = controller.rearrangePages(request);
ResponseEntity<Resource> response = controller.rearrangePages(request);
assertNotNull(response);
assertEquals(200, response.getStatusCode().value());
@@ -284,7 +284,7 @@ class RearrangePagesPDFControllerTest {
when(pdfDocumentFactory.createNewDocumentBasedOnOldDocument(mockDoc))
.thenReturn(mockNewDoc);
ResponseEntity<StreamingResponseBody> response = controller.rearrangePages(request);
ResponseEntity<Resource> response = controller.rearrangePages(request);
assertNotNull(response);
assertEquals(200, response.getStatusCode().value());
@@ -308,7 +308,7 @@ class RearrangePagesPDFControllerTest {
when(pdfDocumentFactory.createNewDocumentBasedOnOldDocument(mockDoc))
.thenReturn(mockNewDoc);
ResponseEntity<StreamingResponseBody> response = controller.rearrangePages(request);
ResponseEntity<Resource> response = controller.rearrangePages(request);
assertNotNull(response);
// 2 pages * 3 duplicates = 6 addPage calls
@@ -333,7 +333,7 @@ class RearrangePagesPDFControllerTest {
when(pdfDocumentFactory.createNewDocumentBasedOnOldDocument(mockDoc))
.thenReturn(mockNewDoc);
ResponseEntity<StreamingResponseBody> response = controller.rearrangePages(request);
ResponseEntity<Resource> response = controller.rearrangePages(request);
assertNotNull(response);
assertEquals(200, response.getStatusCode().value());
@@ -20,10 +20,10 @@ import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import stirling.software.SPDF.model.api.general.RotatePDFRequest;
import stirling.software.common.service.CustomPDFDocumentFactory;
@@ -75,7 +75,7 @@ public class RotationControllerTest {
when(mockPage.getRotation()).thenReturn(0);
// Act
ResponseEntity<StreamingResponseBody> response = rotationController.rotatePDF(request);
ResponseEntity<Resource> response = rotationController.rotatePDF(request);
// Assert
verify(mockPage).setRotation(90);
@@ -20,11 +20,12 @@ import org.junit.jupiter.api.io.TempDir;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import stirling.software.SPDF.model.api.general.ScalePagesRequest;
import stirling.software.common.service.CustomPDFDocumentFactory;
@@ -33,14 +34,15 @@ import stirling.software.common.util.TempFileManager;
@ExtendWith(MockitoExtension.class)
class ScalePagesControllerTest {
private static ResponseEntity<StreamingResponseBody> streamingOk(byte[] bytes) {
return ResponseEntity.ok(out -> out.write(bytes));
private static ResponseEntity<Resource> streamingOk(byte[] bytes) {
return ResponseEntity.ok(new ByteArrayResource(bytes));
}
private static byte[] drainBody(ResponseEntity<StreamingResponseBody> response)
throws java.io.IOException {
private static byte[] drainBody(ResponseEntity<Resource> response) throws java.io.IOException {
java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
response.getBody().writeTo(baos);
try (java.io.InputStream __in = response.getBody().getInputStream()) {
__in.transferTo(baos);
}
return baos.toByteArray();
}
@@ -101,7 +103,7 @@ class ScalePagesControllerTest {
setupFactory();
ResponseEntity<StreamingResponseBody> response = controller.scalePages(request);
ResponseEntity<Resource> response = controller.scalePages(request);
assertNotNull(response);
assertEquals(200, response.getStatusCode().value());
@@ -123,7 +125,7 @@ class ScalePagesControllerTest {
setupFactory();
ResponseEntity<StreamingResponseBody> response = controller.scalePages(request);
ResponseEntity<Resource> response = controller.scalePages(request);
assertNotNull(response);
assertEquals(200, response.getStatusCode().value());
@@ -143,7 +145,7 @@ class ScalePagesControllerTest {
setupFactory();
ResponseEntity<StreamingResponseBody> response = controller.scalePages(request);
ResponseEntity<Resource> response = controller.scalePages(request);
assertNotNull(response);
assertEquals(200, response.getStatusCode().value());
@@ -163,7 +165,7 @@ class ScalePagesControllerTest {
setupFactory();
ResponseEntity<StreamingResponseBody> response = controller.scalePages(request);
ResponseEntity<Resource> response = controller.scalePages(request);
assertNotNull(response);
assertEquals(200, response.getStatusCode().value());
@@ -183,7 +185,7 @@ class ScalePagesControllerTest {
setupFactory();
ResponseEntity<StreamingResponseBody> response = controller.scalePages(request);
ResponseEntity<Resource> response = controller.scalePages(request);
assertNotNull(response);
assertEquals(200, response.getStatusCode().value());
@@ -220,7 +222,7 @@ class ScalePagesControllerTest {
setupFactory();
ResponseEntity<StreamingResponseBody> response = controller.scalePages(request);
ResponseEntity<Resource> response = controller.scalePages(request);
assertNotNull(response);
assertEquals(200, response.getStatusCode().value());
@@ -240,7 +242,7 @@ class ScalePagesControllerTest {
setupFactory();
ResponseEntity<StreamingResponseBody> response = controller.scalePages(request);
ResponseEntity<Resource> response = controller.scalePages(request);
assertNotNull(response);
assertEquals(200, response.getStatusCode().value());
@@ -281,7 +283,7 @@ class ScalePagesControllerTest {
setupFactory();
ResponseEntity<StreamingResponseBody> response = controller.scalePages(request);
ResponseEntity<Resource> response = controller.scalePages(request);
assertNotNull(response);
assertEquals(200, response.getStatusCode().value());
@@ -31,9 +31,10 @@ import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
import org.springframework.http.ResponseEntity;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import stirling.software.SPDF.config.EndpointConfiguration;
import stirling.software.SPDF.model.api.converters.ConvertEbookToPdfRequest;
@@ -48,14 +49,15 @@ import stirling.software.common.util.WebResponseUtils;
@ExtendWith(MockitoExtension.class)
class ConvertEbookToPDFControllerTest {
private static ResponseEntity<StreamingResponseBody> streamingOk(byte[] bytes) {
return ResponseEntity.ok(out -> out.write(bytes));
private static ResponseEntity<Resource> streamingOk(byte[] bytes) {
return ResponseEntity.ok(new ByteArrayResource(bytes));
}
private static byte[] drainBody(ResponseEntity<StreamingResponseBody> response)
throws java.io.IOException {
private static byte[] drainBody(ResponseEntity<Resource> response) throws java.io.IOException {
java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
response.getBody().writeTo(baos);
try (java.io.InputStream __in = response.getBody().getInputStream()) {
__in.transferTo(baos);
}
return baos.toByteArray();
}
@@ -145,14 +147,13 @@ class ConvertEbookToPDFControllerTest {
return execResult;
});
ResponseEntity<StreamingResponseBody> expectedResponse =
streamingOk("result".getBytes());
ResponseEntity<Resource> expectedResponse = streamingOk("result".getBytes());
wr.when(() -> WebResponseUtils.pdfFileToWebResponse(any(TempFile.class), anyString()))
.thenReturn(expectedResponse);
gu.when(() -> GeneralUtils.generateFilename("ebook.epub", "_convertedToPDF.pdf"))
.thenReturn("ebook_convertedToPDF.pdf");
ResponseEntity<StreamingResponseBody> response = controller.convertEbookToPdf(request);
ResponseEntity<Resource> response = controller.convertEbookToPdf(request);
assertSame(expectedResponse, response);
@@ -262,11 +263,11 @@ class ConvertEbookToPDFControllerTest {
gu.when(() -> GeneralUtils.optimizePdfWithGhostscript(Mockito.any(byte[].class)))
.thenReturn(optimizedBytes);
ResponseEntity<StreamingResponseBody> expectedResponse = streamingOk(optimizedBytes);
ResponseEntity<Resource> expectedResponse = streamingOk(optimizedBytes);
wr.when(() -> WebResponseUtils.pdfFileToWebResponse(any(TempFile.class), anyString()))
.thenReturn(expectedResponse);
ResponseEntity<StreamingResponseBody> response = controller.convertEbookToPdf(request);
ResponseEntity<Resource> response = controller.convertEbookToPdf(request);
assertSame(expectedResponse, response);
gu.verify(() -> GeneralUtils.optimizePdfWithGhostscript(Mockito.any(byte[].class)));
@@ -23,11 +23,12 @@ import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
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.servlet.mvc.method.annotation.StreamingResponseBody;
import stirling.software.common.configuration.RuntimePathConfig;
import stirling.software.common.model.api.converters.EmlToPdfRequest;
@@ -40,14 +41,15 @@ import stirling.software.common.util.WebResponseUtils;
@ExtendWith(MockitoExtension.class)
class ConvertEmlToPDFTest {
private static ResponseEntity<StreamingResponseBody> streamingOk(byte[] bytes) {
return ResponseEntity.ok(out -> out.write(bytes));
private static ResponseEntity<Resource> streamingOk(byte[] bytes) {
return ResponseEntity.ok(new ByteArrayResource(bytes));
}
private static byte[] drainBody(ResponseEntity<StreamingResponseBody> response)
throws java.io.IOException {
private static byte[] drainBody(ResponseEntity<Resource> response) throws java.io.IOException {
java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
response.getBody().writeTo(baos);
try (java.io.InputStream __in = response.getBody().getInputStream()) {
__in.transferTo(baos);
}
return baos.toByteArray();
}
@@ -82,7 +84,7 @@ class ConvertEmlToPDFTest {
EmlToPdfRequest request = new EmlToPdfRequest();
request.setFileInput(emptyFile);
ResponseEntity<StreamingResponseBody> response = controller.convertEmlToPdf(request);
ResponseEntity<Resource> response = controller.convertEmlToPdf(request);
assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode());
assertTrue(
@@ -98,7 +100,7 @@ class ConvertEmlToPDFTest {
EmlToPdfRequest request = new EmlToPdfRequest();
request.setFileInput(file);
ResponseEntity<StreamingResponseBody> response = controller.convertEmlToPdf(request);
ResponseEntity<Resource> response = controller.convertEmlToPdf(request);
assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode());
assertTrue(
@@ -113,7 +115,7 @@ class ConvertEmlToPDFTest {
EmlToPdfRequest request = new EmlToPdfRequest();
request.setFileInput(file);
ResponseEntity<StreamingResponseBody> response = controller.convertEmlToPdf(request);
ResponseEntity<Resource> response = controller.convertEmlToPdf(request);
assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode());
}
@@ -126,7 +128,7 @@ class ConvertEmlToPDFTest {
EmlToPdfRequest request = new EmlToPdfRequest();
request.setFileInput(file);
ResponseEntity<StreamingResponseBody> response = controller.convertEmlToPdf(request);
ResponseEntity<Resource> response = controller.convertEmlToPdf(request);
assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode());
assertTrue(
@@ -146,7 +148,7 @@ class ConvertEmlToPDFTest {
when(runtimePathConfig.getWeasyPrintPath()).thenReturn("/usr/bin/weasyprint");
ResponseEntity<StreamingResponseBody> expectedResponse = streamingOk(pdfBytes);
ResponseEntity<Resource> expectedResponse = streamingOk(pdfBytes);
try (MockedStatic<EmlToPdf> emlMock = Mockito.mockStatic(EmlToPdf.class);
MockedStatic<WebResponseUtils> wrMock =
@@ -170,7 +172,7 @@ class ConvertEmlToPDFTest {
any(TempFile.class), anyString()))
.thenReturn(expectedResponse);
ResponseEntity<StreamingResponseBody> response = controller.convertEmlToPdf(request);
ResponseEntity<Resource> response = controller.convertEmlToPdf(request);
assertEquals(HttpStatus.OK, response.getStatusCode());
assertArrayEquals(pdfBytes, drainBody(response));
@@ -188,7 +190,7 @@ class ConvertEmlToPDFTest {
request.setFileInput(file);
request.setDownloadHtml(true);
ResponseEntity<StreamingResponseBody> expectedResponse =
ResponseEntity<Resource> expectedResponse =
streamingOk(htmlContent.getBytes(StandardCharsets.UTF_8));
try (MockedStatic<EmlToPdf> emlMock = Mockito.mockStatic(EmlToPdf.class);
@@ -209,7 +211,7 @@ class ConvertEmlToPDFTest {
any(TempFile.class), anyString(), any(MediaType.class)))
.thenReturn(expectedResponse);
ResponseEntity<StreamingResponseBody> response = controller.convertEmlToPdf(request);
ResponseEntity<Resource> response = controller.convertEmlToPdf(request);
assertEquals(HttpStatus.OK, response.getStatusCode());
}
@@ -235,7 +237,7 @@ class ConvertEmlToPDFTest {
eq(customHtmlSanitizer)))
.thenThrow(new IOException("Parse error"));
ResponseEntity<StreamingResponseBody> response = controller.convertEmlToPdf(request);
ResponseEntity<Resource> response = controller.convertEmlToPdf(request);
assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, response.getStatusCode());
assertTrue(
@@ -263,7 +265,7 @@ class ConvertEmlToPDFTest {
any(), any(), any(), any(), any(), any(), any()))
.thenReturn(null);
ResponseEntity<StreamingResponseBody> response = controller.convertEmlToPdf(request);
ResponseEntity<Resource> response = controller.convertEmlToPdf(request);
assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, response.getStatusCode());
assertTrue(
@@ -287,7 +289,7 @@ class ConvertEmlToPDFTest {
when(runtimePathConfig.getWeasyPrintPath()).thenReturn("/usr/bin/weasyprint");
ResponseEntity<StreamingResponseBody> expectedResponse = streamingOk(pdfBytes);
ResponseEntity<Resource> expectedResponse = streamingOk(pdfBytes);
try (MockedStatic<EmlToPdf> emlMock = Mockito.mockStatic(EmlToPdf.class);
MockedStatic<WebResponseUtils> wrMock =
@@ -305,7 +307,7 @@ class ConvertEmlToPDFTest {
any(TempFile.class), anyString()))
.thenReturn(expectedResponse);
ResponseEntity<StreamingResponseBody> response = controller.convertEmlToPdf(request);
ResponseEntity<Resource> response = controller.convertEmlToPdf(request);
assertEquals(HttpStatus.OK, response.getStatusCode());
}
@@ -330,7 +332,7 @@ class ConvertEmlToPDFTest {
any(), any(), any(), any(), any(), any(), any()))
.thenThrow(new InterruptedException("interrupted"));
ResponseEntity<StreamingResponseBody> response = controller.convertEmlToPdf(request);
ResponseEntity<Resource> response = controller.convertEmlToPdf(request);
assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, response.getStatusCode());
assertTrue(
@@ -20,10 +20,11 @@ import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import stirling.software.common.configuration.RuntimePathConfig;
import stirling.software.common.model.api.converters.HTMLToPdfRequest;
@@ -37,14 +38,15 @@ import stirling.software.common.util.WebResponseUtils;
@ExtendWith(MockitoExtension.class)
class ConvertHtmlToPDFTest {
private static ResponseEntity<StreamingResponseBody> streamingOk(byte[] bytes) {
return ResponseEntity.ok(out -> out.write(bytes));
private static ResponseEntity<Resource> streamingOk(byte[] bytes) {
return ResponseEntity.ok(new ByteArrayResource(bytes));
}
private static byte[] drainBody(ResponseEntity<StreamingResponseBody> response)
throws java.io.IOException {
private static byte[] drainBody(ResponseEntity<Resource> response) throws java.io.IOException {
java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
response.getBody().writeTo(baos);
try (java.io.InputStream __in = response.getBody().getInputStream()) {
__in.transferTo(baos);
}
return baos.toByteArray();
}
@@ -104,7 +106,7 @@ class ConvertHtmlToPDFTest {
when(pdfDocumentFactory.createNewBytesBasedOnOldDocument(pdfBytes))
.thenReturn(processedPdf);
ResponseEntity<StreamingResponseBody> expectedResponse = streamingOk(processedPdf);
ResponseEntity<Resource> expectedResponse = streamingOk(processedPdf);
try (MockedStatic<FileToPdf> ftpMock = Mockito.mockStatic(FileToPdf.class);
MockedStatic<GeneralUtils> guMock = Mockito.mockStatic(GeneralUtils.class);
@@ -131,7 +133,7 @@ class ConvertHtmlToPDFTest {
any(TempFile.class), anyString()))
.thenReturn(expectedResponse);
ResponseEntity<StreamingResponseBody> response = controller.HtmlToPdf(request);
ResponseEntity<Resource> response = controller.HtmlToPdf(request);
assertEquals(HttpStatus.OK, response.getStatusCode());
}
@@ -152,7 +154,7 @@ class ConvertHtmlToPDFTest {
when(pdfDocumentFactory.createNewBytesBasedOnOldDocument(pdfBytes))
.thenReturn(processedPdf);
ResponseEntity<StreamingResponseBody> expectedResponse = streamingOk(processedPdf);
ResponseEntity<Resource> expectedResponse = streamingOk(processedPdf);
try (MockedStatic<FileToPdf> ftpMock = Mockito.mockStatic(FileToPdf.class);
MockedStatic<GeneralUtils> guMock = Mockito.mockStatic(GeneralUtils.class);
@@ -179,7 +181,7 @@ class ConvertHtmlToPDFTest {
any(TempFile.class), anyString()))
.thenReturn(expectedResponse);
ResponseEntity<StreamingResponseBody> response = controller.HtmlToPdf(request);
ResponseEntity<Resource> response = controller.HtmlToPdf(request);
assertEquals(HttpStatus.OK, response.getStatusCode());
}
@@ -12,9 +12,10 @@ import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
import org.springframework.http.ResponseEntity;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import stirling.software.SPDF.config.EndpointConfiguration;
import stirling.software.SPDF.model.api.converters.ConvertToPdfRequest;
@@ -26,14 +27,15 @@ import stirling.software.common.util.WebResponseUtils;
@ExtendWith(MockitoExtension.class)
class ConvertImgPDFControllerTest {
private static ResponseEntity<StreamingResponseBody> streamingOk(byte[] bytes) {
return ResponseEntity.ok(out -> out.write(bytes));
private static ResponseEntity<Resource> streamingOk(byte[] bytes) {
return ResponseEntity.ok(new ByteArrayResource(bytes));
}
private static byte[] drainBody(ResponseEntity<StreamingResponseBody> response)
throws java.io.IOException {
private static byte[] drainBody(ResponseEntity<Resource> response) throws java.io.IOException {
java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
response.getBody().writeTo(baos);
try (java.io.InputStream __in = response.getBody().getInputStream()) {
__in.transferTo(baos);
}
return baos.toByteArray();
}
@@ -22,10 +22,11 @@ import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import stirling.software.common.configuration.RuntimePathConfig;
import stirling.software.common.model.api.GeneralFile;
@@ -39,14 +40,15 @@ import stirling.software.common.util.WebResponseUtils;
@ExtendWith(MockitoExtension.class)
class ConvertMarkdownToPdfTest {
private static ResponseEntity<StreamingResponseBody> streamingOk(byte[] bytes) {
return ResponseEntity.ok(out -> out.write(bytes));
private static ResponseEntity<Resource> streamingOk(byte[] bytes) {
return ResponseEntity.ok(new ByteArrayResource(bytes));
}
private static byte[] drainBody(ResponseEntity<StreamingResponseBody> response)
throws java.io.IOException {
private static byte[] drainBody(ResponseEntity<Resource> response) throws java.io.IOException {
java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
response.getBody().writeTo(baos);
try (java.io.InputStream __in = response.getBody().getInputStream()) {
__in.transferTo(baos);
}
return baos.toByteArray();
}
@@ -106,7 +108,7 @@ class ConvertMarkdownToPdfTest {
when(pdfDocumentFactory.createNewBytesBasedOnOldDocument(any(byte[].class)))
.thenReturn(processedPdf);
ResponseEntity<StreamingResponseBody> expectedResponse = streamingOk(processedPdf);
ResponseEntity<Resource> expectedResponse = streamingOk(processedPdf);
try (MockedStatic<FileToPdf> ftpMock = Mockito.mockStatic(FileToPdf.class);
MockedStatic<GeneralUtils> guMock = Mockito.mockStatic(GeneralUtils.class);
@@ -133,7 +135,7 @@ class ConvertMarkdownToPdfTest {
any(TempFile.class), anyString()))
.thenReturn(expectedResponse);
ResponseEntity<StreamingResponseBody> response = controller.markdownToPdf(generalFile);
ResponseEntity<Resource> response = controller.markdownToPdf(generalFile);
assertEquals(HttpStatus.OK, response.getStatusCode());
}
@@ -30,10 +30,11 @@ import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import stirling.software.SPDF.config.EndpointConfiguration;
import stirling.software.SPDF.model.api.converters.ConvertPdfToEpubRequest;
@@ -48,14 +49,15 @@ import stirling.software.common.util.TempFileManager;
@ExtendWith(MockitoExtension.class)
class ConvertPDFToEpubControllerTest {
private static ResponseEntity<StreamingResponseBody> streamingOk(byte[] bytes) {
return ResponseEntity.ok(out -> out.write(bytes));
private static ResponseEntity<Resource> streamingOk(byte[] bytes) {
return ResponseEntity.ok(new ByteArrayResource(bytes));
}
private static byte[] drainBody(ResponseEntity<StreamingResponseBody> response)
throws java.io.IOException {
private static byte[] drainBody(ResponseEntity<Resource> response) throws java.io.IOException {
java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
response.getBody().writeTo(baos);
try (java.io.InputStream __in = response.getBody().getInputStream()) {
__in.transferTo(baos);
}
return baos.toByteArray();
}
@@ -142,7 +144,7 @@ class ConvertPDFToEpubControllerTest {
gu.when(() -> GeneralUtils.generateFilename("novel.pdf", "_convertedToEPUB.epub"))
.thenReturn("novel_convertedToEPUB.epub");
ResponseEntity<StreamingResponseBody> response = controller.convertPdfToEpub(request);
ResponseEntity<Resource> response = controller.convertPdfToEpub(request);
List<String> command = commandCaptor.getValue();
assertEquals(13, command.size());
@@ -234,7 +236,7 @@ class ConvertPDFToEpubControllerTest {
gu.when(() -> GeneralUtils.generateFilename("story.pdf", "_convertedToEPUB.epub"))
.thenReturn("story_convertedToEPUB.epub");
ResponseEntity<StreamingResponseBody> response = controller.convertPdfToEpub(request);
ResponseEntity<Resource> response = controller.convertPdfToEpub(request);
List<String> command = commandCaptor.getValue();
assertTrue(command.stream().noneMatch(arg -> "--chapter".equals(arg)));
@@ -319,7 +321,7 @@ class ConvertPDFToEpubControllerTest {
gu.when(() -> GeneralUtils.generateFilename("book.pdf", "_convertedToAZW3.azw3"))
.thenReturn("book_convertedToAZW3.azw3");
ResponseEntity<StreamingResponseBody> response = controller.convertPdfToEpub(request);
ResponseEntity<Resource> response = controller.convertPdfToEpub(request);
List<String> command = commandCaptor.getValue();
assertEquals("ebook-convert", command.get(0));
@@ -20,10 +20,10 @@ import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import stirling.software.SPDF.model.api.PDFWithPageNums;
import stirling.software.common.service.CustomPDFDocumentFactory;
@@ -81,7 +81,7 @@ class ConvertPDFToExcelControllerTest {
Mockito.eq(true)))
.thenReturn(List.of(1));
ResponseEntity<StreamingResponseBody> response = controller.pdfToExcel(request);
ResponseEntity<Resource> response = controller.pdfToExcel(request);
// tabula may or may not find tables in an empty page
assertNotNull(response);
@@ -21,10 +21,11 @@ import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import stirling.software.SPDF.model.api.converters.PdfToPresentationRequest;
import stirling.software.SPDF.model.api.converters.PdfToTextOrRTFRequest;
@@ -40,14 +41,15 @@ import stirling.software.common.util.WebResponseUtils;
@ExtendWith(MockitoExtension.class)
class ConvertPDFToOfficeTest {
private static ResponseEntity<StreamingResponseBody> streamingOk(byte[] bytes) {
return ResponseEntity.ok(out -> out.write(bytes));
private static ResponseEntity<Resource> streamingOk(byte[] bytes) {
return ResponseEntity.ok(new ByteArrayResource(bytes));
}
private static byte[] drainBody(ResponseEntity<StreamingResponseBody> response)
throws java.io.IOException {
private static byte[] drainBody(ResponseEntity<Resource> response) throws java.io.IOException {
java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
response.getBody().writeTo(baos);
try (java.io.InputStream __in = response.getBody().getInputStream()) {
__in.transferTo(baos);
}
return baos.toByteArray();
}
@@ -85,8 +87,7 @@ class ConvertPDFToOfficeTest {
request.setFileInput(pdfFile);
request.setOutputFormat("pptx");
ResponseEntity<StreamingResponseBody> expectedResponse =
streamingOk("pptx-content".getBytes());
ResponseEntity<Resource> expectedResponse = streamingOk("pptx-content".getBytes());
try (MockedStatic<PDFToFile> mock =
Mockito.mockStatic(PDFToFile.class, Mockito.CALLS_REAL_METHODS)) {
@@ -115,8 +116,7 @@ class ConvertPDFToOfficeTest {
realDoc.addPage(new org.apache.pdfbox.pdmodel.PDPage());
when(pdfDocumentFactory.load(pdfFile)).thenReturn(realDoc);
ResponseEntity<StreamingResponseBody> expectedResponse =
streamingOk("text content".getBytes());
ResponseEntity<Resource> expectedResponse = streamingOk("text content".getBytes());
try (MockedStatic<GeneralUtils> guMock = Mockito.mockStatic(GeneralUtils.class);
MockedStatic<WebResponseUtils> wrMock =
@@ -131,8 +131,7 @@ class ConvertPDFToOfficeTest {
any(TempFile.class), anyString(), any(MediaType.class)))
.thenReturn(expectedResponse);
ResponseEntity<StreamingResponseBody> response =
controller.processPdfToRTForTXT(request);
ResponseEntity<Resource> response = controller.processPdfToRTForTXT(request);
assertSame(expectedResponse, response);
}
@@ -4,16 +4,18 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Files;
import org.junit.jupiter.api.BeforeEach;
@@ -22,11 +24,11 @@ import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.core.io.Resource;
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.servlet.mvc.method.annotation.StreamingResponseBody;
import stirling.software.SPDF.service.PdfJsonConversionService;
import stirling.software.common.model.api.GeneralFile;
@@ -58,10 +60,11 @@ class ConvertPdfJsonControllerTest {
});
}
private static byte[] drainBody(ResponseEntity<StreamingResponseBody> response)
throws IOException {
private static byte[] drainBody(ResponseEntity<Resource> response) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
response.getBody().writeTo(baos);
try (java.io.InputStream in = response.getBody().getInputStream()) {
in.transferTo(baos);
}
return baos.toByteArray();
}
@@ -82,10 +85,17 @@ class ConvertPdfJsonControllerTest {
PDFFile request = new PDFFile();
request.setFileInput(pdfFile);
when(pdfJsonConversionService.convertPdfToJson(pdfFile, false)).thenReturn(jsonBytes);
// Service writes directly to the OutputStream passed by the controller
doAnswer(
inv -> {
OutputStream os = inv.getArgument(2, OutputStream.class);
os.write(jsonBytes);
return null;
})
.when(pdfJsonConversionService)
.convertPdfToJson(eq(pdfFile), eq(false), any(OutputStream.class));
ResponseEntity<StreamingResponseBody> response =
controller.convertPdfToJson(request, false);
ResponseEntity<Resource> response = controller.convertPdfToJson(request, false);
assertEquals(HttpStatus.OK, response.getStatusCode());
assertNotNull(response.getBody());
@@ -100,12 +110,20 @@ class ConvertPdfJsonControllerTest {
PDFFile request = new PDFFile();
request.setFileInput(pdfFile);
when(pdfJsonConversionService.convertPdfToJson(pdfFile, true)).thenReturn(jsonBytes);
doAnswer(
inv -> {
OutputStream os = inv.getArgument(2, OutputStream.class);
os.write(jsonBytes);
return null;
})
.when(pdfJsonConversionService)
.convertPdfToJson(eq(pdfFile), eq(true), any(OutputStream.class));
ResponseEntity<StreamingResponseBody> response = controller.convertPdfToJson(request, true);
ResponseEntity<Resource> response = controller.convertPdfToJson(request, true);
assertEquals(HttpStatus.OK, response.getStatusCode());
verify(pdfJsonConversionService).convertPdfToJson(pdfFile, true);
verify(pdfJsonConversionService)
.convertPdfToJson(eq(pdfFile), eq(true), any(OutputStream.class));
}
@Test
@@ -125,9 +143,16 @@ class ConvertPdfJsonControllerTest {
GeneralFile request = new GeneralFile();
request.setFileInput(jsonFile);
when(pdfJsonConversionService.convertJsonToPdf(jsonFile)).thenReturn(pdfBytes);
doAnswer(
inv -> {
OutputStream os = inv.getArgument(1, OutputStream.class);
os.write(pdfBytes);
return null;
})
.when(pdfJsonConversionService)
.convertJsonToPdf(eq(jsonFile), any(OutputStream.class));
ResponseEntity<StreamingResponseBody> response = controller.convertJsonToPdf(request);
ResponseEntity<Resource> response = controller.convertJsonToPdf(request);
assertEquals(HttpStatus.OK, response.getStatusCode());
assertNotNull(response.getBody());
@@ -150,10 +175,16 @@ class ConvertPdfJsonControllerTest {
PDFFile request = new PDFFile();
request.setFileInput(pdfFile);
when(pdfJsonConversionService.extractDocumentMetadata(eq(pdfFile), any(String.class)))
.thenReturn(jsonBytes);
doAnswer(
inv -> {
OutputStream os = inv.getArgument(2, OutputStream.class);
os.write(jsonBytes);
return null;
})
.when(pdfJsonConversionService)
.extractDocumentMetadata(eq(pdfFile), any(String.class), any(OutputStream.class));
ResponseEntity<StreamingResponseBody> response = controller.extractPdfMetadata(request);
ResponseEntity<Resource> response = controller.extractPdfMetadata(request);
assertEquals(HttpStatus.OK, response.getStatusCode());
assertEquals(MediaType.APPLICATION_JSON, response.getHeaders().getContentType());
@@ -175,9 +206,16 @@ class ConvertPdfJsonControllerTest {
byte[] jsonBytes = "{\"content\":[]}".getBytes();
String jobId = "test-job-id";
when(pdfJsonConversionService.extractSinglePage(jobId, 1)).thenReturn(jsonBytes);
doAnswer(
inv -> {
OutputStream os = inv.getArgument(2, OutputStream.class);
os.write(jsonBytes);
return null;
})
.when(pdfJsonConversionService)
.extractSinglePage(eq(jobId), anyInt(), any(OutputStream.class));
ResponseEntity<StreamingResponseBody> response = controller.extractSinglePage(jobId, 1);
ResponseEntity<Resource> response = controller.extractSinglePage(jobId, 1);
assertEquals(HttpStatus.OK, response.getStatusCode());
assertNotNull(response.getBody());
@@ -188,9 +226,16 @@ class ConvertPdfJsonControllerTest {
byte[] jsonBytes = "{\"fonts\":[]}".getBytes();
String jobId = "test-job-id";
when(pdfJsonConversionService.extractPageFonts(jobId, 1)).thenReturn(jsonBytes);
doAnswer(
inv -> {
OutputStream os = inv.getArgument(2, OutputStream.class);
os.write(jsonBytes);
return null;
})
.when(pdfJsonConversionService)
.extractPageFonts(eq(jobId), anyInt(), any(OutputStream.class));
ResponseEntity<StreamingResponseBody> response = controller.extractPageFonts(jobId, 1);
ResponseEntity<Resource> response = controller.extractPageFonts(jobId, 1);
assertEquals(HttpStatus.OK, response.getStatusCode());
assertNotNull(response.getBody());
@@ -21,10 +21,11 @@ import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import stirling.software.SPDF.model.api.converters.SvgToPdfRequest;
import stirling.software.SPDF.utils.SvgToPdf;
@@ -37,14 +38,15 @@ import stirling.software.common.util.WebResponseUtils;
@ExtendWith(MockitoExtension.class)
class ConvertSvgToPDFTest {
private static ResponseEntity<StreamingResponseBody> streamingOk(byte[] bytes) {
return ResponseEntity.ok(out -> out.write(bytes));
private static ResponseEntity<Resource> streamingOk(byte[] bytes) {
return ResponseEntity.ok(new ByteArrayResource(bytes));
}
private static byte[] drainBody(ResponseEntity<StreamingResponseBody> response)
throws java.io.IOException {
private static byte[] drainBody(ResponseEntity<Resource> response) throws java.io.IOException {
java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
response.getBody().writeTo(baos);
try (java.io.InputStream __in = response.getBody().getInputStream()) {
__in.transferTo(baos);
}
return baos.toByteArray();
}
@@ -75,7 +77,7 @@ class ConvertSvgToPDFTest {
SvgToPdfRequest request = new SvgToPdfRequest();
request.setFileInput(null);
ResponseEntity<StreamingResponseBody> response = controller.convertSvgToPdf(request);
ResponseEntity<Resource> response = controller.convertSvgToPdf(request);
assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode());
assertTrue(
@@ -88,7 +90,7 @@ class ConvertSvgToPDFTest {
SvgToPdfRequest request = new SvgToPdfRequest();
request.setFileInput(new MockMultipartFile[0]);
ResponseEntity<StreamingResponseBody> response = controller.convertSvgToPdf(request);
ResponseEntity<Resource> response = controller.convertSvgToPdf(request);
assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode());
}
@@ -102,7 +104,7 @@ class ConvertSvgToPDFTest {
request.setFileInput(new MockMultipartFile[] {txtFile});
request.setCombineIntoSinglePdf(false);
ResponseEntity<StreamingResponseBody> response = controller.convertSvgToPdf(request);
ResponseEntity<Resource> response = controller.convertSvgToPdf(request);
assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode());
assertTrue(
@@ -118,7 +120,7 @@ class ConvertSvgToPDFTest {
request.setFileInput(new MockMultipartFile[] {emptyFile});
request.setCombineIntoSinglePdf(false);
ResponseEntity<StreamingResponseBody> response = controller.convertSvgToPdf(request);
ResponseEntity<Resource> response = controller.convertSvgToPdf(request);
assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode());
}
@@ -141,7 +143,7 @@ class ConvertSvgToPDFTest {
when(pdfDocumentFactory.createNewBytesBasedOnOldDocument(pdfBytes))
.thenReturn(processedPdf);
ResponseEntity<StreamingResponseBody> expectedResponse = streamingOk(processedPdf);
ResponseEntity<Resource> expectedResponse = streamingOk(processedPdf);
try (MockedStatic<SvgToPdf> svgMock = Mockito.mockStatic(SvgToPdf.class);
MockedStatic<GeneralUtils> guMock = Mockito.mockStatic(GeneralUtils.class);
@@ -159,7 +161,7 @@ class ConvertSvgToPDFTest {
any(TempFile.class), anyString()))
.thenReturn(expectedResponse);
ResponseEntity<StreamingResponseBody> response = controller.convertSvgToPdf(request);
ResponseEntity<Resource> response = controller.convertSvgToPdf(request);
assertEquals(HttpStatus.OK, response.getStatusCode());
}
@@ -188,7 +190,7 @@ class ConvertSvgToPDFTest {
when(pdfDocumentFactory.createNewBytesBasedOnOldDocument(combinedPdf))
.thenReturn(processedPdf);
ResponseEntity<StreamingResponseBody> expectedResponse = streamingOk(processedPdf);
ResponseEntity<Resource> expectedResponse = streamingOk(processedPdf);
try (MockedStatic<SvgToPdf> svgMock = Mockito.mockStatic(SvgToPdf.class);
MockedStatic<GeneralUtils> guMock = Mockito.mockStatic(GeneralUtils.class);
@@ -206,7 +208,7 @@ class ConvertSvgToPDFTest {
any(TempFile.class), anyString()))
.thenReturn(expectedResponse);
ResponseEntity<StreamingResponseBody> response = controller.convertSvgToPdf(request);
ResponseEntity<Resource> response = controller.convertSvgToPdf(request);
assertEquals(HttpStatus.OK, response.getStatusCode());
}
@@ -221,7 +223,7 @@ class ConvertSvgToPDFTest {
request.setFileInput(new MockMultipartFile[] {nullNameFile});
request.setCombineIntoSinglePdf(false);
ResponseEntity<StreamingResponseBody> response = controller.convertSvgToPdf(request);
ResponseEntity<Resource> response = controller.convertSvgToPdf(request);
assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode());
}
@@ -238,7 +240,7 @@ class ConvertSvgToPDFTest {
when(svgSanitizer.sanitize(svgContent)).thenThrow(new IOException("sanitization error"));
ResponseEntity<StreamingResponseBody> response = controller.convertSvgToPdf(request);
ResponseEntity<Resource> response = controller.convertSvgToPdf(request);
assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode());
}
@@ -30,12 +30,13 @@ import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import stirling.software.SPDF.model.api.converters.UrlToPdfRequest;
import stirling.software.common.configuration.RuntimePathConfig;
@@ -49,14 +50,15 @@ import stirling.software.common.util.TempFile;
import stirling.software.common.util.TempFileManager;
public class ConvertWebsiteToPdfTest {
private static ResponseEntity<StreamingResponseBody> streamingOk(byte[] bytes) {
return ResponseEntity.ok(out -> out.write(bytes));
private static ResponseEntity<Resource> streamingOk(byte[] bytes) {
return ResponseEntity.ok(new ByteArrayResource(bytes));
}
private static byte[] drainBody(ResponseEntity<StreamingResponseBody> response)
throws java.io.IOException {
private static byte[] drainBody(ResponseEntity<Resource> response) throws java.io.IOException {
java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
response.getBody().writeTo(baos);
try (java.io.InputStream __in = response.getBody().getInputStream()) {
__in.transferTo(baos);
}
return baos.toByteArray();
}
@@ -24,10 +24,10 @@ import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import stirling.software.SPDF.config.EndpointConfiguration;
import stirling.software.SPDF.model.api.converters.PdfVectorExportRequest;
@@ -122,8 +122,7 @@ class PdfVectorExportControllerTest {
PdfVectorExportRequest request = new PdfVectorExportRequest();
request.setFileInput(file);
ResponseEntity<StreamingResponseBody> response =
controller.convertGhostscriptInputsToPdf(request);
ResponseEntity<Resource> response = controller.convertGhostscriptInputsToPdf(request);
assertThat(response.getStatusCode()).isEqualTo(org.springframework.http.HttpStatus.OK);
assertThat(response.getHeaders().getContentType()).isEqualTo(MediaType.APPLICATION_PDF);
@@ -140,13 +139,14 @@ class PdfVectorExportControllerTest {
PdfVectorExportRequest request = new PdfVectorExportRequest();
request.setFileInput(file);
ResponseEntity<StreamingResponseBody> response =
controller.convertGhostscriptInputsToPdf(request);
ResponseEntity<Resource> response = controller.convertGhostscriptInputsToPdf(request);
assertThat(response.getStatusCode()).isEqualTo(org.springframework.http.HttpStatus.OK);
assertThat(response.getHeaders().getContentType()).isEqualTo(MediaType.APPLICATION_PDF);
java.io.ByteArrayOutputStream baosVerify = new java.io.ByteArrayOutputStream();
response.getBody().writeTo(baosVerify);
try (java.io.InputStream __in = response.getBody().getInputStream()) {
__in.transferTo(baosVerify);
}
assertThat(baosVerify.toByteArray()).contains(content);
}
@@ -13,11 +13,12 @@ import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
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.servlet.mvc.method.annotation.StreamingResponseBody;
import stirling.software.SPDF.model.api.PDFComparisonAndCount;
import stirling.software.SPDF.model.api.PDFWithPageNums;
@@ -32,14 +33,15 @@ import stirling.software.common.util.WebResponseUtils;
@ExtendWith(MockitoExtension.class)
class FilterControllerTest {
private static ResponseEntity<StreamingResponseBody> streamingOk(byte[] bytes) {
return ResponseEntity.ok(out -> out.write(bytes));
private static ResponseEntity<Resource> streamingOk(byte[] bytes) {
return ResponseEntity.ok(new ByteArrayResource(bytes));
}
private static byte[] drainBody(ResponseEntity<StreamingResponseBody> response)
throws java.io.IOException {
private static byte[] drainBody(ResponseEntity<Resource> response) throws java.io.IOException {
java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
response.getBody().writeTo(baos);
try (java.io.InputStream __in = response.getBody().getInputStream()) {
__in.transferTo(baos);
}
return baos.toByteArray();
}
@@ -72,7 +74,7 @@ class FilterControllerTest {
PDDocument mockDoc = mock(PDDocument.class);
when(pdfDocumentFactory.load(mockFile)).thenReturn(mockDoc);
ResponseEntity<StreamingResponseBody> expectedResponse = streamingOk(new byte[] {1, 2, 3});
ResponseEntity<Resource> expectedResponse = streamingOk(new byte[] {1, 2, 3});
try (MockedStatic<PdfUtils> pdfUtilsMock = mockStatic(PdfUtils.class);
MockedStatic<WebResponseUtils> webMock = mockStatic(WebResponseUtils.class)) {
@@ -84,7 +86,7 @@ class FilterControllerTest {
mockDoc, "test.pdf", tempFileManager))
.thenReturn(expectedResponse);
ResponseEntity<StreamingResponseBody> result = filterController.containsText(request);
ResponseEntity<Resource> result = filterController.containsText(request);
assertEquals(HttpStatus.OK, result.getStatusCode());
assertArrayEquals(new byte[] {1, 2, 3}, drainBody(result));
@@ -104,7 +106,7 @@ class FilterControllerTest {
try (MockedStatic<PdfUtils> pdfUtilsMock = mockStatic(PdfUtils.class)) {
pdfUtilsMock.when(() -> PdfUtils.hasText(mockDoc, "all", "missing")).thenReturn(false);
ResponseEntity<StreamingResponseBody> result = filterController.containsText(request);
ResponseEntity<Resource> result = filterController.containsText(request);
assertEquals(HttpStatus.NO_CONTENT, result.getStatusCode());
assertNull(result.getBody());
@@ -122,7 +124,7 @@ class FilterControllerTest {
PDDocument mockDoc = mock(PDDocument.class);
when(pdfDocumentFactory.load(mockFile)).thenReturn(mockDoc);
ResponseEntity<StreamingResponseBody> expectedResponse = streamingOk(new byte[] {4, 5, 6});
ResponseEntity<Resource> expectedResponse = streamingOk(new byte[] {4, 5, 6});
try (MockedStatic<PdfUtils> pdfUtilsMock = mockStatic(PdfUtils.class);
MockedStatic<WebResponseUtils> webMock = mockStatic(WebResponseUtils.class)) {
@@ -134,7 +136,7 @@ class FilterControllerTest {
mockDoc, "test.pdf", tempFileManager))
.thenReturn(expectedResponse);
ResponseEntity<StreamingResponseBody> result = filterController.containsImage(request);
ResponseEntity<Resource> result = filterController.containsImage(request);
assertEquals(HttpStatus.OK, result.getStatusCode());
assertArrayEquals(new byte[] {4, 5, 6}, drainBody(result));
@@ -153,7 +155,7 @@ class FilterControllerTest {
try (MockedStatic<PdfUtils> pdfUtilsMock = mockStatic(PdfUtils.class)) {
pdfUtilsMock.when(() -> PdfUtils.hasImages(mockDoc, "1")).thenReturn(false);
ResponseEntity<StreamingResponseBody> result = filterController.containsImage(request);
ResponseEntity<Resource> result = filterController.containsImage(request);
assertEquals(HttpStatus.NO_CONTENT, result.getStatusCode());
}
@@ -22,10 +22,11 @@ import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import stirling.software.common.service.CustomPDFDocumentFactory;
import stirling.software.common.util.TempFile;
@@ -37,14 +38,15 @@ import tools.jackson.databind.json.JsonMapper;
@ExtendWith(MockitoExtension.class)
@DisplayName("FormFillController Tests")
class FormFillControllerTest {
private static ResponseEntity<StreamingResponseBody> streamingOk(byte[] bytes) {
return ResponseEntity.ok(out -> out.write(bytes));
private static ResponseEntity<Resource> streamingOk(byte[] bytes) {
return ResponseEntity.ok(new ByteArrayResource(bytes));
}
private static byte[] drainBody(ResponseEntity<StreamingResponseBody> response)
throws java.io.IOException {
private static byte[] drainBody(ResponseEntity<Resource> response) throws java.io.IOException {
java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
response.getBody().writeTo(baos);
try (java.io.InputStream __in = response.getBody().getInputStream()) {
__in.transferTo(baos);
}
return baos.toByteArray();
}
@@ -232,8 +234,7 @@ class FormFillControllerTest {
when(pdfDocumentFactory.load(eq(file))).thenReturn(doc);
byte[] payload = "{\"field1\":\"value1\"}".getBytes();
ResponseEntity<StreamingResponseBody> response =
controller.fillForm(file, payload, false);
ResponseEntity<Resource> response = controller.fillForm(file, payload, false);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(response.getBody()).isNotNull();
@@ -246,7 +247,7 @@ class FormFillControllerTest {
PDDocument doc = createMinimalPdf();
when(pdfDocumentFactory.load(eq(file))).thenReturn(doc);
ResponseEntity<StreamingResponseBody> response = controller.fillForm(file, null, false);
ResponseEntity<Resource> response = controller.fillForm(file, null, false);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
}
@@ -287,7 +288,7 @@ class FormFillControllerTest {
when(pdfDocumentFactory.load(eq(file))).thenReturn(doc);
byte[] payload = "[\"field1\"]".getBytes();
ResponseEntity<StreamingResponseBody> response = controller.deleteFields(file, payload);
ResponseEntity<Resource> response = controller.deleteFields(file, payload);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
}
@@ -323,8 +324,7 @@ class FormFillControllerTest {
String json =
"[{\"targetName\":\"f1\",\"name\":null,\"label\":null,\"type\":null,"
+ "\"required\":null,\"multiSelect\":null,\"options\":null,\"defaultValue\":\"newVal\",\"tooltip\":null}]";
ResponseEntity<StreamingResponseBody> response =
controller.modifyFields(file, json.getBytes());
ResponseEntity<Resource> response = controller.modifyFields(file, json.getBytes());
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
}
@@ -17,12 +17,13 @@ import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
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;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import stirling.software.SPDF.model.api.misc.AddAttachmentRequest;
import stirling.software.SPDF.service.AttachmentServiceInterface;
@@ -33,14 +34,15 @@ import stirling.software.common.util.WebResponseUtils;
@ExtendWith(MockitoExtension.class)
class AttachmentControllerTest {
private static ResponseEntity<StreamingResponseBody> streamingOk(byte[] bytes) {
return ResponseEntity.ok(out -> out.write(bytes));
private static ResponseEntity<Resource> streamingOk(byte[] bytes) {
return ResponseEntity.ok(new ByteArrayResource(bytes));
}
private static byte[] drainBody(ResponseEntity<StreamingResponseBody> response)
throws java.io.IOException {
private static byte[] drainBody(ResponseEntity<Resource> response) throws java.io.IOException {
java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
response.getBody().writeTo(baos);
try (java.io.InputStream __in = response.getBody().getInputStream()) {
__in.transferTo(baos);
}
return baos.toByteArray();
}
@@ -100,8 +102,7 @@ class AttachmentControllerTest {
List<MultipartFile> attachments = List.of(attachment1, attachment2);
request.setAttachments(attachments);
request.setFileInput(pdfFile);
ResponseEntity<StreamingResponseBody> expectedResponse =
streamingOk("modified PDF content".getBytes());
ResponseEntity<Resource> expectedResponse = streamingOk("modified PDF content".getBytes());
when(pdfDocumentFactory.load(request, false)).thenReturn(mockDocument);
when(pdfAttachmentService.addAttachment(mockDocument, attachments))
@@ -118,8 +119,7 @@ class AttachmentControllerTest {
any(TempFileManager.class)))
.thenReturn(expectedResponse);
ResponseEntity<StreamingResponseBody> response =
attachmentController.addAttachments(request);
ResponseEntity<Resource> response = attachmentController.addAttachments(request);
assertNotNull(response);
assertEquals(HttpStatus.OK, response.getStatusCode());
@@ -134,8 +134,7 @@ class AttachmentControllerTest {
List<MultipartFile> attachments = List.of(attachment1);
request.setAttachments(attachments);
request.setFileInput(pdfFile);
ResponseEntity<StreamingResponseBody> expectedResponse =
streamingOk("modified PDF content".getBytes());
ResponseEntity<Resource> expectedResponse = streamingOk("modified PDF content".getBytes());
when(pdfDocumentFactory.load(request, false)).thenReturn(mockDocument);
when(pdfAttachmentService.addAttachment(mockDocument, attachments))
@@ -152,8 +151,7 @@ class AttachmentControllerTest {
any(TempFileManager.class)))
.thenReturn(expectedResponse);
ResponseEntity<StreamingResponseBody> response =
attachmentController.addAttachments(request);
ResponseEntity<Resource> response = attachmentController.addAttachments(request);
assertNotNull(response);
assertEquals(HttpStatus.OK, response.getStatusCode());
@@ -23,11 +23,12 @@ import org.junit.jupiter.api.io.TempDir;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
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.servlet.mvc.method.annotation.StreamingResponseBody;
import stirling.software.SPDF.model.api.misc.ExtractHeaderRequest;
import stirling.software.common.service.CustomPDFDocumentFactory;
@@ -36,14 +37,15 @@ import stirling.software.common.util.TempFileManager;
@ExtendWith(MockitoExtension.class)
class AutoRenameControllerTest {
private static ResponseEntity<StreamingResponseBody> streamingOk(byte[] bytes) {
return ResponseEntity.ok(out -> out.write(bytes));
private static ResponseEntity<Resource> streamingOk(byte[] bytes) {
return ResponseEntity.ok(new ByteArrayResource(bytes));
}
private static byte[] drainBody(ResponseEntity<StreamingResponseBody> response)
throws java.io.IOException {
private static byte[] drainBody(ResponseEntity<Resource> response) throws java.io.IOException {
java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
response.getBody().writeTo(baos);
try (java.io.InputStream __in = response.getBody().getInputStream()) {
__in.transferTo(baos);
}
return baos.toByteArray();
}
@@ -101,7 +103,7 @@ class AutoRenameControllerTest {
PDDocument doc = Loader.loadPDF(file.getBytes());
when(pdfDocumentFactory.load(file)).thenReturn(doc);
ResponseEntity<StreamingResponseBody> response = controller.extractHeader(request);
ResponseEntity<Resource> response = controller.extractHeader(request);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(drainBody(response)).isNotEmpty();
@@ -127,7 +129,7 @@ class AutoRenameControllerTest {
PDDocument doc = Loader.loadPDF(file.getBytes());
when(pdfDocumentFactory.load(file)).thenReturn(doc);
ResponseEntity<StreamingResponseBody> response = controller.extractHeader(request);
ResponseEntity<Resource> response = controller.extractHeader(request);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
}
@@ -140,7 +142,7 @@ class AutoRenameControllerTest {
PDDocument doc = Loader.loadPDF(file.getBytes());
when(pdfDocumentFactory.load(file)).thenReturn(doc);
ResponseEntity<StreamingResponseBody> response = controller.extractHeader(request);
ResponseEntity<Resource> response = controller.extractHeader(request);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
}
@@ -188,7 +190,7 @@ class AutoRenameControllerTest {
PDDocument doc = Loader.loadPDF(file.getBytes());
when(pdfDocumentFactory.load(file)).thenReturn(doc);
ResponseEntity<StreamingResponseBody> response = controller.extractHeader(request);
ResponseEntity<Resource> response = controller.extractHeader(request);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
// The largest font text should be used as title (URL-encoded in Content-Disposition)
@@ -206,7 +208,7 @@ class AutoRenameControllerTest {
PDDocument doc = Loader.loadPDF(file.getBytes());
when(pdfDocumentFactory.load(file)).thenReturn(doc);
ResponseEntity<StreamingResponseBody> response = controller.extractHeader(request);
ResponseEntity<Resource> response = controller.extractHeader(request);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
// Should fallback to original filename since header is too long
@@ -222,7 +224,7 @@ class AutoRenameControllerTest {
PDDocument doc = Loader.loadPDF(file.getBytes());
when(pdfDocumentFactory.load(file)).thenReturn(doc);
ResponseEntity<StreamingResponseBody> response = controller.extractHeader(request);
ResponseEntity<Resource> response = controller.extractHeader(request);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
// Special characters should be sanitized
@@ -248,7 +250,7 @@ class AutoRenameControllerTest {
PDDocument doc = Loader.loadPDF(file.getBytes());
when(pdfDocumentFactory.load(file)).thenReturn(doc);
ResponseEntity<StreamingResponseBody> response = controller.extractHeader(request);
ResponseEntity<Resource> response = controller.extractHeader(request);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
}
@@ -23,11 +23,12 @@ import org.junit.jupiter.api.io.TempDir;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
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.servlet.mvc.method.annotation.StreamingResponseBody;
import stirling.software.common.model.api.PDFFile;
import stirling.software.common.service.CustomPDFDocumentFactory;
@@ -36,14 +37,15 @@ import stirling.software.common.util.TempFileManager;
@ExtendWith(MockitoExtension.class)
class DecompressPdfControllerTest {
private static ResponseEntity<StreamingResponseBody> streamingOk(byte[] bytes) {
return ResponseEntity.ok(out -> out.write(bytes));
private static ResponseEntity<Resource> streamingOk(byte[] bytes) {
return ResponseEntity.ok(new ByteArrayResource(bytes));
}
private static byte[] drainBody(ResponseEntity<StreamingResponseBody> response)
throws java.io.IOException {
private static byte[] drainBody(ResponseEntity<Resource> response) throws java.io.IOException {
java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
response.getBody().writeTo(baos);
try (java.io.InputStream __in = response.getBody().getInputStream()) {
__in.transferTo(baos);
}
return baos.toByteArray();
}
@@ -97,7 +99,7 @@ class DecompressPdfControllerTest {
PDDocument doc = Loader.loadPDF(file.getBytes());
when(pdfDocumentFactory.load(file)).thenReturn(doc);
ResponseEntity<StreamingResponseBody> response = controller.decompressPdf(request);
ResponseEntity<Resource> response = controller.decompressPdf(request);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(drainBody(response)).isNotEmpty();
@@ -116,7 +118,7 @@ class DecompressPdfControllerTest {
PDDocument doc = Loader.loadPDF(file.getBytes());
when(pdfDocumentFactory.load(file)).thenReturn(doc);
ResponseEntity<StreamingResponseBody> response = controller.decompressPdf(request);
ResponseEntity<Resource> response = controller.decompressPdf(request);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(drainBody(response)).isNotEmpty();
@@ -147,7 +149,7 @@ class DecompressPdfControllerTest {
PDDocument doc = Loader.loadPDF(file.getBytes());
when(pdfDocumentFactory.load(file)).thenReturn(doc);
ResponseEntity<StreamingResponseBody> response = controller.decompressPdf(request);
ResponseEntity<Resource> response = controller.decompressPdf(request);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
String contentDisposition = response.getHeaders().getFirst("Content-Disposition");
@@ -183,7 +185,7 @@ class DecompressPdfControllerTest {
PDDocument doc = Loader.loadPDF(file.getBytes());
when(pdfDocumentFactory.load(file)).thenReturn(doc);
ResponseEntity<StreamingResponseBody> response = controller.decompressPdf(request);
ResponseEntity<Resource> response = controller.decompressPdf(request);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
try (PDDocument result = Loader.loadPDF(drainBody(response))) {
@@ -200,7 +202,7 @@ class DecompressPdfControllerTest {
PDDocument doc = Loader.loadPDF(file.getBytes());
when(pdfDocumentFactory.load(file)).thenReturn(doc);
ResponseEntity<StreamingResponseBody> response = controller.decompressPdf(request);
ResponseEntity<Resource> response = controller.decompressPdf(request);
assertThat(response.getBody()).isNotNull();
// Decompressed PDF should generally be larger or equal to compressed
@@ -216,7 +218,7 @@ class DecompressPdfControllerTest {
PDDocument doc = Loader.loadPDF(file.getBytes());
when(pdfDocumentFactory.load(file)).thenReturn(doc);
ResponseEntity<StreamingResponseBody> response = controller.decompressPdf(request);
ResponseEntity<Resource> response = controller.decompressPdf(request);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
}
@@ -25,11 +25,12 @@ import org.junit.jupiter.api.io.TempDir;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
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.servlet.mvc.method.annotation.StreamingResponseBody;
import stirling.software.SPDF.model.api.misc.FlattenRequest;
import stirling.software.common.service.CustomPDFDocumentFactory;
@@ -38,14 +39,15 @@ import stirling.software.common.util.TempFileManager;
@ExtendWith(MockitoExtension.class)
class FlattenControllerTest {
private static ResponseEntity<StreamingResponseBody> streamingOk(byte[] bytes) {
return ResponseEntity.ok(out -> out.write(bytes));
private static ResponseEntity<Resource> streamingOk(byte[] bytes) {
return ResponseEntity.ok(new ByteArrayResource(bytes));
}
private static byte[] drainBody(ResponseEntity<StreamingResponseBody> response)
throws java.io.IOException {
private static byte[] drainBody(ResponseEntity<Resource> response) throws java.io.IOException {
java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
response.getBody().writeTo(baos);
try (java.io.InputStream __in = response.getBody().getInputStream()) {
__in.transferTo(baos);
}
return baos.toByteArray();
}
@@ -98,7 +100,7 @@ class FlattenControllerTest {
PDDocument doc = Loader.loadPDF(file.getBytes());
when(pdfDocumentFactory.load(file)).thenReturn(doc);
ResponseEntity<StreamingResponseBody> response = controller.flatten(request);
ResponseEntity<Resource> response = controller.flatten(request);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(drainBody(response)).isNotEmpty();
@@ -118,7 +120,7 @@ class FlattenControllerTest {
when(doc.getDocumentCatalog()).thenReturn(catalog);
when(catalog.getAcroForm()).thenReturn(null);
ResponseEntity<StreamingResponseBody> response = controller.flatten(request);
ResponseEntity<Resource> response = controller.flatten(request);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
verify(doc).close();
@@ -138,7 +140,7 @@ class FlattenControllerTest {
when(doc.getDocumentCatalog()).thenReturn(catalog);
when(catalog.getAcroForm()).thenReturn(form);
ResponseEntity<StreamingResponseBody> response = controller.flatten(request);
ResponseEntity<Resource> response = controller.flatten(request);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
verify(form).flatten();
@@ -170,7 +172,7 @@ class FlattenControllerTest {
when(pdfDocumentFactory.load(file)).thenReturn(doc);
when(pdfDocumentFactory.createNewDocumentBasedOnOldDocument(doc)).thenReturn(newDoc);
ResponseEntity<StreamingResponseBody> response = controller.flatten(request);
ResponseEntity<Resource> response = controller.flatten(request);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(drainBody(response)).isNotEmpty();
@@ -188,7 +190,7 @@ class FlattenControllerTest {
when(pdfDocumentFactory.load(file)).thenReturn(doc);
when(pdfDocumentFactory.createNewDocumentBasedOnOldDocument(doc)).thenReturn(newDoc);
ResponseEntity<StreamingResponseBody> response = controller.flatten(request);
ResponseEntity<Resource> response = controller.flatten(request);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
}
@@ -206,7 +208,7 @@ class FlattenControllerTest {
when(pdfDocumentFactory.load(file)).thenReturn(doc);
when(pdfDocumentFactory.createNewDocumentBasedOnOldDocument(doc)).thenReturn(newDoc);
ResponseEntity<StreamingResponseBody> response = controller.flatten(request);
ResponseEntity<Resource> response = controller.flatten(request);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
}
@@ -224,7 +226,7 @@ class FlattenControllerTest {
when(pdfDocumentFactory.load(file)).thenReturn(doc);
when(pdfDocumentFactory.createNewDocumentBasedOnOldDocument(doc)).thenReturn(newDoc);
ResponseEntity<StreamingResponseBody> response = controller.flatten(request);
ResponseEntity<Resource> response = controller.flatten(request);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
}
@@ -23,11 +23,12 @@ import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
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.servlet.mvc.method.annotation.StreamingResponseBody;
import stirling.software.SPDF.model.api.misc.OverlayImageRequest;
import stirling.software.common.service.CustomPDFDocumentFactory;
@@ -37,14 +38,15 @@ import stirling.software.common.util.WebResponseUtils;
@ExtendWith(MockitoExtension.class)
class OverlayImageControllerTest {
private static ResponseEntity<StreamingResponseBody> streamingOk(byte[] bytes) {
return ResponseEntity.ok(out -> out.write(bytes));
private static ResponseEntity<Resource> streamingOk(byte[] bytes) {
return ResponseEntity.ok(new ByteArrayResource(bytes));
}
private static byte[] drainBody(ResponseEntity<StreamingResponseBody> response)
throws java.io.IOException {
private static byte[] drainBody(ResponseEntity<Resource> response) throws java.io.IOException {
java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
response.getBody().writeTo(baos);
try (java.io.InputStream __in = response.getBody().getInputStream()) {
__in.transferTo(baos);
}
return baos.toByteArray();
}
@@ -108,8 +110,7 @@ class OverlayImageControllerTest {
try (MockedStatic<WebResponseUtils> mockedWebResponse =
mockStatic(WebResponseUtils.class)) {
ResponseEntity<StreamingResponseBody> expectedResponse =
streamingOk("result".getBytes());
ResponseEntity<Resource> expectedResponse = streamingOk("result".getBytes());
mockedWebResponse
.when(
() ->
@@ -117,7 +118,7 @@ class OverlayImageControllerTest {
any(TempFile.class), anyString()))
.thenReturn(expectedResponse);
ResponseEntity<StreamingResponseBody> response = controller.overlayImage(request);
ResponseEntity<Resource> response = controller.overlayImage(request);
assertNotNull(response);
assertEquals(HttpStatus.OK, response.getStatusCode());
@@ -136,7 +137,7 @@ class OverlayImageControllerTest {
when(pdfDocumentFactory.load(any(byte[].class))).thenThrow(new IOException("bad PDF"));
ResponseEntity<StreamingResponseBody> response = controller.overlayImage(request);
ResponseEntity<Resource> response = controller.overlayImage(request);
assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode());
}
@@ -157,8 +158,7 @@ class OverlayImageControllerTest {
try (MockedStatic<WebResponseUtils> mockedWebResponse =
mockStatic(WebResponseUtils.class)) {
ResponseEntity<StreamingResponseBody> expectedResponse =
streamingOk("result".getBytes());
ResponseEntity<Resource> expectedResponse = streamingOk("result".getBytes());
mockedWebResponse
.when(
() ->
@@ -166,7 +166,7 @@ class OverlayImageControllerTest {
any(TempFile.class), anyString()))
.thenReturn(expectedResponse);
ResponseEntity<StreamingResponseBody> response = controller.overlayImage(request);
ResponseEntity<Resource> response = controller.overlayImage(request);
assertNotNull(response);
assertEquals(HttpStatus.OK, response.getStatusCode());
@@ -189,8 +189,7 @@ class OverlayImageControllerTest {
try (MockedStatic<WebResponseUtils> mockedWebResponse =
mockStatic(WebResponseUtils.class)) {
ResponseEntity<StreamingResponseBody> expectedResponse =
streamingOk("result".getBytes());
ResponseEntity<Resource> expectedResponse = streamingOk("result".getBytes());
mockedWebResponse
.when(
() ->
@@ -198,7 +197,7 @@ class OverlayImageControllerTest {
any(TempFile.class), anyString()))
.thenReturn(expectedResponse);
ResponseEntity<StreamingResponseBody> response = controller.overlayImage(request);
ResponseEntity<Resource> response = controller.overlayImage(request);
assertNotNull(response);
assertEquals(HttpStatus.OK, response.getStatusCode());
@@ -221,8 +220,7 @@ class OverlayImageControllerTest {
try (MockedStatic<WebResponseUtils> mockedWebResponse =
mockStatic(WebResponseUtils.class)) {
ResponseEntity<StreamingResponseBody> expectedResponse =
streamingOk("result".getBytes());
ResponseEntity<Resource> expectedResponse = streamingOk("result".getBytes());
mockedWebResponse
.when(
() ->
@@ -231,7 +229,7 @@ class OverlayImageControllerTest {
.thenReturn(expectedResponse);
// Should not throw - coordinates are passed to contentStream.drawImage
ResponseEntity<StreamingResponseBody> response = controller.overlayImage(request);
ResponseEntity<Resource> response = controller.overlayImage(request);
assertNotNull(response);
assertEquals(HttpStatus.OK, response.getStatusCode());
@@ -17,12 +17,13 @@ import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.InputStreamResource;
import org.springframework.core.io.Resource;
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.servlet.mvc.method.annotation.StreamingResponseBody;
import stirling.software.SPDF.model.api.misc.ReplaceAndInvertColorRequest;
import stirling.software.SPDF.service.misc.ReplaceAndInvertColorService;
@@ -34,14 +35,15 @@ import stirling.software.common.util.WebResponseUtils;
@ExtendWith(MockitoExtension.class)
class ReplaceAndInvertColorControllerTest {
private static ResponseEntity<StreamingResponseBody> streamingOk(byte[] bytes) {
return ResponseEntity.ok(out -> out.write(bytes));
private static ResponseEntity<Resource> streamingOk(byte[] bytes) {
return ResponseEntity.ok(new ByteArrayResource(bytes));
}
private static byte[] drainBody(ResponseEntity<StreamingResponseBody> response)
throws java.io.IOException {
private static byte[] drainBody(ResponseEntity<Resource> response) throws java.io.IOException {
java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
response.getBody().writeTo(baos);
try (java.io.InputStream __in = response.getBody().getInputStream()) {
__in.transferTo(baos);
}
return baos.toByteArray();
}
@@ -96,7 +98,7 @@ class ReplaceAndInvertColorControllerTest {
try (MockedStatic<WebResponseUtils> mockedWebResponse =
mockStatic(WebResponseUtils.class)) {
ResponseEntity<StreamingResponseBody> expectedResponse = streamingOk(resultBytes);
ResponseEntity<Resource> expectedResponse = streamingOk(resultBytes);
mockedWebResponse
.when(
() ->
@@ -104,8 +106,7 @@ class ReplaceAndInvertColorControllerTest {
any(TempFile.class), anyString()))
.thenReturn(expectedResponse);
ResponseEntity<StreamingResponseBody> response =
controller.replaceAndInvertColor(request);
ResponseEntity<Resource> response = controller.replaceAndInvertColor(request);
assertNotNull(response);
assertEquals(HttpStatus.OK, response.getStatusCode());
@@ -132,7 +133,7 @@ class ReplaceAndInvertColorControllerTest {
try (MockedStatic<WebResponseUtils> mockedWebResponse =
mockStatic(WebResponseUtils.class)) {
ResponseEntity<StreamingResponseBody> expectedResponse = streamingOk(resultBytes);
ResponseEntity<Resource> expectedResponse = streamingOk(resultBytes);
mockedWebResponse
.when(
() ->
@@ -140,8 +141,7 @@ class ReplaceAndInvertColorControllerTest {
any(TempFile.class), anyString()))
.thenReturn(expectedResponse);
ResponseEntity<StreamingResponseBody> response =
controller.replaceAndInvertColor(request);
ResponseEntity<Resource> response = controller.replaceAndInvertColor(request);
assertNotNull(response);
assertEquals(HttpStatus.OK, response.getStatusCode());
@@ -166,7 +166,7 @@ class ReplaceAndInvertColorControllerTest {
try (MockedStatic<WebResponseUtils> mockedWebResponse =
mockStatic(WebResponseUtils.class)) {
ResponseEntity<StreamingResponseBody> expectedResponse = streamingOk(resultBytes);
ResponseEntity<Resource> expectedResponse = streamingOk(resultBytes);
mockedWebResponse
.when(
() ->
@@ -174,8 +174,7 @@ class ReplaceAndInvertColorControllerTest {
any(TempFile.class), anyString()))
.thenReturn(expectedResponse);
ResponseEntity<StreamingResponseBody> response =
controller.replaceAndInvertColor(request);
ResponseEntity<Resource> response = controller.replaceAndInvertColor(request);
assertNotNull(response);
assertEquals(HttpStatus.OK, response.getStatusCode());
@@ -205,7 +204,7 @@ class ReplaceAndInvertColorControllerTest {
try (MockedStatic<WebResponseUtils> mockedWebResponse =
mockStatic(WebResponseUtils.class)) {
ResponseEntity<StreamingResponseBody> expectedResponse = streamingOk(resultBytes);
ResponseEntity<Resource> expectedResponse = streamingOk(resultBytes);
mockedWebResponse
.when(
() ->
@@ -19,11 +19,12 @@ import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
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.servlet.mvc.method.annotation.StreamingResponseBody;
import stirling.software.common.model.api.PDFFile;
import stirling.software.common.service.CustomPDFDocumentFactory;
@@ -33,14 +34,15 @@ import stirling.software.common.util.WebResponseUtils;
@ExtendWith(MockitoExtension.class)
class ShowJavascriptTest {
private static ResponseEntity<StreamingResponseBody> streamingOk(byte[] bytes) {
return ResponseEntity.ok(out -> out.write(bytes));
private static ResponseEntity<Resource> streamingOk(byte[] bytes) {
return ResponseEntity.ok(new ByteArrayResource(bytes));
}
private static byte[] drainBody(ResponseEntity<StreamingResponseBody> response)
throws java.io.IOException {
private static byte[] drainBody(ResponseEntity<Resource> response) throws java.io.IOException {
java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
response.getBody().writeTo(baos);
try (java.io.InputStream __in = response.getBody().getInputStream()) {
__in.transferTo(baos);
}
return baos.toByteArray();
}
@@ -86,8 +88,7 @@ class ShowJavascriptTest {
try (MockedStatic<WebResponseUtils> mockedWebResponse =
mockStatic(WebResponseUtils.class)) {
ResponseEntity<StreamingResponseBody> expectedResponse =
streamingOk("no js".getBytes());
ResponseEntity<Resource> expectedResponse = streamingOk("no js".getBytes());
mockedWebResponse
.when(
() ->
@@ -97,7 +98,7 @@ class ShowJavascriptTest {
eq(MediaType.TEXT_PLAIN)))
.thenReturn(expectedResponse);
ResponseEntity<StreamingResponseBody> response = showJavascript.extractHeader(request);
ResponseEntity<Resource> response = showJavascript.extractHeader(request);
assertNotNull(response);
assertEquals(HttpStatus.OK, response.getStatusCode());
@@ -129,8 +130,7 @@ class ShowJavascriptTest {
try (MockedStatic<WebResponseUtils> mockedWebResponse =
mockStatic(WebResponseUtils.class)) {
ResponseEntity<StreamingResponseBody> expectedResponse =
streamingOk("js content".getBytes());
ResponseEntity<Resource> expectedResponse = streamingOk("js content".getBytes());
mockedWebResponse
.when(
() ->
@@ -140,7 +140,7 @@ class ShowJavascriptTest {
eq(MediaType.TEXT_PLAIN)))
.thenReturn(expectedResponse);
ResponseEntity<StreamingResponseBody> response = showJavascript.extractHeader(request);
ResponseEntity<Resource> response = showJavascript.extractHeader(request);
assertNotNull(response);
mockedWebResponse.verify(
@@ -160,8 +160,7 @@ class ShowJavascriptTest {
try (MockedStatic<WebResponseUtils> mockedWebResponse =
mockStatic(WebResponseUtils.class)) {
ResponseEntity<StreamingResponseBody> expectedResponse =
streamingOk("no js".getBytes());
ResponseEntity<Resource> expectedResponse = streamingOk("no js".getBytes());
mockedWebResponse
.when(
() ->
@@ -171,7 +170,7 @@ class ShowJavascriptTest {
eq(MediaType.TEXT_PLAIN)))
.thenReturn(expectedResponse);
ResponseEntity<StreamingResponseBody> response = showJavascript.extractHeader(request);
ResponseEntity<Resource> response = showJavascript.extractHeader(request);
assertNotNull(response);
mockedWebResponse.verify(
@@ -200,8 +199,7 @@ class ShowJavascriptTest {
try (MockedStatic<WebResponseUtils> mockedWebResponse =
mockStatic(WebResponseUtils.class)) {
ResponseEntity<StreamingResponseBody> expectedResponse =
streamingOk("no js".getBytes());
ResponseEntity<Resource> expectedResponse = streamingOk("no js".getBytes());
mockedWebResponse
.when(
() ->
@@ -211,7 +209,7 @@ class ShowJavascriptTest {
eq(MediaType.TEXT_PLAIN)))
.thenReturn(expectedResponse);
ResponseEntity<StreamingResponseBody> response = showJavascript.extractHeader(request);
ResponseEntity<Resource> response = showJavascript.extractHeader(request);
assertNotNull(response);
mockedWebResponse.verify(
@@ -16,9 +16,9 @@ import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.core.io.Resource;
import org.springframework.http.ResponseEntity;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import stirling.software.common.model.api.PDFFile;
import stirling.software.common.service.CustomPDFDocumentFactory;
@@ -67,7 +67,7 @@ class UnlockPDFFormsControllerTest {
PDFFile file = new PDFFile();
file.setFileInput(mockPdfFile);
ResponseEntity<StreamingResponseBody> response = controller.unlockPDFForms(file);
ResponseEntity<Resource> response = controller.unlockPDFForms(file);
assertNotNull(response);
assertEquals(200, response.getStatusCode().value());
@@ -85,7 +85,7 @@ class UnlockPDFFormsControllerTest {
PDFFile file = new PDFFile();
file.setFileInput(mockPdfFile);
ResponseEntity<StreamingResponseBody> response = controller.unlockPDFForms(file);
ResponseEntity<Resource> response = controller.unlockPDFForms(file);
assertNotNull(response);
assertEquals(200, response.getStatusCode().value());
@@ -99,7 +99,7 @@ class UnlockPDFFormsControllerTest {
PDFFile file = new PDFFile();
file.setFileInput(mockPdfFile);
ResponseEntity<StreamingResponseBody> response = controller.unlockPDFForms(file);
ResponseEntity<Resource> response = controller.unlockPDFForms(file);
// Controller catches exceptions and returns null
assertNull(response);
@@ -114,7 +114,7 @@ class UnlockPDFFormsControllerTest {
PDFFile file = new PDFFile();
file.setFileInput(mockPdfFile);
ResponseEntity<StreamingResponseBody> response = controller.unlockPDFForms(file);
ResponseEntity<Resource> response = controller.unlockPDFForms(file);
assertNotNull(response);
String contentDisposition = response.getHeaders().getFirst("Content-Disposition");
@@ -133,7 +133,7 @@ class UnlockPDFFormsControllerTest {
PDFFile file = new PDFFile();
file.setFileInput(mockPdfFile);
ResponseEntity<StreamingResponseBody> response = controller.unlockPDFForms(file);
ResponseEntity<Resource> response = controller.unlockPDFForms(file);
assertNotNull(response);
assertTrue(acroForm.getNeedAppearances());

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