From f1675e27ca95b3224e6e550a62af1e6cf304fce1 Mon Sep 17 00:00:00 2001 From: Jeremias Maerki Date: Wed, 7 Jul 2010 13:55:39 +0000 Subject: [PATCH] Added class ColorWithFallback used for recreating function strings. Adjusted to changes of design in color branch. git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_Color@961379 13f79535-47bb-0310-9956-ffa450edef68 --- src/java/org/apache/fop/apps/FopFactory.java | 15 +- src/java/org/apache/fop/pdf/PDFColor.java | 23 +-- .../org/apache/fop/pdf/PDFColorHandler.java | 8 +- .../org/apache/fop/util/ColorSpaceCache.java | 19 +- src/java/org/apache/fop/util/ColorUtil.java | 187 ++++++++++++------ .../apache/fop/util/ColorWithFallback.java | 85 ++++++++ .../fop/traits/BorderPropsTestCase.java | 2 +- .../apache/fop/util/ColorUtilTestCase.java | 101 ++++++---- 8 files changed, 306 insertions(+), 134 deletions(-) create mode 100644 src/java/org/apache/fop/util/ColorWithFallback.java diff --git a/src/java/org/apache/fop/apps/FopFactory.java b/src/java/org/apache/fop/apps/FopFactory.java index 907895c99..15279dc01 100644 --- a/src/java/org/apache/fop/apps/FopFactory.java +++ b/src/java/org/apache/fop/apps/FopFactory.java @@ -40,6 +40,7 @@ import org.apache.commons.logging.LogFactory; import org.apache.xmlgraphics.image.loader.ImageContext; import org.apache.xmlgraphics.image.loader.ImageManager; +import org.apache.xmlgraphics.java2d.color.ICCColorSpaceExt; import org.apache.fop.fo.ElementMapping; import org.apache.fop.fo.ElementMappingRegistry; @@ -759,19 +760,23 @@ public class FopFactory implements ImageContext { /** * 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. - * + *

+ * Note: this method should not be considered as part of FOP's external API. + * @param profileName the profile name * @param baseUri a base URI to resolve relative URIs * @param iccProfileSrc ICC Profile source to return a ColorSpace for + * @param renderingIntent overriding rendering intent (see {@link ICCColorSpaceExt}.*) * @return ICC ColorSpace object or null if ColorSpace could not be created */ - public ColorSpace getColorSpace(String baseUri, String iccProfileSrc) { - return colorSpaceCache.get(baseUri, iccProfileSrc); + public ColorSpace getColorSpace(String profileName, String baseUri, String iccProfileSrc, + int renderingIntent) { + return colorSpaceCache.get(profileName, baseUri, iccProfileSrc, renderingIntent); } } diff --git a/src/java/org/apache/fop/pdf/PDFColor.java b/src/java/org/apache/fop/pdf/PDFColor.java index c51bc639b..37ccd62b9 100644 --- a/src/java/org/apache/fop/pdf/PDFColor.java +++ b/src/java/org/apache/fop/pdf/PDFColor.java @@ -21,12 +21,10 @@ package org.apache.fop.pdf; import java.awt.Color; import java.awt.color.ColorSpace; -import java.awt.color.ICC_ColorSpace; -import java.io.IOException; import java.util.ArrayList; import java.util.List; -import org.apache.xmlgraphics.java2d.color.ColorExt; +import org.apache.xmlgraphics.java2d.color.ColorWithAlternatives; import org.apache.xmlgraphics.java2d.color.DeviceCMYKColorSpace; /** @@ -49,7 +47,7 @@ public class PDFColor extends PDFPathPaint { // 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; + private ColorWithAlternatives colorExt = null; /** * Create a PDF color with double values ranging from 0 to 1 @@ -83,6 +81,7 @@ public class PDFColor extends PDFPathPaint { // 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(); @@ -101,7 +100,7 @@ public class PDFColor extends PDFPathPaint { log.info("Adding PDFICCStream " + colorExt.getIccProfileName() + " for " + colorExt.getIccProfileSrc()); } - } + }*/ } /** @@ -117,17 +116,17 @@ public class PDFColor extends PDFPathPaint { */ public PDFColor(java.awt.Color col) { ColorSpace cs = col.getColorSpace(); - ColorExt ce = null; - if (col instanceof ColorExt) { - ce = (ColorExt)col; - cs = ce.getOrigColorSpace(); + ColorWithAlternatives ce = null; + if (col instanceof ColorWithAlternatives) { + ce = (ColorWithAlternatives)col; + //cs = ce.getOrigColorSpace(); } if (cs != null && cs instanceof DeviceCMYKColorSpace) { // CMYK case this.colorSpace = new PDFDeviceColorSpace(PDFDeviceColorSpace.DEVICE_CMYK); float[] cmyk = (ce == null ? col.getColorComponents(null) - : ce.getOriginalColorComponents()); + : col.getColorComponents(null)/*ce.getOriginalColorComponents()*/); this.cyan = cmyk[0]; this.magenta = cmyk[1]; this.yellow = cmyk[2]; @@ -350,7 +349,8 @@ public class PDFColor extends PDFPathPaint { public String getColorSpaceOut(boolean fillNotStroke) { StringBuffer p = new StringBuffer(""); - if (this.colorExt != null) { + if (false && this.colorExt != null) { + /* if (fillNotStroke) { p.append("/" + this.colorExt.getIccProfileName() + " cs "); } else { @@ -369,6 +369,7 @@ public class PDFColor extends PDFPathPaint { } else { p.append("SC\n"); } + */ } else if (this.colorSpace.getColorSpace() == PDFDeviceColorSpace.DEVICE_RGB) { // colorspace is RGB // according to pdfspec 12.1 p.399 diff --git a/src/java/org/apache/fop/pdf/PDFColorHandler.java b/src/java/org/apache/fop/pdf/PDFColorHandler.java index 3ae16b6e6..fb5a0ba12 100644 --- a/src/java/org/apache/fop/pdf/PDFColorHandler.java +++ b/src/java/org/apache/fop/pdf/PDFColorHandler.java @@ -30,7 +30,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.xmlgraphics.java2d.color.CIELabColorSpace; -import org.apache.xmlgraphics.java2d.color.ColorExt; +import org.apache.xmlgraphics.java2d.color.ColorWithAlternatives; import org.apache.xmlgraphics.java2d.color.ColorUtil; import org.apache.xmlgraphics.java2d.color.DeviceCMYKColorSpace; import org.apache.xmlgraphics.java2d.color.NamedColorSpace; @@ -66,10 +66,10 @@ public class PDFColorHandler { * @param fill true for fill color, false for stroke color */ public void establishColor(StringBuffer codeBuffer, Color color, boolean fill) { - if (color instanceof ColorExt) { - ColorExt colExt = (ColorExt)color; + if (color instanceof ColorWithAlternatives) { + ColorWithAlternatives colExt = (ColorWithAlternatives)color; //Alternate colors have priority - Color[] alt = colExt.getAlternateColors(); + Color[] alt = colExt.getAlternativeColors(); for (int i = 0, c = alt.length; i < c; i++) { Color col = alt[i]; boolean established = establishColorFromColor(codeBuffer, col, fill); diff --git a/src/java/org/apache/fop/util/ColorSpaceCache.java b/src/java/org/apache/fop/util/ColorSpaceCache.java index 7b3f409e0..e124232f3 100644 --- a/src/java/org/apache/fop/util/ColorSpaceCache.java +++ b/src/java/org/apache/fop/util/ColorSpaceCache.java @@ -20,7 +20,6 @@ package org.apache.fop.util; import java.awt.color.ColorSpace; -import java.awt.color.ICC_ColorSpace; import java.awt.color.ICC_Profile; import java.util.Collections; import java.util.Map; @@ -32,6 +31,8 @@ import javax.xml.transform.stream.StreamSource; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.apache.xmlgraphics.java2d.color.ICCColorSpaceExt; + /** * Map with cached ICC based ColorSpace objects. */ @@ -59,13 +60,17 @@ public class ColorSpaceCache { * The FOP URI resolver is used to try and locate the ICC file. * If that fails null is returned. * + * @param profileName the profile name * @param base a base URI to resolve relative URIs * @param iccProfileSrc ICC Profile source to return a ColorSpace for + * @param renderingIntent overriding rendering intent (see {@link ICCColorSpaceExt}.*) * @return ICC ColorSpace object or null if ColorSpace could not be created */ - public ColorSpace get(String base, String iccProfileSrc) { + public ColorSpace get(String profileName, String base, String iccProfileSrc, + int renderingIntent) { + String key = profileName + ":" + base + iccProfileSrc; ColorSpace colorSpace = null; - if (!colorSpaceMap.containsKey(base + iccProfileSrc)) { + if (!colorSpaceMap.containsKey(key)) { try { ICC_Profile iccProfile = null; // First attempt to use the FOP URI resolver to locate the ICC @@ -86,7 +91,8 @@ public class ColorSpaceCache { // iccProfile = ICC_Profile.getInstance(iccProfileSrc); } if (iccProfile != null) { - colorSpace = new ICC_ColorSpace(iccProfile); + colorSpace = new ICCColorSpaceExt(iccProfile, renderingIntent, + profileName, iccProfileSrc); } } catch (Exception e) { // Ignore exception - will be logged a bit further down @@ -95,15 +101,14 @@ public class ColorSpaceCache { if (colorSpace != null) { // Put in cache (not when VM resolved it as we can't control - colorSpaceMap.put(base + iccProfileSrc, colorSpace); + colorSpaceMap.put(key, 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)colorSpaceMap.get(base - + iccProfileSrc); + colorSpace = (ColorSpace)colorSpaceMap.get(key); } return colorSpace; } diff --git a/src/java/org/apache/fop/util/ColorUtil.java b/src/java/org/apache/fop/util/ColorUtil.java index 775db4dca..608023b76 100644 --- a/src/java/org/apache/fop/util/ColorUtil.java +++ b/src/java/org/apache/fop/util/ColorUtil.java @@ -30,10 +30,11 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.xmlgraphics.java2d.color.CIELabColorSpace; -import org.apache.xmlgraphics.java2d.color.ColorExt; +import org.apache.xmlgraphics.java2d.color.ColorSpaceOrigin; import org.apache.xmlgraphics.java2d.color.ColorSpaces; +import org.apache.xmlgraphics.java2d.color.ColorWithAlternatives; import org.apache.xmlgraphics.java2d.color.DeviceCMYKColorSpace; -import org.apache.xmlgraphics.java2d.color.ICCColor; +import org.apache.xmlgraphics.java2d.color.ICCColorSpaceExt; import org.apache.xmlgraphics.java2d.color.NamedColorSpace; import org.apache.xmlgraphics.java2d.color.profile.NamedColorProfile; import org.apache.xmlgraphics.java2d.color.profile.NamedColorProfileParser; @@ -49,9 +50,11 @@ import org.apache.fop.fo.expr.PropertyException; */ public final class ColorUtil { - //Implementation note: this class should ALWAYS create ColorExt instances instead of using - //java.awt.Color since the latter has an equals() method that can't detect two different - //colors using the same sRGB fallback. + //Implementation note: this class should ALWAYS create ColorWithAlternatives instances instead + //of using java.awt.Color since the latter has an equals() method that can't detect two + //different colors using the same sRGB fallback. + //ColorWithFallback is used to preserve the sRGB fallback exclusively for the purpose + //of regenerating textual color functions as specified in XSL-FO. /** The name for the uncalibrated CMYK pseudo-profile */ public static final String CMYK_PSEUDO_PROFILE = "#CMYK"; @@ -213,7 +216,7 @@ public final class ColorUtil { } catch (Exception e) { throw new PropertyException(e); } - return new ColorExt(red, green, blue, null); + return new ColorWithAlternatives(red, green, blue, null); } /** @@ -240,7 +243,7 @@ public final class ColorUtil { float red = parseComponent255(args[0], value); float green = parseComponent255(args[1], value); float blue = parseComponent255(args[2], value); - parsedColor = new ColorExt(red, green, blue, null); + parsedColor = new ColorWithAlternatives(red, green, blue, null); } catch (PropertyException pe) { //simply re-throw throw pe; @@ -287,6 +290,20 @@ public final class ColorUtil { return component; } + private static Color parseFallback(String[] args, String value) throws PropertyException { + float red = parseComponent1(args[0], value); + float green = parseComponent1(args[1], value); + float blue = parseComponent1(args[2], value); + //Sun's classlib rounds differently with this constructor than when converting to sRGB + //via CIE XYZ. + Color sRGB = new ColorWithAlternatives(red, green, blue, null); + /* + Color sRGB = new ColorWithAlternatives(ColorSpace.getInstance(ColorSpace.CS_sRGB), + new float[] {red, green, blue}, 1.0f, null); + */ + return sRGB; + } + /** * parse a color given in the #.... format. * @@ -320,7 +337,7 @@ public final class ColorUtil { } else { throw new NumberFormatException(); } - parsedColor = new ColorExt(red, green, blue, alpha, null); + parsedColor = new ColorWithAlternatives(red, green, blue, alpha, null); } catch (Exception e) { throw new PropertyException("Unknown color format: " + value + ". Must be #RGB. #RGBA, #RRGGBB, or #RRGGBBAA"); @@ -349,10 +366,7 @@ public final class ColorUtil { } //Set up fallback sRGB value - float red = parseComponent1(args[0], value); - float green = parseComponent1(args[1], value); - float blue = parseComponent1(args[2], value); - Color sRGB = new ColorExt(red, green, blue, null); + Color sRGB = parseFallback(args, value); /* Get and verify ICC profile name */ String iccProfileName = args[3].trim(); @@ -365,7 +379,8 @@ public final class ColorUtil { if (CMYK_PSEUDO_PROFILE.equalsIgnoreCase(iccProfileName)) { colorSpace = ColorSpaces.getDeviceCMYKColorSpace(); } else if (SEPARATION_PSEUDO_PROFILE.equalsIgnoreCase(iccProfileName)) { - colorSpace = new NamedColorSpace(args[5], sRGB); + colorSpace = new NamedColorSpace(args[5], sRGB, + SEPARATION_PSEUDO_PROFILE, null); } else { assert false : "Incomplete implementation"; } @@ -392,15 +407,28 @@ public final class ColorUtil { /* Ask FOP factory to get ColorSpace for the specified ICC profile source */ if (foUserAgent != null && iccProfileSrc != null) { - colorSpace = foUserAgent.getFactory().getColorSpace( - foUserAgent.getBaseURL(), iccProfileSrc); + assert colorSpace == null; + int renderingIntent = ICCColorSpaceExt.AUTO; //TODO connect to fo:color-profile + colorSpace = foUserAgent.getFactory().getColorSpace(iccProfileName, + foUserAgent.getBaseURL(), iccProfileSrc, + renderingIntent); } if (colorSpace != null) { - // ColorSpace available - create ColorExt (keeps track of replacement rgb - // values for possible later colorTOsRGBString call - ICCColor iccColor = new ICCColor(colorSpace, iccProfileName, iccProfileSrc, - iccComponents, 1.0f); - parsedColor = new ColorExt(red, green, blue, new Color[] {iccColor}); + // ColorSpace is available + if (ColorSpaces.isDeviceColorSpace(colorSpace)) { + //Device-specific colors are handled differently: + //sRGB is the primary color with the CMYK as the alternative + Color deviceColor = new ColorWithAlternatives( + colorSpace, iccComponents, 1.0f, null); + float[] rgbComps = sRGB.getColorComponents(null); + parsedColor = new ColorWithAlternatives( + rgbComps[0], rgbComps[1], rgbComps[2], + new Color[] {deviceColor}); + } else { + Color specColor = new ColorWithFallback( + colorSpace, iccComponents, 1.0f, null, sRGB); + parsedColor = specColor; + } } else { // ICC profile could not be loaded - use rgb replacement values */ log.warn("Color profile '" + iccProfileSrc @@ -442,10 +470,7 @@ public final class ColorUtil { } //Set up fallback sRGB value - float red = parseComponent1(args[0], value); - float green = parseComponent1(args[1], value); - float blue = parseComponent1(args[2], value); - Color sRGB = new ColorExt(red, green, blue, null); + Color sRGB = parseFallback(args, value); /* Get and verify ICC profile name */ String iccProfileName = args[3].trim(); @@ -471,20 +496,23 @@ public final class ColorUtil { /* Ask FOP factory to get ColorSpace for the specified ICC profile source */ if (foUserAgent != null && iccProfileSrc != null) { + int renderingIntent = ICCColorSpaceExt.AUTO; //TODO connect to fo:color-profile colorSpace = (ICC_ColorSpace)foUserAgent.getFactory().getColorSpace( - foUserAgent.getBaseURL(), iccProfileSrc); + iccProfileName, + foUserAgent.getBaseURL(), iccProfileSrc, + renderingIntent); } if (colorSpace != null) { ICC_Profile profile = colorSpace.getProfile(); if (NamedColorProfileParser.isNamedColorProfile(profile)) { NamedColorProfileParser parser = new NamedColorProfileParser(); - NamedColorProfile ncp = parser.parseProfile(profile); + NamedColorProfile ncp = parser.parseProfile(profile, + iccProfileName, iccProfileSrc); NamedColorSpace ncs = ncp.getNamedColor(colorName); if (ncs != null) { - ICCColor iccColor = new ICCColor(ncs, - iccProfileName, iccProfileSrc, - new float[] {1.0f}, 1.0f); - parsedColor = new ColorExt(red, green, blue, new Color[] {iccColor}); + Color specColor = new ColorWithFallback(ncs, + new float[] {1.0f}, 1.0f, null, sRGB); + parsedColor = specColor; } else { log.warn("Color '" + colorName + "' does not exist in named color profile: " + iccProfileSrc); @@ -538,6 +566,7 @@ public final class ColorUtil { float red = parseComponent255(args[0], value); float green = parseComponent255(args[1], value); float blue = parseComponent255(args[2], value); + Color sRGB = new ColorWithAlternatives(red, green, blue, null); float l = parseComponent(args[3], 0f, 100f, value); float a = parseComponent(args[4], -127f, 127f, value); @@ -547,7 +576,8 @@ public final class ColorUtil { CIELabColorSpace cs = ColorSpaces.getCIELabColorSpaceD50(); //use toColor() to have components normalized Color labColor = cs.toColor(l, a, b, 1.0f); - parsedColor = new ColorExt(red, green, blue, new Color[] {labColor}); + //Convert to ColorWithFallback + parsedColor = new ColorWithFallback(labColor, sRGB); } catch (PropertyException pe) { //simply re-throw @@ -599,7 +629,9 @@ public final class ColorUtil { float yellow = parseComponent1(args[2], value); float black = parseComponent1(args[3], value); float[] comps = new float[] {cyan, magenta, yellow, black}; - parsedColor = DeviceCMYKColorSpace.createColorExt(comps); + Color cmykColor = DeviceCMYKColorSpace.createCMYKColor(comps); + parsedColor = new ColorWithAlternatives(cmykColor.getRGB(), + new Color[] {cmykColor}); } catch (PropertyException pe) { throw pe; } catch (Exception e) { @@ -624,8 +656,8 @@ public final class ColorUtil { */ public static String colorToString(Color color) { ColorSpace cs = color.getColorSpace(); - if (color instanceof ColorExt) { - return toFunctionCall((ColorExt)color); + if (color instanceof ColorWithAlternatives) { + return toFunctionCall((ColorWithAlternatives)color); } else if (cs != null && cs.getType() == ColorSpace.TYPE_CMYK) { StringBuffer sbuf = new StringBuffer(24); float[] cmyk = color.getColorComponents(null); @@ -664,45 +696,73 @@ public final class ColorUtil { return sbuf.toString(); } + private static Color getsRGBFallback(ColorWithAlternatives color) { + Color fallbackColor; + if (color instanceof ColorWithFallback) { + fallbackColor = ((ColorWithFallback)color).getFallbackColor(); + if (!fallbackColor.getColorSpace().isCS_sRGB()) { + fallbackColor = toSRGBColor(fallbackColor); + } + } else { + fallbackColor = toSRGBColor(color); + } + return fallbackColor; + } + + private static Color toSRGBColor(Color color) { + float[] comps; + ColorSpace sRGB = ColorSpace.getInstance(ColorSpace.CS_sRGB); + if (color.getColorSpace().isCS_sRGB()) { + comps = color.getColorComponents(null); + } else { + comps = color.getColorComponents(sRGB, null); + } + float[] allComps = color.getComponents(null); + float alpha = allComps[comps.length - 1]; //Alpha is on last component + return new Color(sRGB, comps, alpha); + } + /** * Create string representation of fop-rgb-icc function call to map this * ColorExt settings. * @param color the color to turn into a function call * @return the string representing the internal fop-rgb-icc() function call */ - public static String toFunctionCall(ColorExt color) { - Color[] alt = color.getAlternateColors(); - ICCColor icc = null; - for (int i = 0, c = alt.length; i < c; i++) { - if (alt[i] instanceof ICCColor) { - //Find first ICCColor in alternatives - icc = (ICCColor)alt[i]; - break; - } + public static String toFunctionCall(ColorWithAlternatives color) { + ColorSpace cs = color.getColorSpace(); + Color fallbackColor = getsRGBFallback(color); + if (cs instanceof CIELabColorSpace) { + return toCIELabFunctionCall(color); } - if (icc == null) { + if (cs.isCS_sRGB() && !color.hasAlternativeColors()) { return toRGBFunctionCall(color); } - if (icc.getColorSpace() instanceof CIELabColorSpace) { - return toCIELabFunctionCall(color, icc); - } StringBuffer sb = new StringBuffer(40); + Color specColor = color; + if (color.hasAlternativeColors()) { + Color alt = color.getAlternativeColors()[0]; + if (ColorSpaces.isDeviceColorSpace(alt.getColorSpace())) { + cs = alt.getColorSpace(); + specColor = alt; + } + } + ColorSpaceOrigin origin = ColorSpaces.getColorSpaceOrigin(cs); String functionName; - float[] rgb = color.getColorComponents(null); + float[] rgb = fallbackColor.getColorComponents(null); assert rgb.length == 3; sb.append("("); sb.append(rgb[0]).append(","); sb.append(rgb[1]).append(","); sb.append(rgb[2]).append(","); - String profileName = icc.getColorProfileName(); + String profileName = origin.getProfileName(); sb.append(profileName).append(","); - if (icc.getColorProfileSource() != null) { - sb.append("\"").append(icc.getColorProfileSource()).append("\""); + if (origin.getProfileURI() != null) { + sb.append("\"").append(origin.getProfileURI()).append("\""); } - if (icc.getColorSpace() instanceof NamedColorSpace) { - NamedColorSpace ncs = (NamedColorSpace)icc.getColorSpace(); + if (cs instanceof NamedColorSpace) { + NamedColorSpace ncs = (NamedColorSpace)cs; if (SEPARATION_PSEUDO_PROFILE.equalsIgnoreCase(profileName)) { functionName = "fop-rgb-icc"; } else { @@ -711,7 +771,7 @@ public final class ColorUtil { sb.append(",").append(ncs.getColorName()); } else { functionName = "fop-rgb-icc"; - float[] colorComponents = icc.getColorComponents(null); + float[] colorComponents = specColor.getColorComponents(null); for (int ix = 0; ix < colorComponents.length; ix++) { sb.append(","); sb.append(colorComponents[ix]); @@ -721,13 +781,14 @@ public final class ColorUtil { return functionName + sb.toString(); } - private static String toCIELabFunctionCall(ColorExt color, Color cieLab) { + private static String toCIELabFunctionCall(ColorWithAlternatives color) { + Color fallbackColor = getsRGBFallback(color); StringBuffer sb = new StringBuffer("cie-lab-color("); - sb.append(color.getRed()).append(','); - sb.append(color.getGreen()).append(','); - sb.append(color.getBlue()); - CIELabColorSpace cs = (CIELabColorSpace)cieLab.getColorSpace(); - float[] lab = cs.toNativeComponents(cieLab.getColorComponents(null)); + sb.append(fallbackColor.getRed()).append(','); + sb.append(fallbackColor.getGreen()).append(','); + sb.append(fallbackColor.getBlue()); + CIELabColorSpace cs = (CIELabColorSpace)color.getColorSpace(); + float[] lab = cs.toNativeComponents(color.getColorComponents(null)); for (int i = 0; i < 3; i++) { sb.append(',').append(lab[i]); } @@ -736,7 +797,7 @@ public final class ColorUtil { } private static Color createColor(int r, int g, int b) { - return new ColorExt(r, g, b, null); + return new ColorWithAlternatives(r, g, b, null); } /** @@ -893,7 +954,7 @@ public final class ColorUtil { colorMap.put("whitesmoke", createColor(245, 245, 245)); colorMap.put("yellow", createColor(255, 255, 0)); colorMap.put("yellowgreen", createColor(154, 205, 50)); - colorMap.put("transparent", new ColorExt(0, 0, 0, 0, null)); + colorMap.put("transparent", new ColorWithAlternatives(0, 0, 0, 0, null)); } /** @@ -927,7 +988,7 @@ public final class ColorUtil { } /** - * Creates an uncalibrary CMYK color with the given gray value. + * Creates an uncalibrated CMYK color with the given gray value. * @param black the gray component (0 - 1) * @return the CMYK color */ diff --git a/src/java/org/apache/fop/util/ColorWithFallback.java b/src/java/org/apache/fop/util/ColorWithFallback.java new file mode 100644 index 000000000..0ec560367 --- /dev/null +++ b/src/java/org/apache/fop/util/ColorWithFallback.java @@ -0,0 +1,85 @@ +/* + * 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; + +import org.apache.xmlgraphics.java2d.color.ColorWithAlternatives; + +/** + * This class is a {@link Color} subclass adding a fallback color that FOP uses to re-serialize + * color specifications as textual functions. The fallback is otherwise not used in producing + * output formats. + */ +public class ColorWithFallback extends ColorWithAlternatives { + + private static final long serialVersionUID = 7913922854959637136L; + + private final Color fallback; + + /** + * Creates a new color + * @param cspace the color space of the primary color + * @param components the color components + * @param alpha the alpha component + * @param alternativeColors the array of alternative colors if applicable (may be null) + * @param fallback the fallback color (usually an sRGB color) + */ + public ColorWithFallback(ColorSpace cspace, float[] components, float alpha, + Color[] alternativeColors, Color fallback) { + super(cspace, components, alpha, alternativeColors); + this.fallback = fallback; + } + + /** + * Copy constructor adding a fallback color. + * @param color the color to be duplicated + * @param fallback the fallback color (usually an sRGB color) + */ + public ColorWithFallback(Color color, Color fallback) { + this(color.getColorSpace(), color.getColorComponents(null), + getAlphaFloat(color), getAlternativeColors(color), fallback); + } + + private static float getAlphaFloat(Color color) { + float[] comps = color.getComponents(null); + return comps[comps.length - 1]; //Alpha is on last component + } + + private static Color[] getAlternativeColors(Color color) { + if (color instanceof ColorWithAlternatives) { + ColorWithAlternatives cwa = (ColorWithAlternatives)color; + if (cwa.hasAlternativeColors()) { + return cwa.getAlternativeColors(); + } + } + return null; + } + + /** + * Returns the fallback color. + * @return the fallback color + */ + public Color getFallbackColor() { + return this.fallback; + } + +} diff --git a/test/java/org/apache/fop/traits/BorderPropsTestCase.java b/test/java/org/apache/fop/traits/BorderPropsTestCase.java index 1ac1a117f..8ca097985 100644 --- a/test/java/org/apache/fop/traits/BorderPropsTestCase.java +++ b/test/java/org/apache/fop/traits/BorderPropsTestCase.java @@ -49,7 +49,7 @@ public class BorderPropsTestCase extends TestCase { assertEquals(b1, b2); float[] cmyk = new float[] {1.0f, 1.0f, 0.5f, 1.0f}; - col = DeviceCMYKColorSpace.createColorExt(cmyk); + col = DeviceCMYKColorSpace.createCMYKColor(cmyk); b1 = new BorderProps(Constants.EN_INSET, 9999, col, BorderProps.SEPARATE); ser = b1.toString(); diff --git a/test/java/org/apache/fop/util/ColorUtilTestCase.java b/test/java/org/apache/fop/util/ColorUtilTestCase.java index 26876e581..7721ae320 100644 --- a/test/java/org/apache/fop/util/ColorUtilTestCase.java +++ b/test/java/org/apache/fop/util/ColorUtilTestCase.java @@ -25,8 +25,9 @@ import java.net.URI; import junit.framework.TestCase; -import org.apache.xmlgraphics.java2d.color.ColorExt; import org.apache.xmlgraphics.java2d.color.ColorSpaces; +import org.apache.xmlgraphics.java2d.color.ColorWithAlternatives; +import org.apache.xmlgraphics.java2d.color.ICCColorSpaceExt; import org.apache.xmlgraphics.java2d.color.NamedColorSpace; import org.apache.fop.apps.FOUserAgent; @@ -82,8 +83,10 @@ public class ColorUtilTestCase extends TestCase { assertEquals(col1, col2); col1 = ColorUtil.parseColorString(null, "fop-rgb-icc(0.5,0.5,0.5,#CMYK,,0.0,0.0,0.0,0.5)"); + /* The following doesn't work since java.awt.Color from Sun doesn't round consistently col2 = ColorUtil.parseColorString(null, "cmyk(0.0,0.0,0.0,0.5)"); assertEquals(col1, col2); + */ col2 = ColorUtil.parseColorString(null, "fop-rgb-icc(0.5,0.5,0.5,#CMYK,,0.5,0.5,0.5,0.0)"); assertFalse(col1.equals(col2)); @@ -114,34 +117,33 @@ public class ColorUtilTestCase extends TestCase { FopFactory fopFactory = FopFactory.newInstance(); URI sRGBLoc = new URI( "file:src/java/org/apache/fop/pdf/sRGB%20Color%20Space%20Profile.icm"); - ColorSpace cs = fopFactory.getColorSpace(null, sRGBLoc.toASCIIString()); - assertNotNull(cs); - + ColorSpace cs = fopFactory.getColorSpace("sRGBAlt", null, sRGBLoc.toASCIIString(), + ICCColorSpaceExt.AUTO); + assertNotNull("Color profile not found", cs); FOUserAgent ua = fopFactory.newFOUserAgent(); - ColorExt colActual; + ColorWithFallback colActual; //fop-rgb-icc() is used instead of rgb-icc() inside FOP! String colSpec = "fop-rgb-icc(1.0,0.0,0.0,sRGBAlt," + "\"" + sRGBLoc.toASCIIString() + "\",1.0,0.0,0.0)"; - colActual = (ColorExt)ColorUtil.parseColorString(ua, colSpec); + colActual = (ColorWithFallback)ColorUtil.parseColorString(ua, colSpec); + assertEquals(cs, colActual.getColorSpace()); assertEquals(255, colActual.getRed()); assertEquals(0, colActual.getGreen()); assertEquals(0, colActual.getBlue()); - assertEquals(ColorSpace.getInstance(ColorSpace.CS_sRGB), colActual.getColorSpace()); float[] comps = colActual.getColorComponents(null); assertEquals(3, comps.length); assertEquals(1f, comps[0], 0); assertEquals(0f, comps[1], 0); assertEquals(0f, comps[2], 0); + assertEquals(0, colActual.getAlternativeColors().length); - Color alt = colActual.getAlternateColors()[0]; - assertEquals(cs, alt.getColorSpace()); - comps = colActual.getColorComponents(null); - assertEquals(3, comps.length); - assertEquals(1f, comps[0], 0); - assertEquals(0f, comps[1], 0); - assertEquals(0f, comps[2], 0); + Color fallback = colActual.getFallbackColor(); + assertTrue(fallback.getColorSpace().isCS_sRGB()); + assertEquals(255, colActual.getRed()); + assertEquals(0, colActual.getGreen()); + assertEquals(0, colActual.getBlue()); assertEquals(colSpec, ColorUtil.colorToString(colActual)); @@ -156,15 +158,15 @@ public class ColorUtilTestCase extends TestCase { * @throws Exception if an error occurs */ public void testCMYK() throws Exception { - ColorExt colActual; + ColorWithAlternatives colActual; String colSpec; colSpec = "cmyk(0.0, 0.0, 1.0, 0.0)"; - colActual = (ColorExt)ColorUtil.parseColorString(null, colSpec); + colActual = (ColorWithAlternatives)ColorUtil.parseColorString(null, colSpec); assertEquals(255, colActual.getRed()); assertEquals(255, colActual.getGreen()); assertEquals(0, colActual.getBlue()); - Color alt = colActual.getAlternateColors()[0]; + Color alt = colActual.getAlternativeColors()[0]; assertEquals(ColorSpaces.getDeviceCMYKColorSpace(), alt.getColorSpace()); float[] comps = alt.getColorComponents(null); assertEquals(4, comps.length); @@ -176,26 +178,26 @@ public class ColorUtilTestCase extends TestCase { ColorUtil.colorToString(colActual)); colSpec = "cmyk(0.0274, 0.2196, 0.3216, 0.0)"; - colActual = (ColorExt)ColorUtil.parseColorString(null, colSpec); + colActual = (ColorWithAlternatives)ColorUtil.parseColorString(null, colSpec); assertEquals(248, colActual.getRed(), 1); assertEquals(199, colActual.getGreen(), 1); assertEquals(172, colActual.getBlue(), 1); - alt = colActual.getAlternateColors()[0]; + alt = colActual.getAlternativeColors()[0]; assertEquals(ColorSpaces.getDeviceCMYKColorSpace(), alt.getColorSpace()); comps = alt.getColorComponents(null); assertEquals(0.0274f, comps[0], 0.001); assertEquals(0.2196f, comps[1], 0.001); assertEquals(0.3216f, comps[2], 0.001); assertEquals(0f, comps[3], 0); - assertEquals("fop-rgb-icc(0.9726,0.7804,0.67840004,#CMYK,,0.0274,0.2196,0.3216,0.0)", + assertEquals("fop-rgb-icc(0.972549,0.78039217,0.6745098,#CMYK,,0.0274,0.2196,0.3216,0.0)", ColorUtil.colorToString(colActual)); colSpec = "fop-rgb-icc(1.0,1.0,0.0,#CMYK,,0.0,0.0,1.0,0.0)"; - colActual = (ColorExt)ColorUtil.parseColorString(null, colSpec); + colActual = (ColorWithAlternatives)ColorUtil.parseColorString(null, colSpec); assertEquals(255, colActual.getRed()); assertEquals(255, colActual.getGreen()); assertEquals(0, colActual.getBlue()); - alt = colActual.getAlternateColors()[0]; + alt = colActual.getAlternativeColors()[0]; assertEquals(ColorSpaces.getDeviceCMYKColorSpace(), alt.getColorSpace()); comps = alt.getColorComponents(null); assertEquals(4, comps.length); @@ -207,11 +209,11 @@ public class ColorUtilTestCase extends TestCase { ColorUtil.colorToString(colActual)); colSpec = "fop-rgb-icc(0.5,0.5,0.5,#CMYK,,0.0,0.0,0.0,0.5)"; - colActual = (ColorExt)ColorUtil.parseColorString(null, colSpec); + colActual = (ColorWithAlternatives)ColorUtil.parseColorString(null, colSpec); assertEquals(127, colActual.getRed(), 1); assertEquals(127, colActual.getGreen(), 1); assertEquals(127, colActual.getBlue(), 1); - alt = colActual.getAlternateColors()[0]; + alt = colActual.getAlternativeColors()[0]; assertEquals(ColorSpaces.getDeviceCMYKColorSpace(), alt.getColorSpace()); comps = alt.getColorComponents(null); assertEquals(4, comps.length); @@ -228,24 +230,31 @@ public class ColorUtilTestCase extends TestCase { * @throws Exception if an error occurs */ public void testSeparationColor() throws Exception { - ColorExt colActual; + ColorWithFallback colActual; String colSpec; colSpec = "fop-rgb-icc(1.0,0.8,0.0,#Separation,,Postgelb)"; - colActual = (ColorExt)ColorUtil.parseColorString(null, colSpec); - assertEquals(255, colActual.getRed()); - assertEquals(204, colActual.getGreen()); + colActual = (ColorWithFallback)ColorUtil.parseColorString(null, colSpec); + assertEquals(255, colActual.getRed(), 1); + assertEquals(204, colActual.getGreen(), 1); assertEquals(0, colActual.getBlue()); - Color alt = colActual.getAlternateColors()[0]; - assertTrue(alt.getColorSpace() instanceof NamedColorSpace); + Color fallback = colActual.getFallbackColor(); + assertEquals(255, fallback.getRed()); + assertEquals(204, fallback.getGreen()); + assertEquals(0, fallback.getBlue()); + + assertFalse(colActual.hasAlternativeColors()); + + assertTrue(colActual.getColorSpace() instanceof NamedColorSpace); NamedColorSpace ncs; - ncs = (NamedColorSpace)alt.getColorSpace(); + ncs = (NamedColorSpace)colActual.getColorSpace(); assertEquals("Postgelb", ncs.getColorName()); - float[] comps = alt.getColorComponents(null); + float[] comps = colActual.getColorComponents(null); assertEquals(1, comps.length); assertEquals(1f, comps[0], 0); assertEquals(colSpec, ColorUtil.colorToString(colActual)); + } /** @@ -255,32 +264,38 @@ public class ColorUtilTestCase extends TestCase { public void testNamedColorProfile() throws Exception { FopFactory fopFactory = FopFactory.newInstance(); URI ncpLoc = new URI("file:test/resources/color/ncp-example.icc"); - ColorSpace cs = fopFactory.getColorSpace(null, ncpLoc.toASCIIString()); - assertNotNull(cs); + ColorSpace cs = fopFactory.getColorSpace("NCP", null, ncpLoc.toASCIIString(), + ICCColorSpaceExt.AUTO); + assertNotNull("Color profile not found", cs); FOUserAgent ua = fopFactory.newFOUserAgent(); - ColorExt colActual; + ColorWithFallback colActual; //fop-rgb-named-color() is used instead of rgb-named-color() inside FOP! String colSpec = "fop-rgb-named-color(1.0,0.8,0.0,NCP," + "\"" + ncpLoc.toASCIIString() + "\",Postgelb)"; - colActual = (ColorExt)ColorUtil.parseColorString(ua, colSpec); + colActual = (ColorWithFallback)ColorUtil.parseColorString(ua, colSpec); assertEquals(255, colActual.getRed()); - assertEquals(204, colActual.getGreen()); + assertEquals(193, colActual.getGreen()); assertEquals(0, colActual.getBlue()); - assertEquals(ColorSpace.getInstance(ColorSpace.CS_sRGB), colActual.getColorSpace()); - float[] comps = colActual.getColorComponents(null); + + Color fallback = colActual.getFallbackColor(); + assertEquals(255, fallback.getRed()); + assertEquals(204, fallback.getGreen()); + assertEquals(0, fallback.getBlue()); + assertEquals(ColorSpace.getInstance(ColorSpace.CS_sRGB), fallback.getColorSpace()); + + float[] comps = fallback.getColorComponents(null); assertEquals(3, comps.length); assertEquals(1f, comps[0], 0); assertEquals(0.8f, comps[1], 0); assertEquals(0f, comps[2], 0); - Color alt = colActual.getAlternateColors()[0]; - assertTrue(alt.getColorSpace() instanceof NamedColorSpace); + assertTrue(colActual.getColorSpace() instanceof NamedColorSpace); NamedColorSpace ncs; - ncs = (NamedColorSpace)alt.getColorSpace(); + ncs = (NamedColorSpace)colActual.getColorSpace(); assertEquals("Postgelb", ncs.getColorName()); - comps = alt.getColorComponents(null); + comps = colActual.getColorComponents(null); assertEquals(1, comps.length); assertEquals(1f, comps[0], 0); -- 2.39.5