feat: custom error handling when calling renderImageWithDPI, controllers to respect global DPI (#4407)

This commit is contained in:
Balázs Szücs
2025-09-18 14:43:21 +02:00
committed by GitHub
parent a438a15105
commit c684a51cf9
9 changed files with 237 additions and 9 deletions

View File

@@ -324,4 +324,63 @@ public class ExceptionUtils {
return createIllegalArgumentException(
"error.argumentRequired", "{0} must not be null", argumentName);
}
/**
* Create a RuntimeException for memory/image size errors when rendering PDF images with DPI.
* Handles OutOfMemoryError and related conditions (e.g., NegativeArraySizeException) that
* result from images exceeding Java's array/memory limits.
*
* @param pageNumber the page number that caused the error
* @param dpi the DPI value used
* @param cause the original error/exception (e.g., OutOfMemoryError,
* NegativeArraySizeException)
* @return RuntimeException with user-friendly message
*/
public static RuntimeException createOutOfMemoryDpiException(
int pageNumber, int dpi, Throwable cause) {
String message =
MessageFormat.format(
"Out of memory or image-too-large error while rendering PDF page {0} at {1} DPI. "
+ "This can occur when the resulting image exceeds Java's array/memory limits (e.g., NegativeArraySizeException). "
+ "Please use a lower DPI value (recommended: 150 or less) or process the document in smaller chunks.",
pageNumber, dpi);
return new RuntimeException(message, cause);
}
/**
* Create a RuntimeException for OutOfMemoryError when rendering PDF images with DPI.
*
* @param pageNumber the page number that caused the error
* @param dpi the DPI value used
* @param cause the original OutOfMemoryError
* @return RuntimeException with user-friendly message
*/
public static RuntimeException createOutOfMemoryDpiException(
int pageNumber, int dpi, OutOfMemoryError cause) {
return createOutOfMemoryDpiException(pageNumber, dpi, (Throwable) cause);
}
/**
* Create a RuntimeException for memory/image size errors when rendering PDF images with DPI.
* Handles OutOfMemoryError and related conditions (e.g., NegativeArraySizeException) that
* result from images exceeding Java's array/memory limits.
*
* @param dpi the DPI value used
* @param cause the original error/exception (e.g., OutOfMemoryError,
* NegativeArraySizeException)
* @return RuntimeException with user-friendly message
*/
public static RuntimeException createOutOfMemoryDpiException(int dpi, Throwable cause) {
String message =
MessageFormat.format(
"Out of memory or image-too-large error while rendering PDF at {0} DPI. "
+ "This can occur when the resulting image exceeds Java's array/memory limits (e.g., NegativeArraySizeException). "
+ "Please use a lower DPI value (recommended: 150 or less) or process the document in smaller chunks.",
dpi);
return new RuntimeException(message, cause);
}
public static RuntimeException createOutOfMemoryDpiException(int dpi, OutOfMemoryError cause) {
return createOutOfMemoryDpiException(dpi, (Throwable) cause);
}
}

View File

