]> source.dussan.org Git - xmlgraphics-fop.git/commitdiff
Bugzilla #40729:
authorJeremias Maerki <jeremias@apache.org>
Mon, 13 Nov 2006 10:08:19 +0000 (10:08 +0000)
committerJeremias Maerki <jeremias@apache.org>
Mon, 13 Nov 2006 10:08:19 +0000 (10:08 +0000)
Support for the rgb-icc() function and for a proprietary cmyk() function (for device CMYK colors only through the PDF renderer so far).
Submitted by: Peter Coppens <pc.subscriptions.at.gmail.com>

Patch slightly modified to comply with our Java conventions, plus some minor editing.

git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@474225 13f79535-47bb-0310-9956-ffa450edef68

40 files changed:
build.xml
src/java/org/apache/fop/apps/FopFactory.java
src/java/org/apache/fop/area/AreaTreeParser.java
src/java/org/apache/fop/area/Trait.java
src/java/org/apache/fop/fo/FOPropertyMapping.java
src/java/org/apache/fop/fo/FOText.java
src/java/org/apache/fop/fo/PropertyList.java
src/java/org/apache/fop/fo/expr/CMYKcolorFunction.java [new file with mode: 0644]
src/java/org/apache/fop/fo/expr/ICCColorFunction.java [new file with mode: 0644]
src/java/org/apache/fop/fo/expr/NCnameProperty.java
src/java/org/apache/fop/fo/expr/NumericProperty.java
src/java/org/apache/fop/fo/expr/PropertyParser.java
src/java/org/apache/fop/fo/expr/RGBColorFunction.java
src/java/org/apache/fop/fo/expr/SystemColorFunction.java
src/java/org/apache/fop/fo/flow/BidiOverride.java
src/java/org/apache/fop/fo/flow/Block.java
src/java/org/apache/fop/fo/flow/Character.java
src/java/org/apache/fop/fo/flow/InitialPropertySet.java
src/java/org/apache/fop/fo/flow/InlineLevel.java
src/java/org/apache/fop/fo/flow/PageNumber.java
src/java/org/apache/fop/fo/flow/PageNumberCitation.java
src/java/org/apache/fop/fo/pagination/ColorProfile.java
src/java/org/apache/fop/fo/pagination/Declarations.java
src/java/org/apache/fop/fo/properties/ColorProperty.java
src/java/org/apache/fop/fo/properties/CommonBorderPaddingBackground.java
src/java/org/apache/fop/fo/properties/CommonTextDecoration.java
src/java/org/apache/fop/fo/properties/NumberProperty.java
src/java/org/apache/fop/fo/properties/Property.java
src/java/org/apache/fop/pdf/PDFColor.java
src/java/org/apache/fop/render/pdf/PDFRenderer.java
src/java/org/apache/fop/render/xml/XMLRenderer.java
src/java/org/apache/fop/svg/PDFGraphics2D.java
src/java/org/apache/fop/traits/BorderProps.java
src/java/org/apache/fop/util/CMYKColorSpace.java
src/java/org/apache/fop/util/ColorExt.java [new file with mode: 0644]
src/java/org/apache/fop/util/ColorUtil.java
status.xml
test/java/org/apache/fop/traits/BorderPropsTestCase.java
test/java/org/apache/fop/traits/TraitColorTestCase.java
test/layoutengine/standard-testcases/color_1.xml [new file with mode: 0644]

index 746752c9a35af9987212ab81459ebc90406c4ea3..8e9748e115a5229b4f21a0dc3edef066440ab671 100644 (file)
--- a/build.xml
+++ b/build.xml
@@ -604,6 +604,7 @@ list of possible build targets.
       <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"/>
index d5cc4dee994501feec6f3b2d634322130411089d..363d680590a621ec486f9596e8753eafc781fe56 100644 (file)
 
 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;
 
