aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/java/org/apache/fop/afp/goca/GraphicsSetProcessColor.java90
-rw-r--r--src/java/org/apache/fop/afp/modca/GraphicsObject.java9
-rw-r--r--src/java/org/apache/fop/afp/ptoca/PtocaBuilder.java34
-rw-r--r--src/java/org/apache/fop/apps/FopFactory.java23
-rw-r--r--src/java/org/apache/fop/fo/expr/CIELabColorFunction.java94
-rw-r--r--src/java/org/apache/fop/fo/expr/NamedColorFunction.java112
-rw-r--r--src/java/org/apache/fop/fo/expr/PropertyParser.java2
-rw-r--r--src/java/org/apache/fop/fo/expr/PropertyTokenizer.java5
-rw-r--r--src/java/org/apache/fop/fo/properties/ColorProperty.java9
-rw-r--r--src/java/org/apache/fop/pdf/PDFCIELabColorSpace.java91
-rw-r--r--src/java/org/apache/fop/pdf/PDFColor.java103
-rw-r--r--src/java/org/apache/fop/pdf/PDFColorHandler.java231
-rw-r--r--src/java/org/apache/fop/pdf/PDFDeviceColorSpace.java26
-rw-r--r--src/java/org/apache/fop/pdf/PDFDocument.java16
-rw-r--r--src/java/org/apache/fop/pdf/PDFFactory.java73
-rw-r--r--src/java/org/apache/fop/pdf/PDFName.java23
-rw-r--r--src/java/org/apache/fop/pdf/PDFPaintingState.java20
-rw-r--r--src/java/org/apache/fop/pdf/PDFResources.java117
-rw-r--r--src/java/org/apache/fop/pdf/PDFSeparationColorSpace.java88
-rw-r--r--src/java/org/apache/fop/render/intermediate/IFSerializer.java15
-rw-r--r--src/java/org/apache/fop/render/intermediate/IFState.java4
-rw-r--r--src/java/org/apache/fop/render/java2d/Java2DGraphicsState.java15
-rw-r--r--src/java/org/apache/fop/render/pdf/AbstractImageAdapter.java4
-rw-r--r--src/java/org/apache/fop/render/pdf/PDFContentGenerator.java13
-rw-r--r--src/java/org/apache/fop/svg/PDFDocumentGraphics2D.java17
-rw-r--r--src/java/org/apache/fop/svg/PDFGraphics2D.java62
-rw-r--r--src/java/org/apache/fop/traits/BorderProps.java6
-rw-r--r--src/java/org/apache/fop/util/AbstractPaintingState.java10
-rw-r--r--src/java/org/apache/fop/util/ColorExt.java1
-rw-r--r--src/java/org/apache/fop/util/ColorSpaceCache.java23
-rw-r--r--src/java/org/apache/fop/util/ColorUtil.java838
-rw-r--r--src/java/org/apache/fop/util/ColorWithFallback.java85
-rw-r--r--status.xml3
-rw-r--r--test/java/org/apache/fop/traits/BorderPropsTestCase.java6
-rw-r--r--test/java/org/apache/fop/util/ColorUtilTestCase.java161
-rw-r--r--test/resources/color/ncp-example.iccbin0 -> 532 bytes
36 files changed, 1823 insertions, 606 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..edde6b95f 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,26 +44,50 @@ 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;
+ this.componentsSize = 4;
+ } 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.
+ this.componentsSize = 3;
+ } else {
+ if (!color.getColorSpace().isCS_sRGB()) {
+ this.color = ColorUtil.toSRGBColor(color);
+ } else {
+ this.color = color;
+ }
+ this.componentsSize = 3;
+ }
}
/** {@inheritDoc} */
public int getDataLength() {
- return 12 + colorComponents.length;
+ return 12 + this.componentsSize;
}
/** {@inheritDoc} */
@@ -66,27 +97,44 @@ public class GraphicsSetProcessColor extends AbstractGraphicsDrawingOrder {
/** {@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,12 +148,8 @@ 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} */
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..a85b2d305 100644
--- a/src/java/org/apache/fop/apps/FopFactory.java
+++ b/src/java/org/apache/fop/apps/FopFactory.java
@@ -43,6 +43,7 @@ import org.apache.commons.logging.LogFactory;
import org.apache.xmlgraphics.image.loader.ImageContext;
import org.apache.xmlgraphics.image.loader.ImageManager;
+import org.apache.xmlgraphics.java2d.color.RenderingIntent;
import org.apache.xmlgraphics.util.UnitConv;
import org.apache.fop.fo.ElementMapping;
@@ -165,6 +166,7 @@ public class FopFactory implements ImageContext {
this.fontManager = new FontManager() {
/** {@inheritDoc} */
+ @Override
public void setFontBaseURL(String fontBase) throws MalformedURLException {
super.setFontBaseURL(getFOURIResolver().checkBaseURL(fontBase));
}
@@ -381,6 +383,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 +392,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 +520,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 +530,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);
}
@@ -742,6 +748,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 +758,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 +768,7 @@ public class FopFactory implements ImageContext {
* @return the font cache
* @deprecated use getFontManager().getFontCache() instead
*/
+ @Deprecated
public FontCache getFontCache() {
return getFontManager().getFontCache();
}
@@ -794,19 +803,23 @@ public class FopFactory implements ImageContext {
/**
* Create (if needed) and return an ICC ColorSpace instance.
- *
+ * <p>
* 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.
- *
+ * <p>
* The FOP URI resolver is used to try and locate the ICC file.
* If that fails null is returned.
- *
+ * <p>
+ * Note: this method should not be considered as part of FOP's external API.
+ * @param profileName the profile name
* @param baseUri a base URI to resolve relative URIs
* @param iccProfileSrc ICC Profile source to return a ColorSpace for
+ * @param renderingIntent overriding rendering intent
* @return ICC ColorSpace object or null if ColorSpace could not be created
*/
- public ColorSpace getColorSpace(String baseUri, String iccProfileSrc) {
- return colorSpaceCache.get(baseUri, iccProfileSrc);
+ public ColorSpace getColorSpace(String profileName, String baseUri, String iccProfileSrc,
+ RenderingIntent renderingIntent) {
+ return colorSpaceCache.get(profileName, baseUri, iccProfileSrc, renderingIntent);
}
}
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..fb5a0ba12
--- /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.ColorWithAlternatives;
+import org.apache.xmlgraphics.java2d.color.ColorUtil;
+import org.apache.xmlgraphics.java2d.color.DeviceCMYKColorSpace;
+import org.apache.xmlgraphics.java2d.color.NamedColorSpace;
+
+import org.apache.fop.util.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 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();
+ }
+ float[] wp = labCS.getWhitePoint();
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < 3; i++) {
+ if (i > 0) {
+ sb.append(',');
+ }
+ sb.append(wp[i]);
+ }
+ String key = sb.toString();
+ PDFCIELabColorSpace cielab = (PDFCIELabColorSpace)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 12eca75e4..46462bbd7 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.ZapfDingbats;
import org.apache.fop.util.ColorProfileUtil;
/**
- * 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 2130ef685..d3cc554c8 100644
--- a/src/java/org/apache/fop/render/pdf/AbstractImageAdapter.java
+++ b/src/java/org/apache/fop/render/pdf/AbstractImageAdapter.java
@@ -34,6 +34,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;
import org.apache.fop.util.ColorProfileUtil;
@@ -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/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..b87882d99 100644
--- a/src/java/org/apache/fop/util/ColorExt.java
+++ b/src/java/org/apache/fop/util/ColorExt.java
@@ -28,6 +28,7 @@ import java.util.Arrays;
* <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 ColorWithFallback}
*/
public final class ColorExt extends Color {
//
diff --git a/src/java/org/apache/fop/util/ColorSpaceCache.java b/src/java/org/apache/fop/util/ColorSpaceCache.java
index 7b3f409e0..645245003 100644
--- a/src/java/org/apache/fop/util/ColorSpaceCache.java
+++ b/src/java/org/apache/fop/util/ColorSpaceCache.java
@@ -20,7 +20,6 @@
package org.apache.fop.util;
import java.awt.color.ColorSpace;
-import java.awt.color.ICC_ColorSpace;
import java.awt.color.ICC_Profile;
import java.util.Collections;
import java.util.Map;
@@ -32,6 +31,9 @@ import javax.xml.transform.stream.StreamSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.apache.xmlgraphics.java2d.color.ICCColorSpaceWithIntent;
+import org.apache.xmlgraphics.java2d.color.RenderingIntent;
+
/**
* Map with cached ICC based ColorSpace objects.
*/
@@ -40,7 +42,8 @@ public class ColorSpaceCache {
private static Log log = LogFactory.getLog(ColorSpaceCache.class);
private URIResolver resolver;
- private Map colorSpaceMap = Collections.synchronizedMap(new java.util.HashMap());
+ private Map<String, ColorSpace> colorSpaceMap
+ = Collections.synchronizedMap(new java.util.HashMap<String, ColorSpace>());
/**
* Default constructor
@@ -59,13 +62,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
@@ -86,7 +93,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
@@ -95,15 +103,14 @@ public class ColorSpaceCache {
if (colorSpace != null) {
// Put in cache (not when VM resolved it as we can't control
- colorSpaceMap.put(base + iccProfileSrc, colorSpace);
+ colorSpaceMap.put(key, colorSpace);
} else {
// TODO To avoid an excessive amount of warnings perhaps
// register a null ColorMap in the colorSpaceMap
log.warn("Color profile '" + iccProfileSrc + "' not found.");
}
} else {
- colorSpace = (ColorSpace)colorSpaceMap.get(base
- + iccProfileSrc);
+ colorSpace = 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..3c456dbbc 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,9 +50,18 @@ import org.apache.fop.fo.expr.PropertyException;
*/
public final class ColorUtil {
+ //Implementation note: this class should ALWAYS create ColorWithAlternatives instances instead
+ //of using java.awt.Color since the latter has an equals() method that can't detect two
+ //different colors using the same sRGB fallback.
+ //ColorWithFallback is used to preserve the sRGB fallback exclusively for the purpose
+ //of regenerating textual color functions as specified in XSL-FO.
+
/** The name for the uncalibrated CMYK pseudo-profile */
public static final String CMYK_PSEUDO_PROFILE = "#CMYK";
+ /** 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.
@@ -98,7 +116,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 +132,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);
}
@@ -150,7 +172,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);
}
/**
@@ -194,7 +216,7 @@ public final class ColorUtil {
} catch (Exception e) {
throw new PropertyException(e);
}
- return new Color(red, green, blue);
+ return new ColorWithAlternatives(red, green, blue, null);
}
/**
@@ -218,34 +240,10 @@ 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);
+ parsedColor = new ColorWithAlternatives(red, green, blue, null);
} catch (PropertyException pe) {
//simply re-throw
throw pe;
@@ -260,6 +258,52 @@ 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 ColorWithAlternatives(red, green, blue, null);
+ /*
+ Color sRGB = new ColorWithAlternatives(ColorSpace.getInstance(ColorSpace.CS_sRGB),
+ new float[] {red, green, blue}, 1.0f, null);
+ */
+ return sRGB;
+ }
+
/**
* parse a color given in the #.... format.
*
@@ -293,7 +337,7 @@ public final class ColorUtil {
} else {
throw new NumberFormatException();
}
- parsedColor = new Color(red, green, blue, alpha);
+ parsedColor = new ColorWithAlternatives(red, green, blue, alpha, null);
} catch (Exception e) {
throw new PropertyException("Unknown color format: " + value
+ ". Must be #RGB. #RGBA, #RRGGBB, or #RRGGBBAA");
@@ -320,6 +364,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 +378,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 +390,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().getColorSpace(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 ColorWithAlternatives(
+ colorSpace, iccComponents, 1.0f, null);
+ float[] rgbComps = sRGB.getColorComponents(null);
+ parsedColor = new ColorWithAlternatives(
+ rgbComps[0], rgbComps[1], rgbComps[2],
+ new Color[] {deviceColor});
+ } else {
+ Color specColor = new ColorWithFallback(
+ colorSpace, iccComponents, 1.0f, null, sRGB);
+ parsedColor = specColor;
+ }
} else {
// ICC profile could not be loaded - use rgb replacement values */
log.warn("Color profile '" + iccProfileSrc
- + "' 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 +451,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().getColorSpace(
+ 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 ColorWithAlternatives(red, green, blue, null);
+
+ 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 +626,14 @@ 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);
+ parsedColor = new ColorWithAlternatives(cmykColor.getRGB(),
+ new Color[] {cmykColor});
} catch (PropertyException pe) {
throw pe;
} catch (Exception e) {
@@ -482,40 +658,148 @@ 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);
+ if (color.getColorSpace().isCS_sRGB()) {
+ comps = color.getColorComponents(null);
+ } else {
+ comps = color.getColorComponents(sRGB, null);
+ }
+ float[] allComps = color.getComponents(null);
+ float alpha = allComps[comps.length - 1]; //Alpha is on last component
+ return new Color(sRGB, comps, alpha);
+ }
+
+ /**
+ * Create string representation of fop-rgb-icc function call to map this
+ * ColorExt settings.
+ * @param color the color to turn into a function call
+ * @return the string representing the internal fop-rgb-icc() function call
+ */
+ public static String toFunctionCall(ColorWithAlternatives color) {
+ ColorSpace cs = color.getColorSpace();
+ Color fallbackColor = getsRGBFallback(color);
+ if (cs instanceof CIELabColorSpace) {
+ return toCIELabFunctionCall(color);
+ }
+ if (cs.isCS_sRGB() && !color.hasAlternativeColors()) {
+ return toRGBFunctionCall(color);
+ }
+ StringBuffer sb = new StringBuffer(40);
+
+ Color specColor = color;
+ if (color.hasAlternativeColors()) {
+ Color alt = color.getAlternativeColors()[0];
+ if (ColorSpaces.isDeviceColorSpace(alt.getColorSpace())) {
+ cs = alt.getColorSpace();
+ specColor = alt;
}
- 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;
+ float[] rgb = fallbackColor.getColorComponents(null);
+ assert rgb.length == 3;
+ sb.append("(");
+ sb.append(rgb[0]).append(",");
+ sb.append(rgb[1]).append(",");
+ sb.append(rgb[2]).append(",");
+ String profileName = 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 ColorWithAlternatives(r, g, b, null);
}
/**
@@ -524,155 +808,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 +976,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 +995,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;
+ }
+
+}
diff --git a/status.xml b/status.xml
index c80ba0668..660dda79d 100644
--- a/status.xml
+++ b/status.xml
@@ -59,6 +59,9 @@
documents. Example: the fix of marks layering will be such a case when it's done.
-->
<release version="FOP Trunk" date="TBD">
+ <action context="Renderers" dev="JM" type="add" fixes-bug="49403" due-to="Patrick Jaromin">
+ Initial work on spot colors (aka named colors) for PDF output.
+ </action>
<action context="Config" dev="SP" type="fix">
Bugfix: relative URIs in the configuration file (base, font-base, hyphenation-base) are evaluated relative to the base URI of the configuration file.
</action>
diff --git a/test/java/org/apache/fop/traits/BorderPropsTestCase.java b/test/java/org/apache/fop/traits/BorderPropsTestCase.java
index e142a4358..6eb41daac 100644
--- a/test/java/org/apache/fop/traits/BorderPropsTestCase.java
+++ b/test/java/org/apache/fop/traits/BorderPropsTestCase.java
@@ -27,7 +27,6 @@ import org.apache.xmlgraphics.java2d.color.ColorSpaces;
import org.apache.xmlgraphics.java2d.color.DeviceCMYKColorSpace;
import org.apache.fop.fo.Constants;
-import org.apache.fop.util.ColorExt;
import org.apache.fop.util.ColorUtil;
/**
@@ -51,10 +50,7 @@ public class BorderPropsTestCase extends TestCase {
assertEquals(b1, b2);
float[] cmyk = new float[] {1.0f, 1.0f, 0.5f, 1.0f};
- DeviceCMYKColorSpace cmykCs = ColorSpaces.getDeviceCMYKColorSpace();
- float[] rgb = cmykCs.toRGB(cmyk);
- col = ColorExt.createFromFoRgbIcc(rgb[0], rgb[1], rgb[2],
- "#CMYK", null, cmykCs, cmyk);
+ col = DeviceCMYKColorSpace.createCMYKColor(cmyk);
b1 = new BorderProps(Constants.EN_INSET, 9999,
col, BorderProps.SEPARATE);
ser = b1.toString();
diff --git a/test/java/org/apache/fop/util/ColorUtilTestCase.java b/test/java/org/apache/fop/util/ColorUtilTestCase.java
index c2fb0fbc9..bc871794c 100644
--- a/test/java/org/apache/fop/util/ColorUtilTestCase.java
+++ b/test/java/org/apache/fop/util/ColorUtilTestCase.java
@@ -21,10 +21,14 @@ package org.apache.fop.util;
import java.awt.Color;
import java.awt.color.ColorSpace;
+import java.net.URI;
import junit.framework.TestCase;
import org.apache.xmlgraphics.java2d.color.ColorSpaces;
+import org.apache.xmlgraphics.java2d.color.ColorWithAlternatives;
+import org.apache.xmlgraphics.java2d.color.NamedColorSpace;
+import org.apache.xmlgraphics.java2d.color.RenderingIntent;
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.apps.FopFactory;
@@ -79,11 +83,14 @@ public class ColorUtilTestCase extends TestCase {
assertEquals(col1, col2);
col1 = ColorUtil.parseColorString(null, "fop-rgb-icc(0.5,0.5,0.5,#CMYK,,0.0,0.0,0.0,0.5)");
+ /* The following doesn't work since java.awt.Color from Sun doesn't round consistently
col2 = ColorUtil.parseColorString(null, "cmyk(0.0,0.0,0.0,0.5)");
assertEquals(col1, col2);
+ */
col2 = ColorUtil.parseColorString(null, "fop-rgb-icc(0.5,0.5,0.5,#CMYK,,0.5,0.5,0.5,0.0)");
- assertFalse(col1.equals(col2));
+ assertTrue(col1.equals(col2));
+ assertFalse(org.apache.xmlgraphics.java2d.color.ColorUtil.isSameColor(col1, col2));
}
/**
@@ -109,31 +116,35 @@ public class ColorUtilTestCase extends TestCase {
*/
public void testRGBICC() throws Exception {
FopFactory fopFactory = FopFactory.newInstance();
- ColorSpace cs = fopFactory.getColorSpace(null,
- "src/java/org/apache/fop/pdf/sRGB Color Space Profile.icm");
- assertNotNull(cs);
-
+ URI sRGBLoc = new URI(
+ "file:src/java/org/apache/fop/pdf/sRGB%20Color%20Space%20Profile.icm");
+ ColorSpace cs = fopFactory.getColorSpace("sRGBAlt", null, sRGBLoc.toASCIIString(),
+ RenderingIntent.AUTO);
+ assertNotNull("Color profile not found", cs);
FOUserAgent ua = fopFactory.newFOUserAgent();
- ColorExt colActual;
+ ColorWithFallback colActual;
//fop-rgb-icc() is used instead of rgb-icc() inside FOP!
String colSpec = "fop-rgb-icc(1.0,0.0,0.0,sRGBAlt,"
- + "\"src/java/org/apache/fop/pdf/sRGB Color Space Profile.icm\",1.0,0.0,0.0)";
- colActual = (ColorExt)ColorUtil.parseColorString(ua, colSpec);
- //assertEquals(255, colActual.getRed()); //253 is returned
- //assertEquals(24, colActual.getGreen()); //24 is returned
- //I don't understand the difference. Maybe Java's sRGB and HP's sRGB are somehow not
- //equivalent. This is only going to be a problem if anyone actually makes use of the
- //RGB fallback in any renderer.
- //TODO Anyone know what's going on here?
- assertEquals(0, colActual.getBlue());
+ + "\"" + sRGBLoc.toASCIIString() + "\",1.0,0.0,0.0)";
+ colActual = (ColorWithFallback)ColorUtil.parseColorString(ua, colSpec);
assertEquals(cs, colActual.getColorSpace());
+ assertEquals(255, colActual.getRed());
+ assertEquals(0, colActual.getGreen());
+ assertEquals(0, colActual.getBlue());
float[] comps = colActual.getColorComponents(null);
assertEquals(3, comps.length);
assertEquals(1f, comps[0], 0);
assertEquals(0f, comps[1], 0);
assertEquals(0f, comps[2], 0);
+ assertEquals(0, colActual.getAlternativeColors().length);
+
+ Color fallback = colActual.getFallbackColor();
+ assertTrue(fallback.getColorSpace().isCS_sRGB());
+ assertEquals(255, colActual.getRed());
+ assertEquals(0, colActual.getGreen());
+ assertEquals(0, colActual.getBlue());
assertEquals(colSpec, ColorUtil.colorToString(colActual));
@@ -148,16 +159,17 @@ public class ColorUtilTestCase extends TestCase {
* @throws Exception if an error occurs
*/
public void testCMYK() throws Exception {
- ColorExt colActual;
+ ColorWithAlternatives colActual;
String colSpec;
colSpec = "cmyk(0.0, 0.0, 1.0, 0.0)";
- colActual = (ColorExt)ColorUtil.parseColorString(null, colSpec);
+ colActual = (ColorWithAlternatives)ColorUtil.parseColorString(null, colSpec);
assertEquals(255, colActual.getRed());
assertEquals(255, colActual.getGreen());
assertEquals(0, colActual.getBlue());
- assertEquals(ColorSpaces.getDeviceCMYKColorSpace(), colActual.getColorSpace());
- float[] comps = colActual.getColorComponents(null);
+ Color alt = colActual.getAlternativeColors()[0];
+ assertEquals(ColorSpaces.getDeviceCMYKColorSpace(), alt.getColorSpace());
+ float[] comps = alt.getColorComponents(null);
assertEquals(4, comps.length);
assertEquals(0f, comps[0], 0);
assertEquals(0f, comps[1], 0);
@@ -167,26 +179,28 @@ public class ColorUtilTestCase extends TestCase {
ColorUtil.colorToString(colActual));
colSpec = "cmyk(0.0274, 0.2196, 0.3216, 0.0)";
- colActual = (ColorExt)ColorUtil.parseColorString(null, colSpec);
- assertEquals(248, colActual.getRed());
- assertEquals(199, colActual.getGreen());
- assertEquals(172, colActual.getBlue());
- assertEquals(ColorSpaces.getDeviceCMYKColorSpace(), colActual.getColorSpace());
- comps = colActual.getColorComponents(null);
+ colActual = (ColorWithAlternatives)ColorUtil.parseColorString(null, colSpec);
+ assertEquals(248, colActual.getRed(), 1);
+ assertEquals(199, colActual.getGreen(), 1);
+ assertEquals(172, colActual.getBlue(), 1);
+ alt = colActual.getAlternativeColors()[0];
+ assertEquals(ColorSpaces.getDeviceCMYKColorSpace(), alt.getColorSpace());
+ comps = alt.getColorComponents(null);
assertEquals(0.0274f, comps[0], 0.001);
assertEquals(0.2196f, comps[1], 0.001);
assertEquals(0.3216f, comps[2], 0.001);
assertEquals(0f, comps[3], 0);
- assertEquals("fop-rgb-icc(0.9726,0.7804,0.67840004,#CMYK,,0.0274,0.2196,0.3216,0.0)",
+ assertEquals("fop-rgb-icc(0.972549,0.78039217,0.6745098,#CMYK,,0.0274,0.2196,0.3216,0.0)",
ColorUtil.colorToString(colActual));
colSpec = "fop-rgb-icc(1.0,1.0,0.0,#CMYK,,0.0,0.0,1.0,0.0)";
- colActual = (ColorExt)ColorUtil.parseColorString(null, colSpec);
+ colActual = (ColorWithAlternatives)ColorUtil.parseColorString(null, colSpec);
assertEquals(255, colActual.getRed());
assertEquals(255, colActual.getGreen());
assertEquals(0, colActual.getBlue());
- assertEquals(ColorSpaces.getDeviceCMYKColorSpace(), colActual.getColorSpace());
- comps = colActual.getColorComponents(null);
+ alt = colActual.getAlternativeColors()[0];
+ assertEquals(ColorSpaces.getDeviceCMYKColorSpace(), alt.getColorSpace());
+ comps = alt.getColorComponents(null);
assertEquals(4, comps.length);
assertEquals(0f, comps[0], 0);
assertEquals(0f, comps[1], 0);
@@ -196,12 +210,13 @@ public class ColorUtilTestCase extends TestCase {
ColorUtil.colorToString(colActual));
colSpec = "fop-rgb-icc(0.5,0.5,0.5,#CMYK,,0.0,0.0,0.0,0.5)";
- colActual = (ColorExt)ColorUtil.parseColorString(null, colSpec);
- assertEquals(127, colActual.getRed());
- assertEquals(127, colActual.getGreen());
- assertEquals(127, colActual.getBlue());
- assertEquals(ColorSpaces.getDeviceCMYKColorSpace(), colActual.getColorSpace());
- comps = colActual.getColorComponents(null);
+ colActual = (ColorWithAlternatives)ColorUtil.parseColorString(null, colSpec);
+ assertEquals(127, colActual.getRed(), 1);
+ assertEquals(127, colActual.getGreen(), 1);
+ assertEquals(127, colActual.getBlue(), 1);
+ alt = colActual.getAlternativeColors()[0];
+ assertEquals(ColorSpaces.getDeviceCMYKColorSpace(), alt.getColorSpace());
+ comps = alt.getColorComponents(null);
assertEquals(4, comps.length);
assertEquals(0f, comps[0], 0);
assertEquals(0f, comps[1], 0);
@@ -211,4 +226,80 @@ public class ColorUtilTestCase extends TestCase {
ColorUtil.colorToString(colActual));
}
+ /**
+ * Tests color for the #Separation pseudo-colorspace.
+ * @throws Exception if an error occurs
+ */
+ public void testSeparationColor() throws Exception {
+ ColorWithFallback colActual;
+ String colSpec;
+
+ colSpec = "fop-rgb-icc(1.0,0.8,0.0,#Separation,,Postgelb)";
+ colActual = (ColorWithFallback)ColorUtil.parseColorString(null, colSpec);
+ assertEquals(255, colActual.getRed(), 1);
+ assertEquals(204, colActual.getGreen(), 1);
+ assertEquals(0, colActual.getBlue());
+
+ Color fallback = colActual.getFallbackColor();
+ assertEquals(255, fallback.getRed());
+ assertEquals(204, fallback.getGreen());
+ assertEquals(0, fallback.getBlue());
+
+ assertFalse(colActual.hasAlternativeColors());
+
+ assertTrue(colActual.getColorSpace() instanceof NamedColorSpace);
+ NamedColorSpace ncs;
+ ncs = (NamedColorSpace)colActual.getColorSpace();
+ assertEquals("Postgelb", ncs.getColorName());
+ float[] comps = colActual.getColorComponents(null);
+ assertEquals(1, comps.length);
+ assertEquals(1f, comps[0], 0);
+ assertEquals(colSpec, ColorUtil.colorToString(colActual));
+
+ }
+
+ /**
+ * Tests the fop-rgb-named-color() function.
+ * @throws Exception if an error occurs
+ */
+ public void testNamedColorProfile() throws Exception {
+ FopFactory fopFactory = FopFactory.newInstance();
+ URI ncpLoc = new URI("file:test/resources/color/ncp-example.icc");
+ ColorSpace cs = fopFactory.getColorSpace("NCP", null, ncpLoc.toASCIIString(),
+ RenderingIntent.AUTO);
+ assertNotNull("Color profile not found", cs);
+
+ FOUserAgent ua = fopFactory.newFOUserAgent();
+ ColorWithFallback colActual;
+
+ //fop-rgb-named-color() is used instead of rgb-named-color() inside FOP!
+ String colSpec = "fop-rgb-named-color(1.0,0.8,0.0,NCP,"
+ + "\"" + ncpLoc.toASCIIString() + "\",Postgelb)";
+ colActual = (ColorWithFallback)ColorUtil.parseColorString(ua, colSpec);
+ assertEquals(255, colActual.getRed());
+ assertEquals(193, colActual.getGreen());
+ assertEquals(0, colActual.getBlue());
+
+ Color fallback = colActual.getFallbackColor();
+ assertEquals(255, fallback.getRed());
+ assertEquals(204, fallback.getGreen());
+ assertEquals(0, fallback.getBlue());
+ assertEquals(ColorSpace.getInstance(ColorSpace.CS_sRGB), fallback.getColorSpace());
+
+ float[] comps = fallback.getColorComponents(null);
+ assertEquals(3, comps.length);
+ assertEquals(1f, comps[0], 0);
+ assertEquals(0.8f, comps[1], 0);
+ assertEquals(0f, comps[2], 0);
+
+ assertTrue(colActual.getColorSpace() instanceof NamedColorSpace);
+ NamedColorSpace ncs;
+ ncs = (NamedColorSpace)colActual.getColorSpace();
+ assertEquals("Postgelb", ncs.getColorName());
+ comps = colActual.getColorComponents(null);
+ assertEquals(1, comps.length);
+ assertEquals(1f, comps[0], 0);
+
+ assertEquals(colSpec, ColorUtil.colorToString(colActual));
+ }
}
diff --git a/test/resources/color/ncp-example.icc b/test/resources/color/ncp-example.icc
new file mode 100644
index 000000000..7afb2d8fc
--- /dev/null
+++ b/test/resources/color/ncp-example.icc
Binary files differ