From 1736cabb9b6261c42b95100c33d95ede59c8dc9d Mon Sep 17 00:00:00 2001 From: Shane O'Donovan Date: Tue, 21 Jul 2026 12:51:24 +0100 Subject: [PATCH] LDEV-6446 Reduce heap when thumbnailing PDFs with large page images Thumbnails decoded the source page at full resolution before downscaling, so a first page holding a high-resolution image could use hundreds of MB of heap and OOM the rendering pod. Enable PDFRenderer subsampling so the page is decoded near thumbnail resolution, and close the document afterwards. --- .../org/lucee/extension/pdf/util/PDFUtil.java | 34 ++++++++++--------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/source/java/src/org/lucee/extension/pdf/util/PDFUtil.java b/source/java/src/org/lucee/extension/pdf/util/PDFUtil.java index e7129dc..590f997 100755 --- a/source/java/src/org/lucee/extension/pdf/util/PDFUtil.java +++ b/source/java/src/org/lucee/extension/pdf/util/PDFUtil.java @@ -399,27 +399,29 @@ public static void thumbnail(PageContext pc, PDFStruct doc, String destination, CFMLEngine engine = CFMLEngineFactory.getInstance(); - PDDocument pdDoc = doc.toPDDocument(); - int n = pdDoc.getNumberOfPages(); - Iterator it = pageNumbers.iterator(); - int p; + try (PDDocument pdDoc = doc.toPDDocument()) { + int n = pdDoc.getNumberOfPages(); + Iterator it = pageNumbers.iterator(); + int p; - PDFRenderer pdfRender = new PDFRenderer(pdDoc); + PDFRenderer pdfRender = new PDFRenderer(pdDoc); + pdfRender.setSubsamplingAllowed(true); - while (it.hasNext()) { - p = it.next(); + while (it.hasNext()) { + p = it.next(); - if (p > n) throw new RuntimeException("pdf page size [" + p + "] out of range, maximum page size is [" + n + "]"); + if (p > n) throw new RuntimeException("pdf page size [" + p + "] out of range, maximum page size is [" + n + "]"); - // thumbnail image file destination - String imageDestination = destination + "/" + imagePrefix + "_page_" + p + "." + format; + // thumbnail image file destination + String imageDestination = destination + "/" + imagePrefix + "_page_" + p + "." + format; - BufferedImage thumbnailImage = pdfRender.renderImageWithDPI(p - 1, scale); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - ImageIO.write(thumbnailImage, format, baos); // this one not support .tiff format - Resource res = engine.getResourceUtil().toResourceNotExisting(pc, imageDestination); - if (res.exists() && !overwrite) throw new RuntimeException("Thumbnail image file already exists [" + imageDestination + "] and overwrite was false"); - engine.getIOUtil().copy(new ByteArrayInputStream(baos.toByteArray()), res, true); + BufferedImage thumbnailImage = pdfRender.renderImageWithDPI(p - 1, scale); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ImageIO.write(thumbnailImage, format, baos); // this one not support .tiff format + Resource res = engine.getResourceUtil().toResourceNotExisting(pc, imageDestination); + if (res.exists() && !overwrite) throw new RuntimeException("Thumbnail image file already exists [" + imageDestination + "] and overwrite was false"); + engine.getIOUtil().copy(new ByteArrayInputStream(baos.toByteArray()), res, true); + } } }