--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.fo.expr;
+import org.apache.fop.apps.FOUserAgent;
+import org.apache.fop.datatypes.PercentBase;
+import org.apache.fop.datatypes.PercentBaseContext;
+import org.apache.fop.fo.properties.ColorProperty;
+import org.apache.fop.fo.properties.Property;
+
+/**
+ * Implements the cie-lab-color() function.
+ * @since XSL-FO 2.0
+ */
+class CIELabColorFunction extends FunctionBase {
+
+ /**
+ * cie-lab-color() takes 2 times 3 arguments.
+ * {@inheritDoc}
+ */
+ public int nbArgs() {
+ return 2 * 3;
+ }
+
+ public PercentBase getPercentBase() {
+ return new CIELabPercentBase();
+ }
+
+ /** {@inheritDoc} */
+ public Property eval(Property[] args,
+ PropertyInfo pInfo) throws PropertyException {
+
+ float red = args[0].getNumber().floatValue();
+ float green = args[1].getNumber().floatValue();
+ float blue = args[2].getNumber().floatValue();
+ /* Verify sRGB replacement arguments */
+ if ((red < 0 || red > 255)
+ || (green < 0 || green > 255)
+ || (blue < 0 || blue > 255)) {
+ throw new PropertyException("sRGB color values out of range. "
+ + "Arguments to cie-lab-color() must be [0..255] or [0%..100%]");
+ }
+
+ float l = args[3].getNumber().floatValue();
+ float a = args[4].getNumber().floatValue();
+ float b = args[5].getNumber().floatValue();
+ if (l < 0 || l > 100) {
+ throw new PropertyException("L* value out of range. Valid range: [0..100]");
+ }
+ if (a < -127 || a > 127 || b < -127 || b > 127) {
+ throw new PropertyException("a* and b* values out of range. Valid range: [-127..+127]");
+ }
+
+ StringBuffer sb = new StringBuffer();
+ sb.append("cie-lab-color(" + red + "," + green + "," + blue + ","
+ + l + "," + a + "," + b + ")");
+ FOUserAgent ua = (pInfo == null)
+ ? null
+ : (pInfo.getFO() == null ? null : pInfo.getFO().getUserAgent());
+ return ColorProperty.getInstance(ua, sb.toString());
+ }
+
+ private static class CIELabPercentBase implements PercentBase {
+ public int getDimension() {
+ return 0;
+ }
+
+ public double getBaseValue() {
+ return 1.0f;
+ }
+
+ public int getBaseLength(PercentBaseContext context) {
+ return 0;
+ }
+
+ }
+
+}
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.pdf;
+
+/**
+ * This class represents a "CIE L*a*b*" color space. It is expected that the components have
+ * the following ranges: L* [0..100], a* and b* [-127..127]
+ */
+public class PDFCIELabColorSpace extends PDFArray implements PDFColorSpace {
+
+ /**
+ * Creates a new "CIE L*a*b*" color space. Valid value ranges for the white and black point
+ * are [0..1] as per the PDF spec.
+ * @param whitePoint the white point
+ * @param blackPoint the optional black point (may be null)
+ */
+ public PDFCIELabColorSpace(float[] whitePoint, float[] blackPoint) {
+ super();
+
+ add(new PDFName("Lab"));
+ PDFDictionary dict = new PDFDictionary();
+ dict.put("WhitePoint", toPDFArray("White point", whitePoint));
+ if (whitePoint[1] != 1f) {
+ throw new IllegalArgumentException("The white point's Y coordinate must be 1.0");
+ }
+ if (blackPoint != null) {
+ dict.put("BlackPoint", toPDFArray("Black point", blackPoint));
+ }
+ dict.put("Range", new PDFArray(dict, new int[] {-128, 128, -128, 128}));
+ add(dict);
+ }
+
+ private PDFArray toPDFArray(String name, float[] whitePoint) {
+ PDFArray wp = new PDFArray();
+ if (whitePoint == null || whitePoint.length != 3) {
+ throw new IllegalArgumentException(name + " must be given an have 3 components");
+ }
+ for (int i = 0; i < 3; i++) {
+ wp.add(whitePoint[i]);
+ }
+ return wp;
+ }
+
+ /** {@inheritDoc} */
+ public String getName() {
+ return "CS" + this.getObjectNumber();
+ }
+
+ /** {@inheritDoc} */
+ public int getNumComponents() {
+ return 3;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isCMYKColorSpace() {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isDeviceColorSpace() {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isGrayColorSpace() {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isRGBColorSpace() {
+ return false;
+ }
+
+}
import java.awt.color.ICC_ColorSpace;
import java.awt.color.ICC_Profile;
import java.text.DecimalFormat;
+import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.apache.xmlgraphics.java2d.color.CIELabColorSpace;
import org.apache.xmlgraphics.java2d.color.ColorExt;
import org.apache.xmlgraphics.java2d.color.ColorUtil;
import org.apache.xmlgraphics.java2d.color.DeviceCMYKColorSpace;
private PDFResources resources;
+ private Map cieLabColorSpaces;
+
public PDFColorHandler(PDFResources resources) {
this.resources = resources;
}
return;
}
}
+ if (log.isDebugEnabled() && alt.length > 0) {
+ log.debug("None of the alternative colors are supported. Using fallback: "
+ + color);
+ }
}
//Fallback
- establishColorFromColor(codeBuffer, color, fill);
+ boolean established = establishColorFromColor(codeBuffer, color, fill);
+ if (!established) {
+ establishDeviceRGB(codeBuffer, color, fill);
+ }
}
private boolean establishColorFromColor(StringBuffer codeBuffer, Color color, boolean fill) {
PDFSeparationColorSpace sepcs = getSeparationColorSpace((NamedColorSpace)cs);
establishColor(codeBuffer, sepcs, color, fill);
return true;
+ } else if (cs instanceof CIELabColorSpace) {
+ CIELabColorSpace labcs = (CIELabColorSpace)cs;
+ PDFCIELabColorSpace pdflab = getCIELabColorSpace(labcs);
+ selectColorSpace(codeBuffer, pdflab, fill);
+ float[] comps = color.getColorComponents(null);
+ float[] nativeComps = labcs.toNativeComponents(comps);
+ writeColor(codeBuffer, nativeComps, labcs.getNumComponents(), (fill ? "sc" : "SC"));
+ return true;
}
}
- //Fallback (RGB) Color
- establishDeviceRGB(codeBuffer, color, fill);
- return true;
+ return false;
}
private PDFICCBasedColorSpace getICCBasedColorSpace(ICC_ColorSpace cs) {
return sepcs;
}
+ private PDFCIELabColorSpace getCIELabColorSpace(CIELabColorSpace labCS) {
+ if (this.cieLabColorSpaces == null) {
+ this.cieLabColorSpaces = new java.util.HashMap();
+ }
+ float[] wp = labCS.getWhitePoint();
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < 3; i++) {
+ if (i > 0) {
+ sb.append(',');
+ }
+ sb.append(wp[i]);
+ }
+ String key = sb.toString();
+ PDFCIELabColorSpace cielab = (PDFCIELabColorSpace)this.cieLabColorSpaces.get(key);
+ if (cielab == null) {
+ //color space is not in the PDF, yet
+ float[] wp1 = new float[] {wp[0] / 100f, wp[1] / 100f, wp[2] / 100f};
+ cielab = new PDFCIELabColorSpace(wp1, null);
+ getDocument().registerObject(cielab);
+ this.resources.addColorSpace(cielab);
+ this.cieLabColorSpaces.put(key, cielab);
+ }
+ return cielab;
+ }
+
private void establishColor(StringBuffer codeBuffer,
PDFColorSpace pdfcs, Color color, boolean fill) {
+ selectColorSpace(codeBuffer, pdfcs, fill);
+ writeColor(codeBuffer, color, pdfcs.getNumComponents(), (fill ? "sc" : "SC"));
+ }
+
+ private void selectColorSpace(StringBuffer codeBuffer, PDFColorSpace pdfcs, boolean fill) {
codeBuffer.append(new PDFName(pdfcs.getName()));
if (fill) {
codeBuffer.append(" cs ");
} else {
codeBuffer.append(" CS ");
}
- writeColor(codeBuffer, color, pdfcs.getNumComponents(), (fill ? "sc" : "SC"));
}
private void establishDeviceRGB(StringBuffer codeBuffer, Color color, boolean fill) {
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.apache.xmlgraphics.java2d.color.CIELabColorSpace;
import org.apache.xmlgraphics.java2d.color.ColorExt;
import org.apache.xmlgraphics.java2d.color.ColorSpaces;
import org.apache.xmlgraphics.java2d.color.DeviceCMYKColorSpace;
parsedColor = parseAsFopRgbIcc(foUserAgent, value);
} else if (value.startsWith("fop-rgb-named-color")) {
parsedColor = parseAsFopRgbNamedColor(foUserAgent, value);
+ } else if (value.startsWith("cie-lab-color")) {
+ parsedColor = parseAsCIELabColor(foUserAgent, value);
} else if (value.startsWith("cmyk")) {
parsedColor = parseAsCMYK(value);
}
throw new PropertyException(
"Invalid number of arguments: rgb(" + value + ")");
}
- float red = 0.0f, green = 0.0f, blue = 0.0f;
- String str = args[0].trim();
- if (str.endsWith("%")) {
- red = Float.parseFloat(str.substring(0,
- str.length() - 1)) / 100f;
- } else {
- red = Float.parseFloat(str) / 255f;
- }
- str = args[1].trim();
- if (str.endsWith("%")) {
- green = Float.parseFloat(str.substring(0,
- str.length() - 1)) / 100f;
- } else {
- green = Float.parseFloat(str) / 255f;
- }
- str = args[2].trim();
- if (str.endsWith("%")) {
- blue = Float.parseFloat(str.substring(0,
- str.length() - 1)) / 100f;
- } else {
- blue = Float.parseFloat(str) / 255f;
- }
- if ((red < 0.0 || red > 1.0)
- || (green < 0.0 || green > 1.0)
- || (blue < 0.0 || blue > 1.0)) {
- throw new PropertyException("Color values out of range");
- }
+ float red = parseComponent255(args[0], value);
+ float green = parseComponent255(args[1], value);
+ float blue = parseComponent255(args[2], value);
parsedColor = new ColorExt(red, green, blue, null);
} catch (PropertyException pe) {
//simply re-throw
return parsedColor;
}
+ private static float parseComponent255(String str, String function)
+ throws PropertyException {
+ float component;
+ str = str.trim();
+ if (str.endsWith("%")) {
+ component = Float.parseFloat(str.substring(0,
+ str.length() - 1)) / 100f;
+ } else {
+ component = Float.parseFloat(str) / 255f;
+ }
+ if ((component < 0.0 || component > 1.0)) {
+ throw new PropertyException("Color value out of range for " + function + ": "
+ + str + ". Valid range: [0..255] or [0%..100%]");
+ }
+ return component;
+ }
+
+ private static float parseComponent1(String argument, String function)
+ throws PropertyException {
+ return parseComponent(argument, 0f, 1f, function);
+ }
+
+ private static float parseComponent(String argument, float min, float max, String function)
+ throws PropertyException {
+ float component = Float.parseFloat(argument.trim());
+ if ((component < min || component > max)) {
+ throw new PropertyException("Color value out of range for " + function + ": "
+ + argument + ". Valid range: [" + min + ".." + max + "]");
+ }
+ return component;
+ }
+
/**
* parse a color given in the #.... format.
*
}
//Set up fallback sRGB value
- float red = 0, green = 0, blue = 0;
- red = Float.parseFloat(args[0].trim());
- green = Float.parseFloat(args[1].trim());
- blue = Float.parseFloat(args[2].trim());
- /* Verify rgb replacement arguments */
- if ((red < 0 || red > 1)
- || (green < 0 || green > 1)
- || (blue < 0 || blue > 1)) {
- throw new PropertyException("Color values out of range. "
- + "Fallback RGB arguments to fop-rgb-icc() must be [0..1]");
- }
+ float red = parseComponent1(args[0], value);
+ float green = parseComponent1(args[1], value);
+ float blue = parseComponent1(args[2], value);
Color sRGB = new ColorExt(red, green, blue, null);
/* Get and verify ICC profile name */
try {
if (args.length != 6) {
- throw new PropertyException("rgb-named() function must have 6 arguments");
+ throw new PropertyException("rgb-named-color() function must have 6 arguments");
}
//Set up fallback sRGB value
- float red = Float.parseFloat(args[0].trim());
- float green = Float.parseFloat(args[1].trim());
- float blue = Float.parseFloat(args[2].trim());
- /* Verify rgb replacement arguments */
- if ((red < 0 || red > 1)
- || (green < 0 || green > 1)
- || (blue < 0 || blue > 1)) {
- throw new PropertyException("Color values out of range. "
- + "Fallback RGB arguments to fop-rgb-named-color() must be [0..1]");
- }
+ float red = parseComponent1(args[0], value);
+ float green = parseComponent1(args[1], value);
+ float blue = parseComponent1(args[2], value);
Color sRGB = new ColorExt(red, green, blue, null);
/* Get and verify ICC profile name */
return parsedColor;
}
+ /**
+ * Parse a color specified using the cie-lab-color() function.
+ *
+ * @param value the function call
+ * @return a color if possible
+ * @throws PropertyException if the format is wrong.
+ */
+ private static Color parseAsCIELabColor(FOUserAgent foUserAgent, String value)
+ throws PropertyException {
+ Color parsedColor;
+ int poss = value.indexOf("(");
+ int pose = value.indexOf(")");
+ if (poss != -1 && pose != -1) {
+ String[] args = value.substring(poss + 1, pose).split(",");
+
+ try {
+ if (args.length != 6) {
+ throw new PropertyException("cie-lab-color() function must have 6 arguments");
+ }
+
+ //Set up fallback sRGB value
+ float red = parseComponent255(args[0], value);
+ float green = parseComponent255(args[1], value);
+ float blue = parseComponent255(args[2], value);
+
+ float l = parseComponent(args[3], 0f, 100f, value);
+ float a = parseComponent(args[4], -127f, 127f, value);
+ float b = parseComponent(args[5], -127f, 127f, value);
+
+ //Assuming the XSL-FO spec uses the D50 white point
+ CIELabColorSpace cs = ColorSpaces.getCIELabColorSpaceD50();
+ //use toColor() to have components normalized
+ Color labColor = cs.toColor(l, a, b, 1.0f);
+ parsedColor = new ColorExt(red, green, blue, new Color[] {labColor});
+
+ } catch (PropertyException pe) {
+ //simply re-throw
+ throw pe;
+ } catch (Exception e) {
+ //wrap in a PropertyException
+ throw new PropertyException(e);
+ }
+ } else {
+ throw new PropertyException("Unknown color format: " + value
+ + ". Must be cie-lab-color(r,g,b,Lightness,a-value,b-value)");
+ }
+ return parsedColor;
+ }
+
private static String unescapeString(String iccProfileSrc) {
if (iccProfileSrc.startsWith("\"") || iccProfileSrc.startsWith("'")) {
iccProfileSrc = iccProfileSrc.substring(1);
throw new PropertyException(
"Invalid number of arguments: cmyk(" + value + ")");
}
- float cyan = 0.0f, magenta = 0.0f, yellow = 0.0f, black = 0.0f;
- String str = args[0].trim();
- if (str.endsWith("%")) {
- cyan = Float.parseFloat(str.substring(0,
- str.length() - 1)) / 100.0f;
- } else {
- cyan = Float.parseFloat(str);
- }
- str = args[1].trim();
- if (str.endsWith("%")) {
- magenta = Float.parseFloat(str.substring(0,
- str.length() - 1)) / 100.0f;
- } else {
- magenta = Float.parseFloat(str);
- }
- str = args[2].trim();
- if (str.endsWith("%")) {
- yellow = Float.parseFloat(str.substring(0,
- str.length() - 1)) / 100.0f;
- } else {
- yellow = Float.parseFloat(str);
- }
- str = args[3].trim();
- if (str.endsWith("%")) {
- black = Float.parseFloat(str.substring(0,
- str.length() - 1)) / 100.0f;
- } else {
- black = Float.parseFloat(str);
- }
-
- if ((cyan < 0.0 || cyan > 1.0)
- || (magenta < 0.0 || magenta > 1.0)
- || (yellow < 0.0 || yellow > 1.0)
- || (black < 0.0 || black > 1.0)) {
- throw new PropertyException("Color values out of range"
- + "Arguments to cmyk() must be in the range [0%-100%] or [0.0-1.0]");
- }
+ float cyan = parseComponent1(args[0], value);
+ float magenta = parseComponent1(args[1], value);
+ float yellow = parseComponent1(args[2], value);
+ float black = parseComponent1(args[3], value);
float[] comps = new float[] {cyan, magenta, yellow, black};
parsedColor = DeviceCMYKColorSpace.createColorExt(comps);
} catch (PropertyException pe) {
if (icc == null) {
return toRGBFunctionCall(color);
}
+ if (icc.getColorSpace() instanceof CIELabColorSpace) {
+ return toCIELabFunctionCall(color, icc);
+ }
StringBuffer sb = new StringBuffer(40);
String functionName;
return functionName + sb.toString();
}
+ private static String toCIELabFunctionCall(ColorExt color, Color cieLab) {
+ StringBuffer sb = new StringBuffer("cie-lab-color(");
+ sb.append(color.getRed()).append(',');
+ sb.append(color.getGreen()).append(',');
+ sb.append(color.getBlue());
+ CIELabColorSpace cs = (CIELabColorSpace)cieLab.getColorSpace();
+ float[] lab = cs.toNativeComponents(cieLab.getColorComponents(null));
+ for (int i = 0; i < 3; i++) {
+ sb.append(',').append(lab[i]);
+ }
+ sb.append(')');
+ return sb.toString();
+ }
+
private static Color createColor(int r, int g, int b) {
return new ColorExt(r, g, b, null);
}