Browse Source

Added support for the cie-lab-color() function that is found in the current XSL 2.0 design notes.

Some refactoring of ColorUtil to reduce code duplication.

git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_Color@959286 13f79535-47bb-0310-9956-ffa450edef68
pull/20/head
Jeremias Maerki 14 years ago
parent
commit
2dc30b14e3

+ 94
- 0
src/java/org/apache/fop/fo/expr/CIELabColorFunction.java View File

@@ -0,0 +1,94 @@
/*
* 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;
}

}

}

+ 2
- 1
src/java/org/apache/fop/fo/expr/PropertyParser.java View File

@@ -69,7 +69,8 @@ public final class PropertyParser extends PropertyTokenizer {
FUNCTION_TABLE.put("label-end", new LabelEndFunction());
FUNCTION_TABLE.put("body-start", new BodyStartFunction());
FUNCTION_TABLE.put("rgb-icc", new ICCColorFunction());
FUNCTION_TABLE.put("rgb-named-color", new NamedColorFunction());
FUNCTION_TABLE.put("rgb-named-color", new NamedColorFunction()); //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!!!

/**

+ 91
- 0
src/java/org/apache/fop/pdf/PDFCIELabColorSpace.java View File

@@ -0,0 +1,91 @@
/*
* 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;
}

}

+ 51
- 5
src/java/org/apache/fop/pdf/PDFColorHandler.java View File

@@ -24,10 +24,12 @@ import java.awt.color.ColorSpace;
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;
@@ -46,6 +48,8 @@ public class PDFColorHandler {

private PDFResources resources;

private Map cieLabColorSpaces;

public PDFColorHandler(PDFResources resources) {
this.resources = resources;
}
@@ -73,10 +77,17 @@ public class PDFColorHandler {
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) {
@@ -93,11 +104,17 @@ public class PDFColorHandler {
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) {
@@ -130,15 +147,44 @@ public class PDFColorHandler {
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) {

+ 8
- 0
src/java/org/apache/fop/pdf/PDFName.java View File

@@ -82,6 +82,14 @@ public class PDFName extends PDFObject {
return this.name;
}

/**
* Returns the name without the leading slash.
* @return the name without the leading slash
*/
public String getName() {
return this.name.substring(1);
}

/** {@inheritDoc} */
public boolean equals(Object obj) {
if (!(obj instanceof PDFName)) {

+ 115
- 86
src/java/org/apache/fop/util/ColorUtil.java View File

@@ -29,6 +29,7 @@ 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.ColorSpaces;
import org.apache.xmlgraphics.java2d.color.DeviceCMYKColorSpace;
@@ -130,6 +131,8 @@ public final class ColorUtil {
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);
}
@@ -234,33 +237,9 @@ public final class ColorUtil {
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
@@ -276,6 +255,38 @@ public final class ColorUtil {
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.
*
@@ -338,17 +349,9 @@ public final class ColorUtil {
}

//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 */
@@ -435,20 +438,13 @@ public final class ColorUtil {

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 */
@@ -518,6 +514,55 @@ public final class ColorUtil {
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);
@@ -549,43 +594,10 @@ public final class ColorUtil {
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) {
@@ -671,6 +683,9 @@ public final class ColorUtil {
if (icc == null) {
return toRGBFunctionCall(color);
}
if (icc.getColorSpace() instanceof CIELabColorSpace) {
return toCIELabFunctionCall(color, icc);
}
StringBuffer sb = new StringBuffer(40);

String functionName;
@@ -706,6 +721,20 @@ public final class ColorUtil {
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);
}

Loading…
Cancel
Save