diff options
Diffstat (limited to 'src/java/org/apache/fop/util')
-rw-r--r-- | src/java/org/apache/fop/util/CMYKColorSpace.java | 5 | ||||
-rw-r--r-- | src/java/org/apache/fop/util/ColorExt.java | 189 | ||||
-rw-r--r-- | src/java/org/apache/fop/util/ColorUtil.java | 256 |
3 files changed, 418 insertions, 32 deletions
diff --git a/src/java/org/apache/fop/util/CMYKColorSpace.java b/src/java/org/apache/fop/util/CMYKColorSpace.java index 00a3f32a2..655c0b3d9 100644 --- a/src/java/org/apache/fop/util/CMYKColorSpace.java +++ b/src/java/org/apache/fop/util/CMYKColorSpace.java @@ -51,7 +51,10 @@ public class CMYKColorSpace extends ColorSpace { * @see java.awt.color.ColorSpace#toRGB(float[]) */ public float[] toRGB(float[] colorvalue) { - throw new UnsupportedOperationException("NYI"); + return new float [] { + (1 - colorvalue[0]) * (1 - colorvalue[3]), + (1 - colorvalue[1]) * (1 - colorvalue[3]), + (1 - colorvalue[2]) * (1 - colorvalue[3])}; } /** diff --git a/src/java/org/apache/fop/util/ColorExt.java b/src/java/org/apache/fop/util/ColorExt.java new file mode 100644 index 000000000..bd2c95a33 --- /dev/null +++ b/src/java/org/apache/fop/util/ColorExt.java @@ -0,0 +1,189 @@ +/* + * 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.util; + +import java.awt.Color; +import java.awt.color.ColorSpace; + +/** + * Color helper class. + * <p> + * This class extends java.awt.Color class keeping track of the original color + * property values specified by the fo user in a rgb-icc call. + */ +public final class ColorExt extends Color { + // + private static final long serialVersionUID = 1L; + + // Values of fop-rgb-icc arguments + private float rgbReplacementRed; + private float rgbReplacementGreen; + private float rgbReplacementBlue; + + private String iccProfileName; + private String iccProfileSrc; + private ColorSpace colorSpace; + + private float[] colorValues; + + /* + * Helper for createFromFoRgbIcc + */ + private ColorExt(ColorSpace colorSpace, float[] colorValues, float opacity) { + super(colorSpace, colorValues, opacity); + } + + /* + * Helper for createFromSvgIccColor + */ + private ColorExt(float red, float green, float blue, float opacity) { + super(red, green, blue, opacity); + } + + /** + * Create ColorExt object backup up FO's rgb-icc color function + * + * @param redReplacement + * Red part of RGB replacement color that will be used when ICC + * profile can not be loaded + * @param greenReplacement + * Green part of RGB replacement color that will be used when ICC + * profile can not be loaded + * @param blueReplacement + * Blue part of RGB replacement color that will be used when ICC + * profile can not be loaded + * @param profileName + * Name of ICC profile + * @param profileSrc + * Source of ICC profile + * @param colorSpace + * ICC ColorSpace for the ICC profile + * @param iccValues + * color values + * @return the requested color object + */ + public static ColorExt createFromFoRgbIcc(float redReplacement, + float greenReplacement, float blueReplacement, String profileName, + String profileSrc, ColorSpace colorSpace, float[] iccValues) { + ColorExt ce = new ColorExt(colorSpace, iccValues, 1.0f); + ce.rgbReplacementRed = redReplacement; + ce.rgbReplacementGreen = greenReplacement; + ce.rgbReplacementBlue = blueReplacement; + ce.iccProfileName = profileName; + ce.iccProfileSrc = profileSrc; + ce.colorSpace = colorSpace; + ce.colorValues = iccValues; + return ce; + } + + /** + * Create ColorExt object backing up SVG's icc-color function. + * + * @param red + * Red value resulting from the conversion from the user provided + * (icc) color values to the batik (rgb) color space + * @param green + * Green value resulting from the conversion from the user + * provided (icc) color values to the batik (rgb) color space + * @param blue + * Blue value resulting from the conversion from the user + * provided (icc) color values to the batik (rgb) color space + * @param opacity + * Opacity + * @param profileName + * ICC profile name + * @param profileHref + * the URI to the color profile + * @param profileCS + * ICC ColorSpace profile + * @param colorValues + * ICC color values + * @return the requested color object + */ + public static ColorExt createFromSvgIccColor(float red, float green, + float blue, float opacity, String profileName, String profileHref, + ColorSpace profileCS, float[] colorValues) { + ColorExt ce = new ColorExt(red, green, blue, opacity); + ce.rgbReplacementRed = -1; + ce.rgbReplacementGreen = -1; + ce.rgbReplacementBlue = -1; + ce.iccProfileName = profileName; + ce.iccProfileSrc = profileHref; + ce.colorSpace = profileCS; + ce.colorValues = colorValues; + return ce; + + } + + /** + * Get ICC profile name + * + * @return ICC profile name + */ + public String getIccProfileName() { + return this.iccProfileName; + } + + /** + * Get ICC profile source + * + * @return ICC profile source + */ + public String getIccProfileSrc() { + return this.iccProfileSrc; + } + + /** + * @return the original ColorSpace + */ + public ColorSpace getOrigColorSpace() { + return this.colorSpace; + } + + /** + * @return the original color values + */ + public float[] getOriginalColorComponents() { + return this.colorValues; + } + + /** + * Create string representation of fop-rgb-icc function call to map this + * ColorExt settings + * @return the string representing the internal fop-rgb-icc() function call + */ + public String toFunctionCall() { + StringBuffer sb = new StringBuffer(40); + sb.append("fop-rgb-icc("); + sb.append(this.rgbReplacementRed + ","); + sb.append(this.rgbReplacementGreen + ","); + sb.append(this.rgbReplacementBlue + ","); + sb.append(this.iccProfileName + ","); + sb.append("\"" + this.iccProfileSrc + "\""); + float[] colorComponents = this.getColorComponents(null); + for (int ix = 0; ix < colorComponents.length; ix++) { + sb.append(","); + sb.append(colorComponents[ix]); + } + sb.append(")"); + return sb.toString(); + } + +} diff --git a/src/java/org/apache/fop/util/ColorUtil.java b/src/java/org/apache/fop/util/ColorUtil.java index 652924025..37762b1e8 100644 --- a/src/java/org/apache/fop/util/ColorUtil.java +++ b/src/java/org/apache/fop/util/ColorUtil.java @@ -20,10 +20,16 @@ package org.apache.fop.util; import java.awt.Color; +import java.awt.color.ColorSpace; import java.util.Collections; +import java.util.LinkedList; +import java.util.List; import java.util.Map; import java.util.StringTokenizer; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.fop.apps.FOUserAgent; import org.apache.fop.fo.expr.PropertyException; /** @@ -31,19 +37,21 @@ import org.apache.fop.fo.expr.PropertyException; * <p> * This class supports parsing string values into color values and creating * color values for strings. It provides a list of standard color names. - * <p> - * TODO: Add support for color Profiles. */ public final class ColorUtil { /** + * * keeps all the predefined and parsed colors. * <p> * This map is used to predefine given colors, as well as speeding up * parsing of already parsed colors. */ private static Map colorMap = null; - + + /** Logger instance */ + protected static Log log = LogFactory.getLog(ColorUtil.class); + static { initializeColorMap(); } @@ -68,8 +76,11 @@ public final class ColorUtil { * <li>system-color(colorname)</li> * <li>transparent</li> * <li>colorname</li> + * <li>fop-rgb-icc</li> + * <li>cmyk</li> * </ul> * + * @param foUserAgent FOUserAgent object * @param value * the string to parse. * @return a Color representing the string if possible @@ -77,7 +88,8 @@ public final class ColorUtil { * if the string is not parsable or does not follow any of the * given formats. */ - public static Color parseColorString(String value) throws PropertyException { + public static Color parseColorString(FOUserAgent foUserAgent, String value) + throws PropertyException { if (value == null) { return null; } @@ -96,18 +108,23 @@ public final class ColorUtil { parsedColor = parseAsJavaAWTColor(value); } else if (value.startsWith("system-color(")) { parsedColor = parseAsSystemColor(value); + } else if (value.startsWith("fop-rgb-icc")) { + parsedColor = parseAsFopRgbIcc(foUserAgent, value); + } else if (value.startsWith("cmyk")) { + parsedColor = parseAsCMYK(value); } - + if (parsedColor == null) { - throw new PropertyException("Unkown Color: " + value); + throw new PropertyException("Unknown Color: " + value); } colorMap.put(value, parsedColor); } - // TODO: Check if this is really necessary - return new Color(parsedColor.getRed(), parsedColor.getGreen(), - parsedColor.getBlue(), parsedColor.getAlpha()); + // TODO - Returned Color object can be one from the static colorMap cache. + // That means it should be treated as read only for the rest of its lifetime. + // Not sure that is the case though. + return parsedColor; } /** @@ -282,6 +299,174 @@ public final class ColorUtil { } /** + * Parse a color specified using the fop-rgb-icc() function. + * + * @param value the function call + * @return a color if possible + * @throws PropertyException if the format is wrong. + */ + private static Color parseAsFopRgbIcc(FOUserAgent foUserAgent, String value) + throws PropertyException { + Color parsedColor; + int poss = value.indexOf("("); + int pose = value.indexOf(")"); + if (poss != -1 && pose != -1) { + value = value.substring(poss + 1, pose); + StringTokenizer st = new StringTokenizer(value, ","); + try { + float red = 0.0f, green = 0.0f, blue = 0.0f; + if (st.hasMoreTokens()) { + String str = st.nextToken().trim(); + red = Float.parseFloat(str); + } + if (st.hasMoreTokens()) { + String str = st.nextToken().trim(); + green = Float.parseFloat(str); + } + if (st.hasMoreTokens()) { + String str = st.nextToken().trim(); + blue = Float.parseFloat(str); + } + /* Verify rgb replacement arguments */ + 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"); + } + /* Get and verify ICC profile name */ + String iccProfileName = null; + if (st.hasMoreTokens()) { + iccProfileName = st.nextToken().trim(); + } + if (iccProfileName == null || iccProfileName.length() == 0) { + throw new PropertyException("ICC profile name missing"); + } + /* Get and verify ICC profile source */ + String iccProfileSrc = null; + if (st.hasMoreTokens()) { + iccProfileSrc = st.nextToken().trim(); + // Strip quotes + iccProfileSrc = iccProfileSrc.substring(1, iccProfileSrc.length() - 1); + } + if (iccProfileSrc == null || iccProfileSrc.length() == 0) { + throw new PropertyException("ICC profile source missing"); + } + /* ICC profile arguments */ + List iccArgList = new LinkedList(); + while (st.hasMoreTokens()) { + String str = st.nextToken().trim(); + iccArgList.add(new Float(str)); + } + /* Copy ICC profile arguments from list to array */ + float[] iccComponents = new float[iccArgList.size()]; + for (int ix = 0; ix < iccArgList.size(); ix++) { + iccComponents[ix] = ((Float)iccArgList.get(ix)).floatValue(); + } + /* Ask FOP factory to get ColorSpace for the specified ICC profile source */ + ColorSpace colorSpace = (foUserAgent != null + ? foUserAgent.getFactory().getColorSpace( + foUserAgent.getBaseURL(), iccProfileSrc) : null); + if (colorSpace != null) { + // ColorSpace available - create ColorExt (keeps track of replacement rgb + // values for possible later colorTOsRGBString call + parsedColor = ColorExt.createFromFoRgbIcc(red, green, blue, + iccProfileName, iccProfileSrc, colorSpace, iccComponents); + } else { + // ICC profile could not be loaded - use rgb replacement values */ + log.warn("Color profile '" + iccProfileSrc + + "' not found. Using rgb replacement values."); + parsedColor = new Color(red, green, blue); + } + } catch (Exception e) { + throw new PropertyException( + "Arguments to rgb-icc() must be [0..255] or [0%..100%]"); + } + } else { + throw new PropertyException("Unknown color format: " + value + + ". Must be fop-rgb-icc(r,g,b,NCNAME,\"src\",....)"); + } + return parsedColor; + } + + /** + * Parse a color given with the cmyk() function. + * + * @param value + * the complete line + * @return a color if possible + * @throws PropertyException + * if the format is wrong. + */ + private static Color parseAsCMYK(String value) throws PropertyException { + Color parsedColor; + int poss = value.indexOf("("); + int pose = value.indexOf(")"); + if (poss != -1 && pose != -1) { + value = value.substring(poss + 1, pose); + StringTokenizer st = new StringTokenizer(value, ","); + try { + float cyan = 0.0f, magenta = 0.0f, yellow = 0.0f, black = 0.0f; + if (st.hasMoreTokens()) { + String str = st.nextToken().trim(); + if (str.endsWith("%")) { + cyan = Float.parseFloat(str.substring(0, + str.length() - 1)) / 100.0f; + } else { + cyan = Float.parseFloat(str); + } + } + if (st.hasMoreTokens()) { + String str = st.nextToken().trim(); + if (str.endsWith("%")) { + magenta = Float.parseFloat(str.substring(0, + str.length() - 1)) / 100.0f; + } else { + magenta = Float.parseFloat(str); + } + } + if (st.hasMoreTokens()) { + String str = st.nextToken().trim(); + if (str.endsWith("%")) { + yellow = Float.parseFloat(str.substring(0, + str.length() - 1)) / 100.0f; + } else { + yellow = Float.parseFloat(str); + } + } + if (st.hasMoreTokens()) { + String str = st.nextToken().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"); + } + float[] cmyk = new float[] {cyan, magenta, yellow, black}; + CMYKColorSpace cmykCs = CMYKColorSpace.getInstance(); + float[] rgb = cmykCs.toRGB(cmyk); + parsedColor = ColorExt.createFromFoRgbIcc(rgb[0], rgb[1], rgb[2], + null, "#CMYK", cmykCs, cmyk); + + + } catch (Exception e) { + throw new PropertyException( + "Arguments to cmyk() must be in the range [0%-100%] or [0.0-1.0]"); + } + } else { + throw new PropertyException("Unknown color format: " + value + + ". Must be cmyk(c,m,y,k)"); + } + return parsedColor; + } + + /** * Creates a re-parsable string representation of the given color. * <p> * First, the color will be converted into the sRGB colorspace. It will then @@ -291,33 +476,42 @@ public final class ColorUtil { * the color to represent. * @return a re-parsable string representadion. */ - public static String colorTOsRGBString(Color color) { - StringBuffer sbuf = new StringBuffer(10); - sbuf.append('#'); - String s = Integer.toHexString(color.getRed()); - if (s.length() == 1) { - sbuf.append('0'); - } - sbuf.append(s); - s = Integer.toHexString(color.getGreen()); - if (s.length() == 1) { - sbuf.append('0'); - } - sbuf.append(s); - s = Integer.toHexString(color.getBlue()); - if (s.length() == 1) { - sbuf.append('0'); - } - sbuf.append(s); - if (color.getAlpha() != 255) { - s = Integer.toHexString(color.getAlpha()); + public static String colorToString(Color color) { + ColorSpace cs = color.getColorSpace(); + if (cs != null && cs.getType() == ColorSpace.TYPE_CMYK) { + StringBuffer sbuf = new StringBuffer(24); + float[] cmyk = color.getColorComponents(null); + sbuf.append("cmyk(" + cmyk[0] + "," + cmyk[1] + "," + cmyk[2] + "," + cmyk[3] + ")"); + return sbuf.toString(); + } else if (color instanceof ColorExt) { + return ((ColorExt)color).toFunctionCall(); + } else { + StringBuffer sbuf = new StringBuffer(); + sbuf.append('#'); + String s = Integer.toHexString(color.getRed()); + if (s.length() == 1) { + sbuf.append('0'); + } + sbuf.append(s); + s = Integer.toHexString(color.getGreen()); if (s.length() == 1) { sbuf.append('0'); } sbuf.append(s); + s = Integer.toHexString(color.getBlue()); + if (s.length() == 1) { + sbuf.append('0'); + } + sbuf.append(s); + if (color.getAlpha() != 255) { + s = Integer.toHexString(color.getAlpha()); + if (s.length() == 1) { + sbuf.append('0'); + } + sbuf.append(s); + } + return sbuf.toString(); } - return sbuf.toString(); - } /** |