@@ -119,11 +124,16 @@ public class FopFactory {
 
     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());
     }
     
     /**
@@ -632,7 +642,63 @@ public class FopFactory {
         }
         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;
+    }
     
 }
index b6d1e8163ffea7b43cfd109eafcc48287eed287d..fce776e220f8325fc87c90e4bd677e09161df17d 100644 (file)
@@ -928,16 +928,15 @@ public class AreaTreeParser {
                         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());
@@ -970,7 +969,7 @@ public class AreaTreeParser {
                         }
                         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) {
index 6db47949ac53512046a938379b8c13a6bd8087c9..3cddc80062b07dd3d1b9c186277aecd72e0e238d 100644 (file)
@@ -571,7 +571,7 @@ public class Trait implements Serializable {
         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) {
index 2cb2da653e0db4dd3ee64d6cb82e7bc20381ca9b..48a58b94ad0ab04ec66f7ac1b44358c5087dcae8 100644 (file)
@@ -22,6 +22,7 @@ package org.apache.fop.fo;
 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;
@@ -564,7 +565,9 @@ public final class FOPropertyMapping implements Constants {
                     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);
             }
@@ -1289,7 +1292,7 @@ public final class FOPropertyMapping implements Constants {
         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]);
index 70f0fd5bf5ab5f40dc91804d440388788b210425..f2c4c4e543578375288c929eb29f1d5ce635dba2 100644 (file)
@@ -145,7 +145,7 @@ public class FOText extends FONode {
      * @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 
@@ -164,8 +164,7 @@ public class FOText extends FONode {
     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();
index 8b7c67abd6c06b0548a2d71114a48c06672e29f1..4ec6ec26d7addde157c0ebae3226b7d06d865f3c 100644 (file)
@@ -421,7 +421,7 @@ public abstract class PropertyList {
                 }
             } catch (PropertyException e) {
                 log.error("Ignoring property: " 
-                        + attributeName + "=\"" + attributeValue + "\"");
+                        + attributeName + "=\"" + attributeValue + "\" (" + e.getMessage() + ")");
             }
         }
     }
diff --git a/src/java/org/apache/fop/fo/expr/CMYKcolorFunction.java b/src/java/org/apache/fop/fo/expr/CMYKcolorFunction.java
new file mode 100644 (file)
index 0000000..a66e849
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * 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());
+    }
+
+
+}
diff --git a/src/java/org/apache/fop/fo/expr/ICCColorFunction.java b/src/java/org/apache/fop/fo/expr/ICCColorFunction.java
new file mode 100644 (file)
index 0000000..fce0734
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * 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());
+    }
+
+
+}
index 7188fb953d655dd1e6089841a45d37f689774973..8e2f8e27f06bd617bb4eeee1ff30ee1737d14be6 100644 (file)
@@ -21,6 +21,7 @@ package org.apache.fop.fo.expr;
 
 import java.awt.Color;
 
+import org.apache.fop.apps.FOUserAgent;
 import org.apache.fop.fo.properties.Property;
 import org.apache.fop.util.ColorUtil;
 
@@ -42,13 +43,17 @@ public class NCnameProperty extends Property {
     /**
      * 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;
         }
     }
index f00b9951c41e3d8d2b993773c2a9b937081a35d8..4c112964de2e646b2690af11f5827fdc7526a128 100644 (file)
@@ -20,6 +20,8 @@
 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;
@@ -105,8 +107,8 @@ public class NumericProperty extends Property implements Numeric, Length {
         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;
     }
index 699094cc76689d01432c8e38053b2a27b35bc82c..5b5956f446476adb9ccec5e5029243b1cbef4b46 100644 (file)
@@ -19,6 +19,7 @@
 
 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;
@@ -30,6 +31,8 @@ import org.apache.fop.fo.properties.Property;
 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.
@@ -62,12 +65,12 @@ public final class PropertyParser extends PropertyTokenizer {
                           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());
          */
     }
