Seungmin Lee created PDFBOX-6235:
------------------------------------
Summary: JPEGFactory.createFromImage() throws NullPointerException
for 4-component (CMYK) BufferedImages
Key: PDFBOX-6235
URL: https://issues.apache.org/jira/browse/PDFBOX-6235
Project: PDFBox
Issue Type: Bug
Components: PDModel
Affects Versions: 3.0.7 PDFBox, 3.0.1 PDFBox, 2.0.36
Environment: PDFBox 3.0.1
Java: Amazon Corretto 21.0.8+9-LTS (aarch64)
OS: macOS (Apple Silicon)
Reporter: Seungmin Lee
JPEGFactory.createFromImage() fails with a NullPointerException for any
BufferedImage backed by a 4-component CMYK color space -- for example the
result of ImageIO.read() on a CMYK JPEG, which is common for print-ready assets.
Reproduced on 2.0.36, 3.0.1 and 3.0.7 (same failure, only the line number
differs: 396 / 372 / 376). The same unguarded dereference is still present on
trunk.
h3. Root cause
encodeImageToJPEGStream() dereferences the app0JFIF metadata node without a
null check:
{code:java}
Element tree = (Element) data.getAsTree("javax_imageio_jpeg_image_1.0");
Element jfif = (Element) tree.getElementsByTagName("app0JFIF").item(0);
String dpiString = Integer.toString(dpi);
jfif.setAttribute("Xdensity", dpiString); // <-- NPE
jfif.setAttribute("Ydensity", dpiString);
jfif.setAttribute("resUnits", "1");
{code}
The JFIF APP0 segment is only defined for 1-component (grayscale) and
3-component (YCbCr) JPEGs. When the ImageIO JPEG writer encodes a 4-component
image it emits an Adobe APP14 marker instead, so the default metadata tree
contains no "app0JFIF" node and item(0) returns null.
Note that the NPE comes from the metadata of the *re-encoded output*, not from
the input file -- a CMYK input JPEG that does carry a JFIF APP0 marker fails
just the same.
This appears to be an oversight rather than intentionally unsupported input:
getColorSpaceFromAWT() explicitly handles ColorSpace.TYPE_CMYK, so the factory
otherwise looks intended to accept 4-component images.
h3. Steps to reproduce
1. Create a 4-component CMYK JPEG (no attachment needed):
{noformat}
magick -size 200x200 gradient:red-blue -colorspace CMYK cmyk.jpg
{noformat}
Verify with {{file cmyk.jpg}} -> "JPEG image data, baseline, precision 8,
200x200, components 4"
2. Run:
{code:java}
BufferedImage src = ImageIO.read(new File("cmyk.jpg"));
// src.getType() == TYPE_CUSTOM (0)
// src.getColorModel().getNumComponents() == 4
// src.getColorModel().getColorSpace().getType() == ColorSpace.TYPE_CMYK (9)
try (PDDocument doc = new PDDocument()) {
JPEGFactory.createFromImage(doc, src);
}
{code}
h3. Actual result
{noformat}
java.lang.NullPointerException: Cannot invoke
"org.w3c.dom.Element.setAttribute(String, String)" because "jfif" is null
at
org.apache.pdfbox.pdmodel.graphics.image.JPEGFactory.encodeImageToJPEGStream(JPEGFactory.java:376)
at
org.apache.pdfbox.pdmodel.graphics.image.JPEGFactory.createJPEG(JPEGFactory.java:312)
at
org.apache.pdfbox.pdmodel.graphics.image.JPEGFactory.createFromImage(JPEGFactory.java:278)
at
org.apache.pdfbox.pdmodel.graphics.image.JPEGFactory.createFromImage(JPEGFactory.java:255)
at
org.apache.pdfbox.pdmodel.graphics.image.JPEGFactory.createFromImage(JPEGFactory.java:233)
{noformat}
(stack trace from 3.0.7)
h3. Expected result
Either a PDImageXObject is created with a DeviceCMYK color space, or a
descriptive exception is thrown stating that 4-component images are not
supported.
h3. Suggested fix
Guard the dereference. The DPI metadata simply cannot be expressed in a
non-JFIF stream, so skipping those three attributes when jfif is null seems
sufficient:
{code:java}
Element jfif = (Element) tree.getElementsByTagName("app0JFIF").item(0);
if (jfif != null)
{
String dpiString = Integer.toString(dpi);
jfif.setAttribute("Xdensity", dpiString);
jfif.setAttribute("Ydensity", dpiString);
jfif.setAttribute("resUnits", "1"); // 1 = dots/inch
}
{code}
h3. Workaround
When the image does not need resampling, embedding the original bytes with
JPEGFactory.createFromByteArray() avoids the re-encode entirely and also
preserves the DeviceCMYK color space. This is not an option when the image must
be resized.
Possibly related (all older, different symptoms): PDFBOX-2057, PDFBOX-2128,
PDFBOX-3823.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]