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!!!
* 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() {
}
/** {@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
}
}
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(")");
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;
/** 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>
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;
}
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
}
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(
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.
*
}
//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();
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) {
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;
@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));
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();
+ "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());
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"
+ ">>");
}
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);
+ }
}
<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>
<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>