Author: tilman
Date: Mon Sep 14 19:50:39 2026
New Revision: 1938210
Log:
PDFBOX-6258: clamp colors for shading in the colorspace class, cache ranges,
add test
Modified:
pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/color/PDColorSpace.java
pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/color/PDLab.java
pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/shading/PDShading.java
pdfbox/branches/2.0/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/graphics/color/PDLabTest.java
Modified:
pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/color/PDColorSpace.java
==============================================================================
---
pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/color/PDColorSpace.java
Mon Sep 14 19:50:26 2026 (r1938209)
+++
pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/color/PDColorSpace.java
Mon Sep 14 19:50:39 2026 (r1938210)
@@ -380,6 +380,26 @@ public abstract class PDColorSpace imple
return dest;
}
+ /**
+ * Clamp color values to allowed ranges, usually 0..1.
+ *
+ * @param values
+ */
+ public void clamp(float[] values)
+ {
+ for (int i = 0; i < values.length; ++i)
+ {
+ if (values[i] < 0)
+ {
+ values[i] = 0;
+ }
+ else if (values[i] > 1)
+ {
+ values[i] = 1;
+ }
+ }
+ }
+
@Override
public COSBase getCOSObject()
{
Modified:
pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/color/PDLab.java
==============================================================================
---
pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/color/PDLab.java
Mon Sep 14 19:50:26 2026 (r1938209)
+++
pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/color/PDLab.java
Mon Sep 14 19:50:39 2026 (r1938210)
@@ -35,6 +35,11 @@ public final class PDLab extends PDCIEDi
{
private PDColor initialColor;
+ private float minA = -100;
+ private float maxA = 100;
+ private float minB = -100;
+ private float maxB = 100;
+
/**
* Creates a new Lab color space.
*/
@@ -70,12 +75,6 @@ public final class PDLab extends PDCIEDi
BufferedImage rgbImage = new BufferedImage(width, height,
BufferedImage.TYPE_INT_RGB);
WritableRaster rgbRaster = rgbImage.getRaster();
- PDRange aRange = getARange();
- PDRange bRange = getBRange();
- float minA = aRange.getMin();
- float maxA = aRange.getMax();
- float minB = bRange.getMin();
- float maxB = bRange.getMax();
float deltaA = maxA - minA;
float deltaB = maxB - minB;
@@ -158,9 +157,7 @@ public final class PDLab extends PDCIEDi
@Override
public float[] getDefaultDecode(int bitsPerComponent)
{
- PDRange a = getARange();
- PDRange b = getBRange();
- return new float[] { 0, 100, a.getMin(), a.getMax(), b.getMin(),
b.getMax() };
+ return new float[] { 0, 100, minA, maxA, minB, maxB };
}
@Override
@@ -170,8 +167,8 @@ public final class PDLab extends PDCIEDi
{
initialColor = new PDColor(new float[] {
0,
- Math.max(0, getARange().getMin()),
- Math.max(0, getBRange().getMin()) },
+ Math.max(0, minA),
+ Math.max(0, minB) },
this);
}
return initialColor;
@@ -263,6 +260,50 @@ public final class PDLab extends PDCIEDi
}
dictionary.setItem(COSName.RANGE, rangeArray);
initialColor = null;
+ cacheRanges();
+ }
+
+ // called at the beginning and when it's changed
+ private void cacheRanges()
+ {
+ PDRange aRange = getARange();
+ PDRange bRange = getBRange();
+ minA = aRange.getMin();
+ maxA = aRange.getMax();
+ minB = bRange.getMin();
+ maxB = bRange.getMax();
}
+ @Override
+ public void clamp(float[] values)
+ {
+ // assumption: 3 elements: L a b
+ // "The range of the first (L*) component shall be 0 to 100;
+ // the ranges of the second and third (a* and b*) components shall be
defined by
+ // the Range entry in the colour space dictionary"
+ if (values[0] < 0)
+ {
+ values[0] = 0;
+ }
+ else if (values[0] > 100)
+ {
+ values[0] = 100;
+ }
+ if (values[1] < minA)
+ {
+ values[1] = minA;
+ }
+ else if (values[1] > maxA)
+ {
+ values[1] = maxA;
+ }
+ if (values[2] < minB)
+ {
+ values[2] = minB;
+ }
+ else if (values[2] > maxB)
+ {
+ values[2] = maxB;
+ }
+ }
}
Modified:
pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/shading/PDShading.java
==============================================================================
---
pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/shading/PDShading.java
Mon Sep 14 19:50:26 2026 (r1938209)
+++
pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/shading/PDShading.java
Mon Sep 14 19:50:39 2026 (r1938210)
@@ -421,17 +421,7 @@ public abstract class PDShading implemen
// From the PDF spec:
// "If the value returned by the function for a given colour component
// is out of range, it shall be adjusted to the nearest valid value."
- for (int i = 0; i < returnValues.length; ++i)
- {
- if (returnValues[i] < 0)
- {
- returnValues[i] = 0;
- }
- else if (returnValues[i] > 1)
- {
- returnValues[i] = 1;
- }
- }
+ colorSpace.clamp(returnValues);
return returnValues;
}
Modified:
pdfbox/branches/2.0/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/graphics/color/PDLabTest.java
==============================================================================
---
pdfbox/branches/2.0/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/graphics/color/PDLabTest.java
Mon Sep 14 19:50:26 2026 (r1938209)
+++
pdfbox/branches/2.0/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/graphics/color/PDLabTest.java
Mon Sep 14 19:50:39 2026 (r1938210)
@@ -16,22 +16,29 @@
package org.apache.pdfbox.pdmodel.graphics.color;
import java.util.Arrays;
-import junit.framework.TestCase;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
import org.apache.pdfbox.cos.COSArray;
import org.apache.pdfbox.cos.COSDictionary;
import org.apache.pdfbox.pdmodel.common.PDRange;
+import org.junit.Test;
+
/**
*
* @author Tilman Hausherr
*/
-public class PDLabTest extends TestCase
+public class PDLabTest
{
/**
* This test checks that getting default values do not alter the object,
* and checks getters and setters.
*/
+ @Test
public void testLAB()
{
PDLab pdLab = new PDLab();
@@ -43,16 +50,16 @@ public class PDLabTest extends TestCase
assertEquals(3, pdLab.getNumberOfComponents());
assertNotNull(pdLab.getInitialColor());
assertTrue(Arrays.equals(new float[]{0,0,0},
pdLab.getInitialColor().getComponents()));
- assertEquals(0f, pdLab.getBlackPoint().getX());
- assertEquals(0f, pdLab.getBlackPoint().getY());
- assertEquals(0f, pdLab.getBlackPoint().getZ());
- assertEquals(1f, pdLab.getWhitepoint().getX());
- assertEquals(1f, pdLab.getWhitepoint().getY());
- assertEquals(1f, pdLab.getWhitepoint().getZ());
- assertEquals(-100f, pdLab.getARange().getMin());
- assertEquals(100f, pdLab.getARange().getMax());
- assertEquals(-100f, pdLab.getBRange().getMin());
- assertEquals(100f, pdLab.getBRange().getMax());
+ assertEquals(0f, pdLab.getBlackPoint().getX(), 0);
+ assertEquals(0f, pdLab.getBlackPoint().getY(), 0);
+ assertEquals(0f, pdLab.getBlackPoint().getZ(), 0);
+ assertEquals(1f, pdLab.getWhitepoint().getX(), 0);
+ assertEquals(1f, pdLab.getWhitepoint().getY(), 0);
+ assertEquals(1f, pdLab.getWhitepoint().getZ(), 0);
+ assertEquals(-100f, pdLab.getARange().getMin(), 0);
+ assertEquals(100f, pdLab.getARange().getMax(), 0);
+ assertEquals(-100f, pdLab.getBRange().getMin(), 0);
+ assertEquals(100f, pdLab.getBRange().getMax(), 0);
assertEquals("read operations should not change the size of /Lab
objects", 0, dict.size());
dict.toString(); // rev 1571125 did a stack overflow here
@@ -65,10 +72,10 @@ public class PDLabTest extends TestCase
pdRange.setMin(3);
pdRange.setMax(4);
pdLab.setBRange(pdRange);
- assertEquals(-1f, pdLab.getARange().getMin());
- assertEquals(2f, pdLab.getARange().getMax());
- assertEquals(3f, pdLab.getBRange().getMin());
- assertEquals(4f, pdLab.getBRange().getMax());
+ assertEquals(-1f, pdLab.getARange().getMin(), 0);
+ assertEquals(2f, pdLab.getARange().getMax(), 0);
+ assertEquals(3f, pdLab.getBRange().getMin(), 0);
+ assertEquals(4f, pdLab.getBRange().getMax(), 0);
PDTristimulus pdTristimulus = new PDTristimulus();
pdTristimulus.setX(5);
pdTristimulus.setY(6);
@@ -79,13 +86,41 @@ public class PDLabTest extends TestCase
pdTristimulus.setY(9);
pdTristimulus.setZ(10);
pdLab.setBlackPoint(pdTristimulus);
- assertEquals(5f, pdLab.getWhitepoint().getX());
- assertEquals(6f, pdLab.getWhitepoint().getY());
- assertEquals(7f, pdLab.getWhitepoint().getZ());
- assertEquals(8f, pdLab.getBlackPoint().getX());
- assertEquals(9f, pdLab.getBlackPoint().getY());
- assertEquals(10f, pdLab.getBlackPoint().getZ());
+ assertEquals(5f, pdLab.getWhitepoint().getX(), 0);
+ assertEquals(6f, pdLab.getWhitepoint().getY(), 0);
+ assertEquals(7f, pdLab.getWhitepoint().getZ(), 0);
+ assertEquals(8f, pdLab.getBlackPoint().getX(), 0);
+ assertEquals(9f, pdLab.getBlackPoint().getY(), 0);
+ assertEquals(10f, pdLab.getBlackPoint().getZ(), 0);
assertTrue(Arrays.equals(new float[]{0,0,3},
pdLab.getInitialColor().getComponents()));
}
+ @Test
+ public void testClamp()
+ {
+ PDLab lab = new PDLab();
+ PDRange aRange = new PDRange();
+ aRange.setMin(-160);
+ aRange.setMax(160);
+ lab.setARange(aRange);
+ PDRange bRange = new PDRange();
+ bRange.setMin(-160);
+ bRange.setMax(160);
+ lab.setBRange(bRange);
+ float[] lab1 = new float[]{-101, -161, -161};
+ float[] lab2 = new float[]{-100, -160, -160};
+ float[] lab3 = new float[]{0, 0, 0};
+ float[] lab4 = new float[]{100, 160, 160};
+ float[] lab5 = new float[]{101, 161, 161};
+ lab.clamp(lab1);
+ lab.clamp(lab2);
+ lab.clamp(lab3);
+ lab.clamp(lab4);
+ lab.clamp(lab5);
+ assertEquals("[0.0, -160.0, -160.0]", Arrays.toString(lab1));
+ assertEquals("[0.0, -160.0, -160.0]", Arrays.toString(lab2));
+ assertEquals("[0.0, 0.0, 0.0]", Arrays.toString(lab3));
+ assertEquals("[100.0, 160.0, 160.0]", Arrays.toString(lab4));
+ assertEquals("[100.0, 160.0, 160.0]", Arrays.toString(lab5));
+ }
}