Author: tilman
Date: Sat Sep 12 17:55:33 2026
New Revision: 1938154

Log:
PDFBOX-5079: apply the 1-1-1 "hack" only to whitepoints close to D65, by Valery 
Bokov; closes #530

Modified:
   pdfbox/branches/3.0/pdfbox/pom.xml
   
pdfbox/branches/3.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/color/PDCalRGB.java
   
pdfbox/branches/3.0/pdfbox/src/test/java/org/apache/pdfbox/rendering/TestQuality.java

Modified: pdfbox/branches/3.0/pdfbox/pom.xml
==============================================================================
--- pdfbox/branches/3.0/pdfbox/pom.xml  Sat Sep 12 17:55:25 2026        
(r1938153)
+++ pdfbox/branches/3.0/pdfbox/pom.xml  Sat Sep 12 17:55:33 2026        
(r1938154)
@@ -1066,6 +1066,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/3.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/color/PDCalRGB.java
==============================================================================
--- 
pdfbox/branches/3.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/color/PDCalRGB.java
     Sat Sep 12 17:55:25 2026        (r1938153)
+++ 
pdfbox/branches/3.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/color/PDCalRGB.java
     Sat Sep 12 17:55:33 2026        (r1938154)
@@ -77,7 +77,14 @@ public class PDCalRGB extends PDCIEDicti
     @Override
     public float[] toRGB(float[] value)
     {
-        if (isWhitePoint())
+        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/3.0/pdfbox/src/test/java/org/apache/pdfbox/rendering/TestQuality.java
==============================================================================
--- 
pdfbox/branches/3.0/pdfbox/src/test/java/org/apache/pdfbox/rendering/TestQuality.java
       Sat Sep 12 17:55:25 2026        (r1938153)
+++ 
pdfbox/branches/3.0/pdfbox/src/test/java/org/apache/pdfbox/rendering/TestQuality.java
       Sat Sep 12 17:55:33 2026        (r1938154)
@@ -166,7 +166,36 @@ class TestQuality
                     "expected a red-ish gradient pixel but was: " + 
Integer.toHexString(rgb));
         }
     }
-  
+
+    /**
+     * 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
+    void testPDFBox5079() throws IOException
+    {
+        File file = new File(TARGET_PDF_DIR, 
"PDFBOX-5079-PDF2.0imagewithBPC.pdf");
+        try (PDDocument doc = Loader.loadPDF(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;
+            Assertions.assertTrue(red > 200 && green < 50 && blue < 50,
+                    "expected a red pixel but was: " + 
Integer.toHexString(rgb));
+        }
+    }
+
     /**
      * 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

Reply via email to