PdfShadingTest.java
/*
This file is part of the iText (R) project.
Copyright (c) 1998-2025 Apryse Group NV
Authors: Apryse Software.
This program is offered under a commercial and under the AGPL license.
For commercial licensing, contact us at https://itextpdf.com/sales. For AGPL licensing, see below.
AGPL licensing:
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.itextpdf.kernel.pdf.colorspace;
import com.itextpdf.kernel.exceptions.KernelExceptionMessageConstant;
import com.itextpdf.kernel.exceptions.PdfException;
import com.itextpdf.kernel.pdf.CompressionConstants;
import com.itextpdf.kernel.pdf.PdfArray;
import com.itextpdf.kernel.pdf.PdfDictionary;
import com.itextpdf.kernel.pdf.PdfName;
import com.itextpdf.kernel.pdf.PdfNumber;
import com.itextpdf.kernel.pdf.PdfObject;
import com.itextpdf.kernel.pdf.PdfStream;
import com.itextpdf.kernel.pdf.colorspace.PdfDeviceCs.Rgb;
import com.itextpdf.kernel.pdf.colorspace.shading.PdfAxialShading;
import com.itextpdf.kernel.pdf.colorspace.shading.PdfCoonsPatchShading;
import com.itextpdf.kernel.pdf.colorspace.shading.PdfFreeFormGouraudShadedTriangleShading;
import com.itextpdf.kernel.pdf.colorspace.shading.PdfFunctionBasedShading;
import com.itextpdf.kernel.pdf.colorspace.shading.PdfLatticeFormGouraudShadedTriangleShading;
import com.itextpdf.kernel.pdf.colorspace.shading.AbstractPdfShading;
import com.itextpdf.kernel.pdf.colorspace.shading.PdfRadialShading;
import com.itextpdf.kernel.pdf.colorspace.shading.ShadingType;
import com.itextpdf.kernel.pdf.colorspace.shading.PdfTensorProductPatchShading;
import com.itextpdf.kernel.pdf.colorspace.PdfSpecialCs.Pattern;
import com.itextpdf.kernel.pdf.function.IPdfFunction;
import com.itextpdf.kernel.pdf.function.PdfType4Function;
import com.itextpdf.test.ExtendedITextTest;
import java.nio.charset.StandardCharsets;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Tag;
@Tag("UnitTest")
public class PdfShadingTest extends ExtendedITextTest {
@Test
public void axialShadingConstructorNullExtendArgumentTest() {
boolean[] extendArray = null;
Rgb color = new Rgb();
Exception e = Assertions.assertThrows(IllegalArgumentException.class,
() -> new PdfAxialShading(color, 0f, 0f, new float[] {0f, 0f, 0f}, 0.5f, 0.5f, new float[] {0.5f, 0.5f, 0.5f},
extendArray));
Assertions.assertEquals("extend", e.getMessage());
}
@Test
public void axialShadingConstructorInvalidExtendArgumentTest() {
boolean[] extendArray = new boolean[] {true};
Rgb color = new Rgb();
Exception e = Assertions.assertThrows(IllegalArgumentException.class,
() -> new PdfAxialShading(color, 0f, 0f, new float[] {0f, 0f, 0f}, 0.5f, 0.5f, new float[] {0.5f, 0.5f, 0.5f},
extendArray));
Assertions.assertEquals("extend", e.getMessage());
}
@Test
public void radialShadingConstructorNullExtendArgumentTest() {
boolean[] extendArray = null;
Rgb color = new Rgb();
Exception e = Assertions.assertThrows(IllegalArgumentException.class,
() -> new PdfRadialShading(color, 0f, 0f, 0f, new float[] {0f, 0f, 0f}, 0.5f, 0.5f, 10f,
new float[] {0.5f, 0.5f, 0.5f}, extendArray));
Assertions.assertEquals("extend", e.getMessage());
}
@Test
public void radialShadingConstructorInvalidExtendArgumentTest() {
boolean[] extendArray = new boolean[] {true, false, false};
Rgb color = new Rgb();
Exception e = Assertions.assertThrows(IllegalArgumentException.class,
() -> new PdfRadialShading(color, 0f, 0f, 0f, new float[] {0f, 0f, 0f}, 0.5f, 0.5f, 10f,
new float[] {0.5f, 0.5f, 0.5f}, extendArray));
Assertions.assertEquals("extend", e.getMessage());
}
@Test
public void axialShadingGettersTest() {
float[] coordsArray = {0f, 0f, 0.5f, 0.5f};
float[] domainArray = {0f, 0.8f};
boolean[] extendArray = {true, false};
PdfDictionary axialShadingDictionary = initShadingDictionary(coordsArray, domainArray, extendArray,
ShadingType.AXIAL);
PdfAxialShading axial = new PdfAxialShading(axialShadingDictionary);
Assertions.assertArrayEquals(coordsArray, axial.getCoords().toFloatArray(), 0f);
Assertions.assertArrayEquals(domainArray, axial.getDomain().toFloatArray(), 0f);
Assertions.assertArrayEquals(extendArray, axial.getExtend().toBooleanArray());
Assertions.assertEquals(ShadingType.AXIAL, axial.getShadingType());
Assertions.assertEquals(PdfName.DeviceRGB, axial.getColorSpace());
}
@Test
public void setFunctionsTest() {
float[] coordsArray = {0f, 0f, 0.5f, 0.5f};
float[] domainArray = {0f, 0.8f};
boolean[] extendArray = {true, false};
PdfDictionary axialShadingDictionary = initShadingDictionary(coordsArray, domainArray, extendArray, ShadingType.AXIAL);
PdfAxialShading axial = new PdfAxialShading(axialShadingDictionary);
Assertions.assertTrue(axial.getFunction() instanceof PdfDictionary);
byte[] ps = "{2 copy sin abs sin abs 3 index 10 mul sin 1 sub abs}".getBytes(StandardCharsets.ISO_8859_1);
float[] domain = new float[] {0, 1000, 0, 1000};
float[] range = new float[] {0, 1, 0, 1, 0, 1};
IPdfFunction[] functions = new IPdfFunction[] {new PdfType4Function(domain, range, ps)};
axial.setFunction(functions);
final PdfObject funcObj = axial.getFunction();
Assertions.assertTrue(funcObj instanceof PdfArray);
Assertions.assertEquals(1, ((PdfArray) funcObj).size());
Assertions.assertEquals(functions[0].getAsPdfObject(), ((PdfArray) funcObj).get(0));
}
@Test
public void axialShadingViaPdfObjectTest() {
float[] coordsArray = {0f, 0f, 0.5f, 0.5f};
float[] domainArray = {0f, 0.8f};
boolean[] extendArray = {true, false};
PdfDictionary axialShadingDictionary = initShadingDictionary(coordsArray, domainArray, extendArray,
ShadingType.AXIAL);
PdfAxialShading axial = (PdfAxialShading) AbstractPdfShading.makeShading(axialShadingDictionary);
Assertions.assertArrayEquals(coordsArray, axial.getCoords().toFloatArray(), 0f);
Assertions.assertArrayEquals(domainArray, axial.getDomain().toFloatArray(), 0f);
Assertions.assertArrayEquals(extendArray, axial.getExtend().toBooleanArray());
Assertions.assertEquals(ShadingType.AXIAL, axial.getShadingType());
}
@Test
public void axialShadingGettersWithDomainExtendDefaultValuesTest() {
float[] coordsArray = {0f, 0f, 0.5f, 0.5f};
float[] defaultDomainArray = {0f, 1f};
boolean[] defaultExtendArray = {false, false};
PdfDictionary axialShadingDictionary = initShadingDictionary(coordsArray, null, null, ShadingType.AXIAL);
PdfAxialShading axial = new PdfAxialShading(axialShadingDictionary);
Assertions.assertArrayEquals(coordsArray, axial.getCoords().toFloatArray(), 0f);
Assertions.assertArrayEquals(defaultDomainArray, axial.getDomain().toFloatArray(), 0f);
Assertions.assertArrayEquals(defaultExtendArray, axial.getExtend().toBooleanArray());
Assertions.assertEquals(ShadingType.AXIAL, axial.getShadingType());
}
@Test
public void radialShadingGettersTest() {
float[] coordsArray = {0f, 0f, 0f, 0.5f, 0.5f, 10f};
float[] domainArray = {0f, 0.8f};
boolean[] extendArray = {true, false};
PdfDictionary radialShadingDictionary = initShadingDictionary(coordsArray, domainArray, extendArray,
ShadingType.RADIAL);
PdfRadialShading radial = new PdfRadialShading(radialShadingDictionary);
Assertions.assertArrayEquals(coordsArray, radial.getCoords().toFloatArray(), 0f);
Assertions.assertArrayEquals(domainArray, radial.getDomain().toFloatArray(), 0f);
Assertions.assertArrayEquals(extendArray, radial.getExtend().toBooleanArray());
Assertions.assertEquals(ShadingType.RADIAL, radial.getShadingType());
}
@Test
public void radialShadingViaMakeShadingTest() {
float[] coordsArray = {0f, 0f, 0f, 0.5f, 0.5f, 10f};
float[] domainArray = {0f, 0.8f};
boolean[] extendArray = {true, false};
PdfDictionary radialShadingDictionary = initShadingDictionary(coordsArray, domainArray, extendArray,
ShadingType.RADIAL);
PdfRadialShading radial = (PdfRadialShading) AbstractPdfShading.makeShading(radialShadingDictionary);
Assertions.assertArrayEquals(coordsArray, radial.getCoords().toFloatArray(), 0f);
Assertions.assertArrayEquals(domainArray, radial.getDomain().toFloatArray(), 0f);
Assertions.assertArrayEquals(extendArray, radial.getExtend().toBooleanArray());
Assertions.assertEquals(ShadingType.RADIAL, radial.getShadingType());
}
@Test
public void radialShadingGettersWithDomainExtendDefaultValuesTest() {
float[] coordsArray = {0f, 0f, 0f, 0.5f, 0.5f, 10f};
float[] defaultDomainArray = {0f, 1f};
boolean[] defaultExtendArray = {false, false};
PdfDictionary radialShadingDictionary = initShadingDictionary(coordsArray, null, null, ShadingType.RADIAL);
PdfRadialShading radial = new PdfRadialShading(radialShadingDictionary);
Assertions.assertArrayEquals(coordsArray, radial.getCoords().toFloatArray(), 0f);
Assertions.assertArrayEquals(defaultDomainArray, radial.getDomain().toFloatArray(), 0f);
Assertions.assertArrayEquals(defaultExtendArray, radial.getExtend().toBooleanArray());
Assertions.assertEquals(ShadingType.RADIAL, radial.getShadingType());
}
@Test
public void makeShadingShouldFailOnMissingShadeType() {
PdfDictionary shade = new PdfDictionary();
shade.put(PdfName.ColorSpace, new PdfArray());
Exception error = Assertions.assertThrows(PdfException.class, () -> AbstractPdfShading.makeShading(shade));
Assertions.assertEquals(KernelExceptionMessageConstant.SHADING_TYPE_NOT_FOUND, error.getMessage());
}
@Test
public void makeShadingShouldFailOnMissingColorSpace() {
PdfDictionary shade = new PdfDictionary();
shade.put(PdfName.ShadingType, new PdfArray());
Exception error = Assertions.assertThrows(PdfException.class, () -> AbstractPdfShading.makeShading(shade));
Assertions.assertEquals(KernelExceptionMessageConstant.COLOR_SPACE_NOT_FOUND, error.getMessage());
}
@Test
public void usingPatternColorSpaceThrowsException() {
byte[] ps = "{2 copy sin abs sin abs 3 index 10 mul sin 1 sub abs}".getBytes(StandardCharsets.ISO_8859_1);
IPdfFunction function = new PdfType4Function(new float[] {0, 1000, 0, 1000},
new float[] {0, 1, 0, 1, 0, 1}, ps);
Pattern colorSpace = new Pattern();
Exception ex = Assertions.assertThrows(IllegalArgumentException.class,
() -> new PdfFunctionBasedShading(colorSpace, function));
Assertions.assertEquals("colorSpace", ex.getMessage());
}
@Test
public void makeShadingFunctionBased1Test() {
byte[] ps = "{2 copy sin abs sin abs 3 index 10 mul sin 1 sub abs}".getBytes(StandardCharsets.ISO_8859_1);
float[] domain = new float[] {0, 1000, 0, 1000};
float[] range = new float[] {0, 1, 0, 1, 0, 1};
IPdfFunction function = new PdfType4Function(domain,
range, ps);
PdfFunctionBasedShading shade = new PdfFunctionBasedShading(new PdfDeviceCs.Rgb(), function);
PdfDictionary object = shade.getPdfObject();
Assertions.assertEquals(1, object.getAsInt(PdfName.ShadingType).intValue());
Assertions.assertEquals(PdfName.DeviceRGB, object.getAsName(PdfName.ColorSpace));
PdfStream functionStream = object.getAsStream(PdfName.Function);
PdfArray functionDomain = functionStream.getAsArray(PdfName.Domain);
Assertions.assertArrayEquals(domain, functionDomain.toFloatArray(), 0.0f);
PdfArray functionRange = functionStream.getAsArray(PdfName.Range);
Assertions.assertArrayEquals(range, functionRange.toFloatArray(), 0.0f);
Assertions.assertEquals(4, functionStream.getAsInt(PdfName.FunctionType).intValue());
}
@Test
public void makeShadingFunctionBased2Test() {
byte[] ps = "{2 copy sin abs sin abs 3 index 10 mul sin 1 sub abs}".getBytes(StandardCharsets.ISO_8859_1);
PdfArray domain = new PdfArray(new float[] {0, 1000, 0, 1000});
PdfArray range = new PdfArray(new float[] {0, 1, 0, 1, 0, 1});
PdfDictionary shadingDict = new PdfDictionary();
shadingDict.put(PdfName.ShadingType, new PdfNumber(1));
shadingDict.put(PdfName.ColorSpace, PdfName.DeviceRGB);
PdfStream stream = new PdfStream();
stream.put(PdfName.Domain, domain);
stream.put(PdfName.Range, range);
stream.put(PdfName.FunctionType, new PdfNumber(4));
shadingDict.put(PdfName.Function, stream);
stream.setData(ps);
shadingDict.put(PdfName.Function, stream);
AbstractPdfShading shade = AbstractPdfShading.makeShading(shadingDict);
PdfDictionary object = shade.getPdfObject();
Assertions.assertEquals(1, object.getAsInt(PdfName.ShadingType).intValue());
Assertions.assertEquals(PdfName.DeviceRGB, object.getAsName(PdfName.ColorSpace));
PdfStream functionStream = object.getAsStream(PdfName.Function);
PdfArray functionDomain = functionStream.getAsArray(PdfName.Domain);
Assertions.assertArrayEquals(domain.toDoubleArray(), functionDomain.toDoubleArray(), 0.0);
PdfArray functionRange = functionStream.getAsArray(PdfName.Range);
Assertions.assertArrayEquals(range.toDoubleArray(), functionRange.toDoubleArray(), 0.0);
Assertions.assertEquals(4, functionStream.getAsInt(PdfName.FunctionType).intValue());
Assertions.assertEquals(functionStream, shade.getFunction());
}
@Test
public void makeShadingWithInvalidShadeType() {
float[] coordsArray = {0f, 0f, 0f, 0.5f, 0.5f, 10f};
PdfDictionary radialShadingDictionary = initShadingDictionary(coordsArray, null, null, 21);
Exception e = Assertions.assertThrows(PdfException.class, () -> AbstractPdfShading.makeShading(radialShadingDictionary));
Assertions.assertEquals(KernelExceptionMessageConstant.UNEXPECTED_SHADING_TYPE, e.getMessage());
}
@Test
public void makeFreeFormGouraudShadedTriangleMeshTest() {
int x = 36;
int y = 400;
// Side of an equilateral triangle
int side = 500;
byte[] data = toMultiWidthBytes(new int[] {1, 4, 4, 1, 1, 1}, 0, 0, 0, 250, 0, 0, 0, side, 0, 0, 250, 0, 0,
side / 4, (int) (y - (side * Math.sin(Math.PI / 3))), 0, 0, 250);
PdfStream stream = new PdfStream(data, CompressionConstants.DEFAULT_COMPRESSION);
stream.put(PdfName.ColorSpace, PdfName.DeviceRGB);
stream.put(PdfName.ShadingType, new PdfNumber(4));
stream.put(PdfName.BitsPerCoordinate, new PdfNumber(32));
stream.put(PdfName.BitsPerComponent, new PdfNumber(8));
stream.put(PdfName.BitsPerFlag, new PdfNumber(8));
stream.put(PdfName.Decode,
new PdfArray(new float[] {x, x + side, y, y + (int) (side * Math.sin(Math.PI / 3)), 0, 1, 0, 1, 0, 1}));
stream.put(PdfName.Matrix, new PdfArray(new float[] {1, 0, 0, -1, 0, 0}));
PdfFreeFormGouraudShadedTriangleShading shade = (PdfFreeFormGouraudShadedTriangleShading) AbstractPdfShading.makeShading(stream);
Assertions.assertEquals(PdfName.DeviceRGB, shade.getColorSpace());
Assertions.assertEquals(4, shade.getShadingType());
Assertions.assertEquals(32, shade.getBitsPerCoordinate());
Assertions.assertEquals(8, shade.getBitsPerComponent());
Assertions.assertEquals(8, shade.getBitsPerFlag());
Assertions.assertEquals(y, shade.getDecode().getAsNumber(2).intValue());
}
@Test
public void makeLatticeFormGouraudShadedTriangleMeshTest() {
int x = 36;
int y = 400;
// Side of an equilateral triangle
int side = 500;
byte[] data = toMultiWidthBytes(new int[] {4, 4, 1, 1, 1}, 500, 0, 250, 0, 0, 500, 500, 0, 250, 0, 0, 0, 0, 0,
250, 0, 500, 250, 0, 0);
PdfStream stream = new PdfStream(data, CompressionConstants.DEFAULT_COMPRESSION);
stream.put(PdfName.ColorSpace, PdfName.DeviceRGB);
stream.put(PdfName.ShadingType, new PdfNumber(5));
stream.put(PdfName.BitsPerCoordinate, new PdfNumber(32));
stream.put(PdfName.BitsPerComponent, new PdfNumber(8));
stream.put(PdfName.VerticesPerRow, new PdfNumber(2));
stream.put(PdfName.Decode,
new PdfArray(new float[] {x, x + side, y, y + (int) (side * Math.sin(Math.PI / 3)), 0, 1, 0, 1, 0, 1}));
stream.put(PdfName.Matrix, new PdfArray(new float[] {1, 0, 0, -1, 0, 0}));
PdfLatticeFormGouraudShadedTriangleShading shade = (PdfLatticeFormGouraudShadedTriangleShading) AbstractPdfShading.makeShading(
stream);
Assertions.assertEquals(PdfName.DeviceRGB, shade.getColorSpace());
Assertions.assertEquals(5, shade.getShadingType());
Assertions.assertEquals(32, shade.getBitsPerCoordinate());
Assertions.assertEquals(8, shade.getBitsPerComponent());
Assertions.assertEquals(2, shade.getVerticesPerRow());
Assertions.assertEquals(y, shade.getDecode().getAsNumber(2).intValue());
}
@Test
public void coonsPatchMeshGradientTest() {
int x = 36;
int y = 400;
// Side of an equilateral triangle
int side = 500;
PdfStream stream = new PdfStream(CompressionConstants.DEFAULT_COMPRESSION);
stream.setData(toMultiWidthBytes(
new int[] {1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1}, 0, //flag
0, 0, //p1
0, 0, //p2 cp 1 o
0, 100, //p3 cp 4 i
0, 100, //p4
0, 100, //p5 cp4 o
100, 100, //p6 cp 7 i
100, 100, // p7
100, 100, // p8 cp 7 o
110, 10, //p9 cp 10 i
100, 0, // p10
100, 0, // p11 cp 10 o
0, 0, // p12 cp 1 i
250, 0, 0, // c p1
0, 250, 0, // c p4
0, 0, 250, // c p7
250, 250, 250)); // c p10
stream.setData(
toMultiWidthBytes(new int[] {1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 1, 1, 1, 1, 1, 1}, 2,
//flag
200, 0, //p17 cp 18 i
200, 0, // p18
200, 0, // p19 cp 18 o
200, 100, // p20 cp 10 i
200, 100, //p13 cp4 o
200, 100, //p14 cp 15 i
200, 100, // p15
200, 100, // p16 cp 15 o
250, 0, 0, // c p15
0, 250, 0 // c p18
), true); // c p10
stream.put(PdfName.ColorSpace, PdfName.DeviceRGB);
stream.put(PdfName.ShadingType, new PdfNumber(6));
stream.put(PdfName.BitsPerCoordinate, new PdfNumber(32));
stream.put(PdfName.BitsPerComponent, new PdfNumber(8));
stream.put(PdfName.BitsPerFlag, new PdfNumber(8));
stream.put(PdfName.Decode,
new PdfArray(new float[] {x, x + side, y, y + (int) (side * Math.sin(Math.PI / 3)), 0, 1, 0, 1, 0, 1}));
stream.put(PdfName.Matrix, new PdfArray(new float[] {1, 0, 0, -1, 0, 0}));
PdfCoonsPatchShading shade = (PdfCoonsPatchShading) AbstractPdfShading.makeShading(stream);
Assertions.assertEquals(PdfName.DeviceRGB, shade.getColorSpace());
Assertions.assertEquals(6, shade.getShadingType());
Assertions.assertEquals(32, shade.getBitsPerCoordinate());
Assertions.assertEquals(8, shade.getBitsPerComponent());
Assertions.assertEquals(8, shade.getBitsPerFlag());
Assertions.assertEquals(y, shade.getDecode().getAsNumber(2).intValue());
}
@Test
public void TensorProductPatchMeshShadingTest() {
int x = 36;
int y = 400;
// Side of an equilateral triangle
int side = 500;
PdfStream stream = new PdfStream(CompressionConstants.DEFAULT_COMPRESSION);
stream.setData(toMultiWidthBytes(
new int[] {1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 0, //flag
50, 0,// p00
50, 0,// p01
100, 0,// p02
100, 0,// p03
100, 0,// p13
100, 100,// p23
100, 100,// p33
100, 100,// p32
50, 100,// p31
50, 100,// p30
50, 100,// p20
50, 0,// p10
50, 0,// p11
100, 0,// p12
100, 100,// p22
50, 100, // p21
250, 0, 0, // c00
0, 250, 0, // c03
0, 0, 250, // c33
250, 0, 250)); // c30
stream.setData(toMultiWidthBytes(
new int[] {1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 1, 1, 1, 1, 1, 1},
1, //flag
100, 100,// p13
150, 100,// p23
150, 100,// p33
150, 100,// p32
150, 0,// p31
150, 0,// p30
150, 0,// p20
100, 0,// p10
100, 0,// p11
100, 100,// p12
150, 100,// p22
150, 0,// p21
250, 0, 0, // c p33
0, 250, 250 // c p30
), true); // c p10
stream.put(PdfName.ColorSpace, PdfName.DeviceRGB);
stream.put(PdfName.ShadingType, new PdfNumber(7));
stream.put(PdfName.BitsPerCoordinate, new PdfNumber(32));
stream.put(PdfName.BitsPerComponent, new PdfNumber(8));
stream.put(PdfName.BitsPerFlag, new PdfNumber(8));
stream.put(PdfName.Decode,
new PdfArray(new float[] {x, x + side, y, y + (int) (side * Math.sin(Math.PI / 3)), 0, 1, 0, 1, 0, 1}));
stream.put(PdfName.Matrix, new PdfArray(new float[] {-1, 0, 0, 1, 0, 0}));
PdfTensorProductPatchShading shade = (PdfTensorProductPatchShading) AbstractPdfShading.makeShading(stream);
Assertions.assertEquals(PdfName.DeviceRGB, shade.getColorSpace());
Assertions.assertEquals(7, shade.getShadingType());
Assertions.assertEquals(32, shade.getBitsPerCoordinate());
Assertions.assertEquals(8, shade.getBitsPerComponent());
Assertions.assertEquals(8, shade.getBitsPerFlag());
Assertions.assertEquals(y, shade.getDecode().getAsNumber(2).intValue());
}
@Test
public void invalidShadingTypeShouldFailTest() {
PdfDictionary dict = new PdfDictionary();
dict.put(PdfName.ShadingType, new PdfNumber(8));
dict.put(PdfName.ColorSpace, PdfName.DeviceRGB);
Exception e = Assertions.assertThrows(PdfException.class, () -> AbstractPdfShading.makeShading(dict));
Assertions.assertEquals(KernelExceptionMessageConstant.UNEXPECTED_SHADING_TYPE, e.getMessage());
}
@Test
public void basicCoonsPathMeshTest() {
int x = 36;
int y = 400;
int side = 500;
PdfArray decode = new PdfArray(
new float[] {x, x + side, y, y + (int) (side * Math.sin(Math.PI / 3)), 0, 1, 0, 1, 0, 1});
PdfColorSpace cs = PdfColorSpace.makeColorSpace(PdfName.DeviceRGB);
PdfCoonsPatchShading coonsPatchMesh = new PdfCoonsPatchShading(cs, 32, 16, 8, decode);
Assertions.assertEquals(PdfName.DeviceRGB, coonsPatchMesh.getColorSpace());
Assertions.assertEquals(6, coonsPatchMesh.getShadingType());
Assertions.assertEquals(32, coonsPatchMesh.getBitsPerCoordinate());
Assertions.assertEquals(16, coonsPatchMesh.getBitsPerComponent());
Assertions.assertEquals(8, coonsPatchMesh.getBitsPerFlag());
Assertions.assertEquals(y, coonsPatchMesh.getDecode().getAsNumber(2).intValue());
}
@Test
public void basicFreeFormGouraudShadedTriangleMeshTest() {
int x = 36;
int y = 400;
int side = 500;
PdfArray pdfArray = new PdfArray(
new float[] {x, x + side, y, y + (int) (side * Math.sin(Math.PI / 3)), 0, 1, 0, 1, 0, 1});
PdfColorSpace cs = PdfColorSpace.makeColorSpace(PdfName.DeviceRGB);
PdfFreeFormGouraudShadedTriangleShading shade = new PdfFreeFormGouraudShadedTriangleShading(cs, 32, 8, 8, pdfArray);
Assertions.assertEquals(PdfName.DeviceRGB, shade.getColorSpace());
Assertions.assertEquals(4, shade.getShadingType());
Assertions.assertEquals(32, shade.getBitsPerCoordinate());
Assertions.assertEquals(8, shade.getBitsPerComponent());
Assertions.assertEquals(8, shade.getBitsPerFlag());
Assertions.assertEquals(y, shade.getDecode().getAsNumber(2).intValue());
}
@Test
public void basicTensorProductPatchMeshTest() {
int x = 36;
int y = 400;
int side = 500;
PdfArray pdfArray = new PdfArray(
new float[] {x, x + side, y, y + (int) (side * Math.sin(Math.PI / 3)), 0, 1, 0, 1, 0, 1});
PdfColorSpace cs = PdfColorSpace.makeColorSpace(PdfName.DeviceRGB);
PdfTensorProductPatchShading shade = new PdfTensorProductPatchShading(cs, 32, 8, 8, pdfArray);
Assertions.assertEquals(PdfName.DeviceRGB, shade.getColorSpace());
Assertions.assertEquals(7, shade.getShadingType());
Assertions.assertEquals(32, shade.getBitsPerCoordinate());
Assertions.assertEquals(8, shade.getBitsPerComponent());
Assertions.assertEquals(8, shade.getBitsPerFlag());
Assertions.assertEquals(y, shade.getDecode().getAsNumber(2).intValue());
}
@Test
public void basicLatticeFormGouraudShadedTriangleMeshTest() {
int x = 36;
int y = 400;
int side = 500;
PdfArray pdfArray = new PdfArray(
new float[] {x, x + side, y, y + (int) (side * Math.sin(Math.PI / 3)), 0, 1, 0, 1, 0, 1});
PdfColorSpace cs = PdfColorSpace.makeColorSpace(PdfName.DeviceRGB);
PdfLatticeFormGouraudShadedTriangleShading shade = new PdfLatticeFormGouraudShadedTriangleShading(cs, 32, 8, 2, pdfArray);
Assertions.assertEquals(PdfName.DeviceRGB, shade.getColorSpace());
Assertions.assertEquals(5, shade.getShadingType());
Assertions.assertEquals(32, shade.getBitsPerCoordinate());
Assertions.assertEquals(8, shade.getBitsPerComponent());
Assertions.assertEquals(2, shade.getVerticesPerRow());
Assertions.assertEquals(y, shade.getDecode().getAsNumber(2).intValue());
}
@Test
public void basicFunctionBasedShadingTest() {
byte[] ps = "{2 copy sin abs sin abs 3 index 10 mul sin 1 sub abs}".getBytes(StandardCharsets.ISO_8859_1);
float[] domain = new float[] {0, 1000, 0, 1000};
float[] range = new float[] {0, 1, 0, 1, 0, 1};
float[] transformMatrix = new float[] {1, 0, 0, 1, 0, 0};
IPdfFunction function = new PdfType4Function(domain,
range, ps);
PdfColorSpace cs = PdfColorSpace.makeColorSpace(PdfName.DeviceRGB);
PdfFunctionBasedShading shade = new PdfFunctionBasedShading(cs, function);
shade.setDomain(1, 4, 1, 4);
shade.setMatrix(transformMatrix);
Assertions.assertEquals(PdfName.DeviceRGB, shade.getColorSpace());
Assertions.assertEquals(1, shade.getShadingType());
Assertions.assertArrayEquals(transformMatrix, shade.getMatrix().toFloatArray());
Assertions.assertArrayEquals(new float[]{1, 4, 1, 4}, shade.getDomain().toFloatArray());
}
@Test
public void changeFreeFormGouraudShadedTriangleMeshTest() {
int x = 36;
int y = 400;
int side = 500;
float[] decode =
new float[] {x, x + side, y, y + (int) (side * Math.sin(Math.PI / 3)), 0, 1, 0, 1, 0, 1};
PdfColorSpace cs = PdfColorSpace.makeColorSpace(PdfName.DeviceRGB);
PdfFreeFormGouraudShadedTriangleShading shade = new PdfFreeFormGouraudShadedTriangleShading(cs, 16, 8, 8, new PdfArray());
shade.setDecode(decode);
shade.setBitsPerComponent(16);
shade.setBitsPerCoordinate(32);
shade.setBitsPerFlag(4);
Assertions.assertEquals(PdfName.DeviceRGB, shade.getColorSpace());
Assertions.assertEquals(4, shade.getShadingType());
Assertions.assertEquals(32, shade.getBitsPerCoordinate());
Assertions.assertEquals(16, shade.getBitsPerComponent());
Assertions.assertEquals(4, shade.getBitsPerFlag());
Assertions.assertEquals(y, shade.getDecode().getAsNumber(2).intValue());
}
@Test
public void setDecodeCoonsPatchMeshTest() {
int x = 36;
int y = 400;
int side = 500;
PdfArray decode = new PdfArray(
new float[] {x, x + side, y, y + (int) (side * Math.sin(Math.PI / 3)), 0, 1, 0, 1, 0, 1});
PdfColorSpace cs = PdfColorSpace.makeColorSpace(PdfName.DeviceRGB);
PdfCoonsPatchShading coonsPatchMesh = new PdfCoonsPatchShading(cs, 32, 16, 16, new PdfArray());
coonsPatchMesh.setDecode(decode);
Assertions.assertEquals(y, coonsPatchMesh.getDecode().getAsNumber(2).intValue());
}
private static PdfDictionary initShadingDictionary(float[] coordsArray, float[] domainArray, boolean[] extendArray,
int radial2) {
PdfDictionary axialShadingDictionary = new PdfDictionary();
axialShadingDictionary.put(PdfName.ColorSpace, PdfName.DeviceRGB);
axialShadingDictionary.put(PdfName.Coords, new PdfArray(coordsArray));
if (domainArray != null) {
axialShadingDictionary.put(PdfName.Domain, new PdfArray(domainArray));
}
if (extendArray != null) {
axialShadingDictionary.put(PdfName.Extend, new PdfArray(extendArray));
}
axialShadingDictionary.put(PdfName.ShadingType, new PdfNumber(radial2));
PdfDictionary functionDictionary = new PdfDictionary();
functionDictionary.put(PdfName.C0, new PdfArray(new float[] {0f, 0f, 0f}));
functionDictionary.put(PdfName.C1, new PdfArray(new float[] {0.5f, 0.5f, 0.5f}));
functionDictionary.put(PdfName.Domain, new PdfArray(new float[] {0f, 1f}));
functionDictionary.put(PdfName.FunctionType, new PdfNumber(2));
functionDictionary.put(PdfName.N, new PdfNumber(1));
axialShadingDictionary.put(PdfName.Function, functionDictionary);
return axialShadingDictionary;
}
/**
* A helper function to create a mixed width byte array.
*
* <p>
*
* @param pattern the width pattern, each element represents the number of bytes that it will
* occupy in the resulting byte array
* @param ints the values to be converted
*
* @return a byte array where the ints are represented in widths represented by the pattern
*/
private static byte[] toMultiWidthBytes(int[] pattern, int... ints) {
if (ints.length % pattern.length != 0) {
throw new IllegalArgumentException(
"The number of elements must be an exact multiple of" + " the pattern length");
}
int patternSize = 0;
for (int i = 0; i < pattern.length; i++) {
patternSize += pattern[i];
}
byte[] result = new byte[ints.length / pattern.length * patternSize];
int targetSize;
int ri = 0;
for (int i = 0; i < ints.length; i++) {
targetSize = pattern[i % pattern.length];
for (int p = 0; p < targetSize; p++) {
result[ri] = (byte) (ints[i] >> p * 8);
ri++;
}
}
return result;
}
}