mirror of
https://github.com/arsvendg/Stirling-PDF.git
synced 2026-07-16 19:33:11 +02:00
refactor(resource): improve resource management and exception safety across controllers and utilities (#5350)
TLDR: - Use try-with-resources for InputStreams, PDDocument, TempFile, and ByteArrayOutputStream to ensure proper cleanup - Refactor PDF manipulation methods to handle exceptions and resource closure more robustly - Simplify session sorting logic in SessionPersistentRegistry - Add missing resource cleanup in MergeController and SplitPdfBySectionsController - Update PrintFileController to close streams and documents safely - Add unit test for SplitPdfBySizeController # Description of Changes This pull request introduces several improvements to resource management and error handling throughout the codebase, especially for temporary files and PDF document objects. The changes help prevent resource leaks by ensuring files and documents are properly closed or deleted in the event of errors or after use. Notable updates include the use of try-with-resources, custom AutoCloseable wrappers, and enhanced error handling logic. **Resource Management and Error Handling Improvements** * Added try-catch blocks to delete temporary files if an exception occurs during file conversion in `GeneralUtils.java`, ensuring no orphaned temp files are left behind * Introduced the `TempFile` AutoCloseable helper class in `InvertFullColorStrategy.java`, and refactored the PDF processing logic to use try-with-resources for both temporary files and `PDDocument` objects, ensuring proper cleanup * Improved error handling in `MergeController.java` by ensuring that partially created merged documents are closed if an error occurs during the merge process * Enhanced the splitting logic in `SplitPdfBySectionsController.java` to close any partially created `PDDocument` objects if an exception is thrown, preventing resource leaks **Refactoring for Safer Document Handling** * Refactored `handleSplitBySize` in `SplitPdfBySizeController.java` to use a custom `DocHolder` AutoCloseable wrapper for managing the lifecycle of `PDDocument` objects, and updated all related logic to use this wrapper, improving code safety and clarity * Updated `handleSplitByPageCount` in `SplitPdfBySizeController.java` to ensure `PDDocument` objects are set to null after being saved and closed, reducing the risk of operating on closed resources <!-- Please provide a summary of the changes, including: - What was changed - Why the change was made - Any challenges encountered Closes #(issue_number) --> --- ## Checklist ### General - [ ] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [ ] I have read the [Stirling-PDF Developer Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/DeveloperGuide.md) (if applicable) - [ ] I have read the [How to add new languages to Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md) (if applicable) - [ ] I have performed a self-review of my own code - [ ] My changes generate no new warnings ### Documentation - [ ] I have updated relevant docs on [Stirling-PDF's doc repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/) (if functionality has heavily changed) - [ ] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### Translations (if applicable) - [ ] I ran [`scripts/counter_translation.py`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/docs/counter_translation.md) ### UI Changes (if applicable) - [ ] Screenshots or videos demonstrating the UI changes are attached (e.g., as comments or direct attachments in the PR) ### Testing (if applicable) - [ ] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/DeveloperGuide.md#6-testing) for more details. --------- Signed-off-by: Balázs Szücs <[email protected]>
This commit is contained in:
@@ -91,6 +91,14 @@ public class GeneralUtils {
|
|||||||
while ((bytesRead = inputStream.read(buffer)) != -1) {
|
while ((bytesRead = inputStream.read(buffer)) != -1) {
|
||||||
outputStream.write(buffer, 0, bytesRead);
|
outputStream.write(buffer, 0, bytesRead);
|
||||||
}
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
if (tempFile.exists()) {
|
||||||
|
try {
|
||||||
|
Files.delete(tempFile.toPath());
|
||||||
|
} catch (IOException ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
}
|
}
|
||||||
return tempFile;
|
return tempFile;
|
||||||
}
|
}
|
||||||
@@ -499,6 +507,12 @@ public class GeneralUtils {
|
|||||||
while ((bytesRead = in.read(buffer)) != -1) {
|
while ((bytesRead = in.read(buffer)) != -1) {
|
||||||
out.write(buffer, 0, bytesRead);
|
out.write(buffer, 0, bytesRead);
|
||||||
}
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
try {
|
||||||
|
Files.deleteIfExists(tempFile);
|
||||||
|
} catch (IOException ignored) {
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
}
|
}
|
||||||
return tempFile.toFile();
|
return tempFile.toFile();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,15 +63,16 @@ public class ImageProcessingUtils {
|
|||||||
} else {
|
} else {
|
||||||
int width = image.getWidth();
|
int width = image.getWidth();
|
||||||
int height = image.getHeight();
|
int height = image.getHeight();
|
||||||
|
int[] pixels = new int[width * height];
|
||||||
|
|
||||||
|
image.getRGB(0, 0, width, height, pixels, 0, width);
|
||||||
|
|
||||||
byte[] data = new byte[width * height * 3];
|
byte[] data = new byte[width * height * 3];
|
||||||
int index = 0;
|
int index = 0;
|
||||||
for (int y = 0; y < height; y++) {
|
for (int rgb : pixels) {
|
||||||
for (int x = 0; x < width; x++) {
|
data[index++] = (byte) ((rgb >> 16) & 0xFF); // Red
|
||||||
int rgb = image.getRGB(x, y);
|
data[index++] = (byte) ((rgb >> 8) & 0xFF); // Green
|
||||||
data[index++] = (byte) ((rgb >> 16) & 0xFF); // Red
|
data[index++] = (byte) (rgb & 0xFF); // Blue
|
||||||
data[index++] = (byte) ((rgb >> 8) & 0xFF); // Green
|
|
||||||
data[index++] = (byte) (rgb & 0xFF); // Blue
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|||||||
+90
-76
@@ -32,83 +32,76 @@ public class InvertFullColorStrategy extends ReplaceAndInvertColorStrategy {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public InputStreamResource replace() throws IOException {
|
public InputStreamResource replace() throws IOException {
|
||||||
|
try (TempFile tempFile =
|
||||||
File file = null;
|
new TempFile(
|
||||||
try {
|
Files.createTempFile("temp", getFileInput().getOriginalFilename())
|
||||||
// Create a temporary file, with the original filename from the multipart file
|
.toFile())) {
|
||||||
file = Files.createTempFile("temp", getFileInput().getOriginalFilename()).toFile();
|
|
||||||
|
|
||||||
// Transfer the content of the multipart file to the file
|
// Transfer the content of the multipart file to the file
|
||||||
getFileInput().transferTo(file);
|
getFileInput().transferTo(tempFile.getFile());
|
||||||
|
|
||||||
// Load the uploaded PDF
|
// Load the uploaded PDF
|
||||||
PDDocument document = Loader.loadPDF(file);
|
try (PDDocument document = Loader.loadPDF(tempFile.getFile())) {
|
||||||
|
// Render each page and invert colors
|
||||||
|
PDFRenderer pdfRenderer = new PDFRenderer(document);
|
||||||
|
for (int page = 0; page < document.getNumberOfPages(); page++) {
|
||||||
|
BufferedImage image;
|
||||||
|
|
||||||
// Render each page and invert colors
|
// Use global maximum DPI setting, fallback to 300 if not set
|
||||||
PDFRenderer pdfRenderer = new PDFRenderer(document);
|
int renderDpi = 300; // Default fallback
|
||||||
for (int page = 0; page < document.getNumberOfPages(); page++) {
|
ApplicationProperties properties =
|
||||||
BufferedImage image;
|
ApplicationContextProvider.getBean(ApplicationProperties.class);
|
||||||
|
if (properties != null && properties.getSystem() != null) {
|
||||||
|
renderDpi = properties.getSystem().getMaxDPI();
|
||||||
|
}
|
||||||
|
final int dpi = renderDpi;
|
||||||
|
final int pageNum = page;
|
||||||
|
|
||||||
// Use global maximum DPI setting, fallback to 300 if not set
|
image =
|
||||||
int renderDpi = 300; // Default fallback
|
ExceptionUtils.handleOomRendering(
|
||||||
ApplicationProperties properties =
|
pageNum + 1,
|
||||||
ApplicationContextProvider.getBean(ApplicationProperties.class);
|
dpi,
|
||||||
if (properties != null && properties.getSystem() != null) {
|
() -> pdfRenderer.renderImageWithDPI(pageNum, dpi));
|
||||||
renderDpi = properties.getSystem().getMaxDPI();
|
|
||||||
}
|
|
||||||
final int dpi = renderDpi;
|
|
||||||
final int pageNum = page;
|
|
||||||
|
|
||||||
image =
|
// Invert the colors
|
||||||
ExceptionUtils.handleOomRendering(
|
invertImageColors(image);
|
||||||
pageNum + 1,
|
|
||||||
dpi,
|
|
||||||
() -> pdfRenderer.renderImageWithDPI(pageNum, dpi));
|
|
||||||
|
|
||||||
// Invert the colors
|
// Create a new PDPage from the inverted image
|
||||||
invertImageColors(image);
|
PDPage pdPage = document.getPage(page);
|
||||||
|
File tempImageFile = null;
|
||||||
|
try {
|
||||||
|
tempImageFile = convertToBufferedImageTpFile(image);
|
||||||
|
PDImageXObject pdImage =
|
||||||
|
PDImageXObject.createFromFileByContent(tempImageFile, document);
|
||||||
|
|
||||||
// Create a new PDPage from the inverted image
|
try (PDPageContentStream contentStream =
|
||||||
PDPage pdPage = document.getPage(page);
|
new PDPageContentStream(
|
||||||
File tempImageFile = null;
|
document,
|
||||||
try {
|
pdPage,
|
||||||
tempImageFile = convertToBufferedImageTpFile(image);
|
PDPageContentStream.AppendMode.OVERWRITE,
|
||||||
PDImageXObject pdImage =
|
true)) {
|
||||||
PDImageXObject.createFromFileByContent(tempImageFile, document);
|
contentStream.drawImage(
|
||||||
|
pdImage,
|
||||||
PDPageContentStream contentStream =
|
0,
|
||||||
new PDPageContentStream(
|
0,
|
||||||
document,
|
pdPage.getMediaBox().getWidth(),
|
||||||
pdPage,
|
pdPage.getMediaBox().getHeight());
|
||||||
PDPageContentStream.AppendMode.OVERWRITE,
|
}
|
||||||
true);
|
} finally {
|
||||||
contentStream.drawImage(
|
if (tempImageFile != null && tempImageFile.exists()) {
|
||||||
pdImage,
|
Files.delete(tempImageFile.toPath());
|
||||||
0,
|
}
|
||||||
0,
|
|
||||||
pdPage.getMediaBox().getWidth(),
|
|
||||||
pdPage.getMediaBox().getHeight());
|
|
||||||
contentStream.close();
|
|
||||||
} finally {
|
|
||||||
if (tempImageFile != null && tempImageFile.exists()) {
|
|
||||||
Files.delete(tempImageFile.toPath());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Save the modified PDF to a ByteArrayOutputStream
|
// Save the modified PDF to a ByteArrayOutputStream
|
||||||
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
||||||
document.save(byteArrayOutputStream);
|
document.save(byteArrayOutputStream);
|
||||||
document.close();
|
|
||||||
|
|
||||||
// Prepare the modified PDF for download
|
// Prepare the modified PDF for download
|
||||||
ByteArrayInputStream inputStream =
|
ByteArrayInputStream inputStream =
|
||||||
new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
|
new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
|
||||||
InputStreamResource resource = new InputStreamResource(inputStream);
|
InputStreamResource resource = new InputStreamResource(inputStream);
|
||||||
return resource;
|
return resource;
|
||||||
} finally {
|
|
||||||
if (file != null && file.exists()) {
|
|
||||||
Files.delete(file.toPath());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -117,18 +110,20 @@ public class InvertFullColorStrategy extends ReplaceAndInvertColorStrategy {
|
|||||||
private void invertImageColors(BufferedImage image) {
|
private void invertImageColors(BufferedImage image) {
|
||||||
int width = image.getWidth();
|
int width = image.getWidth();
|
||||||
int height = image.getHeight();
|
int height = image.getHeight();
|
||||||
for (int x = 0; x < width; x++) {
|
int[] pixels = new int[width * height];
|
||||||
for (int y = 0; y < height; y++) {
|
image.getRGB(0, 0, width, height, pixels, 0, width);
|
||||||
int rgba = image.getRGB(x, y);
|
for (int i = 0; i < pixels.length; i++) {
|
||||||
Color color = new Color(rgba, true);
|
int pixel = pixels[i];
|
||||||
Color invertedColor =
|
|
||||||
new Color(
|
int a = 0xff;
|
||||||
255 - color.getRed(),
|
|
||||||
255 - color.getGreen(),
|
int r = (pixel >> 16) & 0xff;
|
||||||
255 - color.getBlue());
|
int g = (pixel >> 8) & 0xff;
|
||||||
image.setRGB(x, y, invertedColor.getRGB());
|
int b = pixel & 0xff;
|
||||||
}
|
|
||||||
|
pixels[i] = (a << 24) | ((255 - r) << 16) | ((255 - g) << 8) | (255 - b);
|
||||||
}
|
}
|
||||||
|
image.setRGB(0, 0, width, height, pixels, 0, width);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper method to convert BufferedImage to InputStream
|
// Helper method to convert BufferedImage to InputStream
|
||||||
@@ -137,4 +132,23 @@ public class InvertFullColorStrategy extends ReplaceAndInvertColorStrategy {
|
|||||||
ImageIO.write(image, "png", file);
|
ImageIO.write(image, "png", file);
|
||||||
return file;
|
return file;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class TempFile implements AutoCloseable {
|
||||||
|
private final File file;
|
||||||
|
|
||||||
|
public TempFile(File file) {
|
||||||
|
this.file = file;
|
||||||
|
}
|
||||||
|
|
||||||
|
public File getFile() {
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
if (file != null && file.exists()) {
|
||||||
|
Files.delete(file.toPath());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,12 +57,20 @@ public class MergeController {
|
|||||||
// Merges a list of PDDocument objects into a single PDDocument
|
// Merges a list of PDDocument objects into a single PDDocument
|
||||||
public PDDocument mergeDocuments(List<PDDocument> documents) throws IOException {
|
public PDDocument mergeDocuments(List<PDDocument> documents) throws IOException {
|
||||||
PDDocument mergedDoc = pdfDocumentFactory.createNewDocument();
|
PDDocument mergedDoc = pdfDocumentFactory.createNewDocument();
|
||||||
for (PDDocument doc : documents) {
|
boolean success = false;
|
||||||
for (PDPage page : doc.getPages()) {
|
try {
|
||||||
mergedDoc.addPage(page);
|
for (PDDocument doc : documents) {
|
||||||
|
for (PDPage page : doc.getPages()) {
|
||||||
|
mergedDoc.addPage(page);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
success = true;
|
||||||
|
return mergedDoc;
|
||||||
|
} finally {
|
||||||
|
if (!success) {
|
||||||
|
mergedDoc.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return mergedDoc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Re-order files to match the explicit order provided by the front-end.
|
// Re-order files to match the explicit order provided by the front-end.
|
||||||
@@ -363,9 +371,18 @@ public class MergeController {
|
|||||||
|
|
||||||
// Save the modified document to a temporary file
|
// Save the modified document to a temporary file
|
||||||
outputTempFile = new TempFile(tempFileManager, ".pdf");
|
outputTempFile = new TempFile(tempFileManager, ".pdf");
|
||||||
mergedDocument.save(outputTempFile.getFile());
|
try {
|
||||||
|
mergedDocument.save(outputTempFile.getFile());
|
||||||
|
} catch (Exception e) {
|
||||||
|
outputTempFile.close();
|
||||||
|
outputTempFile = null;
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
|
if (outputTempFile != null) {
|
||||||
|
outputTempFile.close();
|
||||||
|
}
|
||||||
if (ex instanceof IOException && PdfErrorUtils.isCorruptedPdfError((IOException) ex)) {
|
if (ex instanceof IOException && PdfErrorUtils.isCorruptedPdfError((IOException) ex)) {
|
||||||
log.warn("Corrupted PDF detected in merge pdf process: {}", ex.getMessage());
|
log.warn("Corrupted PDF detected in merge pdf process: {}", ex.getMessage());
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
+172
-112
@@ -1,6 +1,5 @@
|
|||||||
package stirling.software.SPDF.controller.api;
|
package stirling.software.SPDF.controller.api;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
@@ -60,8 +59,6 @@ public class SplitPdfBySectionsController {
|
|||||||
+ " Input:PDF Output:ZIP-PDF Type:SISO")
|
+ " Input:PDF Output:ZIP-PDF Type:SISO")
|
||||||
public ResponseEntity<StreamingResponseBody> splitPdf(
|
public ResponseEntity<StreamingResponseBody> splitPdf(
|
||||||
@ModelAttribute SplitPdfBySectionsRequest request) throws Exception {
|
@ModelAttribute SplitPdfBySectionsRequest request) throws Exception {
|
||||||
List<ByteArrayOutputStream> splitDocumentsBoas = new ArrayList<>();
|
|
||||||
|
|
||||||
MultipartFile file = request.getFileInput();
|
MultipartFile file = request.getFileInput();
|
||||||
String pageNumbers = request.getPageNumbers();
|
String pageNumbers = request.getPageNumbers();
|
||||||
SplitTypes splitMode =
|
SplitTypes splitMode =
|
||||||
@@ -69,55 +66,182 @@ public class SplitPdfBySectionsController {
|
|||||||
.map(SplitTypes::valueOf)
|
.map(SplitTypes::valueOf)
|
||||||
.orElse(SplitTypes.SPLIT_ALL);
|
.orElse(SplitTypes.SPLIT_ALL);
|
||||||
|
|
||||||
PDDocument sourceDocument = pdfDocumentFactory.load(file);
|
try (PDDocument sourceDocument = pdfDocumentFactory.load(file)) {
|
||||||
|
Set<Integer> pagesToSplit =
|
||||||
|
getPagesToSplit(pageNumbers, splitMode, sourceDocument.getNumberOfPages());
|
||||||
|
|
||||||
Set<Integer> pagesToSplit =
|
// Process the PDF based on split parameters
|
||||||
getPagesToSplit(pageNumbers, splitMode, sourceDocument.getNumberOfPages());
|
int horiz = request.getHorizontalDivisions() + 1;
|
||||||
|
int verti = request.getVerticalDivisions() + 1;
|
||||||
|
boolean merge = Boolean.TRUE.equals(request.getMerge());
|
||||||
|
String filename = GeneralUtils.generateFilename(file.getOriginalFilename(), "_split");
|
||||||
|
|
||||||
// Process the PDF based on split parameters
|
if (merge) {
|
||||||
int horiz = request.getHorizontalDivisions() + 1;
|
TempFile tempFile = new TempFile(tempFileManager, ".pdf");
|
||||||
int verti = request.getVerticalDivisions() + 1;
|
try (PDDocument mergedDoc = pdfDocumentFactory.createNewDocument();
|
||||||
boolean merge = Boolean.TRUE.equals(request.getMerge());
|
OutputStream out = Files.newOutputStream(tempFile.getPath())) {
|
||||||
List<PDDocument> splitDocuments = splitPdfPages(sourceDocument, verti, horiz, pagesToSplit);
|
LayerUtility layerUtility = new LayerUtility(mergedDoc);
|
||||||
|
for (int pageIndex = 0;
|
||||||
String filename = GeneralUtils.generateFilename(file.getOriginalFilename(), "_split.pdf");
|
pageIndex < sourceDocument.getNumberOfPages();
|
||||||
if (merge) {
|
pageIndex++) {
|
||||||
TempFile tempFile = new TempFile(tempFileManager, ".pdf");
|
if (pagesToSplit.contains(pageIndex)) {
|
||||||
try (PDDocument merged = pdfService.mergeDocuments(splitDocuments);
|
addSplitPageToTarget(
|
||||||
OutputStream out = Files.newOutputStream(tempFile.getPath())) {
|
sourceDocument,
|
||||||
merged.save(out);
|
pageIndex,
|
||||||
for (PDDocument d : splitDocuments) d.close();
|
mergedDoc,
|
||||||
sourceDocument.close();
|
layerUtility,
|
||||||
}
|
horiz,
|
||||||
return WebResponseUtils.pdfFileToWebResponse(tempFile, filename + "_split.pdf");
|
verti);
|
||||||
}
|
} else {
|
||||||
for (PDDocument doc : splitDocuments) {
|
addPageToTarget(sourceDocument, pageIndex, mergedDoc, layerUtility);
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
}
|
||||||
doc.save(baos);
|
}
|
||||||
doc.close();
|
mergedDoc.save(out);
|
||||||
splitDocumentsBoas.add(baos);
|
}
|
||||||
}
|
return WebResponseUtils.pdfFileToWebResponse(tempFile, filename + ".pdf");
|
||||||
|
} else {
|
||||||
sourceDocument.close();
|
TempFile zipTempFile = new TempFile(tempFileManager, ".zip");
|
||||||
|
try (ZipOutputStream zipOut =
|
||||||
TempFile zipTempFile = new TempFile(tempFileManager, ".zip");
|
new ZipOutputStream(Files.newOutputStream(zipTempFile.getPath()))) {
|
||||||
try (ZipOutputStream zipOut =
|
for (int pageIndex = 0;
|
||||||
new ZipOutputStream(Files.newOutputStream(zipTempFile.getPath()))) {
|
pageIndex < sourceDocument.getNumberOfPages();
|
||||||
int pageNum = 1;
|
pageIndex++) {
|
||||||
for (int i = 0; i < splitDocumentsBoas.size(); i++) {
|
int pageNum = pageIndex + 1;
|
||||||
ByteArrayOutputStream baos = splitDocumentsBoas.get(i);
|
if (pagesToSplit.contains(pageIndex)) {
|
||||||
int sectionNum = (i % (horiz * verti)) + 1;
|
for (int i = 0; i < horiz; i++) {
|
||||||
String fileName = filename + "_" + pageNum + "_" + sectionNum + ".pdf";
|
for (int j = 0; j < verti; j++) {
|
||||||
byte[] pdf = baos.toByteArray();
|
try (PDDocument subDoc =
|
||||||
ZipEntry pdfEntry = new ZipEntry(fileName);
|
pdfDocumentFactory.createNewDocument()) {
|
||||||
zipOut.putNextEntry(pdfEntry);
|
LayerUtility subLayerUtility = new LayerUtility(subDoc);
|
||||||
zipOut.write(pdf);
|
addSingleSectionToTarget(
|
||||||
zipOut.closeEntry();
|
sourceDocument,
|
||||||
|
pageIndex,
|
||||||
if (sectionNum == horiz * verti) pageNum++;
|
subDoc,
|
||||||
|
subLayerUtility,
|
||||||
|
i,
|
||||||
|
j,
|
||||||
|
horiz,
|
||||||
|
verti);
|
||||||
|
int sectionNum = i * verti + j + 1;
|
||||||
|
String entryName =
|
||||||
|
filename
|
||||||
|
+ "_"
|
||||||
|
+ pageNum
|
||||||
|
+ "_"
|
||||||
|
+ sectionNum
|
||||||
|
+ ".pdf";
|
||||||
|
saveDocToZip(subDoc, zipOut, entryName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try (PDDocument subDoc = pdfDocumentFactory.createNewDocument()) {
|
||||||
|
LayerUtility subLayerUtility = new LayerUtility(subDoc);
|
||||||
|
addPageToTarget(sourceDocument, pageIndex, subDoc, subLayerUtility);
|
||||||
|
String entryName = filename + "_" + pageNum + "_1.pdf";
|
||||||
|
saveDocToZip(subDoc, zipOut, entryName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return WebResponseUtils.zipFileToWebResponse(zipTempFile, filename + ".zip");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return WebResponseUtils.zipFileToWebResponse(zipTempFile, filename + "_split.zip");
|
}
|
||||||
|
|
||||||
|
private void addPageToTarget(
|
||||||
|
PDDocument sourceDoc, int pageIndex, PDDocument targetDoc, LayerUtility layerUtility)
|
||||||
|
throws IOException {
|
||||||
|
PDPage sourcePage = sourceDoc.getPage(pageIndex);
|
||||||
|
PDPage newPage = new PDPage(sourcePage.getMediaBox());
|
||||||
|
targetDoc.addPage(newPage);
|
||||||
|
|
||||||
|
PDFormXObject form = layerUtility.importPageAsForm(sourceDoc, pageIndex);
|
||||||
|
try (PDPageContentStream contentStream =
|
||||||
|
new PDPageContentStream(targetDoc, newPage, AppendMode.APPEND, true, true)) {
|
||||||
|
contentStream.drawForm(form);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addSplitPageToTarget(
|
||||||
|
PDDocument sourceDoc,
|
||||||
|
int pageIndex,
|
||||||
|
PDDocument targetDoc,
|
||||||
|
LayerUtility layerUtility,
|
||||||
|
int totalHoriz,
|
||||||
|
int totalVert)
|
||||||
|
throws IOException {
|
||||||
|
PDPage sourcePage = sourceDoc.getPage(pageIndex);
|
||||||
|
PDRectangle mediaBox = sourcePage.getMediaBox();
|
||||||
|
float width = mediaBox.getWidth();
|
||||||
|
float height = mediaBox.getHeight();
|
||||||
|
float subPageWidth = width / totalHoriz;
|
||||||
|
float subPageHeight = height / totalVert;
|
||||||
|
|
||||||
|
PDFormXObject form = layerUtility.importPageAsForm(sourceDoc, pageIndex);
|
||||||
|
|
||||||
|
for (int i = 0; i < totalHoriz; i++) {
|
||||||
|
for (int j = 0; j < totalVert; j++) {
|
||||||
|
PDPage subPage = new PDPage(new PDRectangle(subPageWidth, subPageHeight));
|
||||||
|
targetDoc.addPage(subPage);
|
||||||
|
|
||||||
|
try (PDPageContentStream contentStream =
|
||||||
|
new PDPageContentStream(
|
||||||
|
targetDoc, subPage, AppendMode.APPEND, true, true)) {
|
||||||
|
float translateX = -subPageWidth * i;
|
||||||
|
float translateY = -subPageHeight * (totalVert - 1 - j);
|
||||||
|
|
||||||
|
contentStream.saveGraphicsState();
|
||||||
|
contentStream.addRect(0, 0, subPageWidth, subPageHeight);
|
||||||
|
contentStream.clip();
|
||||||
|
contentStream.transform(new Matrix(1, 0, 0, 1, translateX, translateY));
|
||||||
|
contentStream.drawForm(form);
|
||||||
|
contentStream.restoreGraphicsState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addSingleSectionToTarget(
|
||||||
|
PDDocument sourceDoc,
|
||||||
|
int pageIndex,
|
||||||
|
PDDocument targetDoc,
|
||||||
|
LayerUtility layerUtility,
|
||||||
|
int horizIndex,
|
||||||
|
int vertIndex,
|
||||||
|
int totalHoriz,
|
||||||
|
int totalVert)
|
||||||
|
throws IOException {
|
||||||
|
PDPage sourcePage = sourceDoc.getPage(pageIndex);
|
||||||
|
PDRectangle mediaBox = sourcePage.getMediaBox();
|
||||||
|
float subPageWidth = mediaBox.getWidth() / totalHoriz;
|
||||||
|
float subPageHeight = mediaBox.getHeight() / totalVert;
|
||||||
|
|
||||||
|
PDPage subPage = new PDPage(new PDRectangle(subPageWidth, subPageHeight));
|
||||||
|
targetDoc.addPage(subPage);
|
||||||
|
|
||||||
|
PDFormXObject form = layerUtility.importPageAsForm(sourceDoc, pageIndex);
|
||||||
|
|
||||||
|
try (PDPageContentStream contentStream =
|
||||||
|
new PDPageContentStream(targetDoc, subPage, AppendMode.APPEND, true, true)) {
|
||||||
|
float translateX = -subPageWidth * horizIndex;
|
||||||
|
float translateY = -subPageHeight * (totalVert - 1 - vertIndex);
|
||||||
|
|
||||||
|
contentStream.saveGraphicsState();
|
||||||
|
contentStream.addRect(0, 0, subPageWidth, subPageHeight);
|
||||||
|
contentStream.clip();
|
||||||
|
contentStream.transform(new Matrix(1, 0, 0, 1, translateX, translateY));
|
||||||
|
contentStream.drawForm(form);
|
||||||
|
contentStream.restoreGraphicsState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveDocToZip(PDDocument doc, ZipOutputStream zipOut, String entryName)
|
||||||
|
throws IOException {
|
||||||
|
ZipEntry entry = new ZipEntry(entryName);
|
||||||
|
zipOut.putNextEntry(entry);
|
||||||
|
doc.save(zipOut);
|
||||||
|
zipOut.closeEntry();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Based on the mode, get the pages that need to be split and return the pages set
|
// Based on the mode, get the pages that need to be split and return the pages set
|
||||||
@@ -170,68 +294,4 @@ public class SplitPdfBySectionsController {
|
|||||||
|
|
||||||
return pagesToSplit;
|
return pagesToSplit;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<PDDocument> splitPdfPages(
|
|
||||||
PDDocument document,
|
|
||||||
int horizontalDivisions,
|
|
||||||
int verticalDivisions,
|
|
||||||
Set<Integer> pagesToSplit)
|
|
||||||
throws IOException {
|
|
||||||
List<PDDocument> splitDocuments = new ArrayList<>();
|
|
||||||
|
|
||||||
int pageIndex = 0;
|
|
||||||
for (PDPage originalPage : document.getPages()) {
|
|
||||||
// If current page is not to split, add it to the splitDocuments directly.
|
|
||||||
if (!pagesToSplit.contains(pageIndex)) {
|
|
||||||
PDDocument newDoc = pdfDocumentFactory.createNewDocument();
|
|
||||||
newDoc.addPage(originalPage);
|
|
||||||
splitDocuments.add(newDoc);
|
|
||||||
} else {
|
|
||||||
// Otherwise, split current page.
|
|
||||||
PDRectangle originalMediaBox = originalPage.getMediaBox();
|
|
||||||
float width = originalMediaBox.getWidth();
|
|
||||||
float height = originalMediaBox.getHeight();
|
|
||||||
float subPageWidth = width / horizontalDivisions;
|
|
||||||
float subPageHeight = height / verticalDivisions;
|
|
||||||
|
|
||||||
LayerUtility layerUtility = new LayerUtility(document);
|
|
||||||
|
|
||||||
for (int i = 0; i < horizontalDivisions; i++) {
|
|
||||||
for (int j = 0; j < verticalDivisions; j++) {
|
|
||||||
PDDocument subDoc = new PDDocument();
|
|
||||||
PDPage subPage = new PDPage(new PDRectangle(subPageWidth, subPageHeight));
|
|
||||||
subDoc.addPage(subPage);
|
|
||||||
|
|
||||||
PDFormXObject form =
|
|
||||||
layerUtility.importPageAsForm(
|
|
||||||
document, document.getPages().indexOf(originalPage));
|
|
||||||
|
|
||||||
try (PDPageContentStream contentStream =
|
|
||||||
new PDPageContentStream(
|
|
||||||
subDoc, subPage, AppendMode.APPEND, true, true)) {
|
|
||||||
// Set clipping area and position
|
|
||||||
float translateX = -subPageWidth * i;
|
|
||||||
|
|
||||||
// float translateY = height - subPageHeight * (verticalDivisions - j);
|
|
||||||
float translateY = -subPageHeight * (verticalDivisions - 1 - j);
|
|
||||||
|
|
||||||
contentStream.saveGraphicsState();
|
|
||||||
contentStream.addRect(0, 0, subPageWidth, subPageHeight);
|
|
||||||
contentStream.clip();
|
|
||||||
contentStream.transform(new Matrix(1, 0, 0, 1, translateX, translateY));
|
|
||||||
|
|
||||||
// Draw the form
|
|
||||||
contentStream.drawForm(form);
|
|
||||||
contentStream.restoreGraphicsState();
|
|
||||||
}
|
|
||||||
|
|
||||||
splitDocuments.add(subDoc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pageIndex++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return splitDocuments;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+267
-215
@@ -124,124 +124,168 @@ public class SplitPdfBySizeController {
|
|||||||
throws IOException {
|
throws IOException {
|
||||||
log.debug("Starting handleSplitBySize with maxBytes={}", maxBytes);
|
log.debug("Starting handleSplitBySize with maxBytes={}", maxBytes);
|
||||||
|
|
||||||
PDDocument currentDoc =
|
class DocHolder implements AutoCloseable {
|
||||||
pdfDocumentFactory.createNewDocumentBasedOnOldDocument(sourceDocument);
|
private PDDocument doc;
|
||||||
int fileIndex = 1;
|
|
||||||
int totalPages = sourceDocument.getNumberOfPages();
|
|
||||||
int pageAdded = 0;
|
|
||||||
|
|
||||||
// Smart size check frequency - check more often with larger documents
|
public DocHolder(PDDocument doc) {
|
||||||
int baseCheckFrequency = 5;
|
this.doc = doc;
|
||||||
|
}
|
||||||
|
|
||||||
for (int pageIndex = 0; pageIndex < totalPages; pageIndex++) {
|
public PDDocument getDoc() {
|
||||||
PDPage page = sourceDocument.getPage(pageIndex);
|
return doc;
|
||||||
log.debug("Processing page {} of {}", pageIndex + 1, totalPages);
|
}
|
||||||
|
|
||||||
// Add the page to current document
|
public void setDoc(PDDocument doc) {
|
||||||
PDPage newPage = new PDPage(page.getCOSObject());
|
if (this.doc != null) {
|
||||||
currentDoc.addPage(newPage);
|
try {
|
||||||
pageAdded++;
|
this.doc.close();
|
||||||
|
} catch (IOException e) {
|
||||||
// Dynamic size checking based on document size and page count
|
log.error("Error closing document", e);
|
||||||
boolean shouldCheckSize =
|
|
||||||
(pageAdded % baseCheckFrequency == 0)
|
|
||||||
|| (pageIndex == totalPages - 1)
|
|
||||||
|| (pageAdded >= 20); // Always check after 20 pages
|
|
||||||
|
|
||||||
if (shouldCheckSize) {
|
|
||||||
log.debug("Performing size check after {} pages", pageAdded);
|
|
||||||
ByteArrayOutputStream checkSizeStream = new ByteArrayOutputStream();
|
|
||||||
currentDoc.save(checkSizeStream);
|
|
||||||
long actualSize = checkSizeStream.size();
|
|
||||||
log.debug("Current document size: {} bytes (max: {} bytes)", actualSize, maxBytes);
|
|
||||||
|
|
||||||
if (actualSize > maxBytes) {
|
|
||||||
// We exceeded the limit - remove the last page and save
|
|
||||||
if (currentDoc.getNumberOfPages() > 1) {
|
|
||||||
currentDoc.removePage(currentDoc.getNumberOfPages() - 1);
|
|
||||||
pageIndex--; // Process this page again in the next document
|
|
||||||
log.debug("Size limit exceeded - removed last page");
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
this.doc = doc;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
if (doc != null) {
|
||||||
|
doc.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int fileIndex = 1;
|
||||||
|
try (DocHolder holder =
|
||||||
|
new DocHolder(
|
||||||
|
pdfDocumentFactory.createNewDocumentBasedOnOldDocument(sourceDocument))) {
|
||||||
|
int totalPages = sourceDocument.getNumberOfPages();
|
||||||
|
int pageAdded = 0;
|
||||||
|
|
||||||
|
// Smart size check frequency - check more often with larger documents
|
||||||
|
int baseCheckFrequency = 5;
|
||||||
|
|
||||||
|
for (int pageIndex = 0; pageIndex < totalPages; pageIndex++) {
|
||||||
|
PDPage page = sourceDocument.getPage(pageIndex);
|
||||||
|
log.debug("Processing page {} of {}", pageIndex + 1, totalPages);
|
||||||
|
|
||||||
|
// Add the page to current document
|
||||||
|
PDPage newPage = new PDPage(page.getCOSObject());
|
||||||
|
holder.getDoc().addPage(newPage);
|
||||||
|
pageAdded++;
|
||||||
|
|
||||||
|
// Dynamic size checking based on document size and page count
|
||||||
|
boolean shouldCheckSize =
|
||||||
|
(pageAdded % baseCheckFrequency == 0)
|
||||||
|
|| (pageIndex == totalPages - 1)
|
||||||
|
|| (pageAdded >= 20); // Always check after 20 pages
|
||||||
|
|
||||||
|
if (shouldCheckSize) {
|
||||||
|
log.debug("Performing size check after {} pages", pageAdded);
|
||||||
|
long actualSize;
|
||||||
|
try (ByteArrayOutputStream checkSizeStream = new ByteArrayOutputStream()) {
|
||||||
|
holder.getDoc().save(checkSizeStream);
|
||||||
|
actualSize = checkSizeStream.size();
|
||||||
|
}
|
||||||
log.debug(
|
log.debug(
|
||||||
"Saving document with {} pages as part {}",
|
"Current document size: {} bytes (max: {} bytes)",
|
||||||
currentDoc.getNumberOfPages(),
|
actualSize,
|
||||||
fileIndex);
|
maxBytes);
|
||||||
saveDocumentToZip(currentDoc, zipOut, baseFilename, fileIndex++);
|
|
||||||
currentDoc = new PDDocument();
|
|
||||||
pageAdded = 0;
|
|
||||||
} else if (pageIndex < totalPages - 1) {
|
|
||||||
// We're under the limit, calculate if we might fit more pages
|
|
||||||
// Try to predict how many more similar pages might fit
|
|
||||||
if (actualSize < maxBytes * 0.75 && pageAdded > 0) {
|
|
||||||
// Rather than using a ratio, look ahead to test actual upcoming pages
|
|
||||||
int pagesToLookAhead = Math.min(5, totalPages - pageIndex - 1);
|
|
||||||
|
|
||||||
if (pagesToLookAhead > 0) {
|
if (actualSize > maxBytes) {
|
||||||
log.debug(
|
// We exceeded the limit - remove the last page and save
|
||||||
"Testing {} upcoming pages for potential addition",
|
if (holder.getDoc().getNumberOfPages() > 1) {
|
||||||
pagesToLookAhead);
|
holder.getDoc().removePage(holder.getDoc().getNumberOfPages() - 1);
|
||||||
|
pageIndex--; // Process this page again in the next document
|
||||||
|
log.debug("Size limit exceeded - removed last page");
|
||||||
|
}
|
||||||
|
|
||||||
// Create a temp document with current pages + look-ahead pages
|
log.debug(
|
||||||
PDDocument testDoc = new PDDocument();
|
"Saving document with {} pages as part {}",
|
||||||
// First copy existing pages
|
holder.getDoc().getNumberOfPages(),
|
||||||
for (int i = 0; i < currentDoc.getNumberOfPages(); i++) {
|
fileIndex);
|
||||||
testDoc.addPage(new PDPage(currentDoc.getPage(i).getCOSObject()));
|
saveDocumentToZip(holder.getDoc(), zipOut, baseFilename, fileIndex++);
|
||||||
}
|
holder.setDoc(new PDDocument());
|
||||||
|
pageAdded = 0;
|
||||||
|
} else if (pageIndex < totalPages - 1) {
|
||||||
|
// We're under the limit, calculate if we might fit more pages
|
||||||
|
// Try to predict how many more similar pages might fit
|
||||||
|
if (actualSize < maxBytes * 0.75 && pageAdded > 0) {
|
||||||
|
// Rather than using a ratio, look ahead to test actual upcoming pages
|
||||||
|
int pagesToLookAhead = Math.min(5, totalPages - pageIndex - 1);
|
||||||
|
|
||||||
// Try adding look-ahead pages one by one
|
if (pagesToLookAhead > 0) {
|
||||||
int extraPagesAdded = 0;
|
log.debug(
|
||||||
for (int i = 0; i < pagesToLookAhead; i++) {
|
"Testing {} upcoming pages for potential addition",
|
||||||
int testPageIndex = pageIndex + 1 + i;
|
pagesToLookAhead);
|
||||||
PDPage testPage = sourceDocument.getPage(testPageIndex);
|
|
||||||
testDoc.addPage(new PDPage(testPage.getCOSObject()));
|
|
||||||
|
|
||||||
// Check if we're still under size
|
// Create a temp document with current pages + look-ahead pages
|
||||||
ByteArrayOutputStream testStream = new ByteArrayOutputStream();
|
try (PDDocument testDoc = new PDDocument()) {
|
||||||
testDoc.save(testStream);
|
// First copy existing pages
|
||||||
long testSize = testStream.size();
|
for (int i = 0; i < holder.getDoc().getNumberOfPages(); i++) {
|
||||||
|
testDoc.addPage(
|
||||||
|
new PDPage(
|
||||||
|
holder.getDoc().getPage(i).getCOSObject()));
|
||||||
|
}
|
||||||
|
|
||||||
if (testSize <= maxBytes) {
|
// Try adding look-ahead pages one by one
|
||||||
extraPagesAdded++;
|
int extraPagesAdded = 0;
|
||||||
log.debug(
|
for (int i = 0; i < pagesToLookAhead; i++) {
|
||||||
"Test: Can add page {} (size would be {})",
|
int testPageIndex = pageIndex + 1 + i;
|
||||||
testPageIndex + 1,
|
PDPage testPage = sourceDocument.getPage(testPageIndex);
|
||||||
testSize);
|
testDoc.addPage(new PDPage(testPage.getCOSObject()));
|
||||||
} else {
|
|
||||||
log.debug(
|
// Check if we're still under size
|
||||||
"Test: Cannot add page {} (size would be {})",
|
long testSize;
|
||||||
testPageIndex + 1,
|
try (ByteArrayOutputStream testStream =
|
||||||
testSize);
|
new ByteArrayOutputStream()) {
|
||||||
break;
|
testDoc.save(testStream);
|
||||||
|
testSize = testStream.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (testSize <= maxBytes) {
|
||||||
|
extraPagesAdded++;
|
||||||
|
log.debug(
|
||||||
|
"Test: Can add page {} (size would be {})",
|
||||||
|
testPageIndex + 1,
|
||||||
|
testSize);
|
||||||
|
} else {
|
||||||
|
log.debug(
|
||||||
|
"Test: Cannot add page {} (size would be {})",
|
||||||
|
testPageIndex + 1,
|
||||||
|
testSize);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Add the pages we verified would fit
|
||||||
|
if (extraPagesAdded > 0) {
|
||||||
|
log.debug(
|
||||||
|
"Adding {} verified pages ahead", extraPagesAdded);
|
||||||
|
for (int i = 0; i < extraPagesAdded; i++) {
|
||||||
|
int extraPageIndex = pageIndex + 1 + i;
|
||||||
|
PDPage extraPage =
|
||||||
|
sourceDocument.getPage(extraPageIndex);
|
||||||
|
holder.getDoc()
|
||||||
|
.addPage(new PDPage(extraPage.getCOSObject()));
|
||||||
|
}
|
||||||
|
pageIndex += extraPagesAdded;
|
||||||
|
pageAdded += extraPagesAdded;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
testDoc.close();
|
|
||||||
|
|
||||||
// Add the pages we verified would fit
|
|
||||||
if (extraPagesAdded > 0) {
|
|
||||||
log.debug("Adding {} verified pages ahead", extraPagesAdded);
|
|
||||||
for (int i = 0; i < extraPagesAdded; i++) {
|
|
||||||
int extraPageIndex = pageIndex + 1 + i;
|
|
||||||
PDPage extraPage = sourceDocument.getPage(extraPageIndex);
|
|
||||||
currentDoc.addPage(new PDPage(extraPage.getCOSObject()));
|
|
||||||
}
|
|
||||||
pageIndex += extraPagesAdded;
|
|
||||||
pageAdded += extraPagesAdded;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Save final document if it has any pages
|
// Save final document if it has any pages
|
||||||
if (currentDoc.getNumberOfPages() > 0) {
|
if (holder.getDoc() != null && holder.getDoc().getNumberOfPages() > 0) {
|
||||||
log.debug(
|
log.debug(
|
||||||
"Saving final document with {} pages as part {}",
|
"Saving final document with {} pages as part {}",
|
||||||
currentDoc.getNumberOfPages(),
|
holder.getDoc().getNumberOfPages(),
|
||||||
fileIndex);
|
fileIndex);
|
||||||
saveDocumentToZip(currentDoc, zipOut, baseFilename, fileIndex++);
|
saveDocumentToZip(holder.getDoc(), zipOut, baseFilename, fileIndex++);
|
||||||
|
holder.setDoc(null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.debug("Completed handleSplitBySize with {} document parts created", fileIndex - 1);
|
log.debug("Completed handleSplitBySize with {} document parts created", fileIndex - 1);
|
||||||
@@ -252,96 +296,103 @@ public class SplitPdfBySizeController {
|
|||||||
throws IOException {
|
throws IOException {
|
||||||
log.debug("Starting handleSplitByPageCount with pageCount={}", pageCount);
|
log.debug("Starting handleSplitByPageCount with pageCount={}", pageCount);
|
||||||
int currentPageCount = 0;
|
int currentPageCount = 0;
|
||||||
log.debug("Creating initial output document");
|
PDDocument currentDoc = null;
|
||||||
PDDocument currentDoc;
|
|
||||||
try {
|
|
||||||
currentDoc = pdfDocumentFactory.createNewDocumentBasedOnOldDocument(sourceDocument);
|
|
||||||
log.debug("Successfully created initial output document");
|
|
||||||
} catch (Exception e) {
|
|
||||||
ExceptionUtils.logException("initial output document creation", e);
|
|
||||||
throw ExceptionUtils.createFileProcessingException("split", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
int fileIndex = 1;
|
int fileIndex = 1;
|
||||||
int pageIndex = 0;
|
|
||||||
int totalPages = sourceDocument.getNumberOfPages();
|
|
||||||
log.debug("Processing {} pages", totalPages);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
for (PDPage page : sourceDocument.getPages()) {
|
log.debug("Creating initial output document");
|
||||||
pageIndex++;
|
try {
|
||||||
log.debug("Processing page {} of {}", pageIndex, totalPages);
|
currentDoc = pdfDocumentFactory.createNewDocumentBasedOnOldDocument(sourceDocument);
|
||||||
|
log.debug("Successfully created initial output document");
|
||||||
|
} catch (Exception e) {
|
||||||
|
ExceptionUtils.logException("initial output document creation", e);
|
||||||
|
throw ExceptionUtils.createFileProcessingException("split", e);
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
int pageIndex = 0;
|
||||||
log.debug("Adding page {} to current document", pageIndex);
|
int totalPages = sourceDocument.getNumberOfPages();
|
||||||
currentDoc.addPage(page);
|
log.debug("Processing {} pages", totalPages);
|
||||||
log.debug("Successfully added page {} to current document", pageIndex);
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("Error adding page {} to current document", pageIndex, e);
|
|
||||||
throw ExceptionUtils.createFileProcessingException("split", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
currentPageCount++;
|
try {
|
||||||
log.debug("Current page count: {}/{}", currentPageCount, pageCount);
|
for (PDPage page : sourceDocument.getPages()) {
|
||||||
|
pageIndex++;
|
||||||
if (currentPageCount == pageCount) {
|
log.debug("Processing page {} of {}", pageIndex, totalPages);
|
||||||
log.debug(
|
|
||||||
"Reached target page count ({}), saving current document as part {}",
|
|
||||||
pageCount,
|
|
||||||
fileIndex);
|
|
||||||
try {
|
|
||||||
saveDocumentToZip(currentDoc, zipOut, baseFilename, fileIndex++);
|
|
||||||
log.debug("Successfully saved document part {}", fileIndex - 1);
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("Error saving document part {}", fileIndex - 1, e);
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
log.debug("Creating new document for next part");
|
log.debug("Adding page {} to current document", pageIndex);
|
||||||
currentDoc = new PDDocument();
|
currentDoc.addPage(page);
|
||||||
log.debug("Successfully created new document");
|
log.debug("Successfully added page {} to current document", pageIndex);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("Error creating new document for next part", e);
|
log.error("Error adding page {} to current document", pageIndex, e);
|
||||||
throw ExceptionUtils.createFileProcessingException("split", e);
|
throw ExceptionUtils.createFileProcessingException("split", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
currentPageCount = 0;
|
currentPageCount++;
|
||||||
log.debug("Reset current page count to 0");
|
log.debug("Current page count: {}/{}", currentPageCount, pageCount);
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("Error iterating through pages", e);
|
|
||||||
throw ExceptionUtils.createFileProcessingException("split", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add the last document if it contains any pages
|
if (currentPageCount == pageCount) {
|
||||||
try {
|
log.debug(
|
||||||
if (currentDoc.getPages().getCount() != 0) {
|
"Reached target page count ({}), saving current document as part {}",
|
||||||
log.debug(
|
pageCount,
|
||||||
"Saving final document with {} pages as part {}",
|
fileIndex);
|
||||||
currentDoc.getPages().getCount(),
|
try {
|
||||||
fileIndex);
|
saveDocumentToZip(currentDoc, zipOut, baseFilename, fileIndex++);
|
||||||
try {
|
currentDoc = null; // Document is closed by saveDocumentToZip
|
||||||
saveDocumentToZip(currentDoc, zipOut, baseFilename, fileIndex++);
|
log.debug("Successfully saved document part {}", fileIndex - 1);
|
||||||
log.debug("Successfully saved final document part {}", fileIndex - 1);
|
} catch (Exception e) {
|
||||||
} catch (Exception e) {
|
log.error("Error saving document part {}", fileIndex - 1, e);
|
||||||
log.error("Error saving final document part {}", fileIndex - 1, e);
|
throw e;
|
||||||
throw e;
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
log.debug("Creating new document for next part");
|
||||||
|
currentDoc = new PDDocument();
|
||||||
|
log.debug("Successfully created new document");
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Error creating new document for next part", e);
|
||||||
|
throw ExceptionUtils.createFileProcessingException("split", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
currentPageCount = 0;
|
||||||
|
log.debug("Reset current page count to 0");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
log.debug("Final document has no pages, skipping");
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("Error checking or saving final document", e);
|
|
||||||
throw ExceptionUtils.createFileProcessingException("split", e);
|
|
||||||
} finally {
|
|
||||||
try {
|
|
||||||
log.debug("Closing final document");
|
|
||||||
currentDoc.close();
|
|
||||||
log.debug("Successfully closed final document");
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("Error closing final document", e);
|
log.error("Error iterating through pages", e);
|
||||||
|
throw ExceptionUtils.createFileProcessingException("split", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the last document if it contains any pages
|
||||||
|
try {
|
||||||
|
if (currentDoc != null && currentDoc.getPages().getCount() != 0) {
|
||||||
|
log.debug(
|
||||||
|
"Saving final document with {} pages as part {}",
|
||||||
|
currentDoc.getPages().getCount(),
|
||||||
|
fileIndex);
|
||||||
|
try {
|
||||||
|
saveDocumentToZip(currentDoc, zipOut, baseFilename, fileIndex++);
|
||||||
|
currentDoc = null; // Document is closed by saveDocumentToZip
|
||||||
|
log.debug("Successfully saved final document part {}", fileIndex - 1);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Error saving final document part {}", fileIndex - 1, e);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.debug("Final document has no pages, skipping");
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Error checking or saving final document", e);
|
||||||
|
throw ExceptionUtils.createFileProcessingException("split", e);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
if (currentDoc != null) {
|
||||||
|
try {
|
||||||
|
log.debug("Closing remaining document");
|
||||||
|
currentDoc.close();
|
||||||
|
log.debug("Successfully closed remaining document");
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Error closing remaining document", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -367,42 +418,52 @@ public class SplitPdfBySizeController {
|
|||||||
|
|
||||||
for (int i = 0; i < documentCount; i++) {
|
for (int i = 0; i < documentCount; i++) {
|
||||||
log.debug("Creating document {} of {}", i + 1, documentCount);
|
log.debug("Creating document {} of {}", i + 1, documentCount);
|
||||||
PDDocument currentDoc;
|
PDDocument currentDoc = null;
|
||||||
try {
|
try {
|
||||||
currentDoc = pdfDocumentFactory.createNewDocumentBasedOnOldDocument(sourceDocument);
|
currentDoc = pdfDocumentFactory.createNewDocumentBasedOnOldDocument(sourceDocument);
|
||||||
log.debug("Successfully created document {} of {}", i + 1, documentCount);
|
log.debug("Successfully created document {} of {}", i + 1, documentCount);
|
||||||
|
|
||||||
|
int pagesToAdd = pagesPerDocument + (i < extraPages ? 1 : 0);
|
||||||
|
log.debug("Adding {} pages to document {}", pagesToAdd, i + 1);
|
||||||
|
|
||||||
|
for (int j = 0; j < pagesToAdd; j++) {
|
||||||
|
try {
|
||||||
|
log.debug(
|
||||||
|
"Adding page {} (index {}) to document {}",
|
||||||
|
j + 1,
|
||||||
|
currentPageIndex,
|
||||||
|
i + 1);
|
||||||
|
currentDoc.addPage(sourceDocument.getPage(currentPageIndex));
|
||||||
|
log.debug("Successfully added page {} to document {}", j + 1, i + 1);
|
||||||
|
currentPageIndex++;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Error adding page {} to document {}", j + 1, i + 1, e);
|
||||||
|
throw ExceptionUtils.createFileProcessingException("split", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
log.debug("Saving document {} with {} pages", i + 1, pagesToAdd);
|
||||||
|
saveDocumentToZip(currentDoc, zipOut, baseFilename, fileIndex++);
|
||||||
|
// saveDocumentToZip closes the document
|
||||||
|
currentDoc = null;
|
||||||
|
log.debug("Successfully saved document {}", i + 1);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Error saving document {}", i + 1, e);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("Error creating document {} of {}", i + 1, documentCount, e);
|
log.error("Error creating document {} of {}", i + 1, documentCount, e);
|
||||||
throw ExceptionUtils.createFileProcessingException("split", e);
|
throw ExceptionUtils.createFileProcessingException("split", e);
|
||||||
}
|
} finally {
|
||||||
|
if (currentDoc != null) {
|
||||||
int pagesToAdd = pagesPerDocument + (i < extraPages ? 1 : 0);
|
try {
|
||||||
log.debug("Adding {} pages to document {}", pagesToAdd, i + 1);
|
currentDoc.close();
|
||||||
|
} catch (IOException e) {
|
||||||
for (int j = 0; j < pagesToAdd; j++) {
|
log.error("Error closing document {} of {}", i + 1, documentCount, e);
|
||||||
try {
|
}
|
||||||
log.debug(
|
|
||||||
"Adding page {} (index {}) to document {}",
|
|
||||||
j + 1,
|
|
||||||
currentPageIndex,
|
|
||||||
i + 1);
|
|
||||||
currentDoc.addPage(sourceDocument.getPage(currentPageIndex));
|
|
||||||
log.debug("Successfully added page {} to document {}", j + 1, i + 1);
|
|
||||||
currentPageIndex++;
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("Error adding page {} to document {}", j + 1, i + 1, e);
|
|
||||||
throw ExceptionUtils.createFileProcessingException("split", e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
|
||||||
log.debug("Saving document {} with {} pages", i + 1, pagesToAdd);
|
|
||||||
saveDocumentToZip(currentDoc, zipOut, baseFilename, fileIndex++);
|
|
||||||
log.debug("Successfully saved document {}", i + 1);
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("Error saving document {}", i + 1, e);
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
log.debug("Completed handleSplitByDocCount with {} documents created", documentCount);
|
log.debug("Completed handleSplitByDocCount with {} documents created", documentCount);
|
||||||
@@ -414,24 +475,15 @@ public class SplitPdfBySizeController {
|
|||||||
log.debug("Starting saveDocumentToZip for document part {}", index);
|
log.debug("Starting saveDocumentToZip for document part {}", index);
|
||||||
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
|
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
|
||||||
|
|
||||||
try {
|
try (PDDocument doc = document) {
|
||||||
log.debug("Saving document part {} to byte array", index);
|
log.debug("Saving document part {} to byte array", index);
|
||||||
document.save(outStream);
|
doc.save(outStream);
|
||||||
log.debug("Successfully saved document part {} ({} bytes)", index, outStream.size());
|
log.debug("Successfully saved document part {} ({} bytes)", index, outStream.size());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("Error saving document part {} to byte array", index, e);
|
log.error("Error saving document part {} to byte array", index, e);
|
||||||
throw ExceptionUtils.createFileProcessingException("split", e);
|
throw ExceptionUtils.createFileProcessingException("split", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
|
||||||
log.debug("Closing document part {}", index);
|
|
||||||
document.close();
|
|
||||||
log.debug("Successfully closed document part {}", index);
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("Error closing document part {}", index, e);
|
|
||||||
// Continue despite close error
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Create a new zip entry
|
// Create a new zip entry
|
||||||
String entryName = baseFilename + "_" + index + ".pdf";
|
String entryName = baseFilename + "_" + index + ".pdf";
|
||||||
|
|||||||
+47
-47
@@ -7,7 +7,6 @@ import java.io.IOException;
|
|||||||
import org.apache.pdfbox.multipdf.LayerUtility;
|
import org.apache.pdfbox.multipdf.LayerUtility;
|
||||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
import org.apache.pdfbox.pdmodel.PDPage;
|
import org.apache.pdfbox.pdmodel.PDPage;
|
||||||
import org.apache.pdfbox.pdmodel.PDPageContentStream;
|
|
||||||
import org.apache.pdfbox.pdmodel.common.PDRectangle;
|
import org.apache.pdfbox.pdmodel.common.PDRectangle;
|
||||||
import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject;
|
import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
@@ -47,53 +46,54 @@ public class ToSinglePageController {
|
|||||||
throws IOException {
|
throws IOException {
|
||||||
|
|
||||||
// Load the source document
|
// Load the source document
|
||||||
PDDocument sourceDocument = pdfDocumentFactory.load(request);
|
try (PDDocument sourceDocument = pdfDocumentFactory.load(request)) {
|
||||||
|
// Calculate total height and max width
|
||||||
|
float totalHeight = 0;
|
||||||
|
float maxWidth = 0;
|
||||||
|
for (PDPage page : sourceDocument.getPages()) {
|
||||||
|
PDRectangle pageSize = page.getMediaBox();
|
||||||
|
totalHeight += pageSize.getHeight();
|
||||||
|
maxWidth = Math.max(maxWidth, pageSize.getWidth());
|
||||||
|
}
|
||||||
|
|
||||||
// Calculate total height and max width
|
// Create new document and page with calculated dimensions
|
||||||
float totalHeight = 0;
|
try (PDDocument newDocument =
|
||||||
float maxWidth = 0;
|
pdfDocumentFactory.createNewDocumentBasedOnOldDocument(sourceDocument)) {
|
||||||
for (PDPage page : sourceDocument.getPages()) {
|
PDPage newPage = new PDPage(new PDRectangle(maxWidth, totalHeight));
|
||||||
PDRectangle pageSize = page.getMediaBox();
|
newDocument.addPage(newPage);
|
||||||
totalHeight += pageSize.getHeight();
|
|
||||||
maxWidth = Math.max(maxWidth, pageSize.getWidth());
|
LayerUtility layerUtility = new LayerUtility(newDocument);
|
||||||
|
float yOffset = totalHeight;
|
||||||
|
|
||||||
|
// For each page, copy its content to the new page at the correct offset
|
||||||
|
try {
|
||||||
|
layerUtility.wrapInSaveRestore(newPage);
|
||||||
|
} catch (NullPointerException e) {
|
||||||
|
}
|
||||||
|
|
||||||
|
int pageIndex = 0;
|
||||||
|
for (PDPage page : sourceDocument.getPages()) {
|
||||||
|
PDFormXObject form = layerUtility.importPageAsForm(sourceDocument, pageIndex);
|
||||||
|
if (form != null) {
|
||||||
|
AffineTransform af =
|
||||||
|
AffineTransform.getTranslateInstance(
|
||||||
|
0, yOffset - page.getMediaBox().getHeight());
|
||||||
|
String defaultLayerName = "Layer" + pageIndex;
|
||||||
|
layerUtility.appendFormAsLayer(newPage, form, af, defaultLayerName);
|
||||||
|
}
|
||||||
|
yOffset -= page.getMediaBox().getHeight();
|
||||||
|
pageIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
newDocument.save(baos);
|
||||||
|
|
||||||
|
byte[] result = baos.toByteArray();
|
||||||
|
return WebResponseUtils.bytesToWebResponse(
|
||||||
|
result,
|
||||||
|
GeneralUtils.generateFilename(
|
||||||
|
request.getFileInput().getOriginalFilename(), "_singlePage.pdf"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create new document and page with calculated dimensions
|
|
||||||
PDDocument newDocument =
|
|
||||||
pdfDocumentFactory.createNewDocumentBasedOnOldDocument(sourceDocument);
|
|
||||||
PDPage newPage = new PDPage(new PDRectangle(maxWidth, totalHeight));
|
|
||||||
newDocument.addPage(newPage);
|
|
||||||
|
|
||||||
// Initialize the content stream of the new page
|
|
||||||
PDPageContentStream contentStream = new PDPageContentStream(newDocument, newPage);
|
|
||||||
contentStream.close();
|
|
||||||
|
|
||||||
LayerUtility layerUtility = new LayerUtility(newDocument);
|
|
||||||
float yOffset = totalHeight;
|
|
||||||
|
|
||||||
// For each page, copy its content to the new page at the correct offset
|
|
||||||
int pageIndex = 0;
|
|
||||||
for (PDPage page : sourceDocument.getPages()) {
|
|
||||||
PDFormXObject form = layerUtility.importPageAsForm(sourceDocument, pageIndex);
|
|
||||||
AffineTransform af =
|
|
||||||
AffineTransform.getTranslateInstance(
|
|
||||||
0, yOffset - page.getMediaBox().getHeight());
|
|
||||||
layerUtility.wrapInSaveRestore(newPage);
|
|
||||||
String defaultLayerName = "Layer" + pageIndex;
|
|
||||||
layerUtility.appendFormAsLayer(newPage, form, af, defaultLayerName);
|
|
||||||
yOffset -= page.getMediaBox().getHeight();
|
|
||||||
pageIndex++;
|
|
||||||
}
|
|
||||||
|
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
|
||||||
newDocument.save(baos);
|
|
||||||
newDocument.close();
|
|
||||||
sourceDocument.close();
|
|
||||||
|
|
||||||
byte[] result = baos.toByteArray();
|
|
||||||
return WebResponseUtils.bytesToWebResponse(
|
|
||||||
result,
|
|
||||||
GeneralUtils.generateFilename(
|
|
||||||
request.getFileInput().getOriginalFilename(), "_singlePage.pdf"));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -91,8 +91,7 @@ public class UIDataController {
|
|||||||
LicensesData data = new LicensesData();
|
LicensesData data = new LicensesData();
|
||||||
Resource resource = new ClassPathResource("static/3rdPartyLicenses.json");
|
Resource resource = new ClassPathResource("static/3rdPartyLicenses.json");
|
||||||
|
|
||||||
try {
|
try (InputStream is = resource.getInputStream()) {
|
||||||
InputStream is = resource.getInputStream();
|
|
||||||
String json = new String(is.readAllBytes(), StandardCharsets.UTF_8);
|
String json = new String(is.readAllBytes(), StandardCharsets.UTF_8);
|
||||||
ObjectMapper mapper = new ObjectMapper();
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
Map<String, List<Dependency>> licenseData =
|
Map<String, List<Dependency>> licenseData =
|
||||||
|
|||||||
+21
-25
@@ -380,7 +380,7 @@ public class ConvertImgPDFController {
|
|||||||
/**
|
/**
|
||||||
* Rearranges the pages of the given PDF document based on the specified page order.
|
* Rearranges the pages of the given PDF document based on the specified page order.
|
||||||
*
|
*
|
||||||
* @param pdfBytes The byte array of the original PDF file.
|
* @param pdfFile The MultipartFile of the original PDF file.
|
||||||
* @param pageOrderArr An array of page numbers indicating the new order.
|
* @param pageOrderArr An array of page numbers indicating the new order.
|
||||||
* @return A byte array of the rearranged PDF.
|
* @return A byte array of the rearranged PDF.
|
||||||
* @throws IOException If an error occurs while processing the PDF.
|
* @throws IOException If an error occurs while processing the PDF.
|
||||||
@@ -388,35 +388,31 @@ public class ConvertImgPDFController {
|
|||||||
private byte[] rearrangePdfPages(MultipartFile pdfFile, String[] pageOrderArr)
|
private byte[] rearrangePdfPages(MultipartFile pdfFile, String[] pageOrderArr)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
// Load the input PDF
|
// Load the input PDF
|
||||||
PDDocument document = pdfDocumentFactory.load(pdfFile);
|
try (PDDocument document = pdfDocumentFactory.load(pdfFile);
|
||||||
int totalPages = document.getNumberOfPages();
|
ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
|
||||||
List<Integer> newPageOrder = GeneralUtils.parsePageList(pageOrderArr, totalPages, false);
|
int totalPages = document.getNumberOfPages();
|
||||||
|
List<Integer> newPageOrder =
|
||||||
|
GeneralUtils.parsePageList(pageOrderArr, totalPages, false);
|
||||||
|
|
||||||
// Create a new list to hold the pages in the new order
|
// Create a new list to hold the pages in the new order
|
||||||
List<PDPage> newPages = new ArrayList<>();
|
List<PDPage> newPages = new ArrayList<>();
|
||||||
for (int pageIndex : newPageOrder) {
|
for (int pageIndex : newPageOrder) {
|
||||||
newPages.add(document.getPage(pageIndex));
|
newPages.add(document.getPage(pageIndex));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove all the pages from the original document
|
// Remove all the pages from the original document
|
||||||
for (int i = document.getNumberOfPages() - 1; i >= 0; i--) {
|
for (int i = document.getNumberOfPages() - 1; i >= 0; i--) {
|
||||||
document.removePage(i);
|
document.removePage(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the pages in the new order
|
// Add the pages in the new order
|
||||||
for (PDPage page : newPages) {
|
for (PDPage page : newPages) {
|
||||||
document.addPage(page);
|
document.addPage(page);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert PDDocument to byte array
|
// Convert PDDocument to byte array
|
||||||
byte[] newPdfBytes;
|
|
||||||
try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
|
|
||||||
document.save(baos);
|
document.save(baos);
|
||||||
newPdfBytes = baos.toByteArray();
|
return baos.toByteArray();
|
||||||
} finally {
|
|
||||||
document.close();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return newPdfBytes;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-11
@@ -1,5 +1,6 @@
|
|||||||
package stirling.software.SPDF.controller.api.converters;
|
package stirling.software.SPDF.controller.api.converters;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.http.HttpClient;
|
import java.net.http.HttpClient;
|
||||||
@@ -106,7 +107,6 @@ public class ConvertWebsiteToPDF {
|
|||||||
|
|
||||||
Path tempOutputFile = null;
|
Path tempOutputFile = null;
|
||||||
Path tempHtmlInput = null;
|
Path tempHtmlInput = null;
|
||||||
PDDocument doc = null;
|
|
||||||
try {
|
try {
|
||||||
// Download the remote content first to ensure we don't allow dangerous schemes
|
// Download the remote content first to ensure we don't allow dangerous schemes
|
||||||
String htmlContent = fetchRemoteHtml(URL);
|
String htmlContent = fetchRemoteHtml(URL);
|
||||||
@@ -140,18 +140,14 @@ public class ConvertWebsiteToPDF {
|
|||||||
.runCommandWithOutputHandling(command);
|
.runCommandWithOutputHandling(command);
|
||||||
|
|
||||||
// Load the PDF using pdfDocumentFactory
|
// Load the PDF using pdfDocumentFactory
|
||||||
doc = pdfDocumentFactory.load(tempOutputFile.toFile());
|
try (PDDocument doc = pdfDocumentFactory.load(tempOutputFile.toFile());
|
||||||
|
ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
|
||||||
|
// Convert URL to a safe filename
|
||||||
|
String outputFilename = convertURLToFileName(URL);
|
||||||
|
|
||||||
// Convert URL to a safe filename
|
doc.save(baos);
|
||||||
String outputFilename = convertURLToFileName(URL);
|
return WebResponseUtils.baosToWebResponse(baos, outputFilename);
|
||||||
|
|
||||||
ResponseEntity<byte[]> response =
|
|
||||||
WebResponseUtils.pdfDocToWebResponse(doc, outputFilename);
|
|
||||||
if (response == null) {
|
|
||||||
// Defensive fallback - should not happen but avoids null returns breaking tests
|
|
||||||
return ResponseEntity.ok(new byte[0]);
|
|
||||||
}
|
}
|
||||||
return response;
|
|
||||||
} finally {
|
} finally {
|
||||||
if (tempHtmlInput != null) {
|
if (tempHtmlInput != null) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
+3
-19
@@ -119,13 +119,9 @@ public class AutoSplitPdfController {
|
|||||||
MultipartFile file = request.getFileInput();
|
MultipartFile file = request.getFileInput();
|
||||||
boolean duplexMode = Boolean.TRUE.equals(request.getDuplexMode());
|
boolean duplexMode = Boolean.TRUE.equals(request.getDuplexMode());
|
||||||
|
|
||||||
PDDocument document = null;
|
|
||||||
List<PDDocument> splitDocuments = new ArrayList<>();
|
List<PDDocument> splitDocuments = new ArrayList<>();
|
||||||
TempFile outputTempFile = null;
|
try (TempFile outputTempFile = new TempFile(tempFileManager, ".zip");
|
||||||
|
PDDocument document = pdfDocumentFactory.load(file.getInputStream())) {
|
||||||
try {
|
|
||||||
outputTempFile = new TempFile(tempFileManager, ".zip");
|
|
||||||
document = pdfDocumentFactory.load(file.getInputStream());
|
|
||||||
PDFRenderer pdfRenderer = new PDFRenderer(document);
|
PDFRenderer pdfRenderer = new PDFRenderer(document);
|
||||||
pdfRenderer.setSubsamplingAllowed(true);
|
pdfRenderer.setSubsamplingAllowed(true);
|
||||||
|
|
||||||
@@ -201,15 +197,7 @@ public class AutoSplitPdfController {
|
|||||||
log.error("Error in auto split", e);
|
log.error("Error in auto split", e);
|
||||||
throw e;
|
throw e;
|
||||||
} finally {
|
} finally {
|
||||||
// Clean up resources
|
// Clean up split documents
|
||||||
if (document != null) {
|
|
||||||
try {
|
|
||||||
document.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
log.error("Error closing main PDDocument", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (PDDocument splitDoc : splitDocuments) {
|
for (PDDocument splitDoc : splitDocuments) {
|
||||||
try {
|
try {
|
||||||
splitDoc.close();
|
splitDoc.close();
|
||||||
@@ -217,10 +205,6 @@ public class AutoSplitPdfController {
|
|||||||
log.error("Error closing split PDDocument", e);
|
log.error("Error closing split PDDocument", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (outputTempFile != null) {
|
|
||||||
outputTempFile.close();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-8
@@ -53,18 +53,20 @@ public class BlankPageController {
|
|||||||
|
|
||||||
// Convert to binary image based on the threshold
|
// Convert to binary image based on the threshold
|
||||||
int whitePixels = 0;
|
int whitePixels = 0;
|
||||||
int totalPixels = image.getWidth() * image.getHeight();
|
int width = image.getWidth();
|
||||||
|
int height = image.getHeight();
|
||||||
|
int[] pixels = new int[width * height];
|
||||||
|
|
||||||
for (int i = 0; i < image.getHeight(); i++) {
|
image.getRGB(0, 0, width, height, pixels, 0, width);
|
||||||
for (int j = 0; j < image.getWidth(); j++) {
|
|
||||||
int color = image.getRGB(j, i) & 0xFF;
|
for (int pixel : pixels) {
|
||||||
if (color >= 255 - threshold) {
|
int blue = pixel & 0xFF;
|
||||||
whitePixels++;
|
if (blue >= 255 - threshold) {
|
||||||
}
|
whitePixels++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
double whitePixelPercentage = (whitePixels / (double) totalPixels) * 100;
|
double whitePixelPercentage = (whitePixels / (double) (width * height)) * 100;
|
||||||
log.info(
|
log.info(
|
||||||
String.format(
|
String.format(
|
||||||
Locale.ROOT,
|
Locale.ROOT,
|
||||||
|
|||||||
+32
-30
@@ -77,38 +77,40 @@ public class PrintFileController {
|
|||||||
log.info("Selected Printer: {}", selectedService.getName());
|
log.info("Selected Printer: {}", selectedService.getName());
|
||||||
|
|
||||||
if (MediaType.APPLICATION_PDF_VALUE.equals(contentType)) {
|
if (MediaType.APPLICATION_PDF_VALUE.equals(contentType)) {
|
||||||
PDDocument document = Loader.loadPDF(file.getBytes());
|
try (PDDocument document = Loader.loadPDF(file.getBytes())) {
|
||||||
PrinterJob job = PrinterJob.getPrinterJob();
|
PrinterJob job = PrinterJob.getPrinterJob();
|
||||||
job.setPrintService(selectedService);
|
job.setPrintService(selectedService);
|
||||||
job.setPageable(new PDFPageable(document));
|
job.setPageable(new PDFPageable(document));
|
||||||
job.print();
|
job.print();
|
||||||
document.close();
|
}
|
||||||
} else if (contentType.startsWith("image/")) {
|
} else if (contentType.startsWith("image/")) {
|
||||||
BufferedImage image = ImageIO.read(file.getInputStream());
|
try (var inputStream = file.getInputStream()) {
|
||||||
PrinterJob job = PrinterJob.getPrinterJob();
|
BufferedImage image = ImageIO.read(inputStream);
|
||||||
job.setPrintService(selectedService);
|
PrinterJob job = PrinterJob.getPrinterJob();
|
||||||
job.setPrintable(
|
job.setPrintService(selectedService);
|
||||||
new Printable() {
|
job.setPrintable(
|
||||||
public int print(
|
new Printable() {
|
||||||
Graphics graphics, PageFormat pageFormat, int pageIndex)
|
public int print(
|
||||||
throws PrinterException {
|
Graphics graphics, PageFormat pageFormat, int pageIndex)
|
||||||
if (pageIndex != 0) {
|
throws PrinterException {
|
||||||
return NO_SUCH_PAGE;
|
if (pageIndex != 0) {
|
||||||
|
return NO_SUCH_PAGE;
|
||||||
|
}
|
||||||
|
Graphics2D g2d = (Graphics2D) graphics;
|
||||||
|
g2d.translate(
|
||||||
|
pageFormat.getImageableX(), pageFormat.getImageableY());
|
||||||
|
g2d.drawImage(
|
||||||
|
image,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
(int) pageFormat.getImageableWidth(),
|
||||||
|
(int) pageFormat.getImageableHeight(),
|
||||||
|
null);
|
||||||
|
return PAGE_EXISTS;
|
||||||
}
|
}
|
||||||
Graphics2D g2d = (Graphics2D) graphics;
|
});
|
||||||
g2d.translate(
|
job.print();
|
||||||
pageFormat.getImageableX(), pageFormat.getImageableY());
|
}
|
||||||
g2d.drawImage(
|
|
||||||
image,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
(int) pageFormat.getImageableWidth(),
|
|
||||||
(int) pageFormat.getImageableHeight(),
|
|
||||||
null);
|
|
||||||
return PAGE_EXISTS;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
job.print();
|
|
||||||
}
|
}
|
||||||
return new ResponseEntity<>(
|
return new ResponseEntity<>(
|
||||||
"File printed successfully to " + selectedService.getName(), HttpStatus.OK);
|
"File printed successfully to " + selectedService.getName(), HttpStatus.OK);
|
||||||
|
|||||||
+6
-6
@@ -133,13 +133,13 @@ public class CertSignController {
|
|||||||
signature.setReason(reason);
|
signature.setReason(reason);
|
||||||
signature.setSignDate(Calendar.getInstance()); // PDFBox requires Calendar
|
signature.setSignDate(Calendar.getInstance()); // PDFBox requires Calendar
|
||||||
if (Boolean.TRUE.equals(showSignature)) {
|
if (Boolean.TRUE.equals(showSignature)) {
|
||||||
SignatureOptions signatureOptions = new SignatureOptions();
|
try (SignatureOptions signatureOptions = new SignatureOptions()) {
|
||||||
signatureOptions.setVisualSignature(
|
signatureOptions.setVisualSignature(
|
||||||
instance.createVisibleSignature(doc, signature, pageNumber, showLogo));
|
instance.createVisibleSignature(doc, signature, pageNumber, showLogo));
|
||||||
signatureOptions.setPage(pageNumber);
|
signatureOptions.setPage(pageNumber);
|
||||||
|
|
||||||
doc.addSignature(signature, instance, signatureOptions);
|
|
||||||
|
|
||||||
|
doc.addSignature(signature, instance, signatureOptions);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
doc.addSignature(signature, instance);
|
doc.addSignature(signature, instance);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,8 +45,7 @@ public class HomeWebController {
|
|||||||
public String licensesForm(Model model) {
|
public String licensesForm(Model model) {
|
||||||
model.addAttribute("currentPage", "licenses");
|
model.addAttribute("currentPage", "licenses");
|
||||||
Resource resource = new ClassPathResource("static/3rdPartyLicenses.json");
|
Resource resource = new ClassPathResource("static/3rdPartyLicenses.json");
|
||||||
try {
|
try (InputStream is = resource.getInputStream()) {
|
||||||
InputStream is = resource.getInputStream();
|
|
||||||
String json = new String(is.readAllBytes(), StandardCharsets.UTF_8);
|
String json = new String(is.readAllBytes(), StandardCharsets.UTF_8);
|
||||||
ObjectMapper mapper = new ObjectMapper();
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
Map<String, List<Dependency>> data = mapper.readValue(json, new TypeReference<>() {});
|
Map<String, List<Dependency>> data = mapper.readValue(json, new TypeReference<>() {});
|
||||||
|
|||||||
+26
-26
@@ -423,38 +423,38 @@ public class CertificateValidationService {
|
|||||||
private void loadBundledMozillaCACerts() {
|
private void loadBundledMozillaCACerts() {
|
||||||
try {
|
try {
|
||||||
log.info("Loading bundled Mozilla CA certificates from resources");
|
log.info("Loading bundled Mozilla CA certificates from resources");
|
||||||
InputStream certStream =
|
try (InputStream certStream =
|
||||||
getClass().getClassLoader().getResourceAsStream("certs/cacert.pem");
|
getClass().getClassLoader().getResourceAsStream("certs/cacert.pem")) {
|
||||||
if (certStream == null) {
|
if (certStream == null) {
|
||||||
log.warn("Bundled Mozilla CA certificate file not found in resources");
|
log.warn("Bundled Mozilla CA certificate file not found in resources");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
CertificateFactory cf = CertificateFactory.getInstance("X.509");
|
CertificateFactory cf = CertificateFactory.getInstance("X.509");
|
||||||
Collection<? extends Certificate> certs = cf.generateCertificates(certStream);
|
Collection<? extends Certificate> certs = cf.generateCertificates(certStream);
|
||||||
certStream.close();
|
|
||||||
|
|
||||||
int loadedCount = 0;
|
int loadedCount = 0;
|
||||||
int skippedCount = 0;
|
int skippedCount = 0;
|
||||||
|
|
||||||
for (Certificate cert : certs) {
|
for (Certificate cert : certs) {
|
||||||
if (cert instanceof X509Certificate x509) {
|
if (cert instanceof X509Certificate x509) {
|
||||||
// Only add CA certificates to trust anchors
|
// Only add CA certificates to trust anchors
|
||||||
if (isCA(x509)) {
|
if (isCA(x509)) {
|
||||||
String fingerprint = sha256Fingerprint(x509);
|
String fingerprint = sha256Fingerprint(x509);
|
||||||
String alias = "mozilla-" + fingerprint;
|
String alias = "mozilla-" + fingerprint;
|
||||||
signingTrustAnchors.setCertificateEntry(alias, x509);
|
signingTrustAnchors.setCertificateEntry(alias, x509);
|
||||||
loadedCount++;
|
loadedCount++;
|
||||||
} else {
|
} else {
|
||||||
skippedCount++;
|
skippedCount++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
log.info(
|
log.info(
|
||||||
"Loaded {} Mozilla CA certificates as trust anchors (skipped {} non-CA certs)",
|
"Loaded {} Mozilla CA certificates as trust anchors (skipped {} non-CA certs)",
|
||||||
loadedCount,
|
loadedCount,
|
||||||
skippedCount);
|
skippedCount);
|
||||||
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("Failed to load bundled Mozilla CA certificates: {}", e.getMessage(), e);
|
log.error("Failed to load bundled Mozilla CA certificates: {}", e.getMessage(), e);
|
||||||
}
|
}
|
||||||
|
|||||||
+118
@@ -0,0 +1,118 @@
|
|||||||
|
package stirling.software.SPDF.controller.api;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyString;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
|
import org.apache.pdfbox.Loader;
|
||||||
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
|
import org.apache.pdfbox.pdmodel.PDPage;
|
||||||
|
import org.apache.pdfbox.pdmodel.common.PDRectangle;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.DisplayName;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.junit.jupiter.api.io.TempDir;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.mock.web.MockMultipartFile;
|
||||||
|
|
||||||
|
import stirling.software.SPDF.model.api.general.SplitPdfBySizeOrCountRequest;
|
||||||
|
import stirling.software.common.service.CustomPDFDocumentFactory;
|
||||||
|
import stirling.software.common.util.TempFileManager;
|
||||||
|
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
|
class SplitPdfBySizeControllerTest {
|
||||||
|
|
||||||
|
@TempDir Path tempDir;
|
||||||
|
@Mock private CustomPDFDocumentFactory pdfDocumentFactory;
|
||||||
|
@Mock private TempFileManager tempFileManager;
|
||||||
|
@InjectMocks private SplitPdfBySizeController controller;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() throws IOException {
|
||||||
|
when(tempFileManager.createTempFile(anyString()))
|
||||||
|
.thenAnswer(
|
||||||
|
invocation -> {
|
||||||
|
String suffix = invocation.getArgument(0);
|
||||||
|
return Files.createTempFile(tempDir, "test", suffix).toFile();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Should split by page count successfully")
|
||||||
|
void shouldSplitByPageCount() throws Exception {
|
||||||
|
byte[] pdfBytes;
|
||||||
|
try (PDDocument doc = new PDDocument()) {
|
||||||
|
for (int i = 0; i < 5; i++) {
|
||||||
|
doc.addPage(new PDPage(PDRectangle.A4));
|
||||||
|
}
|
||||||
|
Path pdfPath = tempDir.resolve("input.pdf");
|
||||||
|
doc.save(pdfPath.toFile());
|
||||||
|
pdfBytes = Files.readAllBytes(pdfPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
MockMultipartFile file =
|
||||||
|
new MockMultipartFile(
|
||||||
|
"fileInput", "input.pdf", MediaType.APPLICATION_PDF_VALUE, pdfBytes);
|
||||||
|
SplitPdfBySizeOrCountRequest request = new SplitPdfBySizeOrCountRequest();
|
||||||
|
request.setFileInput(file);
|
||||||
|
request.setSplitType(1); // Page count
|
||||||
|
request.setSplitValue("2");
|
||||||
|
|
||||||
|
when(pdfDocumentFactory.load(any(byte[].class)))
|
||||||
|
.thenAnswer(inv -> Loader.loadPDF((byte[]) inv.getArgument(0)));
|
||||||
|
|
||||||
|
when(pdfDocumentFactory.createNewDocumentBasedOnOldDocument(any(PDDocument.class)))
|
||||||
|
.thenAnswer(inv -> new PDDocument());
|
||||||
|
|
||||||
|
ResponseEntity<byte[]> response = controller.autoSplitPdf(request);
|
||||||
|
|
||||||
|
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||||
|
assertThat(response.getBody()).isNotEmpty();
|
||||||
|
assertThat(response.getHeaders().getContentType())
|
||||||
|
.isEqualTo(MediaType.APPLICATION_OCTET_STREAM);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Should split by document count successfully")
|
||||||
|
void shouldSplitByDocCount() throws Exception {
|
||||||
|
byte[] pdfBytes;
|
||||||
|
try (PDDocument doc = new PDDocument()) {
|
||||||
|
for (int i = 0; i < 6; i++) {
|
||||||
|
doc.addPage(new PDPage(PDRectangle.A4));
|
||||||
|
}
|
||||||
|
Path pdfPath = tempDir.resolve("input.pdf");
|
||||||
|
doc.save(pdfPath.toFile());
|
||||||
|
pdfBytes = Files.readAllBytes(pdfPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
MockMultipartFile file =
|
||||||
|
new MockMultipartFile(
|
||||||
|
"fileInput", "input.pdf", MediaType.APPLICATION_PDF_VALUE, pdfBytes);
|
||||||
|
SplitPdfBySizeOrCountRequest request = new SplitPdfBySizeOrCountRequest();
|
||||||
|
request.setFileInput(file);
|
||||||
|
request.setSplitType(2); // Document count
|
||||||
|
request.setSplitValue("3"); // Split into 3 docs (2 pages each)
|
||||||
|
|
||||||
|
when(pdfDocumentFactory.load(any(byte[].class)))
|
||||||
|
.thenAnswer(inv -> Loader.loadPDF((byte[]) inv.getArgument(0)));
|
||||||
|
|
||||||
|
when(pdfDocumentFactory.createNewDocumentBasedOnOldDocument(any(PDDocument.class)))
|
||||||
|
.thenAnswer(inv -> new PDDocument());
|
||||||
|
|
||||||
|
ResponseEntity<byte[]> response = controller.autoSplitPdf(request);
|
||||||
|
|
||||||
|
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||||
|
assertThat(response.getBody()).isNotEmpty();
|
||||||
|
}
|
||||||
|
}
|
||||||
+10
-3
@@ -6,6 +6,7 @@ import static org.mockito.ArgumentMatchers.anyString;
|
|||||||
import static org.mockito.ArgumentMatchers.eq;
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
@@ -189,9 +190,12 @@ public class ConvertWebsiteToPdfTest {
|
|||||||
when(mockExec.runCommandWithOutputHandling(cmdCaptor.capture()))
|
when(mockExec.runCommandWithOutputHandling(cmdCaptor.capture()))
|
||||||
.thenReturn(dummyResult);
|
.thenReturn(dummyResult);
|
||||||
|
|
||||||
// Mock WebResponseUtils
|
|
||||||
ResponseEntity<byte[]> fakeResponse = ResponseEntity.ok(new byte[0]);
|
ResponseEntity<byte[]> fakeResponse = ResponseEntity.ok(new byte[0]);
|
||||||
wr.when(() -> WebResponseUtils.pdfDocToWebResponse(any(PDDocument.class), anyString()))
|
|
||||||
|
wr.when(
|
||||||
|
() ->
|
||||||
|
WebResponseUtils.baosToWebResponse(
|
||||||
|
any(ByteArrayOutputStream.class), any()))
|
||||||
.thenReturn(fakeResponse);
|
.thenReturn(fakeResponse);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
@@ -261,7 +265,10 @@ public class ConvertWebsiteToPdfTest {
|
|||||||
|
|
||||||
// WebResponseUtils
|
// WebResponseUtils
|
||||||
ResponseEntity<byte[]> fakeResponse = ResponseEntity.ok(new byte[0]);
|
ResponseEntity<byte[]> fakeResponse = ResponseEntity.ok(new byte[0]);
|
||||||
wr.when(() -> WebResponseUtils.pdfDocToWebResponse(any(PDDocument.class), anyString()))
|
wr.when(
|
||||||
|
() ->
|
||||||
|
WebResponseUtils.baosToWebResponse(
|
||||||
|
any(ByteArrayOutputStream.class), any()))
|
||||||
.thenReturn(fakeResponse);
|
.thenReturn(fakeResponse);
|
||||||
|
|
||||||
// Act: should not throw and should return a Response
|
// Act: should not throw and should return a Response
|
||||||
|
|||||||
+1
-11
@@ -3,8 +3,6 @@ package stirling.software.proprietary.security.session;
|
|||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
@@ -182,15 +180,7 @@ public class SessionPersistentRegistry implements SessionRegistry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Sort sessions by lastRequest in descending order
|
// Sort sessions by lastRequest in descending order
|
||||||
Collections.sort(
|
allSessions.sort((s1, s2) -> s2.getLastRequest().compareTo(s1.getLastRequest()));
|
||||||
allSessions,
|
|
||||||
new Comparator<SessionEntity>() {
|
|
||||||
@Override
|
|
||||||
public int compare(SessionEntity s1, SessionEntity s2) {
|
|
||||||
// Sort by lastRequest in descending order
|
|
||||||
return s2.getLastRequest().compareTo(s1.getLastRequest());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// The first session in the list is the latest session for the given principal name
|
// The first session in the list is the latest session for the given principal name
|
||||||
return Optional.of(allSessions.get(0));
|
return Optional.of(allSessions.get(0));
|
||||||
|
|||||||
Reference in New Issue
Block a user