@@ -295,7 +298,10 @@ public final class PropertyParser extends PropertyTokenizer {
             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:
@@ -307,7 +313,12 @@ public final class PropertyParser extends PropertyTokenizer {
             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;
         
@@ -362,6 +373,54 @@ public final class PropertyParser extends PropertyTokenizer {
         }
         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;
+    }
 
 
     /**
index d74d1b7f47b2e1c6daeacf43cc0b1fba94b2ebb7..805d8014fb899145f24821569f7c24d2c41773cc 100644 (file)
@@ -19,6 +19,7 @@
  
 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;
@@ -46,7 +47,10 @@ class RGBColorFunction extends FunctionBase {
     /** @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] + ")");
 
     }
 
index 678a56ef847250bd56a034218dab8bf06713360e..ea9e4562e4f5a4c05dc4b6c60bdd9a5857fe7bce 100644 (file)
@@ -19,6 +19,7 @@
  
 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;
 
@@ -35,7 +36,10 @@ class SystemColorFunction extends FunctionBase {
     /** @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] + ")");
 
     }
 
index 8546459fb263539893a6fb173a0faf3256781c82..3df774be951635f97b8628055f4df808b30e947d 100644 (file)
@@ -87,7 +87,7 @@ public class BidiOverride extends FObjMixed {
         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();
index b5dd2626f7c1331278ab7462432fbea1c1e6e7b0..f52ad36b2afdfe12a66617742ad96267c7527154 100644 (file)
@@ -110,7 +110,7 @@ public class Block extends FObjMixed {
 
         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();
index 5afe52eb45e123a0a82304f9f5de8ddd29bd91aa..cb32c9a49134b1d4c06a96f8dea1be0b2bf9c491 100644 (file)
@@ -117,7 +117,7 @@ public class Character extends FObj {
         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();
index e68c118576c216dbb0b40ad10e276245b00a506a..4fd1eae08a599bda3518b9f6fe9b72885ae5df23 100644 (file)
@@ -73,7 +73,7 @@ public class InitialPropertySet extends FObj {
         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();
index 06e7c775af651a521106f592a9ebf2031244bc44..cae0fac1de51941648858ce0dc9615652322d791 100644 (file)
@@ -65,7 +65,7 @@ public abstract class InlineLevel extends FObjMixed {
         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();
     }
index 06f621be5be0d522f32dfc86bb235a0eed33a660..aee4a9afb80ac107c6d910bb1e2dac976d46fe12 100644 (file)
@@ -112,7 +112,7 @@ public class PageNumber extends FObj {
         wrapOption = pList.get(PR_WRAP_OPTION).getEnum();
 
         // implicit properties
-        color = pList.get(Constants.PR_COLOR).getColor();
+        color = pList.get(Constants.PR_COLOR).getColor(getUserAgent());
     }
 
     /**
index 9c63b4c1b26c4d4b4eb9e046be5226febde5c7c2..d6f0b940cdaa89d4184dac69600c3d491b3bfd66 100644 (file)
@@ -117,7 +117,7 @@ public class PageNumberCitation extends FObj {
         wrapOption = pList.get(PR_WRAP_OPTION).getEnum();
         
         // implicit properties
-        color = pList.get(Constants.PR_COLOR).getColor();
+        color = pList.get(Constants.PR_COLOR).getColor(getUserAgent());
     }
 
     /**
index ff77b9eacc4db813b45b20de3ef1f6fa465a16d3..fe379c6afdc4dc9ac7a4a236ff0e6292dabb8529 100644 (file)
 
 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 {
@@ -45,8 +37,6 @@ 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)
@@ -80,38 +70,6 @@ public class ColorProfile extends FObj {
         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";
@@ -123,4 +81,29 @@ public class ColorProfile extends FObj {
     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;
+    }
 }
index a3a96361a93ae5d60757c32ec8642499c3258e1d..4f8273caf5576132b64d536080f0b206d26d8c23 100644 (file)
@@ -83,15 +83,7 @@ public class Declarations extends FObj {
                 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");
                     }
@@ -104,6 +96,18 @@ public class Declarations extends FObj {
         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()
      */
@@ -117,4 +121,22 @@ public class Declarations extends FObj {
     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;
+    }
+    
+    
 }
index bc42ff45100318e8b0a91bd72ca590f7566bbe7e..c49546862703addb09abef77a936cfbd263ee8c5 100644 (file)
@@ -21,6 +21,7 @@ package org.apache.fop.fo.properties;
 
 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;
@@ -74,7 +75,9 @@ public class ColorProperty extends Property  {
             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);
             }
@@ -83,17 +86,17 @@ public class ColorProperty extends Property  {
 
     }
 
