Author: tilman
Date: Sat Sep 12 17:55:25 2026
New Revision: 1938153
Log:
PDFBOX-5079: apply the 1-1-1 "hack" only to whitepoints close to D65, by Valery
Bokov; closes #530
Modified:
pdfbox/branches/2.0/pdfbox/pom.xml
pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/color/PDCalRGB.java
pdfbox/branches/2.0/pdfbox/src/test/java/org/apache/pdfbox/rendering/TestQuality.java
Modified: pdfbox/branches/2.0/pdfbox/pom.xml
==============================================================================
--- pdfbox/branches/2.0/pdfbox/pom.xml Sat Sep 12 13:37:12 2026
(r1938152)
+++ pdfbox/branches/2.0/pdfbox/pom.xml Sat Sep 12 17:55:25 2026
(r1938153)
@@ -1021,6 +1021,19 @@
<sha512>5eb020282f3998c7673983625303fb7634a3ca2a2fc65efc7bd123241a7facae999610bbd38d8a6be8fc26752c7241d806d46252448ba600e2beb118f4567cf3</sha512>
</configuration>
</execution>
+ <execution>
+ <id>PDFBOX-5079</id>
+ <phase>generate-test-resources</phase>
+ <goals>
+ <goal>wget</goal>
+ </goals>
+ <configuration>
+
<url>https://issues.apache.org/jira/secure/attachment/13018679/PDF2.0imagewithBPC.pdf</url>
+
<outputDirectory>${project.build.directory}/pdfs</outputDirectory>
+
<outputFileName>PDFBOX-5079-PDF2.0imagewithBPC.pdf</outputFileName>
+
<sha512>f3d0934c9d0babedd76431565bdb1cc1bf6d9f0a5ace7656a9ded53fb1e701dd0d12c784a58ae87098793f57ba5b98cbfb657e047e27d5464b26634511162264</sha512>
+ </configuration>
+ </execution>
</executions>
</plugin>
</plugins>
Modified:
pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/color/PDCalRGB.java
==============================================================================
---
pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/color/PDCalRGB.java
Sat Sep 12 13:37:12 2026 (r1938152)
+++
pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/color/PDCalRGB.java
Sat Sep 12 17:55:25 2026 (r1938153)
@@ -77,7 +77,14 @@ public class PDCalRGB extends PDCIEDicti
@Override
public float[] toRGB(float[] value)
{
- if (wpX == 1 && wpY == 1 && wpZ == 1)
+ if (isD65WhitePoint())
+ {
+ // this is a hack, we simply skip CIE calibration of the RGB value
+ // this works only with whitepoint D65 (0.9505 1.0 1.089)
+ // see PDFBOX-2553
+ return new float[] { value[0], value[1], value[2] };
+ }
+ else
{
float a = value[0];
float b = value[1];
@@ -104,13 +111,27 @@ public class PDCalRGB extends PDCIEDicti
float z = mZA * powAR + mZB * powBG + mZC * powCB;
return convXYZtoRGB(x, y, z);
}
- else
- {
- // this is a hack, we simply skip CIE calibration of the RGB value
- // this works only with whitepoint D65 (0.9505 1.0 1.089)
- // see PDFBOX-2553
- return new float[] { value[0], value[1], value[2] };
- }
+ }
+
+ // real-world producers embed slightly different roundings of D65, e.g.
(0.9505 1.0 1.089),
+ // (0.95045 1.0 1.08905) or (0.951 1.0 1.089); an exact match rejects
those and wrongly
+ // routes them into full calibration instead of the hack they rely on, see
PDFBOX-5079
+ private static final float D65_TOLERANCE = 0.01f;
+
+ /**
+ * Tests if the whitepoint is close enough to D65 (0.9505 1.0 1.089) to be
considered D65,
+ * the only case for which the CIE calibration skip in {@link
#toRGB(float[])} was verified,
+ * see PDFBOX-2553 and PDFBOX-5079. This is a tolerant match rather than
an exact one because
+ * real-world files use slightly different roundings of the D65
whitepoint; the tolerance is
+ * still well clear of genuinely different whitepoints such as D50 (0.9643
1.0 0.8251).
+ *
+ * @return true if the whitepoint is close to D65.
+ */
+ private boolean isD65WhitePoint()
+ {
+ return Math.abs(wpX - 0.9505f) < D65_TOLERANCE &&
+ Math.abs(wpY - 1) < D65_TOLERANCE &&
+ Math.abs(wpZ - 1.089f) < D65_TOLERANCE;
}
/**
Modified:
pdfbox/branches/2.0/pdfbox/src/test/java/org/apache/pdfbox/rendering/TestQuality.java
==============================================================================
---
pdfbox/branches/2.0/pdfbox/src/test/java/org/apache/pdfbox/rendering/TestQuality.java
Sat Sep 12 13:37:12 2026 (r1938152)
+++
pdfbox/branches/2.0/pdfbox/src/test/java/org/apache/pdfbox/rendering/TestQuality.java
Sat Sep 12 17:55:25 2026 (r1938153)
@@ -138,6 +138,34 @@ public class TestQuality
}
/**
+ * PDFBOX-5079: a CalRGB image whose whitepoint is neither (1 1 1) nor D65
must still be CIE
+ * calibrated instead of having its raw component values used directly as
RGB. Before the
+ * fix, {@code PDCalRGB.toRGB()} only performed the calibration for
whitepoint (1 1 1); any
+ * other whitepoint (here, D50 - 0.9643 1.0 0.8251) fell into the D65-only
shortcut meant for
+ * a different, uncalibrated whitepoint, so the image's intended red
rendered as orange
+ * instead, identical to the uncalibrated DeviceRGB image placed alongside
it.
+ *
+ * @throws IOException
+ */
+ @Test
+ public void testPDFBox5079() throws IOException
+ {
+ File file = new File(TARGET_PDF_DIR,
"PDFBOX-5079-PDF2.0imagewithBPC.pdf");
+ PDDocument doc = PDDocument.load(file);
+ PDFRenderer renderer = new PDFRenderer(doc);
+ BufferedImage renderedImage = renderer.renderImageWithDPI(0, 100);
+ // a pixel within the CalRGB image, whose own caption says "should
appear red";
+ // before the fix this was orange, same as the uncalibrated DeviceRGB
image
+ int rgb = renderedImage.getRGB(170, 200);
+ int red = (rgb >> 16) & 0xFF;
+ int green = (rgb >> 8) & 0xFF;
+ int blue = rgb & 0xFF;
+ assertTrue("expected a red pixel but was: " + Integer.toHexString(rgb),
+ red > 200 && green < 50 && blue < 50);
+ doc.close();
+ }
+
+ /**
* PDFBOX-5876: rendering a page containing a very large JPEG 2000 (JPX)
image at reduced
* scale must not decode the image at full resolution first just to read
its width, height
* and color space. Before the fix, {@code PDImageXObject.initJPXValues()}
did exactly that,