@@ -205,6 +205,10 @@ public class PdfUtils {
DPI);
}
throw e;
} catch (OutOfMemoryError e) {
throw ExceptionUtils.createOutOfMemoryDpiException(i + 1, DPI, e);
} catch (NegativeArraySizeException e) {
throw ExceptionUtils.createOutOfMemoryDpiException(i + 1, DPI, e);
}
writer.writeToSequence(new IIOImage(image, null, null), param);
}
@@ -253,6 +257,10 @@ public class PdfUtils {
DPI);
}
throw e;
} catch (OutOfMemoryError e) {
throw ExceptionUtils.createOutOfMemoryDpiException(i + 1, DPI, e);
} catch (NegativeArraySizeException e) {
throw ExceptionUtils.createOutOfMemoryDpiException(i + 1, DPI, e);
}
pdfSizeImageIndex = i;
dimension =
@@ -296,6 +304,10 @@ public class PdfUtils {
DPI);
}
throw e;
} catch (OutOfMemoryError e) {
throw ExceptionUtils.createOutOfMemoryDpiException(i + 1, DPI, e);
} catch (NegativeArraySizeException e) {
throw ExceptionUtils.createOutOfMemoryDpiException(i + 1, DPI, e);
}
}
@@ -330,6 +342,10 @@ public class PdfUtils {
DPI);
}
throw e;
} catch (OutOfMemoryError e) {
throw ExceptionUtils.createOutOfMemoryDpiException(i + 1, DPI, e);
} catch (NegativeArraySizeException e) {
throw ExceptionUtils.createOutOfMemoryDpiException(i + 1, DPI, e);
}
try (ByteArrayOutputStream baosImage = new ByteArrayOutputStream()) {
ImageIO.write(image, imageType, baosImage);
@@ -369,8 +385,17 @@ public class PdfUtils {
pdfRenderer.setSubsamplingAllowed(true);
for (int page = 0; page < document.getNumberOfPages(); ++page) {
BufferedImage bim;
// Use global maximum DPI setting, fallback to 300 if not set
int renderDpi = 300; // Default fallback
ApplicationProperties properties =
ApplicationContextProvider.getBean(ApplicationProperties.class);
if (properties != null && properties.getSystem() != null) {
renderDpi = properties.getSystem().getMaxDPI();
}
try {
bim = pdfRenderer.renderImageWithDPI(page, 300, ImageType.RGB);
bim = pdfRenderer.renderImageWithDPI(page, renderDpi, ImageType.RGB);
} catch (IllegalArgumentException e) {
if (e.getMessage() != null
&& e.getMessage().contains("Maximum size of image exceeded")) {
@@ -382,6 +407,10 @@ public class PdfUtils {
page + 1);
}
throw e;
} catch (OutOfMemoryError e) {
throw ExceptionUtils.createOutOfMemoryDpiException(page + 1, 300, e);
} catch (NegativeArraySizeException e) {
throw ExceptionUtils.createOutOfMemoryDpiException(page + 1, 300, e);
}
PDPage originalPage = document.getPage(page);

View File

@@ -19,7 +19,10 @@ import org.apache.pdfbox.rendering.PDFRenderer;
import org.springframework.core.io.InputStreamResource;
import org.springframework.web.multipart.MultipartFile;
import stirling.software.common.model.ApplicationProperties;
import stirling.software.common.model.api.misc.ReplaceAndInvert;
import stirling.software.common.util.ApplicationContextProvider;
import stirling.software.common.util.ExceptionUtils;
public class InvertFullColorStrategy extends ReplaceAndInvertColorStrategy {
@@ -44,8 +47,25 @@ public class InvertFullColorStrategy extends ReplaceAndInvertColorStrategy {
// Render each page and invert colors
PDFRenderer pdfRenderer = new PDFRenderer(document);
for (int page = 0; page < document.getNumberOfPages(); page++) {
BufferedImage image =
pdfRenderer.renderImageWithDPI(page, 300); // Render page at 300 DPI
BufferedImage image;
// Use global maximum DPI setting, fallback to 300 if not set
int renderDpi = 300; // Default fallback
ApplicationProperties properties =
ApplicationContextProvider.getBean(ApplicationProperties.class);
if (properties != null && properties.getSystem() != null) {
renderDpi = properties.getSystem().getMaxDPI();
}
try {
image =
pdfRenderer.renderImageWithDPI(
page, renderDpi); // Render page with global DPI setting
} catch (OutOfMemoryError e) {
throw ExceptionUtils.createOutOfMemoryDpiException(page + 1, renderDpi, e);
} catch (NegativeArraySizeException e) {
throw ExceptionUtils.createOutOfMemoryDpiException(page + 1, renderDpi, e);
}
// Invert the colors
invertImageColors(image);