From e523190f395f5921db54e0b0d3fd21918a0b2ddd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Sz=C3=BCcs?= <127139797+balazs-szucs@users.noreply.github.com> Date: Thu, 12 Feb 2026 00:31:06 +0100 Subject: [PATCH] fix(api): address potential backend resource leaks and improve frontend accessibility (#5678) --- .../software/common/util/PdfUtils.java | 55 +++++---- .../api/SplitPdfBySectionsController.java | 110 +++++++++--------- .../api/misc/OverlayImageController.java | 64 +++++----- frontend/src/core/pages/HomePage.tsx | 2 +- 4 files changed, 119 insertions(+), 112 deletions(-) diff --git a/app/common/src/main/java/stirling/software/common/util/PdfUtils.java b/app/common/src/main/java/stirling/software/common/util/PdfUtils.java index ee0fa3f97..dda00fc40 100644 --- a/app/common/src/main/java/stirling/software/common/util/PdfUtils.java +++ b/app/common/src/main/java/stirling/software/common/util/PdfUtils.java @@ -574,34 +574,39 @@ public class PdfUtils { boolean everyPage) throws IOException { - PDDocument document = pdfDocumentFactory.load(pdfBytes); - - // Get the first page of the PDF - int pages = document.getNumberOfPages(); - for (int i = 0; i < pages; i++) { - PDPage page = document.getPage(i); - try (PDPageContentStream contentStream = - new PDPageContentStream( - document, page, PDPageContentStream.AppendMode.APPEND, true, true)) { - // Create an image object from the image bytes - PDImageXObject image = PDImageXObject.createFromByteArray(document, imageBytes, ""); - // Draw the image onto the page at the specified x and y coordinates - contentStream.drawImage(image, x, y); - log.info("Image successfully overlaid onto PDF"); - if (!everyPage && i == 0) { - break; + try (PDDocument document = pdfDocumentFactory.load(pdfBytes)) { + // Get the first page of the PDF + int pages = document.getNumberOfPages(); + for (int i = 0; i < pages; i++) { + PDPage page = document.getPage(i); + try (PDPageContentStream contentStream = + new PDPageContentStream( + document, + page, + PDPageContentStream.AppendMode.APPEND, + true, + true)) { + // Create an image object from the image bytes + PDImageXObject image = + PDImageXObject.createFromByteArray(document, imageBytes, ""); + // Draw the image onto the page at the specified x and y coordinates + contentStream.drawImage(image, x, y); + log.info("Image successfully overlaid onto PDF"); + if (!everyPage && i == 0) { + break; + } + } catch (IOException e) { + // Log an error message if there is an issue overlaying the image onto the PDF + log.error("Error overlaying image onto PDF", e); + throw e; } - } catch (IOException e) { - // Log an error message if there is an issue overlaying the image onto the PDF - log.error("Error overlaying image onto PDF", e); - throw e; } + // Create a ByteArrayOutputStream to save the PDF to + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + document.save(baos); + log.info("PDF successfully saved to byte array"); + return baos.toByteArray(); } - // Create a ByteArrayOutputStream to save the PDF to - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - document.save(baos); - log.info("PDF successfully saved to byte array"); - return baos.toByteArray(); } public boolean containsTextInFile(PDDocument pdfDocument, String text, String pagesToCheck) diff --git a/app/core/src/main/java/stirling/software/SPDF/controller/api/SplitPdfBySectionsController.java b/app/core/src/main/java/stirling/software/SPDF/controller/api/SplitPdfBySectionsController.java index b755a1d53..2ed917b6c 100644 --- a/app/core/src/main/java/stirling/software/SPDF/controller/api/SplitPdfBySectionsController.java +++ b/app/core/src/main/java/stirling/software/SPDF/controller/api/SplitPdfBySectionsController.java @@ -101,66 +101,68 @@ public class SplitPdfBySectionsController { return WebResponseUtils.baosToWebResponse(baos, filename + ".pdf"); } } else { - TempFile zipTempFile = new TempFile(tempFileManager, ".zip"); - try (ZipOutputStream zipOut = - new ZipOutputStream(Files.newOutputStream(zipTempFile.getPath()))) { - for (int pageIndex = 0; - pageIndex < sourceDocument.getNumberOfPages(); - pageIndex++) { - int pageNum = pageIndex + 1; - if (pagesToSplit.contains(pageIndex)) { - for (int i = 0; i < horiz; i++) { - for (int j = 0; j < verti; j++) { - try (PDDocument subDoc = - pdfDocumentFactory.createNewDocument()) { - LayerUtility subLayerUtility = new LayerUtility(subDoc); - addSingleSectionToTarget( - sourceDocument, - pageIndex, - subDoc, - subLayerUtility, - i, - j, - horiz, - verti); - int sectionNum = i * verti + j + 1; - String entryName = - filename - + "_" - + pageNum - + "_" - + sectionNum - + ".pdf"; - saveDocToZip(subDoc, zipOut, entryName); - } catch (IOException e) { - log.error( - "Error creating section {} for page {}", - (i * verti + j + 1), - pageNum, - e); - throw e; + try (TempFile zipTempFile = new TempFile(tempFileManager, ".zip")) { + try (ZipOutputStream zipOut = + new ZipOutputStream(Files.newOutputStream(zipTempFile.getPath()))) { + for (int pageIndex = 0; + pageIndex < sourceDocument.getNumberOfPages(); + pageIndex++) { + int pageNum = pageIndex + 1; + if (pagesToSplit.contains(pageIndex)) { + for (int i = 0; i < horiz; i++) { + for (int j = 0; j < verti; j++) { + try (PDDocument subDoc = + pdfDocumentFactory.createNewDocument()) { + LayerUtility subLayerUtility = new LayerUtility(subDoc); + addSingleSectionToTarget( + sourceDocument, + pageIndex, + subDoc, + subLayerUtility, + i, + j, + horiz, + verti); + int sectionNum = i * verti + j + 1; + String entryName = + filename + + "_" + + pageNum + + "_" + + sectionNum + + ".pdf"; + saveDocToZip(subDoc, zipOut, entryName); + } catch (IOException e) { + log.error( + "Error creating section {} for page {}", + (i * verti + j + 1), + pageNum, + e); + throw e; + } } } - } - } 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); - } catch (IOException e) { - log.error("Error processing unsplit page {}", pageNum, e); - throw e; + } 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); + } catch (IOException e) { + log.error("Error processing unsplit page {}", pageNum, e); + throw e; + } } } + } catch (IOException e) { + log.error("Error creating ZIP file with split PDF sections", e); + throw e; } - } catch (IOException e) { - log.error("Error creating ZIP file with split PDF sections", e); - throw e; + byte[] zipBytes = Files.readAllBytes(zipTempFile.getPath()); + return WebResponseUtils.bytesToWebResponse( + zipBytes, filename + ".zip", MediaType.APPLICATION_OCTET_STREAM); } - byte[] zipBytes = Files.readAllBytes(zipTempFile.getPath()); - return WebResponseUtils.bytesToWebResponse( - zipBytes, filename + ".zip", MediaType.APPLICATION_OCTET_STREAM); } } catch (Exception e) { log.error("Error splitting PDF file: {}", file.getOriginalFilename(), e); diff --git a/app/core/src/main/java/stirling/software/SPDF/controller/api/misc/OverlayImageController.java b/app/core/src/main/java/stirling/software/SPDF/controller/api/misc/OverlayImageController.java index 663c763ba..3b893c17a 100644 --- a/app/core/src/main/java/stirling/software/SPDF/controller/api/misc/OverlayImageController.java +++ b/app/core/src/main/java/stirling/software/SPDF/controller/api/misc/OverlayImageController.java @@ -55,45 +55,45 @@ public class OverlayImageController { boolean isSvg = SvgOverlayUtil.isSvgImage(imageBytes); - PDDocument document = pdfDocumentFactory.load(pdfBytes); + try (PDDocument document = pdfDocumentFactory.load(pdfBytes)) { + int pages = document.getNumberOfPages(); + for (int i = 0; i < pages; i++) { + PDPage page = document.getPage(i); - int pages = document.getNumberOfPages(); - for (int i = 0; i < pages; i++) { - PDPage page = document.getPage(i); + if (isSvg) { + SvgOverlayUtil.overlaySvgOnPage(document, page, imageBytes, x, y); + } else { + try (PDPageContentStream contentStream = + new PDPageContentStream( + document, + page, + PDPageContentStream.AppendMode.APPEND, + true, + true)) { + PDImageXObject image = + PDImageXObject.createFromByteArray(document, imageBytes, ""); + contentStream.drawImage(image, x, y); + log.info("Image successfully overlaid onto PDF page {}", i); + } + } - if (isSvg) { - SvgOverlayUtil.overlaySvgOnPage(document, page, imageBytes, x, y); - } else { - try (PDPageContentStream contentStream = - new PDPageContentStream( - document, - page, - PDPageContentStream.AppendMode.APPEND, - true, - true)) { - PDImageXObject image = - PDImageXObject.createFromByteArray(document, imageBytes, ""); - contentStream.drawImage(image, x, y); - log.info("Image successfully overlaid onto PDF page {}", i); + if (!everyPage && i == 0) { + break; } } - if (!everyPage && i == 0) { - break; - } + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + document.save(baos); + + byte[] result = baos.toByteArray(); + log.info("PDF with overlaid image successfully created"); + + return WebResponseUtils.bytesToWebResponse( + result, + GeneralUtils.generateFilename( + pdfFile.getOriginalFilename(), "_overlayed.pdf")); } - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - document.save(baos); - document.close(); - - byte[] result = baos.toByteArray(); - log.info("PDF with overlaid image successfully created"); - - return WebResponseUtils.bytesToWebResponse( - result, - GeneralUtils.generateFilename(pdfFile.getOriginalFilename(), "_overlayed.pdf")); - } catch (IOException e) { log.error("Failed to add image to PDF", e); return new ResponseEntity<>(HttpStatus.BAD_REQUEST); diff --git a/frontend/src/core/pages/HomePage.tsx b/frontend/src/core/pages/HomePage.tsx index 2416f4fe5..3d387af30 100644 --- a/frontend/src/core/pages/HomePage.tsx +++ b/frontend/src/core/pages/HomePage.tsx @@ -192,7 +192,7 @@ export default function HomePage() {