diff options
Diffstat (limited to 'src/java/org/apache')
34 files changed, 1993 insertions, 586 deletions
diff --git a/src/java/org/apache/fop/afp/goca/GraphicsSetProcessColor.java b/src/java/org/apache/fop/afp/goca/GraphicsSetProcessColor.java index aba02f76c..07d6c78af 100644 --- a/src/java/org/apache/fop/afp/goca/GraphicsSetProcessColor.java +++ b/src/java/org/apache/fop/afp/goca/GraphicsSetProcessColor.java @@ -21,9 +21,16 @@ package org.apache.fop.afp.goca; import java.awt.Color; import java.awt.color.ColorSpace; +import java.io.DataOutput; import java.io.IOException; import java.io.OutputStream; +import org.apache.commons.io.output.ByteArrayOutputStream; + +import org.apache.xmlgraphics.java2d.color.CIELabColorSpace; +import org.apache.xmlgraphics.java2d.color.ColorUtil; +import org.apache.xmlgraphics.java2d.color.ColorWithAlternatives; + /** * Sets the current processing color for the following GOCA structured fields */ @@ -37,56 +44,96 @@ public class GraphicsSetProcessColor extends AbstractGraphicsDrawingOrder { * X'08' CIELAB * X'40' Standard OCA color space */ - private static final byte RGB = 0x01, CMYK = 0x04; + private static final byte RGB = 0x01, CMYK = 0x04, CIELAB = 0x08; private final Color color; - - private final float[] colorComponents; + private final int componentsSize; /** * Main constructor * - * @param color - * the color to set + * @param color the color to set */ public GraphicsSetProcessColor(Color color) { - this.color = color; - this.colorComponents = color.getColorComponents(null); + if (color instanceof ColorWithAlternatives) { + ColorWithAlternatives cwa = (ColorWithAlternatives)color; + Color alt = cwa.getFirstAlternativeOfType(ColorSpace.TYPE_CMYK); + if (alt != null) { + this.color = alt; + this.componentsSize = 4; + return; + } + } + ColorSpace cs = color.getColorSpace(); + int colSpaceType = cs.getType(); + if (colSpaceType == ColorSpace.TYPE_CMYK) { + this.color = color; + } else if (cs instanceof CIELabColorSpace) { + //TODO Convert between illuminants if not D50 according to rendering intents + //Right now, we're assuming D50 as the GOCA spec requires. + this.color = color; + //16bit components didn't work, and 8-bit sadly has reduced accuracy. + } else { + if (!color.getColorSpace().isCS_sRGB()) { + this.color = ColorUtil.toSRGBColor(color); + } else { + this.color = color; + } + } + this.componentsSize = this.color.getColorSpace().getNumComponents(); } /** {@inheritDoc} */ public int getDataLength() { - return 12 + colorComponents.length; + return 12 + this.componentsSize; } /** {@inheritDoc} */ + @Override byte getOrderCode() { return (byte) 0xB2; } /** {@inheritDoc} */ public void writeToStream(OutputStream os) throws IOException { + float[] colorComponents = color.getColorComponents(null); // COLSPCE byte colspace; - int colSpaceType = color.getColorSpace().getType(); + ColorSpace cs = color.getColorSpace(); + int colSpaceType = cs.getType(); + ByteArrayOutputStream baout = new ByteArrayOutputStream(); + byte[] colsizes; if (colSpaceType == ColorSpace.TYPE_CMYK) { colspace = CMYK; + colsizes = new byte[] {0x08, 0x08, 0x08, 0x08}; + for (int i = 0; i < colorComponents.length; i++) { + baout.write(Math.round(colorComponents[i] * 255)); + } } else if (colSpaceType == ColorSpace.TYPE_RGB) { colspace = RGB; + colsizes = new byte[] {0x08, 0x08, 0x08, 0x00}; + for (int i = 0; i < colorComponents.length; i++) { + baout.write(Math.round(colorComponents[i] * 255)); + } + } else if (cs instanceof CIELabColorSpace) { + colspace = CIELAB; + colsizes = new byte[] {0x08, 0x08, 0x08, 0x00}; + DataOutput dout = new java.io.DataOutputStream(baout); + //According to GOCA, I'd expect the multiplicator below to be 255f, not 100f + //But only IBM AFP Workbench seems to support Lab colors and it requires "c * 100f" + int l = Math.round(colorComponents[0] * 100f); + int a = Math.round(colorComponents[1] * 255f) - 128; + int b = Math.round(colorComponents[2] * 255f) - 128; + dout.writeByte(l); + dout.writeByte(a); + dout.writeByte(b); } else { - LOG.error("unsupported colorspace " + colSpaceType); - colspace = RGB; - } - - // COLSIZE(S) - byte[] colsizes = new byte[] {0x00, 0x00, 0x00, 0x00}; - for (int i = 0; i < colorComponents.length; i++) { - colsizes[i] = (byte) 8; + throw new IllegalStateException(); } int len = getDataLength(); - byte[] data = new byte[len]; + byte[] data = new byte[12]; data[0] = getOrderCode(); // GSPCOL order code data[1] = (byte) (len - 2); // LEN data[2] = 0x00; // reserved; must be zero @@ -100,15 +147,12 @@ public class GraphicsSetProcessColor extends AbstractGraphicsDrawingOrder { data[10] = colsizes[2]; data[11] = colsizes[3]; - // COLVALUE(S) - for (int i = 0; i < colorComponents.length; i++) { - data[i + 12] = (byte) (colorComponents[i] * 255); - } - os.write(data); + baout.writeTo(os); } /** {@inheritDoc} */ + @Override public String toString() { return "GraphicsSetProcessColor(col=" + color + ")"; } diff --git a/src/java/org/apache/fop/afp/modca/GraphicsObject.java b/src/java/org/apache/fop/afp/modca/GraphicsObject.java index 0c3781be1..c94ad5ffc 100644 --- a/src/java/org/apache/fop/afp/modca/GraphicsObject.java +++ b/src/java/org/apache/fop/afp/modca/GraphicsObject.java @@ -26,6 +26,7 @@ import java.util.Iterator; import java.util.List; import org.apache.xmlgraphics.java2d.color.ColorConverter; +import org.apache.xmlgraphics.java2d.color.ColorUtil; import org.apache.fop.afp.AFPDataObjectInfo; import org.apache.fop.afp.AFPObjectAreaInfo; @@ -82,6 +83,7 @@ public class GraphicsObject extends AbstractDataObject { } /** {@inheritDoc} */ + @Override public void setViewport(AFPDataObjectInfo dataObjectInfo) { super.setViewport(dataObjectInfo); @@ -145,7 +147,7 @@ public class GraphicsObject extends AbstractDataObject { * @param color the active color to use */ public void setColor(Color color) { - if (!color.equals(graphicsState.color)) { + if (!ColorUtil.isSameColor(color, graphicsState.color)) { addObject(new GraphicsSetProcessColor(colorConverter.convert(color))); graphicsState.color = color; } @@ -341,6 +343,7 @@ public class GraphicsObject extends AbstractDataObject { } /** {@inheritDoc} */ + @Override public String toString() { return "GraphicsObject: " + getName(); } @@ -354,6 +357,7 @@ public class GraphicsObject extends AbstractDataObject { } /** {@inheritDoc} */ + @Override public void setComplete(boolean complete) { Iterator it = objects.iterator(); while (it.hasNext()) { @@ -364,6 +368,7 @@ public class GraphicsObject extends AbstractDataObject { } /** {@inheritDoc} */ + @Override protected void writeStart(OutputStream os) throws IOException { super.writeStart(os); byte[] data = new byte[17]; @@ -372,12 +377,14 @@ public class GraphicsObject extends AbstractDataObject { } /** {@inheritDoc} */ + @Override protected void writeContent(OutputStream os) throws IOException { super.writeContent(os); writeObjects(objects, os); } /** {@inheritDoc} */ + @Override protected void writeEnd(OutputStream os) throws IOException { byte[] data = new byte[17]; copySF(data, Type.END, Category.GRAPHICS); diff --git a/src/java/org/apache/fop/afp/ptoca/PtocaBuilder.java b/src/java/org/apache/fop/afp/ptoca/PtocaBuilder.java index e583b81e6..2962dc76c 100644 --- a/src/java/org/apache/fop/afp/ptoca/PtocaBuilder.java +++ b/src/java/org/apache/fop/afp/ptoca/PtocaBuilder.java @@ -26,6 +26,10 @@ import java.io.OutputStream; import org.apache.commons.io.output.ByteArrayOutputStream; +import org.apache.xmlgraphics.java2d.color.CIELabColorSpace; +import org.apache.xmlgraphics.java2d.color.ColorUtil; +import org.apache.xmlgraphics.java2d.color.ColorWithAlternatives; + /** * Generator class for PTOCA data structures. */ @@ -311,9 +315,18 @@ public abstract class PtocaBuilder implements PtocaConstants { * @throws IOException if an I/O error occurs */ public void setExtendedTextColor(Color col) throws IOException { - if (col.equals(currentColor)) { + if (ColorUtil.isSameColor(col, currentColor)) { return; } + if (col instanceof ColorWithAlternatives) { + ColorWithAlternatives cwa = (ColorWithAlternatives)col; + Color alt = cwa.getFirstAlternativeOfType(ColorSpace.TYPE_CMYK); + if (alt != null) { + col = alt; + } + } + ColorSpace cs = col.getColorSpace(); + newControlSequence(); if (col.getColorSpace().getType() == ColorSpace.TYPE_CMYK) { writeByte(0x00); // Reserved; must be zero @@ -332,6 +345,25 @@ public abstract class PtocaBuilder implements PtocaConstants { int component = Math.round(comps[i] * 255); writeByte(component); } + } else if (cs instanceof CIELabColorSpace) { + writeByte(0x00); // Reserved; must be zero + writeByte(0x08); // Color space - 0x08 = CIELAB + writeByte(0x00); // Reserved; must be zero + writeByte(0x00); // Reserved; must be zero + writeByte(0x00); // Reserved; must be zero + writeByte(0x00); // Reserved; must be zero + writeByte(8); // Number of bits in component 1 + writeByte(8); // Number of bits in component 2 + writeByte(8); // Number of bits in component 3 + writeByte(0); // Number of bits in component 4 + //Sadly, 16 bit components don't seem to work + float[] colorComponents = col.getColorComponents(null); + int l = Math.round(colorComponents[0] * 255f); + int a = Math.round(colorComponents[1] * 255f) - 128; + int b = Math.round(colorComponents[2] * 255f) - 128; + writeByte(l); // L* + writeByte(a); // a* + writeByte(b); // b* } else { writeByte(0x00); // Reserved; must be zero writeByte(0x01); // Color space - 0x01 = RGB diff --git a/src/java/org/apache/fop/apps/FopFactory.java b/src/java/org/apache/fop/apps/FopFactory.java index 181bc85da..2aca8bf99 100644 --- a/src/java/org/apache/fop/apps/FopFactory.java +++ b/src/java/org/apache/fop/apps/FopFactory.java @@ -19,7 +19,6 @@ package org.apache.fop.apps; -import java.awt.color.ColorSpace; import java.io.File; import java.io.IOException; import java.io.OutputStream; @@ -151,7 +150,7 @@ public class FopFactory implements ImageContext { /** Optional overriding LayoutManagerMaker */ private LayoutManagerMaker lmMakerOverride = null; - private Set ignoredNamespaces; + private Set<String> ignoredNamespaces; private FOURIResolver foURIResolver; @@ -165,6 +164,7 @@ public class FopFactory implements ImageContext { this.fontManager = new FontManager() { /** {@inheritDoc} */ + @Override public void setFontBaseURL(String fontBase) throws MalformedURLException { super.setFontBaseURL(getFOURIResolver().checkBaseURL(fontBase)); } @@ -175,7 +175,7 @@ public class FopFactory implements ImageContext { this.rendererFactory = new RendererFactory(); this.xmlHandlers = new XMLHandlerRegistry(); this.imageHandlers = new ImageHandlerRegistry(); - this.ignoredNamespaces = new java.util.HashSet(); + this.ignoredNamespaces = new java.util.HashSet<String>(); } /** @@ -381,6 +381,7 @@ public class FopFactory implements ImageContext { * @throws MalformedURLException if there's a problem with a file URL * @deprecated use getFontManager().setFontBaseURL(fontBase) instead */ + @Deprecated public void setFontBaseURL(String fontBase) throws MalformedURLException { getFontManager().setFontBaseURL(fontBase); } @@ -389,6 +390,7 @@ public class FopFactory implements ImageContext { * @return the font base URL * @deprecated use getFontManager().setFontBaseURL(fontBase) instead */ + @Deprecated public String getFontBaseURL() { return getFontManager().getFontBaseURL(); } @@ -516,6 +518,7 @@ public class FopFactory implements ImageContext { * @return true if kerning on base 14 fonts is enabled * @deprecated use getFontManager().isBase14KerningEnabled() instead */ + @Deprecated public boolean isBase14KerningEnabled() { return getFontManager().isBase14KerningEnabled(); } @@ -525,6 +528,7 @@ public class FopFactory implements ImageContext { * @param value true if kerning should be activated * @deprecated use getFontManager().setBase14KerningEnabled(boolean) instead */ + @Deprecated public void setBase14KerningEnabled(boolean value) { getFontManager().setBase14KerningEnabled(value); } @@ -652,7 +656,7 @@ public class FopFactory implements ImageContext { * namespace is in the ignored set. * @param namespaceURIs the namespace URIs */ - public void ignoreNamespaces(Collection namespaceURIs) { + public void ignoreNamespaces(Collection<String> namespaceURIs) { this.ignoredNamespaces.addAll(namespaceURIs); } @@ -666,7 +670,7 @@ public class FopFactory implements ImageContext { } /** @return the set of namespaces that are ignored by FOP */ - public Set getIgnoredNamespace() { + public Set<String> getIgnoredNamespace() { return Collections.unmodifiableSet(this.ignoredNamespaces); } @@ -742,6 +746,7 @@ public class FopFactory implements ImageContext { * @param useCache use cache or not * @deprecated use getFontManager().setUseCache(boolean) instead */ + @Deprecated public void setUseCache(boolean useCache) { getFontManager().setUseCache(useCache); } @@ -751,6 +756,7 @@ public class FopFactory implements ImageContext { * @return whether this factory is uses the cache * @deprecated use getFontManager().useCache() instead */ + @Deprecated public boolean useCache() { return getFontManager().useCache(); } @@ -760,6 +766,7 @@ public class FopFactory implements ImageContext { * @return the font cache * @deprecated use getFontManager().getFontCache() instead */ + @Deprecated public FontCache getFontCache() { return getFontManager().getFontCache(); } @@ -793,20 +800,13 @@ 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. - * - * @param baseUri 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 + * Returns the color space cache for this instance. + * <p> + * Note: this method should not be considered as part of FOP's external API. + * @return the color space cache */ - public ColorSpace getColorSpace(String baseUri, String iccProfileSrc) { - return colorSpaceCache.get(baseUri, iccProfileSrc); + public ColorSpaceCache getColorSpaceCache() { + return this.colorSpaceCache; } } diff --git a/src/java/org/apache/fop/fo/expr/CIELabColorFunction.java b/src/java/org/apache/fop/fo/expr/CIELabColorFunction.java new file mode 100644 index 000000000..d027a9571 --- /dev/null +++ b/src/java/org/apache/fop/fo/expr/CIELabColorFunction.java @@ -0,0 +1,94 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.fo.expr; +import org.apache.fop.apps.FOUserAgent; +import org.apache.fop.datatypes.PercentBase; +import org.apache.fop.datatypes.PercentBaseContext; +import org.apache.fop.fo.properties.ColorProperty; +import org.apache.fop.fo.properties.Property; + +/** + * Implements the cie-lab-color() function. + * @since XSL-FO 2.0 + */ +class CIELabColorFunction extends FunctionBase { + + /** + * cie-lab-color() takes 2 times 3 arguments. + * {@inheritDoc} + */ + public int nbArgs() { + return 2 * 3; + } + + public PercentBase getPercentBase() { + return new CIELabPercentBase(); + } + + /** {@inheritDoc} */ + public Property eval(Property[] args, + PropertyInfo pInfo) throws PropertyException { + + float red = args[0].getNumber().floatValue(); + float green = args[1].getNumber().floatValue(); + float blue = args[2].getNumber().floatValue(); + /* Verify sRGB replacement arguments */ + if ((red < 0 || red > 255) + || (green < 0 || green > 255) + || (blue < 0 || blue > 255)) { + throw new PropertyException("sRGB color values out of range. " + + "Arguments to cie-lab-color() must be [0..255] or [0%..100%]"); + } + + float l = args[3].getNumber().floatValue(); + float a = args[4].getNumber().floatValue(); + float b = args[5].getNumber().floatValue(); + if (l < 0 || l > 100) { + throw new PropertyException("L* value out of range. Valid range: [0..100]"); + } + if (a < -127 || a > 127 || b < -127 || b > 127) { + throw new PropertyException("a* and b* values out of range. Valid range: [-127..+127]"); + } + + StringBuffer sb = new StringBuffer(); + sb.append("cie-lab-color(" + red + "," + green + "," + blue + "," + + l + "," + a + "," + b + ")"); + FOUserAgent ua = (pInfo == null) + ? null + : (pInfo.getFO() == null ? null : pInfo.getFO().getUserAgent()); + return ColorProperty.getInstance(ua, sb.toString()); + } + + private static class CIELabPercentBase implements PercentBase { + public int getDimension() { + return 0; + } + + public double getBaseValue() { + return 1.0f; + } + + public int getBaseLength(PercentBaseContext context) { + return 0; + } + + } + +} diff --git a/src/java/org/apache/fop/fo/expr/NamedColorFunction.java b/src/java/org/apache/fop/fo/expr/NamedColorFunction.java new file mode 100644 index 000000000..2bceec8dc --- /dev/null +++ b/src/java/org/apache/fop/fo/expr/NamedColorFunction.java @@ -0,0 +1,112 @@ +/* + * 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.datatypes.PercentBase; +import org.apache.fop.datatypes.PercentBaseContext; +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-named-color() function. + * @since XSL-FO 2.0 + */ +class NamedColorFunction extends FunctionBase { + + /** + * rgb-named-color() takes a 5 arguments. + * {@inheritDoc} + */ + public int nbArgs() { + return 5; + } + + /** {@inheritDoc} */ + public PercentBase getPercentBase() { + return new NamedPercentBase(); + } + + /** {@inheritDoc} */ + public Property eval(Property[] args, + PropertyInfo pInfo) throws PropertyException { + // Map color profile NCNAME to src from declarations/color-profile element + String colorProfileName = args[3].getString(); + String colorName = args[4].getString(); + + Declarations decls = pInfo.getFO().getRoot().getDeclarations(); + ColorProfile cp = null; + if (decls != null) { + 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 != null ? cp.getSrc() : ""); + + float red = 0, green = 0, blue = 0; + red = args[0].getNumber().floatValue(); + green = args[1].getNumber().floatValue(); + blue = args[2].getNumber().floatValue(); + /* Verify rgb replacement arguments */ + if ((red < 0 || red > 255) + || (green < 0 || green > 255) + || (blue < 0 || blue > 255)) { + throw new PropertyException("sRGB color values out of range. " + + "Arguments to rgb-named-color() must be [0..255] or [0%..100%]"); + } + + // rgb-named-color is replaced with fop-rgb-named-color which has an extra argument + // containing the color profile src attribute as it is defined in the color-profile + // declarations element. + StringBuffer sb = new StringBuffer(); + sb.append("fop-rgb-named-color("); + sb.append(red / 255f); + sb.append(',').append(green / 255f); + sb.append(',').append(blue / 255f); + sb.append(',').append(colorProfileName); + sb.append(',').append(src); + sb.append(", '").append(colorName).append('\''); + sb.append(")"); + + return ColorProperty.getInstance(pInfo.getUserAgent(), sb.toString()); + } + + private static final class NamedPercentBase implements PercentBase { + + /** {@inheritDoc} */ + public int getBaseLength(PercentBaseContext context) throws PropertyException { + return 0; + } + + /** {@inheritDoc} */ + public double getBaseValue() { + return 255f; + } + + /** {@inheritDoc} */ + public int getDimension() { + return 0; + } + } +} diff --git a/src/java/org/apache/fop/fo/expr/PropertyParser.java b/src/java/org/apache/fop/fo/expr/PropertyParser.java index 87f640651..9ef45befe 100644 --- a/src/java/org/apache/fop/fo/expr/PropertyParser.java +++ b/src/java/org/apache/fop/fo/expr/PropertyParser.java @@ -69,6 +69,8 @@ public final class PropertyParser extends PropertyTokenizer { FUNCTION_TABLE.put("label-end", new LabelEndFunction()); FUNCTION_TABLE.put("body-start", new BodyStartFunction()); FUNCTION_TABLE.put("rgb-icc", new ICCColorFunction()); + FUNCTION_TABLE.put("rgb-named-color", new NamedColorFunction()); //since XSL-FO 2.0 + FUNCTION_TABLE.put("cie-lab-color", new CIELabColorFunction()); //since XSL-FO 2.0 FUNCTION_TABLE.put("cmyk", new CMYKcolorFunction()); //non-standard!!! /** diff --git a/src/java/org/apache/fop/fo/expr/PropertyTokenizer.java b/src/java/org/apache/fop/fo/expr/PropertyTokenizer.java index 3008dbebe..89a387753 100644 --- a/src/java/org/apache/fop/fo/expr/PropertyTokenizer.java +++ b/src/java/org/apache/fop/fo/expr/PropertyTokenizer.java @@ -80,7 +80,6 @@ class PropertyTokenizer { void next() throws PropertyException { currentTokenValue = null; currentTokenStartIndex = exprIndex; - boolean currentMaybeOperator = recognizeOperator; boolean bSawDecimal; recognizeOperator = true; while ( true ) { @@ -244,14 +243,14 @@ class PropertyTokenizer { private void nextColor() throws PropertyException { - if (exprIndex < exprLength - && isHexDigit(expr.charAt(exprIndex))) { + if (exprIndex < exprLength) { ++exprIndex; scanHexDigits(); int len = exprIndex - currentTokenStartIndex - 1; if (len % 3 == 0) { currentToken = TOK_COLORSPEC; } else { + //Actually not a color at all, but an NCNAME starting with "#" scanRestOfName(); currentToken = TOK_NCNAME; } diff --git a/src/java/org/apache/fop/fo/properties/ColorProperty.java b/src/java/org/apache/fop/fo/properties/ColorProperty.java index 293f31577..0550ce684 100644 --- a/src/java/org/apache/fop/fo/properties/ColorProperty.java +++ b/src/java/org/apache/fop/fo/properties/ColorProperty.java @@ -70,6 +70,7 @@ public final class ColorProperty extends Property { * @throws PropertyException * for invalid or inconsistent FO input */ + @Override public Property convertProperty(Property p, PropertyList propertyList, FObj fo) throws PropertyException { @@ -120,11 +121,13 @@ public final class ColorProperty extends Property { * @param foUserAgent FOP user agent * @return float the AWT color represented by this ColorType instance */ + @Override public Color getColor(FOUserAgent foUserAgent) { return color; } /** {@inheritDoc} */ + @Override public String toString() { return ColorUtil.colorToString(color); } @@ -140,23 +143,27 @@ public final class ColorProperty extends Property { /** * @return this.colorType cast as an Object */ + @Override public Object getObject() { return this; } /** {@inheritDoc} */ + @Override public boolean equals(Object o) { if (this == o) { return true; } if (o instanceof ColorProperty) { - return ((ColorProperty) o).color.equals(this.color); + return org.apache.xmlgraphics.java2d.color.ColorUtil.isSameColor( + ((ColorProperty) o).color, this.color); } return false; } /** {@inheritDoc} */ + @Override public int hashCode() { return this.color.hashCode(); } diff --git a/src/java/org/apache/fop/pdf/PDFCIELabColorSpace.java b/src/java/org/apache/fop/pdf/PDFCIELabColorSpace.java new file mode 100644 index 000000000..4d1d7f7ff --- /dev/null +++ b/src/java/org/apache/fop/pdf/PDFCIELabColorSpace.java @@ -0,0 +1,91 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.pdf; + +/** + * This class represents a "CIE L*a*b*" color space. It is expected that the components have + * the following ranges: L* [0..100], a* and b* [-127..127] + */ +public class PDFCIELabColorSpace extends PDFArray implements PDFColorSpace { + + /** + * Creates a new "CIE L*a*b*" color space. Valid value ranges for the white and black point + * are [0..1] as per the PDF spec. + * @param whitePoint the white point + * @param blackPoint the optional black point (may be null) + */ + public PDFCIELabColorSpace(float[] whitePoint, float[] blackPoint) { + super(); + + add(new PDFName("Lab")); + PDFDictionary dict = new PDFDictionary(); + dict.put("WhitePoint", toPDFArray("White point", whitePoint)); + if (whitePoint[1] != 1f) { + throw new IllegalArgumentException("The white point's Y coordinate must be 1.0"); + } + if (blackPoint != null) { + dict.put("BlackPoint", toPDFArray("Black point", blackPoint)); + } + dict.put("Range", new PDFArray(dict, new int[] {-128, 128, -128, 128})); + add(dict); + } + + private PDFArray toPDFArray(String name, float[] whitePoint) { + PDFArray wp = new PDFArray(); + if (whitePoint == null || whitePoint.length != 3) { + throw new IllegalArgumentException(name + " must be given an have 3 components"); + } + for (int i = 0; i < 3; i++) { + wp.add(whitePoint[i]); + } + return wp; + } + + /** {@inheritDoc} */ + public String getName() { + return "CS" + this.getObjectNumber(); + } + + /** {@inheritDoc} */ + public int getNumComponents() { + return 3; + } + + /** {@inheritDoc} */ + public boolean isCMYKColorSpace() { + return false; + } + + /** {@inheritDoc} */ + public boolean isDeviceColorSpace() { + return false; + } + + /** {@inheritDoc} */ + public boolean isGrayColorSpace() { + return false; + } + + /** {@inheritDoc} */ + public boolean isRGBColorSpace() { + return false; + } + +} diff --git a/src/java/org/apache/fop/pdf/PDFColor.java b/src/java/org/apache/fop/pdf/PDFColor.java index 2dc1da1ea..781012cfa 100644 --- a/src/java/org/apache/fop/pdf/PDFColor.java +++ b/src/java/org/apache/fop/pdf/PDFColor.java @@ -21,18 +21,17 @@ 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.DeviceCMYKColorSpace; -import org.apache.fop.util.ColorExt; - /** - * PDF Color object. - * This is used to output color to a PDF content stream. + * PDF Color object. It is currently only used to hold the transparent color of a masked bitmap + * image. And in this context, only RGB and Gray values are used. + * <p> + * Use of this class is discouraged. {@link PDFColorHandler} is now used for in-content color + * selection. For masked bitmaps, it may be wiser to switch to {@link Color} in the long run. */ public class PDFColor extends PDFPathPaint { // could be 3.0 as well. @@ -46,14 +45,8 @@ public class PDFColor extends PDFPathPaint { 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 + * Create a PDF color with double values ranging from 0 to 1. * * @param theRed the red double value * @param theGreen the green double value @@ -69,81 +62,27 @@ public class PDFColor extends PDFPathPaint { } /** - * 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) + * 3. 'Normal' java.awt.Color (RGB case assumed or implicit conversion to sRGB) * * @param col the java.awt.Color object for which to create a PDFColor object */ public PDFColor(java.awt.Color col) { ColorSpace cs = col.getColorSpace(); - ColorExt ce = null; - if (col instanceof ColorExt) { - ce = (ColorExt)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()); + float[] cmyk = col.getColorComponents(null); 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 + // Default (RGB) Color (ICC Colors are converted to sRGB, too) this.colorSpace = new PDFDeviceColorSpace(PDFDeviceColorSpace.DEVICE_RGB); float[] comps = new float[3]; comps = col.getColorComponents(comps); @@ -163,7 +102,6 @@ public class PDFColor extends PDFPathPaint { public PDFColor(int theRed, int theGreen, int theBlue) { this(((double)theRed) / 255d, ((double)theGreen) / 255d, ((double)theBlue) / 255d); - } /** @@ -176,8 +114,6 @@ public class PDFColor extends PDFPathPaint { */ public PDFColor(double theCyan, double theMagenta, double theYellow, double theBlack) { - // super(theNumber);//? - this.colorSpace = new PDFDeviceColorSpace(PDFDeviceColorSpace.DEVICE_CMYK); this.cyan = theCyan; @@ -351,26 +287,7 @@ public class PDFColor extends PDFPathPaint { public String getColorSpaceOut(boolean fillNotStroke) { StringBuffer p = new StringBuffer(""); - 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() + 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 diff --git a/src/java/org/apache/fop/pdf/PDFColorHandler.java b/src/java/org/apache/fop/pdf/PDFColorHandler.java new file mode 100644 index 000000000..76a6cab78 --- /dev/null +++ b/src/java/org/apache/fop/pdf/PDFColorHandler.java @@ -0,0 +1,231 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.pdf; + +import java.awt.Color; +import java.awt.color.ColorSpace; +import java.awt.color.ICC_ColorSpace; +import java.awt.color.ICC_Profile; +import java.text.DecimalFormat; +import java.util.Map; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.apache.xmlgraphics.java2d.color.CIELabColorSpace; +import org.apache.xmlgraphics.java2d.color.ColorUtil; +import org.apache.xmlgraphics.java2d.color.ColorWithAlternatives; +import org.apache.xmlgraphics.java2d.color.DeviceCMYKColorSpace; +import org.apache.xmlgraphics.java2d.color.NamedColorSpace; +import org.apache.xmlgraphics.java2d.color.profile.ColorProfileUtil; + +import org.apache.fop.util.DecimalFormatCache; + +/** + * This class handles the registration of color spaces and the generation of PDF code to select + * the right colors given a {@link Color} instance. + */ +public class PDFColorHandler { + + private Log log = LogFactory.getLog(PDFColorHandler.class); + + private PDFResources resources; + + private Map<String, PDFCIELabColorSpace> cieLabColorSpaces; + + public PDFColorHandler(PDFResources resources) { + this.resources = resources; + } + + private PDFDocument getDocument() { + return this.resources.getDocumentSafely(); + } + + /** + * Generates code to select the given color and handles the registration of color spaces in + * PDF where necessary. + * @param codeBuffer the target buffer to receive the color selection code + * @param color the color + * @param fill true for fill color, false for stroke color + */ + public void establishColor(StringBuffer codeBuffer, Color color, boolean fill) { + if (color instanceof ColorWithAlternatives) { + ColorWithAlternatives colExt = (ColorWithAlternatives)color; + //Alternate colors have priority + Color[] alt = colExt.getAlternativeColors(); + for (int i = 0, c = alt.length; i < c; i++) { + Color col = alt[i]; + boolean established = establishColorFromColor(codeBuffer, col, fill); + if (established) { + return; + } + } + if (log.isDebugEnabled() && alt.length > 0) { + log.debug("None of the alternative colors are supported. Using fallback: " + + color); + } + } + + //Fallback + boolean established = establishColorFromColor(codeBuffer, color, fill); + if (!established) { + establishDeviceRGB(codeBuffer, color, fill); + } + } + + private boolean establishColorFromColor(StringBuffer codeBuffer, Color color, boolean fill) { + ColorSpace cs = color.getColorSpace(); + if (cs instanceof DeviceCMYKColorSpace) { + establishDeviceCMYK(codeBuffer, color, fill); + return true; + } else if (!cs.isCS_sRGB()) { + if (cs instanceof ICC_ColorSpace) { + PDFICCBasedColorSpace pdfcs = getICCBasedColorSpace((ICC_ColorSpace)cs); + establishColor(codeBuffer, pdfcs, color, fill); + return true; + } else if (cs instanceof NamedColorSpace) { + PDFSeparationColorSpace sepcs = getSeparationColorSpace((NamedColorSpace)cs); + establishColor(codeBuffer, sepcs, color, fill); + return true; + } else if (cs instanceof CIELabColorSpace) { + CIELabColorSpace labcs = (CIELabColorSpace)cs; + PDFCIELabColorSpace pdflab = getCIELabColorSpace(labcs); + selectColorSpace(codeBuffer, pdflab, fill); + float[] comps = color.getColorComponents(null); + float[] nativeComps = labcs.toNativeComponents(comps); + writeColor(codeBuffer, nativeComps, labcs.getNumComponents(), (fill ? "sc" : "SC")); + return true; + } + } + return false; + } + + private PDFICCBasedColorSpace getICCBasedColorSpace(ICC_ColorSpace cs) { + ICC_Profile profile = cs.getProfile(); + String desc = ColorProfileUtil.getICCProfileDescription(profile); + if (log.isDebugEnabled()) { + log.trace("ICC profile encountered: " + desc); + } + PDFICCBasedColorSpace pdfcs = this.resources.getICCColorSpaceByProfileName(desc); + if (pdfcs == null) { + //color space is not in the PDF, yet + PDFFactory factory = getDocument().getFactory(); + PDFICCStream pdfICCStream = factory.makePDFICCStream(); + PDFDeviceColorSpace altSpace = PDFDeviceColorSpace.toPDFColorSpace(cs); + pdfICCStream.setColorSpace(profile, altSpace); + pdfcs = factory.makeICCBasedColorSpace(null, desc, pdfICCStream); + } + return pdfcs; + } + + private PDFSeparationColorSpace getSeparationColorSpace(NamedColorSpace cs) { + PDFName colorName = new PDFName(cs.getColorName()); + PDFSeparationColorSpace sepcs = (PDFSeparationColorSpace)this.resources.getColorSpace( + colorName); + if (sepcs == null) { + //color space is not in the PDF, yet + PDFFactory factory = getDocument().getFactory(); + sepcs = factory.makeSeparationColorSpace(null, cs); + } + return sepcs; + } + + private PDFCIELabColorSpace getCIELabColorSpace(CIELabColorSpace labCS) { + if (this.cieLabColorSpaces == null) { + this.cieLabColorSpaces = new java.util.HashMap<String, PDFCIELabColorSpace>(); + } + float[] wp = labCS.getWhitePoint(); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < 3; i++) { + if (i > 0) { + sb.append(','); + } + sb.append(wp[i]); + } + String key = sb.toString(); + PDFCIELabColorSpace cielab = this.cieLabColorSpaces.get(key); + if (cielab == null) { + //color space is not in the PDF, yet + float[] wp1 = new float[] {wp[0] / 100f, wp[1] / 100f, wp[2] / 100f}; + cielab = new PDFCIELabColorSpace(wp1, null); + getDocument().registerObject(cielab); + this.resources.addColorSpace(cielab); + this.cieLabColorSpaces.put(key, cielab); + } + return cielab; + } + + private void establishColor(StringBuffer codeBuffer, + PDFColorSpace pdfcs, Color color, boolean fill) { + selectColorSpace(codeBuffer, pdfcs, fill); + writeColor(codeBuffer, color, pdfcs.getNumComponents(), (fill ? "sc" : "SC")); + } + + private void selectColorSpace(StringBuffer codeBuffer, PDFColorSpace pdfcs, boolean fill) { + codeBuffer.append(new PDFName(pdfcs.getName())); + if (fill) { + codeBuffer.append(" cs "); + } else { + codeBuffer.append(" CS "); + } + } + + private void establishDeviceRGB(StringBuffer codeBuffer, Color color, boolean fill) { + float[] comps; + if (color.getColorSpace().isCS_sRGB()) { + comps = color.getColorComponents(null); + } else { + if (log.isDebugEnabled()) { + log.debug("Converting color to sRGB as a fallback: " + color); + } + ColorSpace sRGB = ColorSpace.getInstance(ColorSpace.CS_sRGB); + comps = color.getColorComponents(sRGB, null); + } + if (ColorUtil.isGray(color)) { + comps = new float[] {comps[0]}; //assuming that all components are the same + writeColor(codeBuffer, comps, 1, (fill ? "g" : "G")); + } else { + writeColor(codeBuffer, comps, 3, (fill ? "rg" : "RG")); + } + } + + private void establishDeviceCMYK(StringBuffer codeBuffer, Color color, boolean fill) { + writeColor(codeBuffer, color, 4, (fill ? "k" : "K")); + } + + private void writeColor(StringBuffer codeBuffer, Color color, int componentCount, + String command) { + float[] comps = color.getColorComponents(null); + writeColor(codeBuffer, comps, componentCount, command); + } + + private void writeColor(StringBuffer codeBuffer, float[] comps, int componentCount, + String command) { + if (comps.length != componentCount) { + throw new IllegalStateException("Color with unexpected component count encountered"); + } + DecimalFormat df = DecimalFormatCache.getDecimalFormat(4); + for (int i = 0, c = comps.length; i < c; i++) { + codeBuffer.append(df.format(comps[i])).append(" "); + } + codeBuffer.append(command).append("\n"); + } + +} diff --git a/src/java/org/apache/fop/pdf/PDFDeviceColorSpace.java b/src/java/org/apache/fop/pdf/PDFDeviceColorSpace.java index 6ccfd39f5..b18c3ac9e 100644 --- a/src/java/org/apache/fop/pdf/PDFDeviceColorSpace.java +++ b/src/java/org/apache/fop/pdf/PDFDeviceColorSpace.java @@ -19,6 +19,8 @@ package org.apache.fop.pdf; +import java.awt.color.ColorSpace; + /** * Represents a device-specific color space. Used for mapping DeviceRGB, DeviceCMYK and DeviceGray. */ @@ -137,4 +139,28 @@ public class PDFDeviceColorSpace implements PDFColorSpace { return getColorSpace() == DEVICE_GRAY; } + /** + * Returns a suitable {@link PDFDeviceColorSpace} object given a {@link ColorSpace} object. + * @param cs ColorSpace instance + * @return a PDF-based color space + */ + public static PDFDeviceColorSpace toPDFColorSpace(ColorSpace cs) { + if (cs == null) { + return null; + } + + PDFDeviceColorSpace pdfCS = new PDFDeviceColorSpace(0); + switch (cs.getType()) { + case ColorSpace.TYPE_CMYK: + pdfCS.setColorSpace(PDFDeviceColorSpace.DEVICE_CMYK); + break; + case ColorSpace.TYPE_GRAY: + pdfCS.setColorSpace(PDFDeviceColorSpace.DEVICE_GRAY); + break; + default: + pdfCS.setColorSpace(PDFDeviceColorSpace.DEVICE_RGB); + } + return pdfCS; + } + } diff --git a/src/java/org/apache/fop/pdf/PDFDocument.java b/src/java/org/apache/fop/pdf/PDFDocument.java index a17c8c4df..9268ae921 100644 --- a/src/java/org/apache/fop/pdf/PDFDocument.java +++ b/src/java/org/apache/fop/pdf/PDFDocument.java @@ -67,7 +67,7 @@ import org.apache.commons.logging.LogFactory; */ public class PDFDocument { - private static final Integer LOCATION_PLACEHOLDER = new Integer(0); + private static final Long LOCATION_PLACEHOLDER = new Long(0); /** Integer constant to represent PDF 1.3 */ public static final int PDF_VERSION_1_3 = 3; @@ -85,13 +85,13 @@ public class PDFDocument { private Log log = LogFactory.getLog("org.apache.fop.pdf"); /** the current character position */ - private int position = 0; + private long position = 0; /** character position of xref table */ - private int xref; + private long xref; /** the character position of each object */ - private List location = new ArrayList(); + private List<Long> location = new ArrayList<Long>(); /** List of objects to write in the trailer */ private List trailerObjects = new ArrayList(); @@ -747,6 +747,7 @@ public class PDFDocument { * @return the image or PDFXObject for the key if found * @deprecated Use getXObject instead (so forms are treated in the same way) */ + @Deprecated public PDFImageXObject getImage(String key) { return (PDFImageXObject)this.xObjectsMap.get(key); } @@ -911,11 +912,11 @@ public class PDFDocument { * @param objidx the object's index * @param position the position */ - private void setLocation(int objidx, int position) { + private void setLocation(int objidx, long position) { while (this.location.size() <= objidx) { this.location.add(LOCATION_PLACEHOLDER); } - this.location.set(objidx, new Integer(position)); + this.location.set(objidx, position); } /** @@ -1071,6 +1072,9 @@ public class PDFDocument { for (int count = 0; count < this.location.size(); count++) { final String padding = "0000000000"; s = this.location.get(count).toString(); + if (s.length() > 10) { + throw new IOException("PDF file too large. PDF cannot grow beyond approx. 9.3GB."); + } /* contruct xref entry for object */ loc = padding.substring(s.length()) + s; diff --git a/src/java/org/apache/fop/pdf/PDFFactory.java b/src/java/org/apache/fop/pdf/PDFFactory.java index 752d14207..23542a49e 100644 --- a/src/java/org/apache/fop/pdf/PDFFactory.java +++ b/src/java/org/apache/fop/pdf/PDFFactory.java @@ -20,6 +20,7 @@ package org.apache.fop.pdf; // Java +import java.awt.Color; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.io.FileNotFoundException; @@ -27,6 +28,7 @@ import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; import java.util.ArrayList; +import java.util.Arrays; import java.util.BitSet; import java.util.Iterator; import java.util.List; @@ -40,6 +42,8 @@ import org.apache.commons.io.output.ByteArrayOutputStream; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.apache.xmlgraphics.java2d.color.ColorUtil; +import org.apache.xmlgraphics.java2d.color.NamedColorSpace; import org.apache.xmlgraphics.xmp.Metadata; import org.apache.fop.fonts.CIDFont; @@ -779,23 +783,23 @@ public class PDFFactory { for (currentPosition = 0; currentPosition < lastPosition; currentPosition++) { // for every consecutive color pair - PDFColor currentColor = (PDFColor)theColors.get(currentPosition); - PDFColor nextColor = (PDFColor)theColors.get(currentPosition - + 1); - // colorspace must be consistant - if (getDocument().getColorSpace() != currentColor.getColorSpace()) { - currentColor.setColorSpace( - getDocument().getColorSpace()); + Color currentColor = (Color)theColors.get(currentPosition); + Color nextColor = (Color)theColors.get(currentPosition + 1); + + // colorspace must be consistent, so we simply convert to sRGB where necessary + if (!currentColor.getColorSpace().isCS_sRGB()) { + //Convert to sRGB + currentColor = ColorUtil.toSRGBColor(currentColor); + theColors.set(currentPosition, currentColor); } - - if (getDocument().getColorSpace() - != nextColor.getColorSpace()) { - nextColor.setColorSpace( - getDocument().getColorSpace()); + if (!nextColor.getColorSpace().isCS_sRGB()) { + //Convert to sRGB + nextColor = ColorUtil.toSRGBColor(nextColor); + theColors.set(currentPosition + 1, nextColor); } - theCzero = currentColor.getVector(); - theCone = nextColor.getVector(); + theCzero = toColorVector(currentColor); + theCone = toColorVector(nextColor); myfunc = makeFunction(2, null, null, theCzero, theCone, interpolation); @@ -843,6 +847,15 @@ public class PDFFactory { return (myPattern); } + private List toColorVector(Color nextColor) { + List vector = new java.util.ArrayList(); + float[] comps = nextColor.getColorComponents(null); + for (int i = 0, c = comps.length; i < c; i++) { + vector.add(new Double(comps[i])); + } + return vector; + } + /* ============= named destinations and the name dictionary ============ */ /** @@ -1782,6 +1795,38 @@ public class PDFFactory { } /** + * Create a new Separation color space. + * @param res the resource context (may be null) + * @param ncs the named color space to map to a separation color space + * @return the newly created Separation color space + */ + public PDFSeparationColorSpace makeSeparationColorSpace(PDFResourceContext res, + NamedColorSpace ncs) { + String colorName = ncs.getColorName(); + final Double zero = new Double(0d); + final Double one = new Double(1d); + List theDomain = Arrays.asList(new Double[] {zero, one}); + List theRange = Arrays.asList(new Double[] {zero, one, zero, one, zero, one}); + List theCZero = Arrays.asList(new Double[] {one, one, one}); + List theCOne = new ArrayList(); + float[] comps = ncs.getRGBColor().getColorComponents(null); + for (int i = 0, c = comps.length; i < c; i++) { + theCOne.add(new Double(comps[i])); + } + PDFFunction tintFunction = makeFunction(2, theDomain, theRange, + theCZero, theCOne, 1.0d); + PDFSeparationColorSpace cs = new PDFSeparationColorSpace(colorName, tintFunction); + getDocument().registerObject(cs); + if (res != null) { + res.getPDFResources().addColorSpace(cs); + } else { + getDocument().getResources().addColorSpace(cs); + } + + return cs; + } + + /** * Make an Array object (ex. Widths array for a font). * * @param values the int array values diff --git a/src/java/org/apache/fop/pdf/PDFName.java b/src/java/org/apache/fop/pdf/PDFName.java index 3f9af7669..e40a12f13 100644 --- a/src/java/org/apache/fop/pdf/PDFName.java +++ b/src/java/org/apache/fop/pdf/PDFName.java @@ -86,6 +86,29 @@ public class PDFName extends PDFObject { return this.name; } + /** + * Returns the name without the leading slash. + * @return the name without the leading slash + */ + public String getName() { + return this.name.substring(1); + } + + /** {@inheritDoc} */ + public boolean equals(Object obj) { + if (!(obj instanceof PDFName)) { + return false; + } + PDFName other = (PDFName)obj; + return this.name.equals(other.name); + } + + /** {@inheritDoc} */ + public int hashCode() { + return name.hashCode(); + } + + /** {@inheritDoc} */ protected int output(OutputStream stream) throws IOException { CountingOutputStream cout = new CountingOutputStream(stream); diff --git a/src/java/org/apache/fop/pdf/PDFPaintingState.java b/src/java/org/apache/fop/pdf/PDFPaintingState.java index ebe2b383b..29d022f61 100644 --- a/src/java/org/apache/fop/pdf/PDFPaintingState.java +++ b/src/java/org/apache/fop/pdf/PDFPaintingState.java @@ -19,12 +19,15 @@ package org.apache.fop.pdf; +import java.awt.Color; import java.awt.Paint; import java.awt.Shape; import java.awt.geom.Area; import java.awt.geom.GeneralPath; import java.util.Iterator; +import org.apache.xmlgraphics.java2d.color.ColorUtil; + import org.apache.fop.util.AbstractPaintingState; /** @@ -63,13 +66,18 @@ public class PDFPaintingState extends org.apache.fop.util.AbstractPaintingState */ public boolean setPaint(Paint p) { PDFData data = getPDFData(); - Paint paint = data.paint; - if (paint == null) { + Paint currentPaint = data.paint; + if (currentPaint == null) { if (p != null) { data.paint = p; return true; } - } else if (!paint.equals(p)) { + } else if (p instanceof Color && currentPaint instanceof Color) { + if (!ColorUtil.isSameColor((Color)p, (Color)currentPaint)) { + data.paint = p; + return true; + } + } else if (!currentPaint.equals(p)) { data.paint = p; return true; } @@ -180,11 +188,13 @@ public class PDFPaintingState extends org.apache.fop.util.AbstractPaintingState } /** {@inheritDoc} */ + @Override protected AbstractData instantiateData() { return new PDFData(); } /** {@inheritDoc} */ + @Override protected AbstractPaintingState instantiate() { return new PDFPaintingState(); } @@ -194,6 +204,7 @@ public class PDFPaintingState extends org.apache.fop.util.AbstractPaintingState * This call should be used when the q operator is used * so that the state is known when popped. */ + @Override public void save() { AbstractData data = getData(); AbstractData copy = (AbstractData)data.clone(); @@ -222,6 +233,7 @@ public class PDFPaintingState extends org.apache.fop.util.AbstractPaintingState private float characterSpacing = 0f; /** {@inheritDoc} */ + @Override public Object clone() { PDFData obj = (PDFData)super.clone(); obj.paint = this.paint; @@ -237,6 +249,7 @@ public class PDFPaintingState extends org.apache.fop.util.AbstractPaintingState } /** {@inheritDoc} */ + @Override public String toString() { return super.toString() + ", paint=" + paint @@ -249,6 +262,7 @@ public class PDFPaintingState extends org.apache.fop.util.AbstractPaintingState } /** {@inheritDoc} */ + @Override protected AbstractData instantiate() { return new PDFData(); } diff --git a/src/java/org/apache/fop/pdf/PDFResources.java b/src/java/org/apache/fop/pdf/PDFResources.java index 99a42433c..317f703e1 100644 --- a/src/java/org/apache/fop/pdf/PDFResources.java +++ b/src/java/org/apache/fop/pdf/PDFResources.java @@ -19,6 +19,8 @@ package org.apache.fop.pdf; +import java.io.IOException; +import java.io.OutputStream; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -33,12 +35,12 @@ import org.apache.fop.fonts.base14.Symbol; import org.apache.fop.fonts.base14.ZapfDingbats; /** - * class representing a /Resources object. + * Class representing a /Resources object. * * /Resources object contain a list of references to the fonts for the * document */ -public class PDFResources extends PDFObject { +public class PDFResources extends PDFDictionary { /** * /Font objects keyed by their internal name @@ -159,11 +161,14 @@ public class PDFResources extends PDFObject { * Add a ColorSpace dictionary to the resources. * @param colorSpace the color space */ - public void addColorSpace(PDFICCBasedColorSpace colorSpace) { - this.colorSpaces.put(colorSpace.getName(), colorSpace); - String desc = ColorProfileUtil.getICCProfileDescription( - colorSpace.getICCStream().getICCProfile()); - this.iccColorSpaces.put(desc, colorSpace); + public void addColorSpace(PDFColorSpace colorSpace) { + this.colorSpaces.put(new PDFName(colorSpace.getName()), colorSpace); + if (colorSpace instanceof PDFICCBasedColorSpace) { + PDFICCBasedColorSpace icc = (PDFICCBasedColorSpace)colorSpace; + String desc = ColorProfileUtil.getICCProfileDescription( + icc.getICCStream().getICCProfile()); + this.iccColorSpaces.put(desc, colorSpace); + } } /** @@ -181,106 +186,80 @@ public class PDFResources extends PDFObject { * @param name the name of the color space * @return the requested color space or null if it wasn't found */ - public PDFICCBasedColorSpace getColorSpace(String name) { - PDFICCBasedColorSpace cs = (PDFICCBasedColorSpace)this.colorSpaces.get(name); + public PDFColorSpace getColorSpace(PDFName name) { + PDFColorSpace cs = (PDFColorSpace)this.colorSpaces.get(name); return cs; } - /** - * represent the object in PDF - * This adds the references to all the objects in the current - * resource context. - * - * @return the PDF - * {@inheritDoc} - */ - @Override - public String toPDFString() { - StringBuffer p = new StringBuffer(128); - p.append(getObjectID() + "<<\n"); - if (!this.fonts.isEmpty()) { - p.append("/Font <<\n"); + /** {@inheritDoc} */ + protected int output(OutputStream stream) throws IOException { + populateDictionary(); + return super.output(stream); + } + private void populateDictionary() { + if (!this.fonts.isEmpty()) { + PDFDictionary dict = new PDFDictionary(this); /* construct PDF dictionary of font object references */ Iterator fontIterator = this.fonts.keySet().iterator(); while (fontIterator.hasNext()) { String fontName = (String)fontIterator.next(); - p.append(" /" + fontName + " " - + ((PDFFont)this.fonts.get(fontName)).referencePDF() - + "\n"); + dict.put(fontName, (PDFFont)this.fonts.get(fontName)); } - - p.append(">>\n"); + put("Font", dict); } - PDFShading currentShading = null; if (!this.shadings.isEmpty()) { - p.append("/Shading <<\n"); - + PDFDictionary dict = new PDFDictionary(this); for (Iterator iter = shadings.iterator(); iter.hasNext();) { - currentShading = (PDFShading)iter.next(); - p.append(" /" + currentShading.getName() + " " - + currentShading.referencePDF() + " "); // \n ?????? + PDFShading currentShading = (PDFShading)iter.next(); + dict.put(currentShading.getName(), currentShading); } - - p.append(">>\n"); + put("Shading", dict); } - // "free" the memory. Sorta. - currentShading = null; - PDFPattern currentPattern = null; if (!this.patterns.isEmpty()) { - p.append("/Pattern <<\n"); - + PDFDictionary dict = new PDFDictionary(this); for (Iterator iter = patterns.iterator(); iter.hasNext();) { - currentPattern = (PDFPattern)iter.next(); - p.append(" /" + currentPattern.getName() + " " - + currentPattern.referencePDF() + " "); + PDFPattern currentPattern = (PDFPattern)iter.next(); + dict.put(currentPattern.getName(), currentPattern); } - - p.append(">>\n"); + put("Pattern", dict); } - // "free" the memory. Sorta. - currentPattern = null; - p.append("/ProcSet [ /PDF /ImageB /ImageC /Text ]\n"); + PDFArray procset = new PDFArray(this); + procset.add(new PDFName("PDF")); + procset.add(new PDFName("ImageB")); + procset.add(new PDFName("ImageC")); + procset.add(new PDFName("Text")); + put("ProcSet", procset); if (this.xObjects != null && !this.xObjects.isEmpty()) { - p = p.append("/XObject <<\n"); + PDFDictionary dict = new PDFDictionary(this); for (Iterator iter = xObjects.iterator(); iter.hasNext();) { PDFXObject xobj = (PDFXObject)iter.next(); - p = p.append(" " + xobj.getName() + " " - + xobj.referencePDF() - + "\n"); + dict.put(xobj.getName().toString(), xobj); } - p = p.append(">>\n"); + put("XObject", dict); } if (!this.gstates.isEmpty()) { - p = p.append("/ExtGState <<\n"); + PDFDictionary dict = new PDFDictionary(this); for (Iterator iter = gstates.iterator(); iter.hasNext();) { PDFGState gs = (PDFGState)iter.next(); - p = p.append(" /" + gs.getName() + " " - + gs.referencePDF() - + "\n"); + dict.put(gs.getName(), gs); } - p = p.append(">>\n"); + put("ExtGState", dict); } if (!this.colorSpaces.isEmpty()) { - p = p.append("/ColorSpace <<\n"); + PDFDictionary dict = new PDFDictionary(this); for (Iterator iter = colorSpaces.values().iterator(); iter.hasNext();) { - PDFICCBasedColorSpace colorSpace = (PDFICCBasedColorSpace)iter.next(); - p = p.append(" /" + colorSpace.getName() + " " - + colorSpace.referencePDF() - + "\n"); + PDFColorSpace colorSpace = (PDFColorSpace)iter.next(); + dict.put(colorSpace.getName(), colorSpace); } - p = p.append(">>\n"); + put("ColorSpace", dict); } - - p = p.append(">>\nendobj\n"); - - return p.toString(); } } diff --git a/src/java/org/apache/fop/pdf/PDFSeparationColorSpace.java b/src/java/org/apache/fop/pdf/PDFSeparationColorSpace.java new file mode 100644 index 000000000..d364eccbc --- /dev/null +++ b/src/java/org/apache/fop/pdf/PDFSeparationColorSpace.java @@ -0,0 +1,88 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.pdf; + +/** + * This class represents a "Separation" color space. It is used in FOP to map named colors. + */ +public class PDFSeparationColorSpace extends PDFArray implements PDFColorSpace { + + /** + * Creates a new "Separation" color space. + * @param colorName the name of the colorant + * @param tintFunction the tint function used as fallback + */ + public PDFSeparationColorSpace(String colorName, PDFFunction tintFunction) { + super(); + add(new PDFName("Separation")); + add(new PDFName(colorName)); + add(new PDFName("DeviceRGB")); + add(new PDFReference(tintFunction)); + } + + /** {@inheritDoc} */ + public String getName() { + //return "CS" + this.getObjectNumber(); + return getColorName().toString(); + } + + /** + * Returns the name of the colorant. + * @return the name of the colorant + */ + public PDFName getColorName() { + return (PDFName)get(1); + } + + /** + * Returns a reference to the tint function that is used as a fallback if the colorant is + * not available. + * @return a reference to the tint function + */ + public PDFReference getTintFunction() { + return (PDFReference)get(2); + } + + /** {@inheritDoc} */ + public int getNumComponents() { + return 1; + } + + /** {@inheritDoc} */ + public boolean isCMYKColorSpace() { + return false; + } + + /** {@inheritDoc} */ + public boolean isDeviceColorSpace() { + return false; + } + + /** {@inheritDoc} */ + public boolean isGrayColorSpace() { + return false; + } + + /** {@inheritDoc} */ + public boolean isRGBColorSpace() { + return false; + } + +} diff --git a/src/java/org/apache/fop/render/intermediate/IFSerializer.java b/src/java/org/apache/fop/render/intermediate/IFSerializer.java index edeef9766..3e178d3cb 100644 --- a/src/java/org/apache/fop/render/intermediate/IFSerializer.java +++ b/src/java/org/apache/fop/render/intermediate/IFSerializer.java @@ -33,6 +33,7 @@ import java.util.Map; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.NodeList; + import org.xml.sax.SAXException; import org.xml.sax.helpers.AttributesImpl; @@ -75,6 +76,7 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler } /** {@inheritDoc} */ + @Override protected String getMainNamespace() { return NAMESPACE; } @@ -101,6 +103,7 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler } /** {@inheritDoc} */ + @Override public IFDocumentNavigationHandler getDocumentNavigationHandler() { return this; } @@ -146,6 +149,7 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler } /** {@inheritDoc} */ + @Override public void startDocument() throws IFException { super.startDocument(); try { @@ -161,6 +165,7 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler } /** {@inheritDoc} */ + @Override public void startDocumentHeader() throws IFException { try { handler.startElement(EL_HEADER); @@ -170,6 +175,7 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler } /** {@inheritDoc} */ + @Override public void endDocumentHeader() throws IFException { try { handler.endElement(EL_HEADER); @@ -179,6 +185,7 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler } /** {@inheritDoc} */ + @Override public void startDocumentTrailer() throws IFException { try { handler.startElement(EL_TRAILER); @@ -188,6 +195,7 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler } /** {@inheritDoc} */ + @Override public void endDocumentTrailer() throws IFException { try { handler.endElement(EL_TRAILER); @@ -264,6 +272,7 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler } /** {@inheritDoc} */ + @Override public void startPageHeader() throws IFException { try { handler.startElement(EL_PAGE_HEADER); @@ -273,6 +282,7 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler } /** {@inheritDoc} */ + @Override public void endPageHeader() throws IFException { try { handler.endElement(EL_PAGE_HEADER); @@ -303,6 +313,7 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler } /** {@inheritDoc} */ + @Override public void startPageTrailer() throws IFException { try { handler.startElement(EL_PAGE_TRAILER); @@ -312,6 +323,7 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler } /** {@inheritDoc} */ + @Override public void endPageTrailer() throws IFException { try { commitNavigation(); @@ -604,7 +616,8 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler } } if (color != null) { - changed = !color.equals(state.getTextColor()); + changed = !org.apache.xmlgraphics.java2d.color.ColorUtil.isSameColor( + color, state.getTextColor()); if (changed) { state.setTextColor(color); addAttribute(atts, "color", toString(color)); diff --git a/src/java/org/apache/fop/render/intermediate/IFState.java b/src/java/org/apache/fop/render/intermediate/IFState.java index c13382192..4d8325d3e 100644 --- a/src/java/org/apache/fop/render/intermediate/IFState.java +++ b/src/java/org/apache/fop/render/intermediate/IFState.java @@ -21,6 +21,8 @@ package org.apache.fop.render.intermediate; import java.awt.Color; +import org.apache.xmlgraphics.java2d.color.ColorUtil; + /** a state class for intermediate format data */ public final class IFState { @@ -184,7 +186,7 @@ public final class IFState { * @param color the new text color */ public void setTextColor(Color color) { - if (!color.equals(this.textColor)) { + if (!ColorUtil.isSameColor(color, this.textColor)) { this.fontChanged = true; } this.textColor = color; diff --git a/src/java/org/apache/fop/render/java2d/Java2DGraphicsState.java b/src/java/org/apache/fop/render/java2d/Java2DGraphicsState.java index a40ee1d5c..b0c003b91 100644 --- a/src/java/org/apache/fop/render/java2d/Java2DGraphicsState.java +++ b/src/java/org/apache/fop/render/java2d/Java2DGraphicsState.java @@ -28,6 +28,8 @@ import java.awt.geom.AffineTransform; import java.awt.geom.Area; import java.awt.geom.GeneralPath; +import org.apache.xmlgraphics.java2d.color.ColorUtil; + import org.apache.fop.fo.Constants; import org.apache.fop.fonts.FontInfo; @@ -103,7 +105,7 @@ public class Java2DGraphicsState { * @return true if the background color has changed */ public boolean updateColor(Color col) { - if (!col.equals(getGraph().getColor())) { + if (!ColorUtil.isSameColor(col, getGraph().getColor())) { getGraph().setColor(col); return true; } else { @@ -217,12 +219,18 @@ public class Java2DGraphicsState { * @return true if the new paint changes the current paint */ public boolean updatePaint(Paint p) { - if (getGraph().getPaint() == null) { + Paint currentPaint = getGraph().getPaint(); + if (currentPaint == null) { if (p != null) { getGraph().setPaint(p); return true; } - } else if (!p.equals(getGraph().getPaint())) { + } else if (p instanceof Color && currentPaint instanceof Color) { + if (!ColorUtil.isSameColor((Color)p, (Color)currentPaint)) { + getGraph().setPaint(p); + return true; + } + } else if (!p.equals(currentPaint)) { getGraph().setPaint(p); return true; } @@ -271,6 +279,7 @@ public class Java2DGraphicsState { } /** {@inheritDoc} */ + @Override public String toString() { String s = "Java2DGraphicsState " + currentGraphics.toString() + ", Stroke (width: " + currentStrokeWidth + " style: " diff --git a/src/java/org/apache/fop/render/pdf/AbstractImageAdapter.java b/src/java/org/apache/fop/render/pdf/AbstractImageAdapter.java index 9e20886e0..d076e6ab9 100644 --- a/src/java/org/apache/fop/render/pdf/AbstractImageAdapter.java +++ b/src/java/org/apache/fop/render/pdf/AbstractImageAdapter.java @@ -35,6 +35,7 @@ import org.apache.fop.pdf.PDFDocument; import org.apache.fop.pdf.PDFICCBasedColorSpace; import org.apache.fop.pdf.PDFICCStream; import org.apache.fop.pdf.PDFImage; +import org.apache.fop.pdf.PDFName; import org.apache.fop.pdf.PDFReference; /** @@ -128,7 +129,8 @@ public abstract class AbstractImageAdapter implements PDFImage { } else { if (cs == null && desc.startsWith("sRGB")) { //It's the default sRGB profile which we mapped to DefaultRGB in PDFRenderer - cs = doc.getResources().getColorSpace("DefaultRGB"); + cs = (PDFICCBasedColorSpace)doc.getResources().getColorSpace( + new PDFName("DefaultRGB")); } if (cs == null) { // sRGB hasn't been set up for the PDF document diff --git a/src/java/org/apache/fop/render/pdf/PDFContentGenerator.java b/src/java/org/apache/fop/render/pdf/PDFContentGenerator.java index 074faa5d3..7cc9b7003 100644 --- a/src/java/org/apache/fop/render/pdf/PDFContentGenerator.java +++ b/src/java/org/apache/fop/render/pdf/PDFContentGenerator.java @@ -25,7 +25,7 @@ import java.awt.geom.AffineTransform; import java.io.IOException; import java.io.OutputStream; -import org.apache.fop.pdf.PDFColor; +import org.apache.fop.pdf.PDFColorHandler; import org.apache.fop.pdf.PDFDocument; import org.apache.fop.pdf.PDFFilterList; import org.apache.fop.pdf.PDFNumber; @@ -51,6 +51,8 @@ public class PDFContentGenerator { /** the current stream to add PDF commands to */ private PDFStream currentStream; + private PDFColorHandler colorHandler; + /** drawing state */ protected PDFPaintingState currentState = null; /** Text generation utility holding the current font status */ @@ -80,6 +82,7 @@ public class PDFContentGenerator { }; this.currentState = new PDFPaintingState(); + this.colorHandler = new PDFColorHandler(document.getResources()); } /** @@ -344,8 +347,9 @@ public class PDFContentGenerator { */ public void setColor(Color col, boolean fill, PDFStream stream) { assert stream != null; - PDFColor color = new PDFColor(this.document, col); - stream.add(color.getColorSpaceOut(fill)); + StringBuffer sb = new StringBuffer(); + setColor(col, fill, sb); + stream.add(sb.toString()); } /** @@ -367,8 +371,7 @@ public class PDFContentGenerator { */ protected void setColor(Color col, boolean fill, StringBuffer pdf) { if (pdf != null) { - PDFColor color = new PDFColor(this.document, col); - pdf.append(color.getColorSpaceOut(fill)); + colorHandler.establishColor(pdf, col, fill); } else { setColor(col, fill, this.currentStream); } diff --git a/src/java/org/apache/fop/render/xml/XMLRenderer.java b/src/java/org/apache/fop/render/xml/XMLRenderer.java index c283dbd98..85995e8b2 100644 --- a/src/java/org/apache/fop/render/xml/XMLRenderer.java +++ b/src/java/org/apache/fop/render/xml/XMLRenderer.java @@ -122,6 +122,7 @@ public class XMLRenderer extends AbstractXMLRenderer { /** * {@inheritDoc} */ + @Override public void setUserAgent(FOUserAgent agent) { super.setUserAgent(agent); @@ -143,6 +144,7 @@ public class XMLRenderer extends AbstractXMLRenderer { } /** {@inheritDoc} */ + @Override public void setupFontInfo(FontInfo inFontInfo) throws FOPException { if (mimic != null) { mimic.setupFontInfo(inFontInfo); @@ -214,7 +216,7 @@ public class XMLRenderer extends AbstractXMLRenderer { //TODO Remove the following line (makes changes in the test checks necessary) addAttribute(name, bkg.toString()); if (bkg.getColor() != null) { - addAttribute("bkg-color", bkg.getColor().toString()); + addAttribute("bkg-color", ColorUtil.colorToString(bkg.getColor())); } if (bkg.getURL() != null) { addAttribute("bkg-img", bkg.getURL()); @@ -277,6 +279,7 @@ public class XMLRenderer extends AbstractXMLRenderer { } /** {@inheritDoc} */ + @Override public void processOffDocumentItem(OffDocumentItem oDI) { if (oDI instanceof BookmarkData) { renderBookmarkTree((BookmarkData) oDI); @@ -298,6 +301,7 @@ public class XMLRenderer extends AbstractXMLRenderer { * Renders a BookmarkTree object * @param bookmarkRoot the BookmarkData object representing the top of the tree */ + @Override protected void renderBookmarkTree(BookmarkData bookmarkRoot) { if (bookmarkRoot.getWhenToProcess() == OffDocumentItem.END_OF_DOC) { endPageSequence(); @@ -346,6 +350,7 @@ public class XMLRenderer extends AbstractXMLRenderer { } /** {@inheritDoc} */ + @Override public void startRenderer(OutputStream outputStream) throws IOException { log.debug("Rendering areas to Area Tree XML"); @@ -377,6 +382,7 @@ public class XMLRenderer extends AbstractXMLRenderer { } /** {@inheritDoc} */ + @Override public void stopRenderer() throws IOException { endPageSequence(); endElement("areaTree"); @@ -392,6 +398,7 @@ public class XMLRenderer extends AbstractXMLRenderer { } /** {@inheritDoc} */ + @Override public void renderPage(PageViewport page) throws IOException, FOPException { atts.clear(); addAttribute("bounds", page.getViewArea()); @@ -416,6 +423,7 @@ public class XMLRenderer extends AbstractXMLRenderer { } /** {@inheritDoc} */ + @Override protected void handleExtensionAttachments(List attachments) { if (attachments != null && attachments.size() > 0) { startElement("extension-attachments"); @@ -438,6 +446,7 @@ public class XMLRenderer extends AbstractXMLRenderer { } /** {@inheritDoc} */ + @Override public void startPageSequence(PageSequence pageSequence) { handleDocumentExtensionAttachments(); endPageSequence(); // move this before handleDocumentExtensionAttachments() ? @@ -502,6 +511,7 @@ public class XMLRenderer extends AbstractXMLRenderer { /** * {@inheritDoc} */ + @Override protected void renderRegionViewport(RegionViewport port) { if (port != null) { atts.clear(); @@ -549,11 +559,13 @@ public class XMLRenderer extends AbstractXMLRenderer { } /** {@inheritDoc} */ + @Override protected void startVParea(CTM ctm, Rectangle2D clippingRect) { //only necessary for graphical output } /** {@inheritDoc} */ + @Override protected void endVParea() { //only necessary for graphical output } @@ -562,6 +574,7 @@ public class XMLRenderer extends AbstractXMLRenderer { * {@inheritDoc} * org.apache.fop.area.inline.InlineArea) */ + @Override protected void renderInlineAreaBackAndBorders(InlineArea area) { //only necessary for graphical output } @@ -569,6 +582,7 @@ public class XMLRenderer extends AbstractXMLRenderer { /** * {@inheritDoc} */ + @Override protected void renderBeforeFloat(BeforeFloat bf) { startElement("beforeFloat"); super.renderBeforeFloat(bf); @@ -578,6 +592,7 @@ public class XMLRenderer extends AbstractXMLRenderer { /** * {@inheritDoc} */ + @Override protected void renderFootnote(Footnote footnote) { atts.clear(); addAttribute("top-offset", footnote.getTop()); @@ -589,6 +604,7 @@ public class XMLRenderer extends AbstractXMLRenderer { /** * {@inheritDoc} */ + @Override protected void renderMainReference(MainReference mr) { atts.clear(); addAreaAttributes(mr); @@ -610,7 +626,7 @@ public class XMLRenderer extends AbstractXMLRenderer { addTraitAttributes(span); startElement("span", atts); for (int c = 0; c < span.getColumnCount(); c++) { - NormalFlow flow = (NormalFlow) span.getNormalFlow(c); + NormalFlow flow = span.getNormalFlow(c); renderFlow(flow); } @@ -622,6 +638,7 @@ public class XMLRenderer extends AbstractXMLRenderer { /** * {@inheritDoc} */ + @Override protected void renderFlow(NormalFlow flow) { // the normal flow reference area contains stacked blocks atts.clear(); @@ -633,6 +650,7 @@ public class XMLRenderer extends AbstractXMLRenderer { } /** {@inheritDoc} */ + @Override protected void renderReferenceArea(Block block) { handleBlockTraits(block); @@ -643,6 +661,7 @@ public class XMLRenderer extends AbstractXMLRenderer { } /** {@inheritDoc} */ + @Override protected void renderBlock(Block block) { atts.clear(); addAreaAttributes(block); @@ -691,6 +710,7 @@ public class XMLRenderer extends AbstractXMLRenderer { /** * {@inheritDoc} */ + @Override protected void renderLineArea(LineArea line) { atts.clear(); addAreaAttributes(line); @@ -703,6 +723,7 @@ public class XMLRenderer extends AbstractXMLRenderer { /** * {@inheritDoc} */ + @Override protected void renderInlineArea(InlineArea inlineArea) { atts.clear(); if (inlineArea.getClass() == InlineArea.class) { @@ -721,6 +742,7 @@ public class XMLRenderer extends AbstractXMLRenderer { /** * {@inheritDoc} */ + @Override protected void renderViewport(Viewport viewport) { atts.clear(); addAreaAttributes(viewport); @@ -738,6 +760,7 @@ public class XMLRenderer extends AbstractXMLRenderer { /** * {@inheritDoc} */ + @Override public void renderImage(Image image, Rectangle2D pos) { atts.clear(); addAreaAttributes(image); @@ -751,6 +774,7 @@ public class XMLRenderer extends AbstractXMLRenderer { /** * {@inheritDoc} */ + @Override public void renderContainer(Container cont) { startElement("container"); super.renderContainer(cont); @@ -763,6 +787,7 @@ public class XMLRenderer extends AbstractXMLRenderer { * @param pos the position of the foreign object * @see org.apache.fop.render.AbstractRenderer#renderForeignObject(ForeignObject, Rectangle2D) */ + @Override public void renderForeignObject(ForeignObject fo, Rectangle2D pos) { atts.clear(); addAreaAttributes(fo); @@ -779,6 +804,7 @@ public class XMLRenderer extends AbstractXMLRenderer { /** * {@inheritDoc} */ + @Override protected void renderInlineSpace(Space space) { atts.clear(); addAreaAttributes(space); @@ -791,6 +817,7 @@ public class XMLRenderer extends AbstractXMLRenderer { /** * {@inheritDoc} */ + @Override protected void renderText(TextArea text) { atts.clear(); if (text.getTextWordSpaceAdjust() != 0) { @@ -811,6 +838,7 @@ public class XMLRenderer extends AbstractXMLRenderer { /** * {@inheritDoc} */ + @Override protected void renderWord(WordArea word) { atts.clear(); addAttribute("offset", word.getOffset()); @@ -838,6 +866,7 @@ public class XMLRenderer extends AbstractXMLRenderer { /** * {@inheritDoc} */ + @Override protected void renderSpace(SpaceArea space) { atts.clear(); addAttribute("offset", space.getOffset()); @@ -853,6 +882,7 @@ public class XMLRenderer extends AbstractXMLRenderer { /** * {@inheritDoc} */ + @Override protected void renderInlineParent(InlineParent ip) { atts.clear(); addAreaAttributes(ip); @@ -866,6 +896,7 @@ public class XMLRenderer extends AbstractXMLRenderer { /** * {@inheritDoc} */ + @Override protected void renderInlineBlockParent(InlineBlockParent ibp) { atts.clear(); addAreaAttributes(ibp); @@ -879,6 +910,7 @@ public class XMLRenderer extends AbstractXMLRenderer { /** * {@inheritDoc} */ + @Override protected void renderLeader(Leader area) { atts.clear(); addAreaAttributes(area); diff --git a/src/java/org/apache/fop/svg/PDFBridgeContext.java b/src/java/org/apache/fop/svg/PDFBridgeContext.java index a38465ed4..4a6e6e275 100644 --- a/src/java/org/apache/fop/svg/PDFBridgeContext.java +++ b/src/java/org/apache/fop/svg/PDFBridgeContext.java @@ -25,6 +25,7 @@ import org.apache.batik.bridge.BridgeContext; import org.apache.batik.bridge.DocumentLoader; import org.apache.batik.bridge.SVGTextElementBridge; import org.apache.batik.bridge.UserAgent; +import org.apache.batik.dom.svg.SVGOMDocument; import org.apache.batik.gvt.TextPainter; import org.apache.xmlgraphics.image.loader.ImageManager; @@ -86,6 +87,7 @@ public class PDFBridgeContext extends AbstractFOPBridgeContext { } /** {@inheritDoc} */ + @Override public void registerSVGBridges() { super.registerSVGBridges(); @@ -123,10 +125,17 @@ public class PDFBridgeContext extends AbstractFOPBridgeContext { putBridge(new PDFImageElementBridge()); } - // Make sure any 'sub bridge contexts' also have our bridges. - //TODO There's no matching method in the super-class here - /** @return the new bridge context */ + /** {@inheritDoc} */ + @Override public BridgeContext createBridgeContext() { + //Retained for pre-Batik-1.7 compatibility + return createBridgeContext(null); + } + + /** {@inheritDoc} */ + @Override + public BridgeContext createBridgeContext(SVGOMDocument doc) { + // Make sure any 'sub bridge contexts' also have our bridges. return new PDFBridgeContext(getUserAgent(), getDocumentLoader(), fontInfo, getImageManager(), diff --git a/src/java/org/apache/fop/svg/PDFDocumentGraphics2D.java b/src/java/org/apache/fop/svg/PDFDocumentGraphics2D.java index db1d9e2de..829d8972d 100644 --- a/src/java/org/apache/fop/svg/PDFDocumentGraphics2D.java +++ b/src/java/org/apache/fop/svg/PDFDocumentGraphics2D.java @@ -36,7 +36,7 @@ import org.apache.fop.Version; import org.apache.fop.fonts.FontInfo; import org.apache.fop.fonts.FontSetup; import org.apache.fop.pdf.PDFAnnotList; -import org.apache.fop.pdf.PDFColor; +import org.apache.fop.pdf.PDFColorHandler; import org.apache.fop.pdf.PDFDocument; import org.apache.fop.pdf.PDFFilterList; import org.apache.fop.pdf.PDFNumber; @@ -101,6 +101,7 @@ public class PDFDocumentGraphics2D extends PDFGraphics2D { this.pdfDoc = new PDFDocument("Apache FOP Version " + Version.getVersion() + ": PDFDocumentGraphics2D"); this.pdfContext = new PDFContext(); + this.colorHandler = new PDFColorHandler(this.pdfDoc.getResources()); } /** @@ -232,15 +233,15 @@ public class PDFDocumentGraphics2D extends PDFGraphics2D { * @param col the background colour to fill */ public void setBackgroundColor(Color col) { - Color c = col; - PDFColor currentColour = new PDFColor(c.getRed(), c.getGreen(), c.getBlue()); - currentStream.write("q\n"); - currentStream.write(currentColour.getColorSpaceOut(true)); + StringBuffer sb = new StringBuffer(); + sb.append("q\n"); + this.colorHandler.establishColor(sb, col, true); - currentStream.write("0 0 " + width + " " + height + " re\n"); + sb.append("0 0 ").append(width).append(" ").append(height).append(" re\n"); - currentStream.write("f\n"); - currentStream.write("Q\n"); + sb.append("f\n"); + sb.append("Q\n"); + currentStream.write(sb.toString()); } /** diff --git a/src/java/org/apache/fop/svg/PDFGraphics2D.java b/src/java/org/apache/fop/svg/PDFGraphics2D.java index 7c8d4a6b9..e93914258 100644 --- a/src/java/org/apache/fop/svg/PDFGraphics2D.java +++ b/src/java/org/apache/fop/svg/PDFGraphics2D.java @@ -75,6 +75,7 @@ import org.apache.fop.fonts.FontSetup; import org.apache.fop.pdf.BitmapImage; import org.apache.fop.pdf.PDFAnnotList; import org.apache.fop.pdf.PDFColor; +import org.apache.fop.pdf.PDFColorHandler; import org.apache.fop.pdf.PDFConformanceException; import org.apache.fop.pdf.PDFDeviceColorSpace; import org.apache.fop.pdf.PDFDocument; @@ -92,7 +93,6 @@ import org.apache.fop.pdf.PDFXObject; import org.apache.fop.render.pdf.ImageRawCCITTFaxAdapter; import org.apache.fop.render.pdf.ImageRawJPEGAdapter; import org.apache.fop.render.pdf.ImageRenderedAdapter; -import org.apache.fop.util.ColorExt; /** * PDF Graphics 2D. @@ -132,6 +132,9 @@ public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHand */ protected PDFPaintingState paintingState; + /** the PDF color handler */ + protected PDFColorHandler colorHandler; + /** * The PDF graphics state level that this svg is being drawn into. */ @@ -194,6 +197,7 @@ public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHand PDFResourceContext page, String pref, String font, float size) { this(textAsShapes); pdfDoc = doc; + this.colorHandler = new PDFColorHandler(doc.getResources()); resourceContext = page; currentFontName = font; currentFontSize = size; @@ -220,6 +224,7 @@ public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHand public PDFGraphics2D(PDFGraphics2D g) { super(g); this.pdfDoc = g.pdfDoc; + this.colorHandler = g.colorHandler; this.resourceContext = g.resourceContext; this.currentFontName = g.currentFontName; this.currentFontSize = g.currentFontSize; @@ -732,39 +737,20 @@ public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHand protected void applyColor(Color col, boolean fill) { preparePainting(); - Color c = col; - 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()); - currentStream.write(currentColour.getColorSpaceOut(fill)); - } else if (c.getColorSpace().getType() - == ColorSpace.TYPE_CMYK) { - if (pdfDoc.getProfile().getPDFAMode().isPDFA1LevelB()) { - //See PDF/A-1, ISO 19005:1:2005(E), 6.2.3.3 - //FOP is currently restricted to DeviceRGB if PDF/A-1 is active. - throw new PDFConformanceException( - "PDF/A-1 does not allow mixing DeviceRGB and DeviceCMYK."); - } - PDFColor currentColour = new PDFColor(c); - currentStream.write(currentColour.getColorSpaceOut(fill)); - } else if (c.getColorSpace().getType() - == ColorSpace.TYPE_2CLR) { - // used for black/magenta - float[] cComps = c.getColorComponents(new float[1]); - double[] blackMagenta = new double[1]; - for (int i = 0; i < 1; i++) { - blackMagenta[i] = cComps[i]; - } - //PDFColor currentColour = new PDFColor(blackMagenta[0], blackMagenta[1]); - //currentStream.write(currentColour.getColorSpaceOut(fill)); - } else { - throw new UnsupportedOperationException( - "Color Space not supported by PDFGraphics2D: " + c.getColorSpace()); + //TODO Handle this in PDFColorHandler by automatically converting the color. + //This won't work properly anyway after the redesign of ColorExt + if (col.getColorSpace().getType() == ColorSpace.TYPE_CMYK) { + if (pdfDoc.getProfile().getPDFAMode().isPDFA1LevelB()) { + //See PDF/A-1, ISO 19005:1:2005(E), 6.2.3.3 + //FOP is currently restricted to DeviceRGB if PDF/A-1 is active. + throw new PDFConformanceException( + "PDF/A-1 does not allow mixing DeviceRGB and DeviceCMYK."); + } } + + StringBuffer sb = new StringBuffer(); + colorHandler.establishColor(sb, col, fill); + currentStream.write(sb.toString()); } /** @@ -858,14 +844,15 @@ public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHand return false; // PDF can't do alpha } - PDFColor color1 = new PDFColor(c1.getRed(), c1.getGreen(), - c1.getBlue()); - someColors.add(color1); + //PDFColor color1 = new PDFColor(c1.getRed(), c1.getGreen(), + // c1.getBlue()); + someColors.add(c1); if (count > 0 && count < cols.length - 1) { theBounds.add(new Double(fractions[count])); } } + //Gradients are currently restricted to sRGB PDFDeviceColorSpace aColorSpace; aColorSpace = new PDFDeviceColorSpace(PDFDeviceColorSpace.DEVICE_RGB); PDFPattern myPat = pdfDoc.getFactory().makeGradient( @@ -933,8 +920,7 @@ public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHand return false; // PDF can't do alpha } - someColors.add(new PDFColor(cc.getRed(), cc.getGreen(), - cc.getBlue())); + someColors.add(cc); } float[] fractions = rgp.getFractions(); diff --git a/src/java/org/apache/fop/traits/BorderProps.java b/src/java/org/apache/fop/traits/BorderProps.java index a76c6e8a3..ae7a9a434 100644 --- a/src/java/org/apache/fop/traits/BorderProps.java +++ b/src/java/org/apache/fop/traits/BorderProps.java @@ -98,11 +98,13 @@ public class BorderProps implements Serializable { } /** {@inheritDoc} */ + @Override public int hashCode() { return toString().hashCode(); } /** {@inheritDoc} */ + @Override public boolean equals(Object obj) { if (obj == null) { return false; @@ -112,7 +114,8 @@ public class BorderProps implements Serializable { if (obj instanceof BorderProps) { BorderProps other = (BorderProps)obj; return (style == other.style) - && color.equals(other.color) + && org.apache.xmlgraphics.java2d.color.ColorUtil.isSameColor( + color, other.color) && width == other.width && mode == other.mode; } @@ -163,6 +166,7 @@ public class BorderProps implements Serializable { } /** {@inheritDoc} */ + @Override public String toString() { StringBuffer sbuf = new StringBuffer(); sbuf.append('('); diff --git a/src/java/org/apache/fop/util/AbstractPaintingState.java b/src/java/org/apache/fop/util/AbstractPaintingState.java index 5944b546c..96c3633e6 100644 --- a/src/java/org/apache/fop/util/AbstractPaintingState.java +++ b/src/java/org/apache/fop/util/AbstractPaintingState.java @@ -75,7 +75,8 @@ public abstract class AbstractPaintingState implements Cloneable, Serializable { * @return true if the color has changed */ public boolean setColor(Color col) { - if (!col.equals(getData().color)) { + Color other = getData().color; + if (!org.apache.xmlgraphics.java2d.color.ColorUtil.isSameColor(col, other)) { getData().color = col; return true; } @@ -114,7 +115,8 @@ public abstract class AbstractPaintingState implements Cloneable, Serializable { * @return true if the color has changed */ public boolean setBackColor(Color col) { - if (!col.equals(getData().backColor)) { + Color other = getData().backColor; + if (!org.apache.xmlgraphics.java2d.color.ColorUtil.isSameColor(col, other)) { getData().backColor = col; return true; } @@ -364,6 +366,7 @@ public abstract class AbstractPaintingState implements Cloneable, Serializable { } /** {@inheritDoc} */ + @Override public Object clone() { AbstractPaintingState state = instantiate(); state.stateStack = new StateStack(this.stateStack); @@ -372,6 +375,7 @@ public abstract class AbstractPaintingState implements Cloneable, Serializable { } /** {@inheritDoc} */ + @Override public String toString() { return ", stateStack=" + stateStack + ", currentData=" + data; @@ -506,6 +510,7 @@ public abstract class AbstractPaintingState implements Cloneable, Serializable { } /** {@inheritDoc} */ + @Override public Object clone() { AbstractData data = instantiate(); data.color = this.color; @@ -522,6 +527,7 @@ public abstract class AbstractPaintingState implements Cloneable, Serializable { } /** {@inheritDoc} */ + @Override public String toString() { return "color=" + color + ", backColor=" + backColor diff --git a/src/java/org/apache/fop/util/ColorExt.java b/src/java/org/apache/fop/util/ColorExt.java index 30f6e9fc3..9d4c9b9d3 100644 --- a/src/java/org/apache/fop/util/ColorExt.java +++ b/src/java/org/apache/fop/util/ColorExt.java @@ -1,3 +1,4 @@ +<<<<<<< .working /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with @@ -248,3 +249,261 @@ public final class ColorExt extends Color { } } +======= +/* + * 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 java.util.Arrays; + +import org.apache.xmlgraphics.java2d.color.ColorWithAlternatives; + +/** + * 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. + * @deprecated Replaced by {@link ColorWithAlternatives} + */ +@Deprecated +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( // CSOK: ParameterNumber + float red, float green, + float blue, float opacity, String profileName, String profileHref, + ColorSpace profileCS, float[] colorValues) { + //TODO this method is not referenced by FOP, can it be deleted? + 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; + + } + + /** {@inheritDoc} */ + @Override + public int hashCode() { + //implementation from the superclass should be good enough for our purposes + return super.hashCode(); + } + + /** {@inheritDoc} */ + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!super.equals(obj)) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + ColorExt other = (ColorExt)obj; + //TODO maybe use super.getColorComponents() instead + if (!Arrays.equals(colorValues, other.colorValues)) { + return false; + } + if (iccProfileName == null) { + if (other.iccProfileName != null) { + return false; + } + } else if (!iccProfileName.equals(other.iccProfileName)) { + return false; + } + if (iccProfileSrc == null) { + if (other.iccProfileSrc != null) { + return false; + } + } else if (!iccProfileSrc.equals(other.iccProfileSrc)) { + return false; + } + if (Float.floatToIntBits(rgbReplacementBlue) + != Float.floatToIntBits(other.rgbReplacementBlue)) { + return false; + } + if (Float.floatToIntBits(rgbReplacementGreen) + != Float.floatToIntBits(other.rgbReplacementGreen)) { + return false; + } + if (Float.floatToIntBits(rgbReplacementRed) + != Float.floatToIntBits(other.rgbReplacementRed)) { + return false; + } + return true; + } + + /** + * 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() { + //TODO this method is probably unnecessary due to super.cs and getColorSpace() + return this.colorSpace; + } + + /** + * Returns the original color values. + * @return the original color values + */ + public float[] getOriginalColorComponents() { + //TODO this method is probably unnecessary due to super.fvalue and getColorComponents() + float[] copy = new float[this.colorValues.length]; + System.arraycopy(this.colorValues, 0, copy, 0, copy.length); + return copy; + } + + /** + * 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 + ","); + if (this.iccProfileSrc != null) { + 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(); + } + +} +>>>>>>> .merge-right.r1069429 diff --git a/src/java/org/apache/fop/util/ColorSpaceCache.java b/src/java/org/apache/fop/util/ColorSpaceCache.java index 63d8746f7..63db937a0 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; @@ -33,6 +32,9 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.xmlgraphics.java2d.color.profile.ColorProfileUtil; +import org.apache.xmlgraphics.java2d.color.ICCColorSpaceWithIntent; +import org.apache.xmlgraphics.java2d.color.RenderingIntent; + /** * Map with cached ICC based ColorSpace objects. */ @@ -61,13 +63,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 * @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, + RenderingIntent 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 @@ -88,7 +94,8 @@ public class ColorSpaceCache { // iccProfile = ICC_Profile.getInstance(iccProfileSrc); } if (iccProfile != null) { - colorSpace = new ICC_ColorSpace(iccProfile); + colorSpace = new ICCColorSpaceWithIntent(iccProfile, renderingIntent, + profileName, iccProfileSrc); } } catch (Exception e) { // Ignore exception - will be logged a bit further down @@ -97,14 +104,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 = colorSpaceMap.get(base + iccProfileSrc); + 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 674079369..e1454a545 100644 --- a/src/java/org/apache/fop/util/ColorUtil.java +++ b/src/java/org/apache/fop/util/ColorUtil.java @@ -21,14 +21,23 @@ package org.apache.fop.util; import java.awt.Color; 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; 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.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.NamedColorSpace; +import org.apache.xmlgraphics.java2d.color.RenderingIntent; +import org.apache.xmlgraphics.java2d.color.profile.NamedColorProfile; +import org.apache.xmlgraphics.java2d.color.profile.NamedColorProfileParser; import org.apache.fop.apps.FOUserAgent; import org.apache.fop.fo.expr.PropertyException; @@ -41,15 +50,22 @@ import org.apache.fop.fo.expr.PropertyException; */ public final class ColorUtil { + //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"; + /** The name for the Separation pseudo-profile used for spot colors */ + public static final String SEPARATION_PSEUDO_PROFILE = "#Separation"; + /** - * - * keeps all the predefined and parsed colors. + * 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. + * <p> + * Important: The use of this color map assumes that all Color instances are immutable! */ private static Map<String, Color> colorMap = null; @@ -98,7 +114,7 @@ public final class ColorUtil { return null; } - Color parsedColor = (Color) colorMap.get(value.toLowerCase()); + Color parsedColor = colorMap.get(value.toLowerCase()); if (parsedColor == null) { if (value.startsWith("#")) { @@ -114,6 +130,10 @@ public final class ColorUtil { parsedColor = parseAsSystemColor(value); } else if (value.startsWith("fop-rgb-icc")) { parsedColor = parseAsFopRgbIcc(foUserAgent, value); + } else if (value.startsWith("fop-rgb-named-color")) { + parsedColor = parseAsFopRgbNamedColor(foUserAgent, value); + } else if (value.startsWith("cie-lab-color")) { + parsedColor = parseAsCIELabColor(foUserAgent, value); } else if (value.startsWith("cmyk")) { parsedColor = parseAsCMYK(value); } @@ -125,9 +145,6 @@ public final class ColorUtil { colorMap.put(value, parsedColor); } - // 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; } @@ -150,7 +167,7 @@ public final class ColorUtil { throw new PropertyException("Unknown color format: " + value + ". Must be system-color(x)"); } - return (Color) colorMap.get(value); + return colorMap.get(value); } /** @@ -218,34 +235,14 @@ public final class ColorUtil { throw new PropertyException( "Invalid number of arguments: rgb(" + value + ")"); } - float red = 0.0f, green = 0.0f, blue = 0.0f; - String str = args[0].trim(); - if (str.endsWith("%")) { - red = Float.parseFloat(str.substring(0, - str.length() - 1)) / 100f; - } else { - red = Float.parseFloat(str) / 255f; - } - str = args[1].trim(); - if (str.endsWith("%")) { - green = Float.parseFloat(str.substring(0, - str.length() - 1)) / 100f; - } else { - green = Float.parseFloat(str) / 255f; - } - str = args[2].trim(); - if (str.endsWith("%")) { - blue = Float.parseFloat(str.substring(0, - str.length() - 1)) / 100f; - } else { - blue = Float.parseFloat(str) / 255f; - } - if ((red < 0.0 || red > 1.0) - || (green < 0.0 || green > 1.0) - || (blue < 0.0 || blue > 1.0)) { - throw new PropertyException("Color values out of range"); - } - parsedColor = new Color(red, green, blue); + float red = parseComponent255(args[0], value); + float green = parseComponent255(args[1], value); + float blue = parseComponent255(args[2], value); + //Convert to ints to synchronize the behaviour with toRGBFunctionCall() + int r = (int)(red * 255 + 0.5); + int g = (int)(green * 255 + 0.5); + int b = (int)(blue * 255 + 0.5); + parsedColor = new Color(r, g, b); } catch (PropertyException pe) { //simply re-throw throw pe; @@ -260,8 +257,50 @@ public final class ColorUtil { return parsedColor; } + private static float parseComponent255(String str, String function) + throws PropertyException { + float component; + str = str.trim(); + if (str.endsWith("%")) { + component = Float.parseFloat(str.substring(0, + str.length() - 1)) / 100f; + } else { + component = Float.parseFloat(str) / 255f; + } + if ((component < 0.0 || component > 1.0)) { + throw new PropertyException("Color value out of range for " + function + ": " + + str + ". Valid range: [0..255] or [0%..100%]"); + } + return component; + } + + private static float parseComponent1(String argument, String function) + throws PropertyException { + return parseComponent(argument, 0f, 1f, function); + } + + private static float parseComponent(String argument, float min, float max, String function) + throws PropertyException { + float component = Float.parseFloat(argument.trim()); + if ((component < min || component > max)) { + throw new PropertyException("Color value out of range for " + function + ": " + + argument + ". Valid range: [" + min + ".." + max + "]"); + } + return component; + } + + 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 Color(red, green, blue); + return sRGB; + } + /** - * parse a color given in the #.... format. + * Parse a color given in the #.... format. * * @param value * the complete line @@ -320,6 +359,10 @@ public final class ColorUtil { if (args.length < 5) { throw new PropertyException("Too few arguments for rgb-icc() function"); } + + //Set up fallback sRGB value + Color sRGB = parseFallback(args, value); + /* Get and verify ICC profile name */ String iccProfileName = args[3].trim(); if (iccProfileName == null || "".equals(iccProfileName)) { @@ -330,6 +373,9 @@ public final class ColorUtil { if (isPseudoProfile(iccProfileName)) { if (CMYK_PSEUDO_PROFILE.equalsIgnoreCase(iccProfileName)) { colorSpace = ColorSpaces.getDeviceCMYKColorSpace(); + } else if (SEPARATION_PSEUDO_PROFILE.equalsIgnoreCase(iccProfileName)) { + colorSpace = new NamedColorSpace(args[5], sRGB, + SEPARATION_PSEUDO_PROFILE, null); } else { assert false : "Incomplete implementation"; } @@ -339,47 +385,51 @@ public final class ColorUtil { if (iccProfileSrc == null || "".equals(iccProfileSrc)) { throw new PropertyException("ICC profile source missing"); } - if (iccProfileSrc.startsWith("\"") || iccProfileSrc.startsWith("'")) { - iccProfileSrc = iccProfileSrc.substring(1); - } - if (iccProfileSrc.endsWith("\"") || iccProfileSrc.endsWith("'")) { - iccProfileSrc = iccProfileSrc.substring(0, iccProfileSrc.length() - 1); - } + iccProfileSrc = unescapeString(iccProfileSrc); } /* ICC profile arguments */ - float[] iccComponents = new float[args.length - 5]; - for (int ix = 4; ++ix < args.length;) { - iccComponents[ix - 5] = Float.parseFloat(args[ix].trim()); + int componentStart = 4; + if (colorSpace instanceof NamedColorSpace) { + componentStart++; } - - float red = 0, green = 0, blue = 0; - red = Float.parseFloat(args[0].trim()); - green = Float.parseFloat(args[1].trim()); - blue = Float.parseFloat(args[2].trim()); - /* Verify rgb replacement arguments */ - if ((red < 0 || red > 1) - || (green < 0 || green > 1) - || (blue < 0 || blue > 1)) { - throw new PropertyException("Color values out of range. " - + "Fallback RGB arguments to fop-rgb-icc() must be [0..1]"); + float[] iccComponents = new float[args.length - componentStart - 1]; + for (int ix = componentStart; ++ix < args.length;) { + iccComponents[ix - componentStart - 1] = Float.parseFloat(args[ix].trim()); + } + if (colorSpace instanceof NamedColorSpace && iccComponents.length == 0) { + iccComponents = new float[] {1.0f}; //full tint if not specified } /* 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; + RenderingIntent renderingIntent = RenderingIntent.AUTO; + //TODO connect to fo:color-profile/@rendering-intent + colorSpace = foUserAgent.getFactory().getColorSpaceCache().get( + iccProfileName, + foUserAgent.getBaseURL(), iccProfileSrc, + renderingIntent); } 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); + // 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 Color(colorSpace, iccComponents, 1.0f); + float[] rgbComps = sRGB.getRGBColorComponents(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 - + "' not found. Using rgb replacement values."); - parsedColor = new Color(Math.round(red * 255), - Math.round(green * 255), Math.round(blue * 255)); + + "' not found. Using sRGB replacement values."); + parsedColor = sRGB; } } catch (PropertyException pe) { //simply re-throw @@ -396,6 +446,161 @@ public final class ColorUtil { } /** + * Parse a color specified using the fop-rgb-named-color() function. + * + * @param value the function call + * @return a color if possible + * @throws PropertyException if the format is wrong. + */ + private static Color parseAsFopRgbNamedColor(FOUserAgent foUserAgent, String value) + throws PropertyException { + Color parsedColor; + int poss = value.indexOf("("); + int pose = value.indexOf(")"); + if (poss != -1 && pose != -1) { + String[] args = value.substring(poss + 1, pose).split(","); + + try { + if (args.length != 6) { + throw new PropertyException("rgb-named-color() function must have 6 arguments"); + } + + //Set up fallback sRGB value + Color sRGB = parseFallback(args, value); + + /* Get and verify ICC profile name */ + String iccProfileName = args[3].trim(); + if (iccProfileName == null || "".equals(iccProfileName)) { + throw new PropertyException("ICC profile name missing"); + } + ICC_ColorSpace colorSpace = null; + String iccProfileSrc = null; + if (isPseudoProfile(iccProfileName)) { + throw new IllegalArgumentException( + "Pseudo-profiles are not allowed with fop-rgb-named-color()"); + } else { + /* Get and verify ICC profile source */ + iccProfileSrc = args[4].trim(); + if (iccProfileSrc == null || "".equals(iccProfileSrc)) { + throw new PropertyException("ICC profile source missing"); + } + iccProfileSrc = unescapeString(iccProfileSrc); + } + + // color name + String colorName = unescapeString(args[5].trim()); + + /* Ask FOP factory to get ColorSpace for the specified ICC profile source */ + if (foUserAgent != null && iccProfileSrc != null) { + RenderingIntent renderingIntent = RenderingIntent.AUTO; + //TODO connect to fo:color-profile/@rendering-intent + colorSpace = (ICC_ColorSpace)foUserAgent.getFactory().getColorSpaceCache().get( + 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, + iccProfileName, iccProfileSrc); + NamedColorSpace ncs = ncp.getNamedColor(colorName); + if (ncs != null) { + 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); + parsedColor = sRGB; + } + } else { + log.warn("ICC profile is no named color profile: " + iccProfileSrc); + parsedColor = sRGB; + } + } else { + // ICC profile could not be loaded - use rgb replacement values */ + log.warn("Color profile '" + iccProfileSrc + + "' not found. Using sRGB replacement values."); + parsedColor = sRGB; + } + } catch (PropertyException pe) { + //simply re-throw + throw pe; + } catch (Exception e) { + //wrap in a PropertyException + throw new PropertyException(e); + } + } else { + throw new PropertyException("Unknown color format: " + value + + ". Must be fop-rgb-named-color(r,g,b,NCNAME,src,color-name)"); + } + return parsedColor; + } + + /** + * Parse a color specified using the cie-lab-color() function. + * + * @param value the function call + * @return a color if possible + * @throws PropertyException if the format is wrong. + */ + private static Color parseAsCIELabColor(FOUserAgent foUserAgent, String value) + throws PropertyException { + Color parsedColor; + int poss = value.indexOf("("); + int pose = value.indexOf(")"); + if (poss != -1 && pose != -1) { + String[] args = value.substring(poss + 1, pose).split(","); + + try { + if (args.length != 6) { + throw new PropertyException("cie-lab-color() function must have 6 arguments"); + } + + //Set up fallback sRGB value + float red = parseComponent255(args[0], value); + float green = parseComponent255(args[1], value); + float blue = parseComponent255(args[2], value); + Color sRGB = new Color(red, green, blue); + + float l = parseComponent(args[3], 0f, 100f, value); + float a = parseComponent(args[4], -127f, 127f, value); + float b = parseComponent(args[5], -127f, 127f, value); + + //Assuming the XSL-FO spec uses the D50 white point + CIELabColorSpace cs = ColorSpaces.getCIELabColorSpaceD50(); + //use toColor() to have components normalized + Color labColor = cs.toColor(l, a, b, 1.0f); + //Convert to ColorWithFallback + parsedColor = new ColorWithFallback(labColor, sRGB); + + } catch (PropertyException pe) { + //simply re-throw + throw pe; + } catch (Exception e) { + //wrap in a PropertyException + throw new PropertyException(e); + } + } else { + throw new PropertyException("Unknown color format: " + value + + ". Must be cie-lab-color(r,g,b,Lightness,a-value,b-value)"); + } + return parsedColor; + } + + private static String unescapeString(String iccProfileSrc) { + if (iccProfileSrc.startsWith("\"") || iccProfileSrc.startsWith("'")) { + iccProfileSrc = iccProfileSrc.substring(1); + } + if (iccProfileSrc.endsWith("\"") || iccProfileSrc.endsWith("'")) { + iccProfileSrc = iccProfileSrc.substring(0, iccProfileSrc.length() - 1); + } + return iccProfileSrc; + } + + /** * Parse a color given with the cmyk() function. * * @param value @@ -416,48 +621,15 @@ public final class ColorUtil { throw new PropertyException( "Invalid number of arguments: cmyk(" + value + ")"); } - float cyan = 0.0f, magenta = 0.0f, yellow = 0.0f, black = 0.0f; - String str = args[0].trim(); - if (str.endsWith("%")) { - cyan = Float.parseFloat(str.substring(0, - str.length() - 1)) / 100.0f; - } else { - cyan = Float.parseFloat(str); - } - str = args[1].trim(); - if (str.endsWith("%")) { - magenta = Float.parseFloat(str.substring(0, - str.length() - 1)) / 100.0f; - } else { - magenta = Float.parseFloat(str); - } - str = args[2].trim(); - if (str.endsWith("%")) { - yellow = Float.parseFloat(str.substring(0, - str.length() - 1)) / 100.0f; - } else { - yellow = Float.parseFloat(str); - } - str = args[3].trim(); - if (str.endsWith("%")) { - black = Float.parseFloat(str.substring(0, - str.length() - 1)) / 100.0f; - } else { - black = Float.parseFloat(str); - } - - if ((cyan < 0.0 || cyan > 1.0) - || (magenta < 0.0 || magenta > 1.0) - || (yellow < 0.0 || yellow > 1.0) - || (black < 0.0 || black > 1.0)) { - throw new PropertyException("Color values out of range" - + "Arguments to cmyk() must be in the range [0%-100%] or [0.0-1.0]"); - } - float[] cmyk = new float[] {cyan, magenta, yellow, black}; - DeviceCMYKColorSpace cmykCs = ColorSpaces.getDeviceCMYKColorSpace(); - float[] rgb = cmykCs.toRGB(cmyk); - parsedColor = ColorExt.createFromFoRgbIcc(rgb[0], rgb[1], rgb[2], - CMYK_PSEUDO_PROFILE, null, cmykCs, cmyk); + float cyan = parseComponent1(args[0], value); + float magenta = parseComponent1(args[1], value); + float yellow = parseComponent1(args[2], value); + float black = parseComponent1(args[3], value); + float[] comps = new float[] {cyan, magenta, yellow, black}; + Color cmykColor = DeviceCMYKColorSpace.createCMYKColor(comps); + float[] rgbComps = cmykColor.getRGBColorComponents(null); + parsedColor = new ColorWithAlternatives(rgbComps[0], rgbComps[1], rgbComps[2], + new Color[] {cmykColor}); } catch (PropertyException pe) { throw pe; } catch (Exception e) { @@ -482,40 +654,146 @@ public final class ColorUtil { */ public static String colorToString(Color color) { ColorSpace cs = color.getColorSpace(); - if (color instanceof ColorExt) { - return ((ColorExt)color).toFunctionCall(); + 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); sbuf.append("cmyk(" + cmyk[0] + "," + cmyk[1] + "," + cmyk[2] + "," + cmyk[3] + ")"); return sbuf.toString(); } else { - StringBuffer sbuf = new StringBuffer(); - sbuf.append('#'); - String s = Integer.toHexString(color.getRed()); + return toRGBFunctionCall(color); + } + } + + private static String toRGBFunctionCall(Color color) { + 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); - s = Integer.toHexString(color.getGreen()); - if (s.length() == 1) { - sbuf.append('0'); + } + 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); } - sbuf.append(s); - s = Integer.toHexString(color.getBlue()); - if (s.length() == 1) { - sbuf.append('0'); + } else { + fallbackColor = toSRGBColor(color); + } + return fallbackColor; + } + + private static Color toSRGBColor(Color color) { + float[] comps; + ColorSpace sRGB = ColorSpace.getInstance(ColorSpace.CS_sRGB); + comps = color.getRGBColorComponents(null); + float[] allComps = color.getComponents(null); + float alpha = allComps[allComps.length - 1]; //Alpha is on last component + return new Color(sRGB, comps, alpha); + } + + /** + * Create string representation of a fop-rgb-icc (or fop-rgb-named-color) function call from + * the given color. + * @param color the color to turn into a function call + * @return the string representing the internal fop-rgb-icc() or fop-rgb-named-color() + * function call + */ + private static String toFunctionCall(ColorWithAlternatives color) { + ColorSpace cs = color.getColorSpace(); + if (cs.isCS_sRGB() && !color.hasAlternativeColors()) { + return toRGBFunctionCall(color); + } + if (cs instanceof CIELabColorSpace) { + return toCIELabFunctionCall(color); + } + + Color specColor = color; + if (color.hasAlternativeColors()) { + Color alt = color.getAlternativeColors()[0]; + if (ColorSpaces.isDeviceColorSpace(alt.getColorSpace())) { + cs = alt.getColorSpace(); + specColor = alt; } - sbuf.append(s); - if (color.getAlpha() != 255) { - s = Integer.toHexString(color.getAlpha()); - if (s.length() == 1) { - sbuf.append('0'); - } - sbuf.append(s); + } + ColorSpaceOrigin origin = ColorSpaces.getColorSpaceOrigin(cs); + String functionName; + + Color fallbackColor = getsRGBFallback(color); + float[] rgb = fallbackColor.getColorComponents(null); + assert rgb.length == 3; + StringBuffer sb = new StringBuffer(40); + sb.append("("); + sb.append(rgb[0]).append(","); + sb.append(rgb[1]).append(","); + sb.append(rgb[2]).append(","); + String profileName = origin.getProfileName(); + sb.append(profileName).append(","); + if (origin.getProfileURI() != null) { + sb.append("\"").append(origin.getProfileURI()).append("\""); + } + + if (cs instanceof NamedColorSpace) { + NamedColorSpace ncs = (NamedColorSpace)cs; + if (SEPARATION_PSEUDO_PROFILE.equalsIgnoreCase(profileName)) { + functionName = "fop-rgb-icc"; + } else { + functionName = "fop-rgb-named-color"; + } + sb.append(",").append(ncs.getColorName()); + } else { + functionName = "fop-rgb-icc"; + float[] colorComponents = specColor.getColorComponents(null); + for (int ix = 0; ix < colorComponents.length; ix++) { + sb.append(","); + sb.append(colorComponents[ix]); } - return sbuf.toString(); } + sb.append(")"); + return functionName + sb.toString(); + } + + private static String toCIELabFunctionCall(ColorWithAlternatives color) { + Color fallbackColor = getsRGBFallback(color); + StringBuffer sb = new StringBuffer("cie-lab-color("); + 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]); + } + sb.append(')'); + return sb.toString(); + } + + private static Color createColor(int r, int g, int b) { + return new Color(r, g, b); } /** @@ -524,155 +802,155 @@ public final class ColorUtil { private static void initializeColorMap() { // CSOK: MethodLength colorMap = Collections.synchronizedMap(new java.util.HashMap<String, Color>()); - colorMap.put("aliceblue", new Color(240, 248, 255)); - colorMap.put("antiquewhite", new Color(250, 235, 215)); - colorMap.put("aqua", new Color(0, 255, 255)); - colorMap.put("aquamarine", new Color(127, 255, 212)); - colorMap.put("azure", new Color(240, 255, 255)); - colorMap.put("beige", new Color(245, 245, 220)); - colorMap.put("bisque", new Color(255, 228, 196)); - colorMap.put("black", new Color(0, 0, 0)); - colorMap.put("blanchedalmond", new Color(255, 235, 205)); - colorMap.put("blue", new Color(0, 0, 255)); - colorMap.put("blueviolet", new Color(138, 43, 226)); - colorMap.put("brown", new Color(165, 42, 42)); - colorMap.put("burlywood", new Color(222, 184, 135)); - colorMap.put("cadetblue", new Color(95, 158, 160)); - colorMap.put("chartreuse", new Color(127, 255, 0)); - colorMap.put("chocolate", new Color(210, 105, 30)); - colorMap.put("coral", new Color(255, 127, 80)); - colorMap.put("cornflowerblue", new Color(100, 149, 237)); - colorMap.put("cornsilk", new Color(255, 248, 220)); - colorMap.put("crimson", new Color(220, 20, 60)); - colorMap.put("cyan", new Color(0, 255, 255)); - colorMap.put("darkblue", new Color(0, 0, 139)); - colorMap.put("darkcyan", new Color(0, 139, 139)); - colorMap.put("darkgoldenrod", new Color(184, 134, 11)); - colorMap.put("darkgray", new Color(169, 169, 169)); - colorMap.put("darkgreen", new Color(0, 100, 0)); - colorMap.put("darkgrey", new Color(169, 169, 169)); - colorMap.put("darkkhaki", new Color(189, 183, 107)); - colorMap.put("darkmagenta", new Color(139, 0, 139)); - colorMap.put("darkolivegreen", new Color(85, 107, 47)); - colorMap.put("darkorange", new Color(255, 140, 0)); - colorMap.put("darkorchid", new Color(153, 50, 204)); - colorMap.put("darkred", new Color(139, 0, 0)); - colorMap.put("darksalmon", new Color(233, 150, 122)); - colorMap.put("darkseagreen", new Color(143, 188, 143)); - colorMap.put("darkslateblue", new Color(72, 61, 139)); - colorMap.put("darkslategray", new Color(47, 79, 79)); - colorMap.put("darkslategrey", new Color(47, 79, 79)); - colorMap.put("darkturquoise", new Color(0, 206, 209)); - colorMap.put("darkviolet", new Color(148, 0, 211)); - colorMap.put("deeppink", new Color(255, 20, 147)); - colorMap.put("deepskyblue", new Color(0, 191, 255)); - colorMap.put("dimgray", new Color(105, 105, 105)); - colorMap.put("dimgrey", new Color(105, 105, 105)); - colorMap.put("dodgerblue", new Color(30, 144, 255)); - colorMap.put("firebrick", new Color(178, 34, 34)); - colorMap.put("floralwhite", new Color(255, 250, 240)); - colorMap.put("forestgreen", new Color(34, 139, 34)); - colorMap.put("fuchsia", new Color(255, 0, 255)); - colorMap.put("gainsboro", new Color(220, 220, 220)); - colorMap.put("ghostwhite", new Color(248, 248, 255)); - colorMap.put("gold", new Color(255, 215, 0)); - colorMap.put("goldenrod", new Color(218, 165, 32)); - colorMap.put("gray", new Color(128, 128, 128)); - colorMap.put("green", new Color(0, 128, 0)); - colorMap.put("greenyellow", new Color(173, 255, 47)); - colorMap.put("grey", new Color(128, 128, 128)); - colorMap.put("honeydew", new Color(240, 255, 240)); - colorMap.put("hotpink", new Color(255, 105, 180)); - colorMap.put("indianred", new Color(205, 92, 92)); - colorMap.put("indigo", new Color(75, 0, 130)); - colorMap.put("ivory", new Color(255, 255, 240)); - colorMap.put("khaki", new Color(240, 230, 140)); - colorMap.put("lavender", new Color(230, 230, 250)); - colorMap.put("lavenderblush", new Color(255, 240, 245)); - colorMap.put("lawngreen", new Color(124, 252, 0)); - colorMap.put("lemonchiffon", new Color(255, 250, 205)); - colorMap.put("lightblue", new Color(173, 216, 230)); - colorMap.put("lightcoral", new Color(240, 128, 128)); - colorMap.put("lightcyan", new Color(224, 255, 255)); - colorMap.put("lightgoldenrodyellow", new Color(250, 250, 210)); - colorMap.put("lightgray", new Color(211, 211, 211)); - colorMap.put("lightgreen", new Color(144, 238, 144)); - colorMap.put("lightgrey", new Color(211, 211, 211)); - colorMap.put("lightpink", new Color(255, 182, 193)); - colorMap.put("lightsalmon", new Color(255, 160, 122)); - colorMap.put("lightseagreen", new Color(32, 178, 170)); - colorMap.put("lightskyblue", new Color(135, 206, 250)); - colorMap.put("lightslategray", new Color(119, 136, 153)); - colorMap.put("lightslategrey", new Color(119, 136, 153)); - colorMap.put("lightsteelblue", new Color(176, 196, 222)); - colorMap.put("lightyellow", new Color(255, 255, 224)); - colorMap.put("lime", new Color(0, 255, 0)); - colorMap.put("limegreen", new Color(50, 205, 50)); - colorMap.put("linen", new Color(250, 240, 230)); - colorMap.put("magenta", new Color(255, 0, 255)); - colorMap.put("maroon", new Color(128, 0, 0)); - colorMap.put("mediumaquamarine", new Color(102, 205, 170)); - colorMap.put("mediumblue", new Color(0, 0, 205)); - colorMap.put("mediumorchid", new Color(186, 85, 211)); - colorMap.put("mediumpurple", new Color(147, 112, 219)); - colorMap.put("mediumseagreen", new Color(60, 179, 113)); - colorMap.put("mediumslateblue", new Color(123, 104, 238)); - colorMap.put("mediumspringgreen", new Color(0, 250, 154)); - colorMap.put("mediumturquoise", new Color(72, 209, 204)); - colorMap.put("mediumvioletred", new Color(199, 21, 133)); - colorMap.put("midnightblue", new Color(25, 25, 112)); - colorMap.put("mintcream", new Color(245, 255, 250)); - colorMap.put("mistyrose", new Color(255, 228, 225)); - colorMap.put("moccasin", new Color(255, 228, 181)); - colorMap.put("navajowhite", new Color(255, 222, 173)); - colorMap.put("navy", new Color(0, 0, 128)); - colorMap.put("oldlace", new Color(253, 245, 230)); - colorMap.put("olive", new Color(128, 128, 0)); - colorMap.put("olivedrab", new Color(107, 142, 35)); - colorMap.put("orange", new Color(255, 165, 0)); - colorMap.put("orangered", new Color(255, 69, 0)); - colorMap.put("orchid", new Color(218, 112, 214)); - colorMap.put("palegoldenrod", new Color(238, 232, 170)); - colorMap.put("palegreen", new Color(152, 251, 152)); - colorMap.put("paleturquoise", new Color(175, 238, 238)); - colorMap.put("palevioletred", new Color(219, 112, 147)); - colorMap.put("papayawhip", new Color(255, 239, 213)); - colorMap.put("peachpuff", new Color(255, 218, 185)); - colorMap.put("peru", new Color(205, 133, 63)); - colorMap.put("pink", new Color(255, 192, 203)); - colorMap.put("plum ", new Color(221, 160, 221)); - colorMap.put("plum", new Color(221, 160, 221)); - colorMap.put("powderblue", new Color(176, 224, 230)); - colorMap.put("purple", new Color(128, 0, 128)); - colorMap.put("red", new Color(255, 0, 0)); - colorMap.put("rosybrown", new Color(188, 143, 143)); - colorMap.put("royalblue", new Color(65, 105, 225)); - colorMap.put("saddlebrown", new Color(139, 69, 19)); - colorMap.put("salmon", new Color(250, 128, 114)); - colorMap.put("sandybrown", new Color(244, 164, 96)); - colorMap.put("seagreen", new Color(46, 139, 87)); - colorMap.put("seashell", new Color(255, 245, 238)); - colorMap.put("sienna", new Color(160, 82, 45)); - colorMap.put("silver", new Color(192, 192, 192)); - colorMap.put("skyblue", new Color(135, 206, 235)); - colorMap.put("slateblue", new Color(106, 90, 205)); - colorMap.put("slategray", new Color(112, 128, 144)); - colorMap.put("slategrey", new Color(112, 128, 144)); - colorMap.put("snow", new Color(255, 250, 250)); - colorMap.put("springgreen", new Color(0, 255, 127)); - colorMap.put("steelblue", new Color(70, 130, 180)); - colorMap.put("tan", new Color(210, 180, 140)); - colorMap.put("teal", new Color(0, 128, 128)); - colorMap.put("thistle", new Color(216, 191, 216)); - colorMap.put("tomato", new Color(255, 99, 71)); - colorMap.put("turquoise", new Color(64, 224, 208)); - colorMap.put("violet", new Color(238, 130, 238)); - colorMap.put("wheat", new Color(245, 222, 179)); - colorMap.put("white", new Color(255, 255, 255)); - colorMap.put("whitesmoke", new Color(245, 245, 245)); - colorMap.put("yellow", new Color(255, 255, 0)); - colorMap.put("yellowgreen", new Color(154, 205, 50)); - colorMap.put("transparent", new Color(0, 0, 0, 0)); + colorMap.put("aliceblue", createColor(240, 248, 255)); + colorMap.put("antiquewhite", createColor(250, 235, 215)); + colorMap.put("aqua", createColor(0, 255, 255)); + colorMap.put("aquamarine", createColor(127, 255, 212)); + colorMap.put("azure", createColor(240, 255, 255)); + colorMap.put("beige", createColor(245, 245, 220)); + colorMap.put("bisque", createColor(255, 228, 196)); + colorMap.put("black", createColor(0, 0, 0)); + colorMap.put("blanchedalmond", createColor(255, 235, 205)); + colorMap.put("blue", createColor(0, 0, 255)); + colorMap.put("blueviolet", createColor(138, 43, 226)); + colorMap.put("brown", createColor(165, 42, 42)); + colorMap.put("burlywood", createColor(222, 184, 135)); + colorMap.put("cadetblue", createColor(95, 158, 160)); + colorMap.put("chartreuse", createColor(127, 255, 0)); + colorMap.put("chocolate", createColor(210, 105, 30)); + colorMap.put("coral", createColor(255, 127, 80)); + colorMap.put("cornflowerblue", createColor(100, 149, 237)); + colorMap.put("cornsilk", createColor(255, 248, 220)); + colorMap.put("crimson", createColor(220, 20, 60)); + colorMap.put("cyan", createColor(0, 255, 255)); + colorMap.put("darkblue", createColor(0, 0, 139)); + colorMap.put("darkcyan", createColor(0, 139, 139)); + colorMap.put("darkgoldenrod", createColor(184, 134, 11)); + colorMap.put("darkgray", createColor(169, 169, 169)); + colorMap.put("darkgreen", createColor(0, 100, 0)); + colorMap.put("darkgrey", createColor(169, 169, 169)); + colorMap.put("darkkhaki", createColor(189, 183, 107)); + colorMap.put("darkmagenta", createColor(139, 0, 139)); + colorMap.put("darkolivegreen", createColor(85, 107, 47)); + colorMap.put("darkorange", createColor(255, 140, 0)); + colorMap.put("darkorchid", createColor(153, 50, 204)); + colorMap.put("darkred", createColor(139, 0, 0)); + colorMap.put("darksalmon", createColor(233, 150, 122)); + colorMap.put("darkseagreen", createColor(143, 188, 143)); + colorMap.put("darkslateblue", createColor(72, 61, 139)); + colorMap.put("darkslategray", createColor(47, 79, 79)); + colorMap.put("darkslategrey", createColor(47, 79, 79)); + colorMap.put("darkturquoise", createColor(0, 206, 209)); + colorMap.put("darkviolet", createColor(148, 0, 211)); + colorMap.put("deeppink", createColor(255, 20, 147)); + colorMap.put("deepskyblue", createColor(0, 191, 255)); + colorMap.put("dimgray", createColor(105, 105, 105)); + colorMap.put("dimgrey", createColor(105, 105, 105)); + colorMap.put("dodgerblue", createColor(30, 144, 255)); + colorMap.put("firebrick", createColor(178, 34, 34)); + colorMap.put("floralwhite", createColor(255, 250, 240)); + colorMap.put("forestgreen", createColor(34, 139, 34)); + colorMap.put("fuchsia", createColor(255, 0, 255)); + colorMap.put("gainsboro", createColor(220, 220, 220)); + colorMap.put("ghostwhite", createColor(248, 248, 255)); + colorMap.put("gold", createColor(255, 215, 0)); + colorMap.put("goldenrod", createColor(218, 165, 32)); + colorMap.put("gray", createColor(128, 128, 128)); + colorMap.put("green", createColor(0, 128, 0)); + colorMap.put("greenyellow", createColor(173, 255, 47)); + colorMap.put("grey", createColor(128, 128, 128)); + colorMap.put("honeydew", createColor(240, 255, 240)); + colorMap.put("hotpink", createColor(255, 105, 180)); + colorMap.put("indianred", createColor(205, 92, 92)); + colorMap.put("indigo", createColor(75, 0, 130)); + colorMap.put("ivory", createColor(255, 255, 240)); + colorMap.put("khaki", createColor(240, 230, 140)); + colorMap.put("lavender", createColor(230, 230, 250)); + colorMap.put("lavenderblush", createColor(255, 240, 245)); + colorMap.put("lawngreen", createColor(124, 252, 0)); + colorMap.put("lemonchiffon", createColor(255, 250, 205)); + colorMap.put("lightblue", createColor(173, 216, 230)); + colorMap.put("lightcoral", createColor(240, 128, 128)); + colorMap.put("lightcyan", createColor(224, 255, 255)); + colorMap.put("lightgoldenrodyellow", createColor(250, 250, 210)); + colorMap.put("lightgray", createColor(211, 211, 211)); + colorMap.put("lightgreen", createColor(144, 238, 144)); + colorMap.put("lightgrey", createColor(211, 211, 211)); + colorMap.put("lightpink", createColor(255, 182, 193)); + colorMap.put("lightsalmon", createColor(255, 160, 122)); + colorMap.put("lightseagreen", createColor(32, 178, 170)); + colorMap.put("lightskyblue", createColor(135, 206, 250)); + colorMap.put("lightslategray", createColor(119, 136, 153)); + colorMap.put("lightslategrey", createColor(119, 136, 153)); + colorMap.put("lightsteelblue", createColor(176, 196, 222)); + colorMap.put("lightyellow", createColor(255, 255, 224)); + colorMap.put("lime", createColor(0, 255, 0)); + colorMap.put("limegreen", createColor(50, 205, 50)); + colorMap.put("linen", createColor(250, 240, 230)); + colorMap.put("magenta", createColor(255, 0, 255)); + colorMap.put("maroon", createColor(128, 0, 0)); + colorMap.put("mediumaquamarine", createColor(102, 205, 170)); + colorMap.put("mediumblue", createColor(0, 0, 205)); + colorMap.put("mediumorchid", createColor(186, 85, 211)); + colorMap.put("mediumpurple", createColor(147, 112, 219)); + colorMap.put("mediumseagreen", createColor(60, 179, 113)); + colorMap.put("mediumslateblue", createColor(123, 104, 238)); + colorMap.put("mediumspringgreen", createColor(0, 250, 154)); + colorMap.put("mediumturquoise", createColor(72, 209, 204)); + colorMap.put("mediumvioletred", createColor(199, 21, 133)); + colorMap.put("midnightblue", createColor(25, 25, 112)); + colorMap.put("mintcream", createColor(245, 255, 250)); + colorMap.put("mistyrose", createColor(255, 228, 225)); + colorMap.put("moccasin", createColor(255, 228, 181)); + colorMap.put("navajowhite", createColor(255, 222, 173)); + colorMap.put("navy", createColor(0, 0, 128)); + colorMap.put("oldlace", createColor(253, 245, 230)); + colorMap.put("olive", createColor(128, 128, 0)); + colorMap.put("olivedrab", createColor(107, 142, 35)); + colorMap.put("orange", createColor(255, 165, 0)); + colorMap.put("orangered", createColor(255, 69, 0)); + colorMap.put("orchid", createColor(218, 112, 214)); + colorMap.put("palegoldenrod", createColor(238, 232, 170)); + colorMap.put("palegreen", createColor(152, 251, 152)); + colorMap.put("paleturquoise", createColor(175, 238, 238)); + colorMap.put("palevioletred", createColor(219, 112, 147)); + colorMap.put("papayawhip", createColor(255, 239, 213)); + colorMap.put("peachpuff", createColor(255, 218, 185)); + colorMap.put("peru", createColor(205, 133, 63)); + colorMap.put("pink", createColor(255, 192, 203)); + colorMap.put("plum ", createColor(221, 160, 221)); + colorMap.put("plum", createColor(221, 160, 221)); + colorMap.put("powderblue", createColor(176, 224, 230)); + colorMap.put("purple", createColor(128, 0, 128)); + colorMap.put("red", createColor(255, 0, 0)); + colorMap.put("rosybrown", createColor(188, 143, 143)); + colorMap.put("royalblue", createColor(65, 105, 225)); + colorMap.put("saddlebrown", createColor(139, 69, 19)); + colorMap.put("salmon", createColor(250, 128, 114)); + colorMap.put("sandybrown", createColor(244, 164, 96)); + colorMap.put("seagreen", createColor(46, 139, 87)); + colorMap.put("seashell", createColor(255, 245, 238)); + colorMap.put("sienna", createColor(160, 82, 45)); + colorMap.put("silver", createColor(192, 192, 192)); + colorMap.put("skyblue", createColor(135, 206, 235)); + colorMap.put("slateblue", createColor(106, 90, 205)); + colorMap.put("slategray", createColor(112, 128, 144)); + colorMap.put("slategrey", createColor(112, 128, 144)); + colorMap.put("snow", createColor(255, 250, 250)); + colorMap.put("springgreen", createColor(0, 255, 127)); + colorMap.put("steelblue", createColor(70, 130, 180)); + colorMap.put("tan", createColor(210, 180, 140)); + colorMap.put("teal", createColor(0, 128, 128)); + colorMap.put("thistle", createColor(216, 191, 216)); + colorMap.put("tomato", createColor(255, 99, 71)); + colorMap.put("turquoise", createColor(64, 224, 208)); + colorMap.put("violet", createColor(238, 130, 238)); + colorMap.put("wheat", createColor(245, 222, 179)); + colorMap.put("white", createColor(255, 255, 255)); + 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 ColorWithAlternatives(0, 0, 0, 0, null)); } /** @@ -692,7 +970,8 @@ public final class ColorUtil { * @return true if the color profile name is of a built-in pseudo-profile */ public static boolean isPseudoProfile(String colorProfileName) { - return CMYK_PSEUDO_PROFILE.equalsIgnoreCase(colorProfileName); + return CMYK_PSEUDO_PROFILE.equalsIgnoreCase(colorProfileName) + || SEPARATION_PSEUDO_PROFILE.equalsIgnoreCase(colorProfileName); } /** @@ -710,11 +989,6 @@ public final class ColorUtil { * @return the CMYK color */ public static Color toCMYKGrayColor(float black) { - float[] cmyk = new float[] {0f, 0f, 0f, 1.0f - black}; - DeviceCMYKColorSpace cmykCs = ColorSpaces.getDeviceCMYKColorSpace(); - float[] rgb = cmykCs.toRGB(cmyk); - return ColorExt.createFromFoRgbIcc(rgb[0], rgb[1], rgb[2], - CMYK_PSEUDO_PROFILE, null, cmykCs, cmyk); + return org.apache.xmlgraphics.java2d.color.ColorUtil.toCMYKGrayColor(black); } - } 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; + } + +} |