-
     /**
      * 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);
     }
 
     /**
@@ -107,9 +110,10 @@ public class ColorProperty extends Property  {
     
     /**
      * 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;
     }
 
@@ -117,7 +121,7 @@ public class ColorProperty extends Property  {
      * @see java.lang.Object#toString()
      */
     public String toString() {
-        return ColorUtil.colorTOsRGBString(color);
+        return ColorUtil.colorToString(color);
     }
 
     /**
index 5d7d36b4bcd36d4924c35db90bf4caa4f5699737..53b90fea99df0a75f914d5cd8dd0d5a94cd05088 100755 (executable)
@@ -144,7 +144,8 @@ public class CommonBorderPaddingBackground implements Cloneable {
     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;
         }
@@ -206,9 +207,12 @@ public class CommonBorderPaddingBackground implements Cloneable {
         // 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);
         }
     }
     
@@ -290,7 +294,7 @@ public class CommonBorderPaddingBackground implements Cloneable {
 
     public Color getBorderColor(int side) {
         if (borderInfo[side] != null) {
-            return borderInfo[side].mColor;
+            return borderInfo[side].getColor();
         } else {
             return null;
         }
index 37aa474f0ebe7154b8a1b5ec7968a87ae2d7dc95..d56d9a10195b4f7e107d1f5be7f7e84c4431f31e 100644 (file)
@@ -23,6 +23,8 @@ import java.util.Iterator;
 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;
@@ -76,6 +78,9 @@ public class CommonTextDecoration {
             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;
@@ -86,33 +91,33 @@ public class CommonTextDecoration {
                         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) {
index b2fdcce68df7b53d8e55d412f3ba2ea335370cf4..c8d44e41724613239d23aad7f1598b696a782239 100644 (file)
@@ -21,6 +21,7 @@ package org.apache.fop.fo.properties;
 
 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;
@@ -176,9 +177,10 @@ public class NumberProperty extends Property implements Numeric {
 
     /**
      * 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?
index 432a14811aa9d0916ffb8b53ebc19c5b6c8198df..0cab9da0117d8addea29c5a403541fe44eb70fd2 100644 (file)
@@ -25,6 +25,7 @@ import java.awt.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;
@@ -74,9 +75,10 @@ public class Property {
 
     /**
      * 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;
     }
 
index 2892f8e0d2a590c731b791b66591cec53282e2f4..ac644650c2eb76cacf52a08e492a70dbc425d2fc 100644 (file)
  
 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.
@@ -38,6 +43,12 @@ public class PDFColor extends PDFPathPaint {
     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
@@ -54,20 +65,90 @@ public class PDFColor extends PDFPathPaint {
         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];
+        }
     }
     
     /**
@@ -268,9 +349,26 @@ public class PDFColor extends PDFPathPaint {
     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
@@ -454,7 +552,8 @@ public class PDFColor extends PDFPathPaint {
         }
         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;
         }
index ae75b929059cd7eeb9b25bf0bd47fa8c6a140129..966aef2e53d2009a1a511b27402dc25b9d133af6 100644 (file)
@@ -1331,7 +1331,7 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
      *     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();
         
index c81dd1c1d5b29f311321b1bb9f37efd9d197f6ac..c1f85be87f630ae858483781f6269e1d0b826e9d 100644 (file)
@@ -384,7 +384,7 @@ public class XMLRenderer extends PrintRenderer {
                     }
                 } 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());
index 0e821975d306a50d45bb32a1b6dd2a59593e3df8..254a93cbafc4d706d9b2f8594d9230697b656fad 100644 (file)
@@ -42,6 +42,7 @@ import org.apache.fop.fonts.LazyFont;
 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;
@@ -821,8 +822,12 @@ public class PDFGraphics2D extends AbstractGraphics2D {
      */
     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());
index 2a00920eb70bf6bb4dd234814c71e93af4d13b18..64c31ff8849301ecec23d54ecb31d413ef50caac 100644 (file)
@@ -23,6 +23,7 @@ import java.awt.Color;
 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;
@@ -154,10 +155,11 @@ public class BorderProps implements Serializable {
     /**
      * 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, ",");
@@ -175,7 +177,7 @@ public class BorderProps implements Serializable {
             }
             Color c;
             try {
-                c = ColorUtil.parseColorString(color);
+                c = ColorUtil.parseColorString(foUserAgent, color);
             } catch (PropertyException e) {
                 throw new IllegalArgumentException(e.getMessage());
             } 
@@ -192,7 +194,7 @@ public class BorderProps implements Serializable {
         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) {
index 00a3f32a21f6d4f941eb76942e213f867618e5c3..655c0b3d90e7a26c40ff2752b5cf8df2b064fd04 100644 (file)
@@ -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 (file)
index 0000000..bd2c95a
--- /dev/null
@@ -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();
+    }
+
+}
index 6529240252f53bf09d20feaff905ee3519275a55..37762b1e8ac53dfb21063b4a14246098c3abcb18 100644 (file)
 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;
     }
 
     /**
@@ -281,6 +298,174 @@ public final class ColorUtil {
         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>
@@ -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();
-
     }
 
     /**
index 13a81ba02f43fa965311266dd7e4e616d3fdb1fc..5a3bcc431cd59b1a2a8f12ffbbfadd4fa31e64fb 100644 (file)
 
   <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).
index e6988d4231cdb2509121684a00f8fb68d18f58a1..2dea98b13a1000dd7c090e472a69c838b5437c19 100644 (file)
@@ -38,18 +38,18 @@ public class BorderPropsTestCase extends TestCase {
     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);
     }
     
index 70c2fdd0926361dbb81dde2eafb9824dba00340f..90f73dcc32e9397c1662608616ca407bfdb19c14 100644 (file)
@@ -37,14 +37,14 @@ public class TraitColorTestCase extends TestCase {
      */
     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);
     }
     
@@ -53,13 +53,13 @@ public class TraitColorTestCase extends TestCase {
      * @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());
@@ -71,9 +71,8 @@ public class TraitColorTestCase extends TestCase {
      * @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);
     }
     
diff --git a/test/layoutengine/standard-testcases/color_1.xml b/test/layoutengine/standard-testcases/color_1.xml
new file mode 100644 (file)
index 0000000..1e4e96c
--- /dev/null
@@ -0,0 +1,57 @@
+<?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,&quot;../../../src/java/org/apache/fop/pdf/sRGB Color Space Profile.icm&quot;,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>