<include name="org/apache/fop/image/Abstract*"/>
<include name="org/apache/fop/image/analyser/*.class"/>
<include name="org/apache/fop/util/CMYKColorSpace*.class"/>
+ <include name="org/apache/fop/util/ColorExt*.class"/>
<include name="org/apache/fop/util/ASCII*.class"/>
<include name="org/apache/fop/util/*OutputStream.class"/>
<include name="org/apache/fop/util/SubInputStream.class"/>
package org.apache.fop.apps;
+import java.awt.color.ColorSpace;
+import java.awt.color.ICC_ColorSpace;
+import java.awt.color.ICC_Profile;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.util.Collection;
import java.util.Collections;
+import java.util.Map;
import java.util.Set;
import javax.xml.transform.Source;
import javax.xml.transform.TransformerException;
import javax.xml.transform.URIResolver;
+import javax.xml.transform.stream.StreamSource;
import org.xml.sax.SAXException;
private Set ignoredNamespaces = new java.util.HashSet();
+ /** Map with cached ICC based ColorSpace objects. */
+ private Map colorSpaceMap = null;
+
/**
* Main constructor.
*/
protected FopFactory() {
this.elementMappingRegistry = new ElementMappingRegistry(this);
+ // Use a synchronized Map - I am not really sure this is needed, but better safe than sorry.
+ this.colorSpaceMap = Collections.synchronizedMap(new java.util.HashMap());
}
/**
}
return source;
}
+
+ /**
+ * Create (if needed) and return an ICC ColorSpace instance.
+ *
+ * The ICC profile source is taken from the src attribute of the color-profile FO element.
+ * If the ICC ColorSpace is not yet in the cache a new one is created and stored in the cache.
+ *
+ * The FOP URI resolver is used to try and locate the ICC file.
+ * If that fails null is returned.
+ *
+ * @param base a base URI to resolve relative URIs
+ * @param iccProfileSrc ICC Profile source to return a ColorSpace for
+ * @return ICC ColorSpace object or null if ColorSpace could not be created
+ */
+ public ColorSpace getColorSpace(String base, String iccProfileSrc) {
+ ColorSpace colorSpace = null;
+ if (!this.colorSpaceMap.containsKey(base + iccProfileSrc)) {
+ try {
+ ICC_Profile iccProfile = null;
+ // First attempt to use the FOP URI resolver to locate the ICC
+ // profile
+ Source src = this.resolveURI(iccProfileSrc, base);
+ if (src != null && src instanceof StreamSource) {
+ // FOP URI resolver found ICC profile - create ICC profile
+ // from the Source
+ iccProfile = ICC_Profile.getInstance(((StreamSource) src)
+ .getInputStream());
+ } else {
+ // TODO - Would it make sense to fall back on VM ICC
+ // resolution
+ // Problem is the cache might be more difficult to maintain
+ //
+ // FOP URI resolver did not find ICC profile - perhaps the
+ // Java VM can find it?
+ // iccProfile = ICC_Profile.getInstance(iccProfileSrc);
+ }
+ if (iccProfile != null) {
+ colorSpace = new ICC_ColorSpace(iccProfile);
+ }
+ } catch (IOException e) {
+ // Ignore exception - will be logged a bit further down
+ // (colorSpace == null case)
+ }
-
+ if (colorSpace != null) {
+ // Put in cache (not when VM resolved it as we can't control
+ this.colorSpaceMap.put(base + iccProfileSrc, colorSpace);
+ } else {
+ // TODO To avoid an excessive amount of warnings perhaps
+ // register a null ColorMap in the colorSpaceMap
+ log.warn("Color profile '" + iccProfileSrc + "' not found.");
+ }
+ } else {
+ colorSpace = (ColorSpace) this.colorSpaceMap.get(base
+ + iccProfileSrc);
+ }
+ return colorSpace;
+ }
}
area.addTrait(trait, value);
} else if (cl == Color.class) {
try {
- area.addTrait(trait, ColorUtil.parseColorString(value));
+ area.addTrait(trait, ColorUtil.parseColorString(this.userAgent, value));
} catch (PropertyException e) {
throw new IllegalArgumentException(e.getMessage());
}
} else if (cl == Background.class) {
Background bkg = new Background();
try {
- Color col = ColorUtil
- .parseColorString(attributes
- .getValue("bkg-color"));
+ Color col = ColorUtil.parseColorString(
+ this.userAgent, attributes.getValue("bkg-color"));
bkg.setColor(col);
} catch (PropertyException e) {
throw new IllegalArgumentException(e.getMessage());
}
area.addTrait(trait, bkg);
} else if (cl == BorderProps.class) {
- area.addTrait(trait, BorderProps.valueOf(value));
+ area.addTrait(trait, BorderProps.valueOf(this.userAgent, value));
}
} else {
if (trait == Trait.FONT) {
public String toString() {
StringBuffer sb = new StringBuffer();
if (color != null) {
- sb.append("color=").append(ColorUtil.colorTOsRGBString(color));
+ sb.append("color=").append(ColorUtil.colorToString(color));
}
if (url != null) {
if (color != null) {
import java.util.HashMap;
import java.util.Map;
+import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.datatypes.LengthBase;
import org.apache.fop.fo.expr.PropertyException;
import org.apache.fop.fo.properties.BackgroundPositionShorthandParser;
Property p, PropertyList propertyList, FObj fo) throws PropertyException {
String nameval = p.getNCname();
if (nameval != null) {
- return new ColorProperty(nameval);
+ FObj fobj = (fo == null ? propertyList.getFObj() : fo);
+ FOUserAgent ua = (fobj == null ? null : fobj.getUserAgent());
+ return new ColorProperty(ua, nameval);
}
return super.convertPropertyDatatype(p, propertyList, fo);
}
m.addEnum("mathematical", getEnumProperty(EN_MATHEMATICAL, "MATHEMATICAL"));
m.addEnum("central", getEnumProperty(EN_CENTRAL, "CENTRAL"));
m.addEnum("middle", getEnumProperty(EN_MIDDLE, "MIDDLE"));
- m.addEnum("text-after-edge", getEnumProperty(EN_TEXT_AFTER_EDGE, "TEXT_AFTER_EDGE" ));
+ m.addEnum("text-after-edge", getEnumProperty(EN_TEXT_AFTER_EDGE, "TEXT_AFTER_EDGE"));
m.addEnum("text-before-edge", getEnumProperty(EN_TEXT_BEFORE_EDGE, "TEXT_BEFORE_EDGE"));
m.setDefault("auto");
m.addShorthand(s_generics[PR_VERTICAL_ALIGN]);
* @see org.apache.fop.fo.FONode#clone(FONode, boolean)
*/
public FONode clone(FONode parent, boolean removeChildren)
- throws FOPException {
+ throws FOPException {
FOText ft = (FOText) super.clone(parent, removeChildren);
if (removeChildren) {
//not really removing, but just make sure the char array
public void bind(PropertyList pList) throws FOPException {
commonFont = pList.getFontProps();
commonHyphenation = pList.getHyphenationProps();
-
- color = pList.get(Constants.PR_COLOR).getColor();
+ color = pList.get(Constants.PR_COLOR).getColor(getUserAgent());
lineHeight = pList.get(Constants.PR_LINE_HEIGHT).getSpace();
letterSpacing = pList.get(Constants.PR_LETTER_SPACING);
whiteSpaceCollapse = pList.get(Constants.PR_WHITE_SPACE_COLLAPSE).getEnum();
}
} catch (PropertyException e) {
log.error("Ignoring property: "
- + attributeName + "=\"" + attributeValue + "\"");
+ + attributeName + "=\"" + attributeValue + "\" (" + e.getMessage() + ")");
}
}
}
--- /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.fo.properties.ColorProperty;
+import org.apache.fop.fo.properties.Property;
+
+/**
+ * Implements the cmyk() function.
+ */
+class CMYKcolorFunction extends FunctionBase {
+
+ /**
+ * cmyk takes four arguments.
+ * @see org.apache.fop.fo.expr.Function#nbArgs()
+ */
+ public int nbArgs() {
+ return 4;
+ }
+
+ /** @see org.apache.fop.fo.expr.Function */
+ public Property eval(Property[] args,
+ PropertyInfo pInfo) throws PropertyException {
+ StringBuffer sb = new StringBuffer();
+ sb.append("cmyk(" + args[0] + "," + args[1] + "," + args[2] + "," + args[3] + ")");
+ FOUserAgent ua = (pInfo == null)
+ ? null
+ : (pInfo.getFO() == null ? null : pInfo.getFO().getUserAgent());
+ return new ColorProperty(ua, sb.toString());
+ }
+
+
+}
--- /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.fo.pagination.ColorProfile;
+import org.apache.fop.fo.pagination.Declarations;
+import org.apache.fop.fo.properties.ColorProperty;
+import org.apache.fop.fo.properties.Property;
+
+/**
+ * Implements the rgb-icc() function.
+ */
+class ICCColorFunction extends FunctionBase {
+
+ /**
+ * rgb-icc takes a variable number of arguments.
+ * At least 4 should be passed - returns -4
+ * @see org.apache.fop.fo.expr.Function#nbArgs()
+ */
+ public int nbArgs() {
+ return -4;
+ }
+
+ /** @see org.apache.fop.fo.expr.Function */
+ public Property eval(Property[] args,
+ PropertyInfo pInfo) throws PropertyException {
+ StringBuffer sb = new StringBuffer();
+
+ // Map color profile NCNAME to src from declarations/color-profile element
+ String colorProfileName = args[3].getString();
+ Declarations decls = pInfo.getFO().getRoot().getDeclarations();
+ ColorProfile cp = decls.getColorProfile(colorProfileName);
+ if (cp == null) {
+ PropertyException pe = new PropertyException("The " + colorProfileName
+ + " color profile was not declared");
+ pe.setPropertyInfo(pInfo);
+ throw pe;
+ }
+ String src = cp.getSrc();
+
+ // 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.
+ sb.append("fop-rgb-icc(" + args[0]);
+ for (int ix = 1; ix < args.length; ix++) {
+ if (ix == 3) {
+ sb.append("," + colorProfileName);
+ sb.append(",\"" + src + "\"");
+ } else {
+ sb.append("," + args[ix]);
+ }
+ }
+ sb.append(")");
+ FOUserAgent ua = (pInfo == null
+ ? null
+ : (pInfo.getFO() == null ? null : pInfo.getFO().getUserAgent()));
+ return new ColorProperty(ua, sb.toString());
+ }
+
+
+}
import java.awt.Color;
+import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.fo.properties.Property;
import org.apache.fop.util.ColorUtil;
/**
* If a system color, return the corresponding value.
*
+ * @param foUserAgent
+ * Reference to FOP user agent - keeps track of cached ColorMaps for ICC colors
* @return Color object corresponding to the NCName
*/
- public Color getColor() {
+ public Color getColor(FOUserAgent foUserAgent) {
try {
- return ColorUtil.parseColorString(ncName);
+ return ColorUtil.parseColorString(foUserAgent, ncName);
} catch (PropertyException e) {
- //TODO: This should probably print an error message?
+ //Not logging this error since for properties like "border" you would get an awful
+ //lot of error messages for things like "solid" not being valid colors.
+ //log.error("Can't create color value: " + e.getMessage());
return null;
}
}
package org.apache.fop.fo.expr;
import java.awt.Color;
+
+import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.datatypes.Length;
import org.apache.fop.datatypes.PercentBaseContext;
import org.apache.fop.datatypes.Numeric;
return null;
}
- /** @see org.apache.fop.fo.properties.Property#getColor() */
- public Color getColor() {
+ /** @see org.apache.fop.fo.properties.Property#getColor(FOUserAgent) */
+ public Color getColor(FOUserAgent foUserAgent) {
// TODO: try converting to numeric number and then to color
return null;
}
package org.apache.fop.fo.expr;
+import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.datatypes.Numeric;
import org.apache.fop.datatypes.PercentBase;
import org.apache.fop.fo.properties.ColorProperty;
import org.apache.fop.fo.properties.StringProperty;
import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
/**
* Class to parse XSL-FO property expressions.
new PPColWidthFunction());
FUNCTION_TABLE.put("label-end", new LabelEndFunction());
FUNCTION_TABLE.put("body-start", new BodyStartFunction());
+ FUNCTION_TABLE.put("rgb-icc", new ICCColorFunction());
+ FUNCTION_TABLE.put("cmyk", new CMYKcolorFunction()); //non-standard!!!
/**
* * NOT YET IMPLEMENTED!!!
- * FUNCTION_TABLE.put("icc-color", new ICCcolorFunction());
* FUNCTION_TABLE.put("system-font", new SystemFontFunction());
- *
* FUNCTION_TABLE.put("merge-property-values", new MergePropsFunction());
*/
}
break;
case TOK_COLORSPEC:
- prop = new ColorProperty(currentTokenValue);
+ FOUserAgent ua = (propInfo == null)
+ ? null
+ : (propInfo.getFO() == null ? null : propInfo.getFO().getUserAgent());
+ prop = new ColorProperty(ua, currentTokenValue);
break;
case TOK_FUNCTION_LPAR:
next();
// Push new function (for function context: getPercentBase())
propInfo.pushFunction(function);
- prop = function.eval(parseArgs(function), propInfo);
+ if (function.nbArgs() < 0) {
+ // Negative nbArgs --> function with variable number of arguments
+ prop = function.eval(parseVarArgs(function), propInfo);
+ } else {
+ prop = function.eval(parseArgs(function), propInfo);
+ }
propInfo.popFunction();
return prop;
}
return args;
}
+
+ /**
+ *
+ * Parse a comma separated list of function arguments. Each argument
+ * may itself be an expression. This method consumes the closing right
+ * parenthesis of the argument list.
+ *
+ * The method differs from parseArgs in that it accepts a variable
+ * number of arguments.
+ *
+ * @param function The function object for which the arguments are
+ * collected.
+ * @return An array of Property objects representing the arguments
+ * found.
+ * @throws PropertyException If the number of arguments found isn't equal
+ * to the number expected.
+ *
+ * TODO Merge this with parseArgs?
+ */
+ Property[] parseVarArgs(Function function) throws PropertyException {
+ // For variable argument functions the minimum number of arguments is returned as a
+ // negative integer from the nbArgs method
+ int nbArgs = -function.nbArgs();
+ List args = new LinkedList();
+ Property prop;
+ if (currentToken == TOK_RPAR) {
+ // No args: func()
+ next();
+ } else {
+ while (true) {
+ prop = parseAdditiveExpr();
+ args.add(prop);
+ // ignore extra args
+ if (currentToken != TOK_COMMA) {
+ break;
+ }
+ next();
+ }
+ expectRpar();
+ }
+ if (nbArgs > args.size()) {
+ throw new PropertyException("Expected at least " + nbArgs
+ + ", but got " + args.size() + " args for function");
+ }
+ Property[] propArray = new Property[args.size()];
+ args.toArray(propArray);
+ return propArray;
+ }
/**
package org.apache.fop.fo.expr;
+import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.datatypes.PercentBaseContext;
import org.apache.fop.datatypes.PercentBase;
import org.apache.fop.fo.properties.ColorProperty;
/** @see org.apache.fop.fo.expr.Function */
public Property eval(Property[] args,
PropertyInfo pInfo) throws PropertyException {
- return new ColorProperty("rgb(" + args[0] + "," + args[1] + "," + args[2] + ")");
+ FOUserAgent ua = (pInfo == null)
+ ? null
+ : (pInfo.getFO() == null ? null : pInfo.getFO().getUserAgent());
+ return new ColorProperty(ua, "rgb(" + args[0] + "," + args[1] + "," + args[2] + ")");
}
package org.apache.fop.fo.expr;
+import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.fo.properties.ColorProperty;
import org.apache.fop.fo.properties.Property;
/** @see org.apache.fop.fo.expr.Function */
public Property eval(Property[] args,
PropertyInfo pInfo) throws PropertyException {
- return new ColorProperty("system-color(" + args[0] + ")");
+ FOUserAgent ua = (pInfo == null)
+ ? null
+ : (pInfo.getFO() == null ? null : pInfo.getFO().getUserAgent());
+ return new ColorProperty(ua, "system-color(" + args[0] + ")");
}
commonAural = pList.getAuralProps();
commonFont = pList.getFontProps();
commonRelativePosition = pList.getRelativePositionProps();
- prColor = pList.get(PR_COLOR).getColor();
+ prColor = pList.get(PR_COLOR).getColor(getUserAgent());
// prDirection = pList.get(PR_DIRECTION);
// prLetterSpacing = pList.get(PR_LETTER_SPACING);
lineHeight = pList.get(PR_LINE_HEIGHT).getSpace();
breakAfter = pList.get(PR_BREAK_AFTER).getEnum();
breakBefore = pList.get(PR_BREAK_BEFORE).getEnum();
- color = pList.get(PR_COLOR).getColor();
+ color = pList.get(PR_COLOR).getColor(getUserAgent());
textDepth = pList.get(PR_TEXT_DEPTH).getLength();
textAltitude = pList.get(PR_TEXT_ALTITUDE).getLength();
hyphenationKeep = pList.get(PR_HYPHENATION_KEEP).getEnum();
alignmentBaseline = pList.get(PR_ALIGNMENT_BASELINE).getEnum();
baselineShift = pList.get(PR_BASELINE_SHIFT).getLength();
character = pList.get(PR_CHARACTER).getCharacter();
- color = pList.get(PR_COLOR).getColor();
+ color = pList.get(PR_COLOR).getColor(getUserAgent());
dominantBaseline = pList.get(PR_DOMINANT_BASELINE).getEnum();
textDepth = pList.get(PR_TEXT_DEPTH).getLength();
textAltitude = pList.get(PR_TEXT_ALTITUDE).getLength();
commonBorderPaddingBackground = pList.getBorderPaddingBackgroundProps();
commonFont = pList.getFontProps();
commonRelativePosition = pList.getRelativePositionProps();
- color = pList.get(PR_COLOR).getColor();
+ color = pList.get(PR_COLOR).getColor(getUserAgent());
id = pList.get(PR_ID).getString();
// letterSpacing = pList.get(PR_LETTER_SPACING);
lineHeight = pList.get(PR_LINE_HEIGHT).getSpace();
commonMarginInline = pList.getMarginInlineProps();
commonAural = pList.getAuralProps();
commonFont = pList.getFontProps();
- color = pList.get(PR_COLOR).getColor();
+ color = pList.get(PR_COLOR).getColor(getUserAgent());
lineHeight = pList.get(PR_LINE_HEIGHT).getSpace();
visibility = pList.get(PR_VISIBILITY).getEnum();
}
wrapOption = pList.get(PR_WRAP_OPTION).getEnum();
// implicit properties
- color = pList.get(Constants.PR_COLOR).getColor();
+ color = pList.get(Constants.PR_COLOR).getColor(getUserAgent());
}
/**
wrapOption = pList.get(PR_WRAP_OPTION).getEnum();
// implicit properties
- color = pList.get(Constants.PR_COLOR).getColor();
+ color = pList.get(Constants.PR_COLOR).getColor(getUserAgent());
}
/**
package org.apache.fop.fo.pagination;
-// Java
-import java.awt.Color;
-import java.awt.color.ICC_ColorSpace;
-import java.awt.color.ICC_Profile;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URL;
-
import org.apache.fop.apps.FOPException;
import org.apache.fop.fo.FONode;
import org.apache.fop.fo.FObj;
import org.apache.fop.fo.PropertyList;
import org.apache.fop.fo.ValidationException;
+
import org.xml.sax.Locator;
/**
* The fo:color-profile formatting object.
- * TODO: This needs to be implemented properly!
* This loads the color profile when needed and resolves a requested color.
*/
public class ColorProfile extends FObj {
private String colorProfileName;
private int renderingIntent;
// End of property values
-
- private ICC_ColorSpace colorSpace = null;
/**
* @see org.apache.fop.fo.FONode#FONode(FONode)
return colorProfileName;
}
- /**
- * Get the color specified with the color values from the color profile.
- * The default values are used if the profile could not be loaded
- * or the value is not found.
- * @param colorVals integer array containing the color profile?
- * @param defR integer value for red channel (0-255)?
- * @param defG integer value for green channel (0-255)?
- * @param defB integer value for blue channel (0-255)?
- * @return the ColorType object corresponding to the input
- */
- public Color getColor(int[] colorVals, int defR, int defG, int defB) {
- // float[] rgbvals = colorSpace.toRGB(colorVals);
- // return new ColorType(rgbvals);
- return null;
- }
-
- /**
- * Load the color profile.
- */
- private void load() {
- try {
- URL url = new URL(src);
- InputStream is = url.openStream();
- ICC_Profile iccProfile = ICC_Profile.getInstance(is);
- colorSpace = new ICC_ColorSpace(iccProfile);
- } catch (IOException ioe) {
- getLogger().error("Could not read Color Profile src", ioe);
- } catch (IllegalArgumentException iae) {
- getLogger().error("Color Profile src not an ICC Profile", iae);
- }
- }
-
/** @see org.apache.fop.fo.FONode#getLocalName() */
public String getLocalName() {
return "color-profile";
public int getNameId() {
return FO_COLOR_PROFILE;
}
+
+ /**
+ * Get src attribute
+ *
+ * @return Value of color-profile src attribute
+ */
+ public String getSrc() {
+ return this.src;
+ }
+
+ /**
+ * Get rendering-intent attribute
+ *
+ * Returned value is one of
+ * Constants.EN_AUTO
+ * Constants.EN_PERCEPTUAL
+ * Constants.EN_RELATIVE_COLOMETRIC
+ * Constants.EN_SATURATION
+ * Constants.EN_ABSOLUTE_COLORMETRIC
+ *
+ * @return Rendering intent attribute
+ */
+ public int getRenderingIntent() {
+ return this.renderingIntent;
+ }
}
if (node.getName().equals("fo:color-profile")) {
ColorProfile cp = (ColorProfile)node;
if (!"".equals(cp.getColorProfileName())) {
- if (colorProfiles == null) {
- colorProfiles = new java.util.HashMap();
- }
- if (colorProfiles.get(cp.getColorProfileName()) != null) {
- // duplicate names
- getLogger().warn("Duplicate fo:color-profile profile name : "
- + cp.getColorProfileName());
- }
- colorProfiles.put(cp.getColorProfileName(), cp);
+ addColorProfile(cp);
} else {
getLogger().warn("color-profile-name required for color profile");
}
childNodes = null;
}
+ private void addColorProfile(ColorProfile cp) {
+ if (colorProfiles == null) {
+ colorProfiles = new java.util.HashMap();
+ }
+ if (colorProfiles.get(cp.getColorProfileName()) != null) {
+ // duplicate names
+ getLogger().warn("Duplicate fo:color-profile profile name: "
+ + cp.getColorProfileName());
+ }
+ colorProfiles.put(cp.getColorProfileName(), cp);
+ }
+
/**
* @see org.apache.fop.fo.FObj#getName()
*/
public int getNameId() {
return FO_DECLARATIONS;
}
+
+ /**
+ * Return ColorProfile with given name.
+ *
+ * @param cpName Name of ColorProfile, i.e. the value of the color-profile-name attribute of
+ * the fo:color-profile element
+ * @return The org.apache.fop.fo.pagination.ColorProfile object associated with this
+ * color-profile-name or null
+ */
+ public ColorProfile getColorProfile(String cpName) {
+ ColorProfile profile = null;
+ if (this.colorProfiles != null) {
+ profile = (ColorProfile)this.colorProfiles.get(cpName);
+ }
+ return profile;
+ }
+
+
}
import java.awt.Color;
+import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.fo.FObj;
import org.apache.fop.fo.PropertyList;
import org.apache.fop.fo.expr.PropertyException;
if (p instanceof ColorProperty) {
return p;
}
- Color val = p.getColor();
+ FObj fobj = (fo == null ? propertyList.getFObj() : fo);
+ FOUserAgent ua = (fobj == null ? null : fobj.getUserAgent());
+ Color val = p.getColor(ua);
if (val != null) {
return new ColorProperty(val);
}
}
-
/**
* Set the color given a particular String. For a full List of supported
* values please see ColorUtil.
*
+ * @param foUserAgent FOP user agent
* @param value RGB value as String to be parsed
* @throws PropertyException if the value can't be parsed
* @see ColorUtil#parseColorString(String)
*/
- public ColorProperty(String value) throws PropertyException {
- this.color = ColorUtil.parseColorString(value);
+ public ColorProperty(FOUserAgent foUserAgent, String value) throws PropertyException {
+ this.color = ColorUtil.parseColorString(foUserAgent, value);
}
/**
/**
* Returns an AWT instance of this color
+ * @param foUserAgent FOP user agent
* @return float the AWT color represented by this ColorType instance
*/
- public Color getColor() {
+ public Color getColor(FOUserAgent foUserAgent) {
return color;
}
* @see java.lang.Object#toString()
*/
public String toString() {
- return ColorUtil.colorTOsRGBString(color);
+ return ColorUtil.colorToString(color);
}
/**
public CommonBorderPaddingBackground(PropertyList pList, FObj fobj) throws PropertyException {
backgroundAttachment = pList.get(Constants.PR_BACKGROUND_ATTACHMENT).getEnum();
- backgroundColor = pList.get(Constants.PR_BACKGROUND_COLOR).getColor();
+ backgroundColor = pList.get(Constants.PR_BACKGROUND_COLOR).getColor(
+ fobj == null ? null : fobj.getUserAgent());
if (backgroundColor.getAlpha() == 0) {
backgroundColor = null;
}
// If style = none, force width to 0, don't get Color (spec 7.7.20)
int style = pList.get(styleProp).getEnum();
if (style != Constants.EN_NONE) {
+ FOUserAgent ua = (pList == null)
+ ? null
+ : (pList.getFObj() == null ? null : pList.getFObj().getUserAgent());
setBorderInfo(new BorderInfo(style,
pList.get(widthProp).getCondLength(),
- pList.get(colorProp).getColor()), side);
+ pList.get(colorProp).getColor(ua)), side);
}
}
public Color getBorderColor(int side) {
if (borderInfo[side] != null) {
- return borderInfo[side].mColor;
+ return borderInfo[side].getColor();
} else {
return null;
}
import java.util.List;
import java.awt.Color;
+
+import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.fo.Constants;
import org.apache.fop.fo.PropertyList;
import org.apache.fop.fo.expr.PropertyException;
while (i.hasNext()) {
Property prop = (Property)i.next();
int propEnum = prop.getEnum();
+ FOUserAgent ua = (pList == null)
+ ? null
+ : (pList.getFObj() == null ? null : pList.getFObj().getUserAgent());
if (propEnum == Constants.EN_NONE) {
if (deco != null) {
deco.decoration = 0;
deco = new CommonTextDecoration();
}
deco.decoration |= UNDERLINE;
- deco.underColor = pList.get(Constants.PR_COLOR).getColor();
+ deco.underColor = pList.get(Constants.PR_COLOR).getColor(ua);
} else if (propEnum == Constants.EN_NO_UNDERLINE) {
if (deco != null) {
deco.decoration &= OVERLINE | LINE_THROUGH | BLINK;
- deco.underColor = pList.get(Constants.PR_COLOR).getColor();
+ deco.underColor = pList.get(Constants.PR_COLOR).getColor(ua);
}
} else if (propEnum == Constants.EN_OVERLINE) {
if (deco == null) {
deco = new CommonTextDecoration();
}
deco.decoration |= OVERLINE;
- deco.overColor = pList.get(Constants.PR_COLOR).getColor();
+ deco.overColor = pList.get(Constants.PR_COLOR).getColor(ua);
} else if (propEnum == Constants.EN_NO_OVERLINE) {
if (deco != null) {
deco.decoration &= UNDERLINE | LINE_THROUGH | BLINK;
- deco.overColor = pList.get(Constants.PR_COLOR).getColor();
+ deco.overColor = pList.get(Constants.PR_COLOR).getColor(ua);
}
} else if (propEnum == Constants.EN_LINE_THROUGH) {
if (deco == null) {
deco = new CommonTextDecoration();
}
deco.decoration |= LINE_THROUGH;
- deco.throughColor = pList.get(Constants.PR_COLOR).getColor();
+ deco.throughColor = pList.get(Constants.PR_COLOR).getColor(ua);
} else if (propEnum == Constants.EN_NO_LINE_THROUGH) {
if (deco != null) {
deco.decoration &= UNDERLINE | OVERLINE | BLINK;
- deco.throughColor = pList.get(Constants.PR_COLOR).getColor();
+ deco.throughColor = pList.get(Constants.PR_COLOR).getColor(ua);
}
} else if (propEnum == Constants.EN_BLINK) {
if (deco == null) {
import java.awt.Color;
+import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.datatypes.Length;
import org.apache.fop.datatypes.Numeric;
import org.apache.fop.datatypes.PercentBaseContext;
/**
* Convert NumberProperty to a Color. Not sure why this is needed.
+ * @param foUserAgent FOUserAgent
* @return Color that corresponds to black
*/
- public Color getColor() {
+ public Color getColor(FOUserAgent foUserAgent) {
// TODO: Implement somehow
// Convert numeric value to color ???
// Convert to hexadecimal and then try to make it into a color?
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.datatypes.Length;
import org.apache.fop.datatypes.Numeric;
import org.apache.fop.fo.Constants;
/**
* This method expects to be overridden by subclasses
+ * @param foUserAgent FOP user agent
* @return ColorType property value
*/
- public Color getColor() {
+ public Color getColor(FOUserAgent foUserAgent) {
return null;
}
package org.apache.fop.pdf;
-// Java
+import java.awt.Color;
+import java.awt.color.ColorSpace;
+import java.awt.color.ICC_ColorSpace;
+import java.io.IOException;
import java.util.List;
import java.util.ArrayList;
+import org.apache.fop.util.ColorExt;
+
/**
* PDF Color object.
* This is used to output color to a PDF content stream.
private double magenta = -1.0;
private double yellow = -1.0;
private double black = -1.0;
+
+ // TODO - It would probably be better to reorganize PDFPathPaint/PDFColor/PDFColorSpace
+ // class hierarchy. However, at this early stages of my FOP understanding, I can
+ // not really oversee the consequences of such a switch (nor whether it would be
+ // appropriate).
+ private ColorExt colorExt = null;
/**
* Create a PDF color with double values ranging from 0 to 1
this.green = theGreen;
this.blue = theBlue;
}
-
+
+ /**
+ * Create PDFColor for the given document and based on the java.awt.Color object
+ *
+ * In case the java.awt.Color is an instance of the ColorExt class a PDFICCStream is added to
+ * the PDFDocument that is being created
+ *
+ * @param pdfDoc PDFDocument that is being created
+ * @param col Color object from which to create this PDFColor
+ */
+ public PDFColor(PDFDocument pdfDoc, Color col) {
+ this(col);
+ // TODO - 1) There is a potential conflict when FOP and Batik elements use the same color
+ // profile name for different profiles.
+ // 2) In case the same color profile is used with different names it will be
+ // included multiple times in the PDF
+ //
+ if (colorExt != null
+ && pdfDoc.getResources().getColorSpace(colorExt.getIccProfileName()) == null) {
+ PDFICCStream pdfIccStream = new PDFICCStream();
+ ColorSpace ceCs = colorExt.getOrigColorSpace();
+ try {
+ pdfIccStream.setColorSpace(((ICC_ColorSpace)ceCs).getProfile(), null);
+ pdfIccStream.setData(
+ ((ICC_ColorSpace)colorExt.getColorSpace()).getProfile().getData());
+ } catch (IOException ioe) {
+ log.error("Failed to set profile data for " + colorExt.getIccProfileName());
+ }
+ pdfDoc.registerObject(pdfIccStream);
+ pdfDoc.getFactory().makeICCBasedColorSpace(
+ null, colorExt.getIccProfileName(), pdfIccStream);
+ if (log.isInfoEnabled()) {
+ log.info("Adding PDFICCStream " + colorExt.getIccProfileName()
+ + " for " + colorExt.getIccProfileSrc());
+ }
+ }
+ }
+
/**
* Create a PDF color from a java.awt.Color object.
+ *
+ * Different Color objects are handled differently. Cases recognized are.
+ *
+ * 1. CMYK color
+ * 2. ColorExt color
+ * 3. 'Normal' java.awt.Color (RGB case assumed)
*
- * @param col the sRGB color
+ * @param col the java.awt.Color object for which to create a PDFColor object
*/
public PDFColor(java.awt.Color col) {
- this.colorSpace = new PDFDeviceColorSpace(PDFDeviceColorSpace.DEVICE_RGB);
- float[] comps = new float[3];
- comps = col.getColorComponents(comps);
-
- this.red = comps[0];
- this.green = comps[1];
- this.blue = comps[2];
+ ColorSpace cs = col.getColorSpace();
+ ColorExt ce = null;
+ if (col instanceof ColorExt) {
+ ce = (ColorExt)col;
+ cs = ce.getOrigColorSpace();
+ }
+ if (cs != null && cs.getType() == ColorSpace.TYPE_CMYK) {
+ // CMYK case
+ this.colorSpace = new PDFDeviceColorSpace(PDFDeviceColorSpace.DEVICE_CMYK);
+ float[] cmyk = (ce == null
+ ? col.getColorComponents(null)
+ : ce.getOriginalColorComponents());
+ this.cyan = cmyk[0];
+ this.magenta = cmyk[1];
+ this.yellow = cmyk[2];
+ this.black = cmyk[3];
+ } else if (ce != null) {
+ // ColorExt (ICC) case
+ this.colorExt = ce;
+ float[] rgb = col.getRGBColorComponents(null);
+ this.red = rgb[0];
+ this.green = rgb[1];
+ this.blue = rgb[2];
+ // TODO - See earlier todo
+ this.colorSpace = new PDFDeviceColorSpace(PDFDeviceColorSpace.DEVICE_RGB);
+ } else {
+ // Default (RGB) Color
+ this.colorSpace = new PDFDeviceColorSpace(PDFDeviceColorSpace.DEVICE_RGB);
+ float[] comps = new float[3];
+ comps = col.getColorComponents(comps);
+ this.red = comps[0];
+ this.green = comps[1];
+ this.blue = comps[2];
+ }
}
/**
public String getColorSpaceOut(boolean fillNotStroke) {
StringBuffer p = new StringBuffer("");
- double tempDouble;
-
- if (this.colorSpace.getColorSpace()
+ if (this.colorExt != null) {
+ if (fillNotStroke) {
+ p.append("/" + this.colorExt.getIccProfileName() + " cs ");
+ } else {
+ p.append("/" + this.colorExt.getIccProfileName() + " CS ");
+ }
+ float[] colorArgs;
+ colorArgs = this.colorExt.getOriginalColorComponents();
+ if (colorArgs == null) {
+ colorArgs = this.colorExt.getColorComponents(null);
+ }
+ for (int ix = 0; ix < colorArgs.length; ix++) {
+ p.append(colorArgs[ix] + " ");
+ }
+ if (fillNotStroke) {
+ p.append("sc\n");
+ } else {
+ p.append("SC\n");
+ }
+ } else if (this.colorSpace.getColorSpace()
== PDFDeviceColorSpace.DEVICE_RGB) { // colorspace is RGB
// according to pdfspec 12.1 p.399
// if the colors are the same then just use the g or G operator
}
PDFColor color = (PDFColor)obj;
- if (color.red == this.red && color.green == this.green
+ if (color.red == this.red
+ && color.green == this.green
&& color.blue == this.blue) {
return true;
}
* written to the current stream.
*/
protected void setColor(Color col, boolean fill, StringBuffer pdf) {
- PDFColor color = new PDFColor(col);
+ PDFColor color = new PDFColor(this.pdfDoc, col);
closeText();
}
} else if (clazz.equals(Color.class)) {
Color c = (Color)value;
- addAttribute(name, ColorUtil.colorTOsRGBString(c));
+ addAttribute(name, ColorUtil.colorToString(c));
} else if (key == Trait.START_INDENT || key == Trait.END_INDENT) {
if (((Integer)value).intValue() != 0) {
addAttribute(name, value.toString());
import org.apache.fop.image.JpegImage;
import org.apache.fop.fonts.CIDFont;
import org.apache.fop.render.pdf.FopPDFImage;
+import org.apache.fop.util.ColorExt;
import org.apache.xmlgraphics.java2d.AbstractGraphics2D;
import org.apache.xmlgraphics.java2d.GraphicContext;
*/
protected void applyColor(Color col, boolean fill) {
preparePainting();
+
Color c = col;
- if (c.getColorSpace().getType()
+ if (col instanceof ColorExt) {
+ PDFColor currentColour = new PDFColor(this.pdfDoc, col);
+ currentStream.write(currentColour.getColorSpaceOut(fill));
+ } else if (c.getColorSpace().getType()
== ColorSpace.TYPE_RGB) {
PDFColor currentColour = new PDFColor(c.getRed(), c.getGreen(),
c.getBlue());
import java.io.Serializable;
import java.util.StringTokenizer;
+import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.fo.Constants;
import org.apache.fop.fo.expr.PropertyException;
import org.apache.fop.util.ColorUtil;
/**
* Returns a BorderProps represtation of a string of the format as written by
* BorderProps.toString().
+ * @param foUserAgent FOP user agent caching ICC profiles
* @param s the string
* @return a BorderProps instance
*/
- public static BorderProps valueOf(String s) {
+ public static BorderProps valueOf(FOUserAgent foUserAgent, String s) {
if (s.startsWith("(") && s.endsWith(")")) {
s = s.substring(1, s.length() - 1);
StringTokenizer st = new StringTokenizer(s, ",");
}
Color c;
try {
- c = ColorUtil.parseColorString(color);
+ c = ColorUtil.parseColorString(foUserAgent, color);
} catch (PropertyException e) {
throw new IllegalArgumentException(e.getMessage());
}
sbuf.append('(');
sbuf.append(getStyleString());
sbuf.append(',');
- sbuf.append(ColorUtil.colorTOsRGBString(color));
+ sbuf.append(ColorUtil.colorToString(color));
sbuf.append(',');
sbuf.append(width);
if (mode != SEPARATE) {
* @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])};
}
/**
--- /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.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();
+ }
+
+}
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;
/**
* <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();
}
* <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
* 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;
}
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;
}
/**
return parsedColor;
}
+ /**
+ * 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>
* 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();
-
}
/**
<changes>
<release version="FOP Trunk">
+ <action context="Code" dev="JM" type="add" fixes-bug="40729" due-to="Peter Coppens">
+ Support for the rgb-icc() function and for a proprietary cmyk() function (for device CMYK
+ colors only through the PDF renderer so far).
+ </action>
<action context="Code" dev="JM" type="update" fixes-bug="40813" due-to="Richard Wheeldon">
Minor fixes and improvements for the AWT Preview (keyboard shortcuts, scrolling, windows
setup).
public void testSerialization() throws Exception {
Color col = new Color(1.0f, 1.0f, 0.5f, 1.0f);
//Normalize: Avoid false alarms due to color conversion (rounding)
- col = ColorUtil.parseColorString(ColorUtil.colorTOsRGBString(col));
+ col = ColorUtil.parseColorString(null, ColorUtil.colorToString(col));
BorderProps b1 = new BorderProps(Constants.EN_DOUBLE, 1250,
col, BorderProps.COLLAPSE_OUTER);
String ser = b1.toString();
- BorderProps b2 = BorderProps.valueOf(ser);
+ BorderProps b2 = BorderProps.valueOf(null, ser);
assertEquals(b1, b2);
b1 = new BorderProps(Constants.EN_INSET, 9999,
col, BorderProps.SEPARATE);
ser = b1.toString();
- b2 = BorderProps.valueOf(ser);
+ b2 = BorderProps.valueOf(null, ser);
assertEquals(b1, b2);
}
*/
public void testSerialization() throws Exception {
Color col = new Color(1.0f, 1.0f, 0.5f, 1.0f);
- String s = ColorUtil.colorTOsRGBString(col);
+ String s = ColorUtil.colorToString(col);
//This is what the old color spit out. Now it is 80 due to rounding
//assertEquals("#ffff7f", s);
assertEquals("#ffff80", s);
col = new Color(1.0f, 0.0f, 0.0f, 0.8f);
- s = ColorUtil.colorTOsRGBString(col);
+ s = ColorUtil.colorToString(col);
assertEquals("#ff0000cc", s);
}
* @throws Exception if an error occurs
*/
public void testDeserialization() throws Exception {
- Color col = ColorUtil.parseColorString("#ffff7f");
+ Color col = ColorUtil.parseColorString(null, "#ffff7f");
assertEquals(255, col.getRed());
assertEquals(255, col.getGreen());
assertEquals(127, col.getBlue());
assertEquals(255, col.getAlpha());
- col = ColorUtil.parseColorString("#ff0000cc");
+ col = ColorUtil.parseColorString(null, "#ff0000cc");
assertEquals(255, col.getRed());
assertEquals(0, col.getGreen());
assertEquals(0, col.getBlue());
* @throws Exception if an error occurs
*/
public void testEquals() throws Exception {
- Color col1 = ColorUtil.parseColorString("#ff0000cc");
- Color col2 = ColorUtil.parseColorString("#ff0000cc");
- assertTrue(col1 != col2);
+ Color col1 = ColorUtil.parseColorString(null, "#ff0000cc");
+ Color col2 = ColorUtil.parseColorString(null, "#ff0000cc");
assertEquals(col1, col2);
}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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$ -->
+<testcase>
+ <info>
+ <p>
+ This test checks colors.
+ </p>
+ </info>
+ <fo>
+ <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" font-family="Gladiator">
+ <fo:layout-master-set>
+ <fo:simple-page-master master-name="normal" page-width="5in" page-height="5in">
+ <fo:region-body/>
+ </fo:simple-page-master>
+ </fo:layout-master-set>
+ <fo:declarations>
+ <fo:color-profile src="../../../src/java/org/apache/fop/pdf/sRGB Color Space Profile.icm" color-profile-name="sRGB"/>
+ <fo:color-profile src="nonexistent.icc" color-profile-name="unknown"/>
+ </fo:declarations>
+ <fo:page-sequence master-reference="normal" white-space-collapse="true">
+ <fo:flow flow-name="xsl-region-body">
+ <fo:block color="red">color "red"</fo:block>
+ <fo:block color="rgb(255,128,0)">color "rgb(255,128,0)"</fo:block>
+ <fo:block color="rgb(100%,50%,0%)">color "rgb(100%,50%,0%)"</fo:block>
+ <fo:block color="rgb-icc(100%,50%,0%, sRGB, 1, 0.5, 0)">color "rgb-icc(100%,50%,0%, sRGB, 1, 0.5, 0)"</fo:block>
+ <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:flow>
+ </fo:page-sequence>
+ </fo:root>
+ </fo>
+ <checks>
+ <!-- Check page -->
+ <eval expected="#ff0000" xpath="//block[1]//text/@color"/>
+ <eval expected="#ff8000" xpath="//block[2]//text/@color"/>
+ <eval expected="#ff8000" xpath="//block[3]//text/@color"/>
+ <eval expected="fop-rgb-icc(1.0,0.5,0.0,sRGB,"../../../src/java/org/apache/fop/pdf/sRGB Color Space Profile.icm",1.0,0.5,0.0)" xpath="//block[4]//text/@color"/>
+ <eval expected="#00ff00" xpath="//block[5]//text/@color"/>
+ <eval expected="cmyk(0.0,0.0,0.2,0.4)" xpath="//block[6]//text/@color"/>
+ </checks>
+</testcase>