aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimon Steiner <ssteiner@apache.org>2022-09-01 10:00:43 +0000
committerSimon Steiner <ssteiner@apache.org>2022-09-01 10:00:43 +0000
commit02656ec73867455a217091cb60f6c8c1f25caa59 (patch)
tree5cc322e7502753792fb917e4d3f6676d27265cf3
parentc974d14e5c8c1fc8fb2b869939613ded4d46745d (diff)
downloadxmlgraphics-fop-02656ec73867455a217091cb60f6c8c1f25caa59.tar.gz
xmlgraphics-fop-02656ec73867455a217091cb60f6c8c1f25caa59.zip
FOP-3091: Add transparency color support for PDF CMYK
git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1903804 13f79535-47bb-0310-9956-ffa450edef68
-rw-r--r--fop-core/src/main/java/org/apache/fop/fo/expr/PropertyParser.java3
-rw-r--r--fop-core/src/main/java/org/apache/fop/fo/expr/RGBICCColorFunction.java48
-rw-r--r--fop-core/src/main/java/org/apache/fop/util/ColorUtil.java82
-rw-r--r--fop-core/src/test/java/org/apache/fop/render/pdf/PDFPainterTestCase.java14
-rw-r--r--fop-core/src/test/java/org/apache/fop/util/ColorUtilTestCase.java8
-rw-r--r--fop/test/layoutengine/standard-testcases/color_1.xml4
6 files changed, 110 insertions, 49 deletions
diff --git a/fop-core/src/main/java/org/apache/fop/fo/expr/PropertyParser.java b/fop-core/src/main/java/org/apache/fop/fo/expr/PropertyParser.java
index d13c3cea8..0ba6284ef 100644
--- a/fop-core/src/main/java/org/apache/fop/fo/expr/PropertyParser.java
+++ b/fop-core/src/main/java/org/apache/fop/fo/expr/PropertyParser.java
@@ -64,7 +64,8 @@ public final class PropertyParser extends PropertyTokenizer {
FUNCTION_TABLE.put("proportional-column-width", new ProportionalColumnWidthFunction());
FUNCTION_TABLE.put("label-end", new LabelEndFunction());
FUNCTION_TABLE.put("body-start", new BodyStartFunction());
- FUNCTION_TABLE.put("rgb-icc", new RGBICCColorFunction());
+ FUNCTION_TABLE.put("rgb-icc", new RGBICCColorFunction(false));
+ FUNCTION_TABLE.put("fox-rgb-icc", new RGBICCColorFunction(true));
FUNCTION_TABLE.put("rgb-named-color", new RGBNamedColorFunction()); //since XSL-FO 2.0
FUNCTION_TABLE.put("cie-lab-color", new CIELabColorFunction()); //since XSL-FO 2.0
FUNCTION_TABLE.put("cmyk", new CMYKColorFunction()); //non-standard!!!
diff --git a/fop-core/src/main/java/org/apache/fop/fo/expr/RGBICCColorFunction.java b/fop-core/src/main/java/org/apache/fop/fo/expr/RGBICCColorFunction.java
index 57cf6a949..0c11070f8 100644
--- a/fop-core/src/main/java/org/apache/fop/fo/expr/RGBICCColorFunction.java
+++ b/fop-core/src/main/java/org/apache/fop/fo/expr/RGBICCColorFunction.java
@@ -30,6 +30,12 @@ import org.apache.fop.util.ColorUtil;
* Implements the rgb-icc() function.
*/
class RGBICCColorFunction extends FunctionBase {
+ private int colors = 3;
+ public RGBICCColorFunction(boolean fox) {
+ if (fox) {
+ colors = 4;
+ }
+ }
/** {@inheritDoc} */
public int getRequiredArgsCount() {
@@ -49,12 +55,14 @@ class RGBICCColorFunction extends FunctionBase {
}
/** {@inheritDoc} */
- public Property eval(Property[] args, PropertyInfo pInfo) throws PropertyException {
+ public Property eval(Property[] args,
+ PropertyInfo pInfo) throws PropertyException {
// Map color profile NCNAME to src from declarations/color-profile element
- String colorProfileName = args[3].getString();
- Declarations decls = (pInfo.getFO() != null
- ? pInfo.getFO().getRoot().getDeclarations()
- : null);
+ String colorProfileName = args[colors].getString();
+ if (colors == 4 && colorProfileName == null) {
+ throw new PropertyException("Alpha channel value missing");
+ }
+ Declarations decls = pInfo.getFO().getRoot().getDeclarations();
ColorProfile cp = null;
if (decls == null) {
//function used in a color-specification
@@ -79,32 +87,36 @@ class RGBICCColorFunction extends FunctionBase {
}
}
String src = (cp != null ? cp.getSrc() : "");
-
- float red = 0;
- float green = 0;
- float blue = 0;
- red = args[0].getNumber().floatValue();
- green = args[1].getNumber().floatValue();
- blue = args[2].getNumber().floatValue();
+ float red = args[0].getNumber().floatValue();
+ float green = args[1].getNumber().floatValue();
+ float blue = args[2].getNumber().floatValue();
+ float alpha = 255;
+ if (colors == 4) {
+ alpha = args[3].getNumber().floatValue();
+ }
/* Verify rgb replacement arguments */
- if ((red < 0 || red > 255) || (green < 0 || green > 255) || (blue < 0 || blue > 255)) {
+ if ((red < 0 || red > 255)
+ || (green < 0 || green > 255)
+ || (blue < 0 || blue > 255)) {
throw new PropertyException("Color values out of range. "
+ "Arguments to rgb-icc() must be [0..255] or [0%..100%]");
}
// rgb-icc is replaced with fop-rgb-icc which has an extra fifth argument containing the
// color profile src attribute as it is defined in the color-profile declarations element.
- StringBuffer sb = new StringBuffer();
- sb.append("fop-rgb-icc(");
+ StringBuilder sb = new StringBuilder("fop-rgb-icc(");
sb.append(red / 255f);
sb.append(',').append(green / 255f);
sb.append(',').append(blue / 255f);
- for (int ix = 3; ix < args.length; ix++) {
- if (ix == 3) {
+ if (colors == 4) {
+ sb.append("," + ColorUtil.ALPHA_PSEUDO_PROFILE + ",").append(alpha / 255f);
+ }
+ for (int i = colors; i < args.length; i++) {
+ if (i == colors) {
sb.append(',').append(colorProfileName);
sb.append(',').append(src);
} else {
- sb.append(',').append(args[ix]);
+ sb.append(',').append(args[i]);
}
}
sb.append(")");
diff --git a/fop-core/src/main/java/org/apache/fop/util/ColorUtil.java b/fop-core/src/main/java/org/apache/fop/util/ColorUtil.java
index 1117b6e38..43b1af656 100644
--- a/fop-core/src/main/java/org/apache/fop/util/ColorUtil.java
+++ b/fop-core/src/main/java/org/apache/fop/util/ColorUtil.java
@@ -24,7 +24,11 @@ import java.awt.color.ColorSpace;
import java.awt.color.ICC_ColorSpace;
import java.awt.color.ICC_Profile;
import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
+import java.util.List;
+import java.util.ListIterator;
import java.util.Map;
import org.apache.commons.logging.Log;
@@ -61,6 +65,8 @@ public final class ColorUtil {
/** The name for the Separation pseudo-profile used for spot colors */
public static final String SEPARATION_PSEUDO_PROFILE = "#Separation";
+ public static final String ALPHA_PSEUDO_PROFILE = "#alpha";
+
/**
* Keeps all the predefined and parsed colors.
* <p>
@@ -290,13 +296,19 @@ public final class ColorUtil {
return component;
}
- private static Color parseFallback(String[] args, String value) throws PropertyException {
- float red = parseComponent1(args[0], value);
- float green = parseComponent1(args[1], value);
- float blue = parseComponent1(args[2], value);
+ private static Color parseFallback(ListIterator<String> args, String value) throws PropertyException {
+ float red = parseComponent1(args.next(), value);
+ float green = parseComponent1(args.next(), value);
+ float blue = parseComponent1(args.next(), value);
+ float alpha = 1;
+ if (ALPHA_PSEUDO_PROFILE.equals(args.next())) {
+ alpha = parseComponent1(args.next(), value);
+ } else {
+ args.previous();
+ }
//Sun's classlib rounds differently with this constructor than when converting to sRGB
//via CIE XYZ.
- Color sRGB = new Color(red, green, blue);
+ Color sRGB = new Color(red, green, blue, alpha);
return sRGB;
}
@@ -356,49 +368,48 @@ public final class ColorUtil {
int poss = value.indexOf("(");
int pose = value.indexOf(")");
if (poss != -1 && pose != -1) {
- String[] args = value.substring(poss + 1, pose).split(",");
+ String[] argsStr = value.substring(poss + 1, pose).split(",");
try {
- if (args.length < 5) {
+ if (argsStr.length < 5) {
throw new PropertyException("Too few arguments for rgb-icc() function");
}
+ ListIterator<String> args = Arrays.asList(argsStr).listIterator();
//Set up fallback sRGB value
Color sRGB = parseFallback(args, value);
/* Get and verify ICC profile name */
- String iccProfileName = args[3].trim();
- if (iccProfileName == null || "".equals(iccProfileName)) {
+ String iccProfileName = args.next().trim();
+ if (iccProfileName.equals("")) {
throw new PropertyException("ICC profile name missing");
}
ColorSpace colorSpace = null;
String iccProfileSrc = null;
+ String iccProfile = args.next();
+ String namedColorSpace = args.next();
if (isPseudoProfile(iccProfileName)) {
if (CMYK_PSEUDO_PROFILE.equalsIgnoreCase(iccProfileName)) {
colorSpace = ColorSpaces.getDeviceCMYKColorSpace();
} else if (SEPARATION_PSEUDO_PROFILE.equalsIgnoreCase(iccProfileName)) {
- colorSpace = new NamedColorSpace(args[5], sRGB,
+ colorSpace = new NamedColorSpace(namedColorSpace, sRGB,
SEPARATION_PSEUDO_PROFILE, null);
} else {
assert false : "Incomplete implementation";
}
} else {
/* Get and verify ICC profile source */
- iccProfileSrc = args[4].trim();
- if (iccProfileSrc == null || "".equals(iccProfileSrc)) {
+ iccProfileSrc = iccProfile.trim();
+ if (iccProfileSrc.equals("")) {
throw new PropertyException("ICC profile source missing");
}
iccProfileSrc = unescapeString(iccProfileSrc);
}
- /* ICC profile arguments */
- int componentStart = 4;
- if (colorSpace instanceof NamedColorSpace) {
- componentStart++;
- }
- float[] iccComponents = new float[args.length - componentStart - 1];
- for (int ix = componentStart; ++ix < args.length;) {
- iccComponents[ix - componentStart - 1] = Float.parseFloat(args[ix].trim());
+
+ if (!(colorSpace instanceof NamedColorSpace)) {
+ args.previous();
}
+ float[] iccComponents = getICCComponents(args);
if (colorSpace instanceof NamedColorSpace && iccComponents.length == 0) {
iccComponents = new float[] {1.0f}; //full tint if not specified
}
@@ -415,10 +426,10 @@ public final class ColorUtil {
if (ColorSpaces.isDeviceColorSpace(colorSpace)) {
//Device-specific colors are handled differently:
//sRGB is the primary color with the CMYK as the alternative
- Color deviceColor = new Color(colorSpace, iccComponents, 1.0f);
- float[] rgbComps = sRGB.getRGBColorComponents(null);
+ float[] rgbComps = sRGB.getRGBComponents(null);
+ Color deviceColor = new Color(colorSpace, iccComponents, rgbComps[3]);
parsedColor = new ColorWithAlternatives(
- rgbComps[0], rgbComps[1], rgbComps[2],
+ rgbComps[0], rgbComps[1], rgbComps[2], rgbComps[3],
new Color[] {deviceColor});
} else {
parsedColor = new ColorWithFallback(
@@ -440,6 +451,21 @@ public final class ColorUtil {
return parsedColor;
}
+ private static float[] getICCComponents(ListIterator<String> args) {
+ /* ICC profile arguments */
+ List<Float> iccComponentsList = new ArrayList<>();
+ while (args.hasNext()) {
+ iccComponentsList.add(Float.parseFloat(args.next().trim()));
+ }
+ float[] iccComponents = new float[iccComponentsList.size()];
+ int i = 0;
+ for (float component : iccComponentsList) {
+ iccComponents[i] = component;
+ i++;
+ }
+ return iccComponents;
+ }
+
/**
* Parse a color specified using the fop-rgb-named-color() function.
*
@@ -461,7 +487,7 @@ public final class ColorUtil {
}
//Set up fallback sRGB value
- Color sRGB = parseFallback(args, value);
+ Color sRGB = parseFallback(Arrays.asList(args).listIterator(), value);
/* Get and verify ICC profile name */
String iccProfileName = args[3].trim();
@@ -768,13 +794,15 @@ public final class ColorUtil {
String functionName;
Color fallbackColor = getsRGBFallback(color);
- float[] rgb = fallbackColor.getColorComponents(null);
- assert rgb.length == 3;
- StringBuffer sb = new StringBuffer(40);
+ float[] rgb = fallbackColor.getComponents(null);
+ StringBuilder sb = new StringBuilder(40);
sb.append("(");
sb.append(rgb[0]).append(",");
sb.append(rgb[1]).append(",");
sb.append(rgb[2]).append(",");
+ if (rgb[3] != 1f) {
+ sb.append(ALPHA_PSEUDO_PROFILE + ",").append(rgb[3]).append(",");
+ }
String profileName = origin.getProfileName();
sb.append(profileName).append(",");
if (origin.getProfileURI() != null) {
diff --git a/fop-core/src/test/java/org/apache/fop/render/pdf/PDFPainterTestCase.java b/fop-core/src/test/java/org/apache/fop/render/pdf/PDFPainterTestCase.java
index 9a1872f5f..7b5d34bdc 100644
--- a/fop-core/src/test/java/org/apache/fop/render/pdf/PDFPainterTestCase.java
+++ b/fop-core/src/test/java/org/apache/fop/render/pdf/PDFPainterTestCase.java
@@ -48,6 +48,8 @@ import static org.mockito.Mockito.when;
import org.apache.xmlgraphics.image.loader.Image;
import org.apache.xmlgraphics.image.loader.ImageException;
import org.apache.xmlgraphics.image.loader.ImageInfo;
+import org.apache.xmlgraphics.java2d.color.ColorSpaces;
+import org.apache.xmlgraphics.java2d.color.ColorWithAlternatives;
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.apps.FopFactory;
@@ -474,6 +476,12 @@ public class PDFPainterTestCase {
@Test
public void testAlphaColor() throws Exception {
+ fillColor(new Color(0, 0, 0, 102), "0 g\n");
+ Color deviceColor = new Color(ColorSpaces.getDeviceCMYKColorSpace(), new float[4], 0.4f);
+ fillColor(new ColorWithAlternatives(0, 0, 0, 102, new Color[]{deviceColor}), "0 0 0 0 k\n");
+ }
+
+ private void fillColor(Color color, String cmd) throws Exception {
FopFactory fopFactory = FopFactory.newInstance(new File(".").toURI());
foUserAgent = fopFactory.newFOUserAgent();
PDFDocumentHandler pdfDocumentHandler = new PDFDocumentHandler(new IFContext(foUserAgent));
@@ -481,7 +489,7 @@ public class PDFPainterTestCase {
pdfDocumentHandler.startDocument();
pdfDocumentHandler.startPage(0, "", "", new Dimension());
PDFPainter pdfPainter = new PDFPainter(pdfDocumentHandler, null);
- pdfPainter.fillRect(new Rectangle(10, 10), new Color(0, 0, 0, 128));
+ pdfPainter.fillRect(new Rectangle(10, 10), color);
PDFFilterList filters = pdfPainter.generator.getStream().getFilterList();
filters.setDisableAllFilters(true);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
@@ -491,7 +499,7 @@ public class PDFPainterTestCase {
+ "q\n"
+ "1 0 0 -1 0 0 cm\n"
+ "/GS1 gs\n"
- + "0 g\n"
+ + cmd
+ "0 0 0.01 0.01 re f\n"
+ "\n"
+ "endstream", bos.toString());
@@ -499,7 +507,7 @@ public class PDFPainterTestCase {
pdfDocumentHandler.getCurrentPage().getGStates().iterator().next().output(bos);
Assert.assertEquals(bos.toString(), "<<\n"
+ "/Type /ExtGState\n"
- + "/ca 0.5019608\n"
+ + "/ca 0.4\n"
+ "/CA 1.0\n"
+ ">>");
}
diff --git a/fop-core/src/test/java/org/apache/fop/util/ColorUtilTestCase.java b/fop-core/src/test/java/org/apache/fop/util/ColorUtilTestCase.java
index 095d3a806..4a530257e 100644
--- a/fop-core/src/test/java/org/apache/fop/util/ColorUtilTestCase.java
+++ b/fop-core/src/test/java/org/apache/fop/util/ColorUtilTestCase.java
@@ -336,4 +336,12 @@ public class ColorUtilTestCase {
assertEquals(colSpec, ColorUtil.colorToString(colActual));
}
+
+ @Test
+ public void testAlphaColor() throws Exception {
+ String colSpec = "fop-rgb-icc(0.6,0.6,0.4,#alpha,0.4,#CMYK,,0.0,0.0,0.2,0.4)";
+ ColorWithAlternatives colActual2 = (ColorWithAlternatives)ColorUtil.parseColorString(null, colSpec);
+ assertEquals(colActual2.getAlternativeColors()[0].getAlpha(), 102);
+ assertEquals(ColorUtil.colorToString(colActual2), colSpec);
+ }
}
diff --git a/fop/test/layoutengine/standard-testcases/color_1.xml b/fop/test/layoutengine/standard-testcases/color_1.xml
index e10f46d19..436b5c4e8 100644
--- a/fop/test/layoutengine/standard-testcases/color_1.xml
+++ b/fop/test/layoutengine/standard-testcases/color_1.xml
@@ -42,6 +42,8 @@
<fo:block color="rgb-icc(0%,100%,0%, unknown, 1, 0.5, 0)">color "rgb-icc(0%,100%,0%, unknown, 1, 0.5, 0)"</fo:block>
<fo:block color="cmyk(0%,0%,20%,40%)">color "cmyk(0%,0%,20%,40%)" (Khaki)</fo:block>
<fo:block color="rgb-icc(153, 153, 102, #CMYK, 0, 0, 0.2, 0.4)">color "rgb-icc(153, 153, 102, #CMYK, 0, 0, 0.2, 0.4)" (Khaki)</fo:block>
+ <fo:block color="rgb-icc(153,153,0.0,#Separation,,Postgelb)">color "rgb-icc(1.0,0.8,0.0,#Separation,,Postgelb)" (Khaki)</fo:block>
+ <fo:block color="fox-rgb-icc(153, 153, 102, 102, #CMYK, 0, 0, 0.2, 0.4)">color "fox-rgb-icc(153, 153, 102, 102, #CMYK, 0, 0, 0.2, 0.4)" (Khaki)</fo:block>
</fo:flow>
</fo:page-sequence>
</fo:root>
@@ -58,5 +60,7 @@
<eval expected="#00ff00" xpath="//block[5]//text/@color"/>
<eval expected="fop-rgb-icc(0.6,0.6,0.48000002,#CMYK,,0.0,0.0,0.2,0.4)" xpath="//block[6]//text/@color"/>
<eval expected="fop-rgb-icc(0.6,0.6,0.4,#CMYK,,0.0,0.0,0.2,0.4)" xpath="//block[7]//text/@color"/>
+ <eval expected="fop-rgb-icc(0.6,0.6,0.0,#Separation,,Postgelb)" xpath="//block[8]//text/@color"/>
+ <eval expected="fop-rgb-icc(0.6,0.6,0.4,#alpha,0.4,#CMYK,,0.0,0.0,0.2,0.4)" xpath="//block[9]//text/@color"/>
</checks>
</testcase>