Browse Source

Re-integrate/merge Temp_Color branch into Trunk.

git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1069439 13f79535-47bb-0310-9956-ffa450edef68
tags/fop-1_1rc1old
Jeremias Maerki 13 years ago
parent
commit
3df0d00cdc
38 changed files with 2145 additions and 623 deletions
  1. 67
    23
      src/java/org/apache/fop/afp/goca/GraphicsSetProcessColor.java
  2. 8
    1
      src/java/org/apache/fop/afp/modca/GraphicsObject.java
  3. 33
    1
      src/java/org/apache/fop/afp/ptoca/PtocaBuilder.java
  4. 18
    18
      src/java/org/apache/fop/apps/FopFactory.java
  5. 94
    0
      src/java/org/apache/fop/fo/expr/CIELabColorFunction.java
  6. 112
    0
      src/java/org/apache/fop/fo/expr/NamedColorFunction.java
  7. 2
    0
      src/java/org/apache/fop/fo/expr/PropertyParser.java
  8. 2
    3
      src/java/org/apache/fop/fo/expr/PropertyTokenizer.java
  9. 8
    1
      src/java/org/apache/fop/fo/properties/ColorProperty.java
  10. 91
    0
      src/java/org/apache/fop/pdf/PDFCIELabColorSpace.java
  11. 10
    93
      src/java/org/apache/fop/pdf/PDFColor.java
  12. 231
    0
      src/java/org/apache/fop/pdf/PDFColorHandler.java
  13. 26
    0
      src/java/org/apache/fop/pdf/PDFDeviceColorSpace.java
  14. 10
    6
      src/java/org/apache/fop/pdf/PDFDocument.java
  15. 59
    14
      src/java/org/apache/fop/pdf/PDFFactory.java
  16. 23
    0
      src/java/org/apache/fop/pdf/PDFName.java
  17. 17
    3
      src/java/org/apache/fop/pdf/PDFPaintingState.java
  18. 48
    69
      src/java/org/apache/fop/pdf/PDFResources.java
  19. 88
    0
      src/java/org/apache/fop/pdf/PDFSeparationColorSpace.java
  20. 14
    1
      src/java/org/apache/fop/render/intermediate/IFSerializer.java
  21. 3
    1
      src/java/org/apache/fop/render/intermediate/IFState.java
  22. 12
    3
      src/java/org/apache/fop/render/java2d/Java2DGraphicsState.java
  23. 3
    1
      src/java/org/apache/fop/render/pdf/AbstractImageAdapter.java
  24. 8
    5
      src/java/org/apache/fop/render/pdf/PDFContentGenerator.java
  25. 34
    2
      src/java/org/apache/fop/render/xml/XMLRenderer.java
  26. 12
    3
      src/java/org/apache/fop/svg/PDFBridgeContext.java
  27. 9
    8
      src/java/org/apache/fop/svg/PDFDocumentGraphics2D.java
  28. 24
    38
      src/java/org/apache/fop/svg/PDFGraphics2D.java
  29. 5
    1
      src/java/org/apache/fop/traits/BorderProps.java
  30. 8
    2
      src/java/org/apache/fop/util/AbstractPaintingState.java
  31. 259
    0
      src/java/org/apache/fop/util/ColorExt.java
  32. 13
    6
      src/java/org/apache/fop/util/ColorSpaceCache.java
  33. 557
    283
      src/java/org/apache/fop/util/ColorUtil.java
  34. 85
    0
      src/java/org/apache/fop/util/ColorWithFallback.java
  35. 6
    0
      status.xml
  36. 5
    6
      test/java/org/apache/fop/traits/BorderPropsTestCase.java
  37. 141
    31
      test/java/org/apache/fop/util/ColorUtilTestCase.java
  38. BIN
      test/resources/color/ncp-example.icc

+ 67
- 23
src/java/org/apache/fop/afp/goca/GraphicsSetProcessColor.java View File

@@ -21,9 +21,16 @@ package org.apache.fop.afp.goca;

import java.awt.Color;
import java.awt.color.ColorSpace;
import java.io.DataOutput;
import java.io.IOException;
import java.io.OutputStream;

import org.apache.commons.io.output.ByteArrayOutputStream;

import org.apache.xmlgraphics.java2d.color.CIELabColorSpace;
import org.apache.xmlgraphics.java2d.color.ColorUtil;
import org.apache.xmlgraphics.java2d.color.ColorWithAlternatives;

/**
* Sets the current processing color for the following GOCA structured fields
*/
@@ -37,56 +44,96 @@ public class GraphicsSetProcessColor extends AbstractGraphicsDrawingOrder {
* X'08' CIELAB
* X'40' Standard OCA color space
*/
private static final byte RGB = 0x01, CMYK = 0x04;
private static final byte RGB = 0x01, CMYK = 0x04, CIELAB = 0x08;

private final Color color;

private final float[] colorComponents;
private final int componentsSize;

/**
* Main constructor
*
* @param color
* the color to set
* @param color the color to set
*/
public GraphicsSetProcessColor(Color color) {
this.color = color;
this.colorComponents = color.getColorComponents(null);
if (color instanceof ColorWithAlternatives) {
ColorWithAlternatives cwa = (ColorWithAlternatives)color;
Color alt = cwa.getFirstAlternativeOfType(ColorSpace.TYPE_CMYK);
if (alt != null) {
this.color = alt;
this.componentsSize = 4;
return;
}
}
ColorSpace cs = color.getColorSpace();
int colSpaceType = cs.getType();
if (colSpaceType == ColorSpace.TYPE_CMYK) {
this.color = color;
} else if (cs instanceof CIELabColorSpace) {
//TODO Convert between illuminants if not D50 according to rendering intents
//Right now, we're assuming D50 as the GOCA spec requires.
this.color = color;
//16bit components didn't work, and 8-bit sadly has reduced accuracy.
} else {
if (!color.getColorSpace().isCS_sRGB()) {
this.color = ColorUtil.toSRGBColor(color);
} else {
this.color = color;
}
}
this.componentsSize = this.color.getColorSpace().getNumComponents();
}

/** {@inheritDoc} */
public int getDataLength() {
return 12 + colorComponents.length;
return 12 + this.componentsSize;
}

/** {@inheritDoc} */
@Override
byte getOrderCode() {
return (byte) 0xB2;
}

/** {@inheritDoc} */
public void writeToStream(OutputStream os) throws IOException {
float[] colorComponents = color.getColorComponents(null);

// COLSPCE
byte colspace;
int colSpaceType = color.getColorSpace().getType();
ColorSpace cs = color.getColorSpace();
int colSpaceType = cs.getType();
ByteArrayOutputStream baout = new ByteArrayOutputStream();
byte[] colsizes;
if (colSpaceType == ColorSpace.TYPE_CMYK) {
colspace = CMYK;
colsizes = new byte[] {0x08, 0x08, 0x08, 0x08};
for (int i = 0; i < colorComponents.length; i++) {
baout.write(Math.round(colorComponents[i] * 255));
}
} else if (colSpaceType == ColorSpace.TYPE_RGB) {
colspace = RGB;
colsizes = new byte[] {0x08, 0x08, 0x08, 0x00};
for (int i = 0; i < colorComponents.length; i++) {
baout.write(Math.round(colorComponents[i] * 255));
}
} else if (cs instanceof CIELabColorSpace) {
colspace = CIELAB;
colsizes = new byte[] {0x08, 0x08, 0x08, 0x00};
DataOutput dout = new java.io.DataOutputStream(baout);
//According to GOCA, I'd expect the multiplicator below to be 255f, not 100f
//But only IBM AFP Workbench seems to support Lab colors and it requires "c * 100f"
int l = Math.round(colorComponents[0] * 100f);
int a = Math.round(colorComponents[1] * 255f) - 128;
int b = Math.round(colorComponents[2] * 255f) - 128;
dout.writeByte(l);
dout.writeByte(a);
dout.writeByte(b);
} else {
LOG.error("unsupported colorspace " + colSpaceType);
colspace = RGB;
}

// COLSIZE(S)
byte[] colsizes = new byte[] {0x00, 0x00, 0x00, 0x00};
for (int i = 0; i < colorComponents.length; i++) {
colsizes[i] = (byte) 8;
throw new IllegalStateException();
}

int len = getDataLength();
byte[] data = new byte[len];
byte[] data = new byte[12];
data[0] = getOrderCode(); // GSPCOL order code
data[1] = (byte) (len - 2); // LEN
data[2] = 0x00; // reserved; must be zero
@@ -100,15 +147,12 @@ public class GraphicsSetProcessColor extends AbstractGraphicsDrawingOrder {
data[10] = colsizes[2];
data[11] = colsizes[3];

// COLVALUE(S)
for (int i = 0; i < colorComponents.length; i++) {
data[i + 12] = (byte) (colorComponents[i] * 255);
}

os.write(data);
baout.writeTo(os);
}

/** {@inheritDoc} */
@Override
public String toString() {
return "GraphicsSetProcessColor(col=" + color + ")";
}

+ 8
- 1
src/java/org/apache/fop/afp/modca/GraphicsObject.java View File

@@ -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);

+ 33
- 1
src/java/org/apache/fop/afp/ptoca/PtocaBuilder.java View File

@@ -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

+ 18
- 18
src/java/org/apache/fop/apps/FopFactory.java View File

@@ -19,7 +19,6 @@

package org.apache.fop.apps;

import java.awt.color.ColorSpace;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
@@ -151,7 +150,7 @@ public class FopFactory implements ImageContext {
/** Optional overriding LayoutManagerMaker */
private LayoutManagerMaker lmMakerOverride = null;

private Set ignoredNamespaces;
private Set<String> ignoredNamespaces;

private FOURIResolver foURIResolver;

@@ -165,6 +164,7 @@ public class FopFactory implements ImageContext {
this.fontManager = new FontManager() {

/** {@inheritDoc} */
@Override
public void setFontBaseURL(String fontBase) throws MalformedURLException {
super.setFontBaseURL(getFOURIResolver().checkBaseURL(fontBase));
}
@@ -175,7 +175,7 @@ public class FopFactory implements ImageContext {
this.rendererFactory = new RendererFactory();
this.xmlHandlers = new XMLHandlerRegistry();
this.imageHandlers = new ImageHandlerRegistry();
this.ignoredNamespaces = new java.util.HashSet();
this.ignoredNamespaces = new java.util.HashSet<String>();
}

/**
@@ -381,6 +381,7 @@ public class FopFactory implements ImageContext {
* @throws MalformedURLException if there's a problem with a file URL
* @deprecated use getFontManager().setFontBaseURL(fontBase) instead
*/
@Deprecated
public void setFontBaseURL(String fontBase) throws MalformedURLException {
getFontManager().setFontBaseURL(fontBase);
}
@@ -389,6 +390,7 @@ public class FopFactory implements ImageContext {
* @return the font base URL
* @deprecated use getFontManager().setFontBaseURL(fontBase) instead
*/
@Deprecated
public String getFontBaseURL() {
return getFontManager().getFontBaseURL();
}
@@ -516,6 +518,7 @@ public class FopFactory implements ImageContext {
* @return true if kerning on base 14 fonts is enabled
* @deprecated use getFontManager().isBase14KerningEnabled() instead
*/
@Deprecated
public boolean isBase14KerningEnabled() {
return getFontManager().isBase14KerningEnabled();
}
@@ -525,6 +528,7 @@ public class FopFactory implements ImageContext {
* @param value true if kerning should be activated
* @deprecated use getFontManager().setBase14KerningEnabled(boolean) instead
*/
@Deprecated
public void setBase14KerningEnabled(boolean value) {
getFontManager().setBase14KerningEnabled(value);
}
@@ -652,7 +656,7 @@ public class FopFactory implements ImageContext {
* namespace is in the ignored set.
* @param namespaceURIs the namespace URIs
*/
public void ignoreNamespaces(Collection namespaceURIs) {
public void ignoreNamespaces(Collection<String> namespaceURIs) {
this.ignoredNamespaces.addAll(namespaceURIs);
}

@@ -666,7 +670,7 @@ public class FopFactory implements ImageContext {
}

/** @return the set of namespaces that are ignored by FOP */
public Set getIgnoredNamespace() {
public Set<String> getIgnoredNamespace() {
return Collections.unmodifiableSet(this.ignoredNamespaces);
}

@@ -742,6 +746,7 @@ public class FopFactory implements ImageContext {
* @param useCache use cache or not
* @deprecated use getFontManager().setUseCache(boolean) instead
*/
@Deprecated
public void setUseCache(boolean useCache) {
getFontManager().setUseCache(useCache);
}
@@ -751,6 +756,7 @@ public class FopFactory implements ImageContext {
* @return whether this factory is uses the cache
* @deprecated use getFontManager().useCache() instead
*/
@Deprecated
public boolean useCache() {
return getFontManager().useCache();
}
@@ -760,6 +766,7 @@ public class FopFactory implements ImageContext {
* @return the font cache
* @deprecated use getFontManager().getFontCache() instead
*/
@Deprecated
public FontCache getFontCache() {
return getFontManager().getFontCache();
}
@@ -793,20 +800,13 @@ public class FopFactory implements ImageContext {
}

/**
* Create (if needed) and return an ICC ColorSpace instance.
*
* The ICC profile source is taken from the src attribute of the color-profile FO element.
* If the ICC ColorSpace is not yet in the cache a new one is created and stored in the cache.
*
* The FOP URI resolver is used to try and locate the ICC file.
* If that fails null is returned.
*
* @param baseUri a base URI to resolve relative URIs
* @param iccProfileSrc ICC Profile source to return a ColorSpace for
* @return ICC ColorSpace object or null if ColorSpace could not be created
* Returns the color space cache for this instance.
* <p>
* Note: this method should not be considered as part of FOP's external API.
* @return the color space cache
*/
public ColorSpace getColorSpace(String baseUri, String iccProfileSrc) {
return colorSpaceCache.get(baseUri, iccProfileSrc);
public ColorSpaceCache getColorSpaceCache() {
return this.colorSpaceCache;
}

}

+ 94
- 0
src/java/org/apache/fop/fo/expr/CIELabColorFunction.java View File

@@ -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;
}

}

}

+ 112
- 0
src/java/org/apache/fop/fo/expr/NamedColorFunction.java View File

@@ -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;
}
}
}

+ 2
- 0
src/java/org/apache/fop/fo/expr/PropertyParser.java View File

@@ -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!!!

/**

+ 2
- 3
src/java/org/apache/fop/fo/expr/PropertyTokenizer.java View File

@@ -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;
}

+ 8
- 1
src/java/org/apache/fop/fo/properties/ColorProperty.java View File

@@ -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();
}

+ 91
- 0
src/java/org/apache/fop/pdf/PDFCIELabColorSpace.java View File

@@ -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;
}

}

+ 10
- 93
src/java/org/apache/fop/pdf/PDFColor.java View File

@@ -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
@@ -68,82 +61,28 @@ public class PDFColor extends PDFPathPaint {
this.blue = theBlue;
}

/**
* Create PDFColor for the given document and based on the java.awt.Color object
*
* In case the java.awt.Color is an instance of the ColorExt class a PDFICCStream is added to
* the PDFDocument that is being created
*
* @param pdfDoc PDFDocument that is being created
* @param col Color object from which to create this PDFColor
*/
public PDFColor(PDFDocument pdfDoc, Color col) {
this(col);
// TODO - 1) There is a potential conflict when FOP and Batik elements use the same color
// profile name for different profiles.
// 2) In case the same color profile is used with different names it will be
// included multiple times in the PDF
//
if (colorExt != null
&& pdfDoc.getResources().getColorSpace(colorExt.getIccProfileName()) == null) {
PDFICCStream pdfIccStream = new PDFICCStream();
ColorSpace ceCs = colorExt.getOrigColorSpace();
try {
pdfIccStream.setColorSpace(((ICC_ColorSpace)ceCs).getProfile(), null);
pdfIccStream.setData(
((ICC_ColorSpace)colorExt.getColorSpace()).getProfile().getData());
} catch (IOException ioe) {
log.error("Failed to set profile data for " + colorExt.getIccProfileName());
}
pdfDoc.registerObject(pdfIccStream);
pdfDoc.getFactory().makeICCBasedColorSpace(
null, colorExt.getIccProfileName(), pdfIccStream);
if (log.isInfoEnabled()) {
log.info("Adding PDFICCStream " + colorExt.getIccProfileName()
+ " for " + colorExt.getIccProfileSrc());
}
}
}

/**
* Create a PDF color from a java.awt.Color object.
*
* Different Color objects are handled differently. Cases recognized are.
*
* 1. CMYK color
* 2. ColorExt color
* 3. 'Normal' java.awt.Color (RGB case assumed)
* 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

+ 231
- 0
src/java/org/apache/fop/pdf/PDFColorHandler.java View File

@@ -0,0 +1,231 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/* $Id$ */

package org.apache.fop.pdf;

import java.awt.Color;
import java.awt.color.ColorSpace;
import java.awt.color.ICC_ColorSpace;
import java.awt.color.ICC_Profile;
import java.text.DecimalFormat;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.apache.xmlgraphics.java2d.color.CIELabColorSpace;
import org.apache.xmlgraphics.java2d.color.ColorUtil;
import org.apache.xmlgraphics.java2d.color.ColorWithAlternatives;
import org.apache.xmlgraphics.java2d.color.DeviceCMYKColorSpace;
import org.apache.xmlgraphics.java2d.color.NamedColorSpace;
import org.apache.xmlgraphics.java2d.color.profile.ColorProfileUtil;

import org.apache.fop.util.DecimalFormatCache;

/**
* This class handles the registration of color spaces and the generation of PDF code to select
* the right colors given a {@link Color} instance.
*/
public class PDFColorHandler {

private Log log = LogFactory.getLog(PDFColorHandler.class);

private PDFResources resources;

private Map<String, PDFCIELabColorSpace> cieLabColorSpaces;

public PDFColorHandler(PDFResources resources) {
this.resources = resources;
}

private PDFDocument getDocument() {
return this.resources.getDocumentSafely();
}

/**
* Generates code to select the given color and handles the registration of color spaces in
* PDF where necessary.
* @param codeBuffer the target buffer to receive the color selection code
* @param color the color
* @param fill true for fill color, false for stroke color
*/
public void establishColor(StringBuffer codeBuffer, Color color, boolean fill) {
if (color instanceof ColorWithAlternatives) {
ColorWithAlternatives colExt = (ColorWithAlternatives)color;
//Alternate colors have priority
Color[] alt = colExt.getAlternativeColors();
for (int i = 0, c = alt.length; i < c; i++) {
Color col = alt[i];
boolean established = establishColorFromColor(codeBuffer, col, fill);
if (established) {
return;
}
}
if (log.isDebugEnabled() && alt.length > 0) {
log.debug("None of the alternative colors are supported. Using fallback: "
+ color);
}
}

//Fallback
boolean established = establishColorFromColor(codeBuffer, color, fill);
if (!established) {
establishDeviceRGB(codeBuffer, color, fill);
}
}

private boolean establishColorFromColor(StringBuffer codeBuffer, Color color, boolean fill) {
ColorSpace cs = color.getColorSpace();
if (cs instanceof DeviceCMYKColorSpace) {
establishDeviceCMYK(codeBuffer, color, fill);
return true;
} else if (!cs.isCS_sRGB()) {
if (cs instanceof ICC_ColorSpace) {
PDFICCBasedColorSpace pdfcs = getICCBasedColorSpace((ICC_ColorSpace)cs);
establishColor(codeBuffer, pdfcs, color, fill);
return true;
} else if (cs instanceof NamedColorSpace) {
PDFSeparationColorSpace sepcs = getSeparationColorSpace((NamedColorSpace)cs);
establishColor(codeBuffer, sepcs, color, fill);
return true;
} else if (cs instanceof CIELabColorSpace) {
CIELabColorSpace labcs = (CIELabColorSpace)cs;
PDFCIELabColorSpace pdflab = getCIELabColorSpace(labcs);
selectColorSpace(codeBuffer, pdflab, fill);
float[] comps = color.getColorComponents(null);
float[] nativeComps = labcs.toNativeComponents(comps);
writeColor(codeBuffer, nativeComps, labcs.getNumComponents(), (fill ? "sc" : "SC"));
return true;
}
}
return false;
}

private PDFICCBasedColorSpace getICCBasedColorSpace(ICC_ColorSpace cs) {
ICC_Profile profile = cs.getProfile();
String desc = ColorProfileUtil.getICCProfileDescription(profile);
if (log.isDebugEnabled()) {
log.trace("ICC profile encountered: " + desc);
}
PDFICCBasedColorSpace pdfcs = this.resources.getICCColorSpaceByProfileName(desc);
if (pdfcs == null) {
//color space is not in the PDF, yet
PDFFactory factory = getDocument().getFactory();
PDFICCStream pdfICCStream = factory.makePDFICCStream();
PDFDeviceColorSpace altSpace = PDFDeviceColorSpace.toPDFColorSpace(cs);
pdfICCStream.setColorSpace(profile, altSpace);
pdfcs = factory.makeICCBasedColorSpace(null, desc, pdfICCStream);
}
return pdfcs;
}

private PDFSeparationColorSpace getSeparationColorSpace(NamedColorSpace cs) {
PDFName colorName = new PDFName(cs.getColorName());
PDFSeparationColorSpace sepcs = (PDFSeparationColorSpace)this.resources.getColorSpace(
colorName);
if (sepcs == null) {
//color space is not in the PDF, yet
PDFFactory factory = getDocument().getFactory();
sepcs = factory.makeSeparationColorSpace(null, cs);
}
return sepcs;
}

private PDFCIELabColorSpace getCIELabColorSpace(CIELabColorSpace labCS) {
if (this.cieLabColorSpaces == null) {
this.cieLabColorSpaces = new java.util.HashMap<String, PDFCIELabColorSpace>();
}
float[] wp = labCS.getWhitePoint();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 3; i++) {
if (i > 0) {
sb.append(',');
}
sb.append(wp[i]);
}
String key = sb.toString();
PDFCIELabColorSpace cielab = this.cieLabColorSpaces.get(key);
if (cielab == null) {
//color space is not in the PDF, yet
float[] wp1 = new float[] {wp[0] / 100f, wp[1] / 100f, wp[2] / 100f};
cielab = new PDFCIELabColorSpace(wp1, null);
getDocument().registerObject(cielab);
this.resources.addColorSpace(cielab);
this.cieLabColorSpaces.put(key, cielab);
}
return cielab;
}

private void establishColor(StringBuffer codeBuffer,
PDFColorSpace pdfcs, Color color, boolean fill) {
selectColorSpace(codeBuffer, pdfcs, fill);
writeColor(codeBuffer, color, pdfcs.getNumComponents(), (fill ? "sc" : "SC"));
}

private void selectColorSpace(StringBuffer codeBuffer, PDFColorSpace pdfcs, boolean fill) {
codeBuffer.append(new PDFName(pdfcs.getName()));
if (fill) {
codeBuffer.append(" cs ");
} else {
codeBuffer.append(" CS ");
}
}

private void establishDeviceRGB(StringBuffer codeBuffer, Color color, boolean fill) {
float[] comps;
if (color.getColorSpace().isCS_sRGB()) {
comps = color.getColorComponents(null);
} else {
if (log.isDebugEnabled()) {
log.debug("Converting color to sRGB as a fallback: " + color);
}
ColorSpace sRGB = ColorSpace.getInstance(ColorSpace.CS_sRGB);
comps = color.getColorComponents(sRGB, null);
}
if (ColorUtil.isGray(color)) {
comps = new float[] {comps[0]}; //assuming that all components are the same
writeColor(codeBuffer, comps, 1, (fill ? "g" : "G"));
} else {
writeColor(codeBuffer, comps, 3, (fill ? "rg" : "RG"));
}
}

private void establishDeviceCMYK(StringBuffer codeBuffer, Color color, boolean fill) {
writeColor(codeBuffer, color, 4, (fill ? "k" : "K"));
}

private void writeColor(StringBuffer codeBuffer, Color color, int componentCount,
String command) {
float[] comps = color.getColorComponents(null);
writeColor(codeBuffer, comps, componentCount, command);
}

private void writeColor(StringBuffer codeBuffer, float[] comps, int componentCount,
String command) {
if (comps.length != componentCount) {
throw new IllegalStateException("Color with unexpected component count encountered");
}
DecimalFormat df = DecimalFormatCache.getDecimalFormat(4);
for (int i = 0, c = comps.length; i < c; i++) {
codeBuffer.append(df.format(comps[i])).append(" ");
}
codeBuffer.append(command).append("\n");
}

}

+ 26
- 0
src/java/org/apache/fop/pdf/PDFDeviceColorSpace.java View File

@@ -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;
}

}

+ 10
- 6
src/java/org/apache/fop/pdf/PDFDocument.java View File

@@ -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;

+ 59
- 14
src/java/org/apache/fop/pdf/PDFFactory.java View File

@@ -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 ============ */

/**
@@ -1781,6 +1794,38 @@ public class PDFFactory {
return cs;
}

/**
* 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).
*

+ 23
- 0
src/java/org/apache/fop/pdf/PDFName.java View File

@@ -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);

+ 17
- 3
src/java/org/apache/fop/pdf/PDFPaintingState.java View File

@@ -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();
}

+ 48
- 69
src/java/org/apache/fop/pdf/PDFResources.java View File

@@ -19,6 +19,8 @@

package org.apache.fop.pdf;

import java.io.IOException;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
@@ -33,12 +35,12 @@ import org.apache.fop.fonts.base14.Symbol;
import org.apache.fop.fonts.base14.ZapfDingbats;

/**
* class representing a /Resources object.
* Class representing a /Resources object.
*
* /Resources object contain a list of references to the fonts for the
* document
*/
public class PDFResources extends PDFObject {
public class PDFResources extends PDFDictionary {

/**
* /Font objects keyed by their internal name
@@ -159,11 +161,14 @@ public class PDFResources extends PDFObject {
* Add a ColorSpace dictionary to the resources.
* @param colorSpace the color space
*/
public void addColorSpace(PDFICCBasedColorSpace colorSpace) {
this.colorSpaces.put(colorSpace.getName(), colorSpace);
String desc = ColorProfileUtil.getICCProfileDescription(
colorSpace.getICCStream().getICCProfile());
this.iccColorSpaces.put(desc, colorSpace);
public void addColorSpace(PDFColorSpace colorSpace) {
this.colorSpaces.put(new PDFName(colorSpace.getName()), colorSpace);
if (colorSpace instanceof PDFICCBasedColorSpace) {
PDFICCBasedColorSpace icc = (PDFICCBasedColorSpace)colorSpace;
String desc = ColorProfileUtil.getICCProfileDescription(
icc.getICCStream().getICCProfile());
this.iccColorSpaces.put(desc, colorSpace);
}
}

/**
@@ -181,106 +186,80 @@ public class PDFResources extends PDFObject {
* @param name the name of the color space
* @return the requested color space or null if it wasn't found
*/
public PDFICCBasedColorSpace getColorSpace(String name) {
PDFICCBasedColorSpace cs = (PDFICCBasedColorSpace)this.colorSpaces.get(name);
public PDFColorSpace getColorSpace(PDFName name) {
PDFColorSpace cs = (PDFColorSpace)this.colorSpaces.get(name);
return cs;
}

/**
* represent the object in PDF
* This adds the references to all the objects in the current
* resource context.
*
* @return the PDF
* {@inheritDoc}
*/
@Override
public String toPDFString() {
StringBuffer p = new StringBuffer(128);
p.append(getObjectID() + "<<\n");
if (!this.fonts.isEmpty()) {
p.append("/Font <<\n");
/** {@inheritDoc} */
protected int output(OutputStream stream) throws IOException {
populateDictionary();
return super.output(stream);
}

private void populateDictionary() {
if (!this.fonts.isEmpty()) {
PDFDictionary dict = new PDFDictionary(this);
/* construct PDF dictionary of font object references */
Iterator fontIterator = this.fonts.keySet().iterator();
while (fontIterator.hasNext()) {
String fontName = (String)fontIterator.next();
p.append(" /" + fontName + " "
+ ((PDFFont)this.fonts.get(fontName)).referencePDF()
+ "\n");
dict.put(fontName, (PDFFont)this.fonts.get(fontName));
}

p.append(">>\n");
put("Font", dict);
}

PDFShading currentShading = null;
if (!this.shadings.isEmpty()) {
p.append("/Shading <<\n");

PDFDictionary dict = new PDFDictionary(this);
for (Iterator iter = shadings.iterator(); iter.hasNext();) {
currentShading = (PDFShading)iter.next();
p.append(" /" + currentShading.getName() + " "
+ currentShading.referencePDF() + " "); // \n ??????
PDFShading currentShading = (PDFShading)iter.next();
dict.put(currentShading.getName(), currentShading);
}

p.append(">>\n");
put("Shading", dict);
}
// "free" the memory. Sorta.
currentShading = null;

PDFPattern currentPattern = null;
if (!this.patterns.isEmpty()) {
p.append("/Pattern <<\n");

PDFDictionary dict = new PDFDictionary(this);
for (Iterator iter = patterns.iterator(); iter.hasNext();) {
currentPattern = (PDFPattern)iter.next();
p.append(" /" + currentPattern.getName() + " "
+ currentPattern.referencePDF() + " ");
PDFPattern currentPattern = (PDFPattern)iter.next();
dict.put(currentPattern.getName(), currentPattern);
}

p.append(">>\n");
put("Pattern", dict);
}
// "free" the memory. Sorta.
currentPattern = null;

p.append("/ProcSet [ /PDF /ImageB /ImageC /Text ]\n");
PDFArray procset = new PDFArray(this);
procset.add(new PDFName("PDF"));
procset.add(new PDFName("ImageB"));
procset.add(new PDFName("ImageC"));
procset.add(new PDFName("Text"));
put("ProcSet", procset);

if (this.xObjects != null && !this.xObjects.isEmpty()) {
p = p.append("/XObject <<\n");
PDFDictionary dict = new PDFDictionary(this);
for (Iterator iter = xObjects.iterator(); iter.hasNext();) {
PDFXObject xobj = (PDFXObject)iter.next();
p = p.append(" " + xobj.getName() + " "
+ xobj.referencePDF()
+ "\n");
dict.put(xobj.getName().toString(), xobj);
}
p = p.append(">>\n");
put("XObject", dict);
}

if (!this.gstates.isEmpty()) {
p = p.append("/ExtGState <<\n");
PDFDictionary dict = new PDFDictionary(this);
for (Iterator iter = gstates.iterator(); iter.hasNext();) {
PDFGState gs = (PDFGState)iter.next();
p = p.append(" /" + gs.getName() + " "
+ gs.referencePDF()
+ "\n");
dict.put(gs.getName(), gs);
}
p = p.append(">>\n");
put("ExtGState", dict);
}

if (!this.colorSpaces.isEmpty()) {
p = p.append("/ColorSpace <<\n");
PDFDictionary dict = new PDFDictionary(this);
for (Iterator iter = colorSpaces.values().iterator(); iter.hasNext();) {
PDFICCBasedColorSpace colorSpace = (PDFICCBasedColorSpace)iter.next();
p = p.append(" /" + colorSpace.getName() + " "
+ colorSpace.referencePDF()
+ "\n");
PDFColorSpace colorSpace = (PDFColorSpace)iter.next();
dict.put(colorSpace.getName(), colorSpace);
}
p = p.append(">>\n");
put("ColorSpace", dict);
}

p = p.append(">>\nendobj\n");

return p.toString();
}

}

+ 88
- 0
src/java/org/apache/fop/pdf/PDFSeparationColorSpace.java View File

@@ -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;
}

}

+ 14
- 1
src/java/org/apache/fop/render/intermediate/IFSerializer.java View File

@@ -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));

+ 3
- 1
src/java/org/apache/fop/render/intermediate/IFState.java View File

@@ -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;

+ 12
- 3
src/java/org/apache/fop/render/java2d/Java2DGraphicsState.java View File

@@ -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: "

+ 3
- 1
src/java/org/apache/fop/render/pdf/AbstractImageAdapter.java View File

@@ -35,6 +35,7 @@ import org.apache.fop.pdf.PDFDocument;
import org.apache.fop.pdf.PDFICCBasedColorSpace;
import org.apache.fop.pdf.PDFICCStream;
import org.apache.fop.pdf.PDFImage;
import org.apache.fop.pdf.PDFName;
import org.apache.fop.pdf.PDFReference;

/**
@@ -128,7 +129,8 @@ public abstract class AbstractImageAdapter implements PDFImage {
} else {
if (cs == null && desc.startsWith("sRGB")) {
//It's the default sRGB profile which we mapped to DefaultRGB in PDFRenderer
cs = doc.getResources().getColorSpace("DefaultRGB");
cs = (PDFICCBasedColorSpace)doc.getResources().getColorSpace(
new PDFName("DefaultRGB"));
}
if (cs == null) {
// sRGB hasn't been set up for the PDF document

+ 8
- 5
src/java/org/apache/fop/render/pdf/PDFContentGenerator.java View File

@@ -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);
}

+ 34
- 2
src/java/org/apache/fop/render/xml/XMLRenderer.java View File

@@ -122,6 +122,7 @@ public class XMLRenderer extends AbstractXMLRenderer {
/**
* {@inheritDoc}
*/
@Override
public void setUserAgent(FOUserAgent agent) {
super.setUserAgent(agent);

@@ -143,6 +144,7 @@ public class XMLRenderer extends AbstractXMLRenderer {
}

/** {@inheritDoc} */
@Override
public void setupFontInfo(FontInfo inFontInfo) throws FOPException {
if (mimic != null) {
mimic.setupFontInfo(inFontInfo);
@@ -214,7 +216,7 @@ public class XMLRenderer extends AbstractXMLRenderer {
//TODO Remove the following line (makes changes in the test checks necessary)
addAttribute(name, bkg.toString());
if (bkg.getColor() != null) {
addAttribute("bkg-color", bkg.getColor().toString());
addAttribute("bkg-color", ColorUtil.colorToString(bkg.getColor()));
}
if (bkg.getURL() != null) {
addAttribute("bkg-img", bkg.getURL());
@@ -277,6 +279,7 @@ public class XMLRenderer extends AbstractXMLRenderer {
}

/** {@inheritDoc} */
@Override
public void processOffDocumentItem(OffDocumentItem oDI) {
if (oDI instanceof BookmarkData) {
renderBookmarkTree((BookmarkData) oDI);
@@ -298,6 +301,7 @@ public class XMLRenderer extends AbstractXMLRenderer {
* Renders a BookmarkTree object
* @param bookmarkRoot the BookmarkData object representing the top of the tree
*/
@Override
protected void renderBookmarkTree(BookmarkData bookmarkRoot) {
if (bookmarkRoot.getWhenToProcess() == OffDocumentItem.END_OF_DOC) {
endPageSequence();
@@ -346,6 +350,7 @@ public class XMLRenderer extends AbstractXMLRenderer {
}

/** {@inheritDoc} */
@Override
public void startRenderer(OutputStream outputStream)
throws IOException {
log.debug("Rendering areas to Area Tree XML");
@@ -377,6 +382,7 @@ public class XMLRenderer extends AbstractXMLRenderer {
}

/** {@inheritDoc} */
@Override
public void stopRenderer() throws IOException {
endPageSequence();
endElement("areaTree");
@@ -392,6 +398,7 @@ public class XMLRenderer extends AbstractXMLRenderer {
}

/** {@inheritDoc} */
@Override
public void renderPage(PageViewport page) throws IOException, FOPException {
atts.clear();
addAttribute("bounds", page.getViewArea());
@@ -416,6 +423,7 @@ public class XMLRenderer extends AbstractXMLRenderer {
}

/** {@inheritDoc} */
@Override
protected void handleExtensionAttachments(List attachments) {
if (attachments != null && attachments.size() > 0) {
startElement("extension-attachments");
@@ -438,6 +446,7 @@ public class XMLRenderer extends AbstractXMLRenderer {
}

/** {@inheritDoc} */
@Override
public void startPageSequence(PageSequence pageSequence) {
handleDocumentExtensionAttachments();
endPageSequence(); // move this before handleDocumentExtensionAttachments() ?
@@ -502,6 +511,7 @@ public class XMLRenderer extends AbstractXMLRenderer {
/**
* {@inheritDoc}
*/
@Override
protected void renderRegionViewport(RegionViewport port) {
if (port != null) {
atts.clear();
@@ -549,11 +559,13 @@ public class XMLRenderer extends AbstractXMLRenderer {
}

/** {@inheritDoc} */
@Override
protected void startVParea(CTM ctm, Rectangle2D clippingRect) {
//only necessary for graphical output
}

/** {@inheritDoc} */
@Override
protected void endVParea() {
//only necessary for graphical output
}
@@ -562,6 +574,7 @@ public class XMLRenderer extends AbstractXMLRenderer {
* {@inheritDoc}
* org.apache.fop.area.inline.InlineArea)
*/
@Override
protected void renderInlineAreaBackAndBorders(InlineArea area) {
//only necessary for graphical output
}
@@ -569,6 +582,7 @@ public class XMLRenderer extends AbstractXMLRenderer {
/**
* {@inheritDoc}
*/
@Override
protected void renderBeforeFloat(BeforeFloat bf) {
startElement("beforeFloat");
super.renderBeforeFloat(bf);
@@ -578,6 +592,7 @@ public class XMLRenderer extends AbstractXMLRenderer {
/**
* {@inheritDoc}
*/
@Override
protected void renderFootnote(Footnote footnote) {
atts.clear();
addAttribute("top-offset", footnote.getTop());
@@ -589,6 +604,7 @@ public class XMLRenderer extends AbstractXMLRenderer {
/**
* {@inheritDoc}
*/
@Override
protected void renderMainReference(MainReference mr) {
atts.clear();
addAreaAttributes(mr);
@@ -610,7 +626,7 @@ public class XMLRenderer extends AbstractXMLRenderer {
addTraitAttributes(span);
startElement("span", atts);
for (int c = 0; c < span.getColumnCount(); c++) {
NormalFlow flow = (NormalFlow) span.getNormalFlow(c);
NormalFlow flow = span.getNormalFlow(c);

renderFlow(flow);
}
@@ -622,6 +638,7 @@ public class XMLRenderer extends AbstractXMLRenderer {
/**
* {@inheritDoc}
*/
@Override
protected void renderFlow(NormalFlow flow) {
// the normal flow reference area contains stacked blocks
atts.clear();
@@ -633,6 +650,7 @@ public class XMLRenderer extends AbstractXMLRenderer {
}

/** {@inheritDoc} */
@Override
protected void renderReferenceArea(Block block) {
handleBlockTraits(block);

@@ -643,6 +661,7 @@ public class XMLRenderer extends AbstractXMLRenderer {
}

/** {@inheritDoc} */
@Override
protected void renderBlock(Block block) {
atts.clear();
addAreaAttributes(block);
@@ -691,6 +710,7 @@ public class XMLRenderer extends AbstractXMLRenderer {
/**
* {@inheritDoc}
*/
@Override
protected void renderLineArea(LineArea line) {
atts.clear();
addAreaAttributes(line);
@@ -703,6 +723,7 @@ public class XMLRenderer extends AbstractXMLRenderer {
/**
* {@inheritDoc}
*/
@Override
protected void renderInlineArea(InlineArea inlineArea) {
atts.clear();
if (inlineArea.getClass() == InlineArea.class) {
@@ -721,6 +742,7 @@ public class XMLRenderer extends AbstractXMLRenderer {
/**
* {@inheritDoc}
*/
@Override
protected void renderViewport(Viewport viewport) {
atts.clear();
addAreaAttributes(viewport);
@@ -738,6 +760,7 @@ public class XMLRenderer extends AbstractXMLRenderer {
/**
* {@inheritDoc}
*/
@Override
public void renderImage(Image image, Rectangle2D pos) {
atts.clear();
addAreaAttributes(image);
@@ -751,6 +774,7 @@ public class XMLRenderer extends AbstractXMLRenderer {
/**
* {@inheritDoc}
*/
@Override
public void renderContainer(Container cont) {
startElement("container");
super.renderContainer(cont);
@@ -763,6 +787,7 @@ public class XMLRenderer extends AbstractXMLRenderer {
* @param pos the position of the foreign object
* @see org.apache.fop.render.AbstractRenderer#renderForeignObject(ForeignObject, Rectangle2D)
*/
@Override
public void renderForeignObject(ForeignObject fo, Rectangle2D pos) {
atts.clear();
addAreaAttributes(fo);
@@ -779,6 +804,7 @@ public class XMLRenderer extends AbstractXMLRenderer {
/**
* {@inheritDoc}
*/
@Override
protected void renderInlineSpace(Space space) {
atts.clear();
addAreaAttributes(space);
@@ -791,6 +817,7 @@ public class XMLRenderer extends AbstractXMLRenderer {
/**
* {@inheritDoc}
*/
@Override
protected void renderText(TextArea text) {
atts.clear();
if (text.getTextWordSpaceAdjust() != 0) {
@@ -811,6 +838,7 @@ public class XMLRenderer extends AbstractXMLRenderer {
/**
* {@inheritDoc}
*/
@Override
protected void renderWord(WordArea word) {
atts.clear();
addAttribute("offset", word.getOffset());
@@ -838,6 +866,7 @@ public class XMLRenderer extends AbstractXMLRenderer {
/**
* {@inheritDoc}
*/
@Override
protected void renderSpace(SpaceArea space) {
atts.clear();
addAttribute("offset", space.getOffset());
@@ -853,6 +882,7 @@ public class XMLRenderer extends AbstractXMLRenderer {
/**
* {@inheritDoc}
*/
@Override
protected void renderInlineParent(InlineParent ip) {
atts.clear();
addAreaAttributes(ip);
@@ -866,6 +896,7 @@ public class XMLRenderer extends AbstractXMLRenderer {
/**
* {@inheritDoc}
*/
@Override
protected void renderInlineBlockParent(InlineBlockParent ibp) {
atts.clear();
addAreaAttributes(ibp);
@@ -879,6 +910,7 @@ public class XMLRenderer extends AbstractXMLRenderer {
/**
* {@inheritDoc}
*/
@Override
protected void renderLeader(Leader area) {
atts.clear();
addAreaAttributes(area);

+ 12
- 3
src/java/org/apache/fop/svg/PDFBridgeContext.java View File

@@ -25,6 +25,7 @@ import org.apache.batik.bridge.BridgeContext;
import org.apache.batik.bridge.DocumentLoader;
import org.apache.batik.bridge.SVGTextElementBridge;
import org.apache.batik.bridge.UserAgent;
import org.apache.batik.dom.svg.SVGOMDocument;
import org.apache.batik.gvt.TextPainter;

import org.apache.xmlgraphics.image.loader.ImageManager;
@@ -86,6 +87,7 @@ public class PDFBridgeContext extends AbstractFOPBridgeContext {
}

/** {@inheritDoc} */
@Override
public void registerSVGBridges() {
super.registerSVGBridges();

@@ -123,10 +125,17 @@ public class PDFBridgeContext extends AbstractFOPBridgeContext {
putBridge(new PDFImageElementBridge());
}

// Make sure any 'sub bridge contexts' also have our bridges.
//TODO There's no matching method in the super-class here
/** @return the new bridge context */
/** {@inheritDoc} */
@Override
public BridgeContext createBridgeContext() {
//Retained for pre-Batik-1.7 compatibility
return createBridgeContext(null);
}

/** {@inheritDoc} */
@Override
public BridgeContext createBridgeContext(SVGOMDocument doc) {
// Make sure any 'sub bridge contexts' also have our bridges.
return new PDFBridgeContext(getUserAgent(), getDocumentLoader(),
fontInfo,
getImageManager(),

+ 9
- 8
src/java/org/apache/fop/svg/PDFDocumentGraphics2D.java View File

@@ -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());
}

/**

+ 24
- 38
src/java/org/apache/fop/svg/PDFGraphics2D.java View File

@@ -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();

+ 5
- 1
src/java/org/apache/fop/traits/BorderProps.java View File

@@ -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('(');

+ 8
- 2
src/java/org/apache/fop/util/AbstractPaintingState.java View File

@@ -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

+ 259
- 0
src/java/org/apache/fop/util/ColorExt.java View File

@@ -1,3 +1,4 @@
<<<<<<< .working
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
@@ -248,3 +249,261 @@ public final class ColorExt extends Color {
}

}
=======
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/* $Id$ */

package org.apache.fop.util;

import java.awt.Color;
import java.awt.color.ColorSpace;
import java.util.Arrays;

import org.apache.xmlgraphics.java2d.color.ColorWithAlternatives;

/**
* Color helper class.
* <p>
* This class extends java.awt.Color class keeping track of the original color
* property values specified by the fo user in a rgb-icc call.
* @deprecated Replaced by {@link ColorWithAlternatives}
*/
@Deprecated
public final class ColorExt extends Color {
//
private static final long serialVersionUID = 1L;

// Values of fop-rgb-icc arguments
private float rgbReplacementRed;
private float rgbReplacementGreen;
private float rgbReplacementBlue;

private String iccProfileName;
private String iccProfileSrc;
private ColorSpace colorSpace;

private float[] colorValues;

/*
* Helper for createFromFoRgbIcc
*/
private ColorExt(ColorSpace colorSpace, float[] colorValues, float opacity) {
super(colorSpace, colorValues, opacity);
}

/*
* Helper for createFromSvgIccColor
*/
private ColorExt(float red, float green, float blue, float opacity) {
super(red, green, blue, opacity);
}

/**
* Create ColorExt object backup up FO's rgb-icc color function
*
* @param redReplacement
* Red part of RGB replacement color that will be used when ICC
* profile can not be loaded
* @param greenReplacement
* Green part of RGB replacement color that will be used when ICC
* profile can not be loaded
* @param blueReplacement
* Blue part of RGB replacement color that will be used when ICC
* profile can not be loaded
* @param profileName
* Name of ICC profile
* @param profileSrc
* Source of ICC profile
* @param colorSpace
* ICC ColorSpace for the ICC profile
* @param iccValues
* color values
* @return the requested color object
*/
public static ColorExt createFromFoRgbIcc(float redReplacement,
float greenReplacement, float blueReplacement, String profileName,
String profileSrc, ColorSpace colorSpace, float[] iccValues) {
ColorExt ce = new ColorExt(colorSpace, iccValues, 1.0f);
ce.rgbReplacementRed = redReplacement;
ce.rgbReplacementGreen = greenReplacement;
ce.rgbReplacementBlue = blueReplacement;
ce.iccProfileName = profileName;
ce.iccProfileSrc = profileSrc;
ce.colorSpace = colorSpace;
ce.colorValues = iccValues;
return ce;
}

/**
* Create ColorExt object backing up SVG's icc-color function.
*
* @param red
* Red value resulting from the conversion from the user provided
* (icc) color values to the batik (rgb) color space
* @param green
* Green value resulting from the conversion from the user
* provided (icc) color values to the batik (rgb) color space
* @param blue
* Blue value resulting from the conversion from the user
* provided (icc) color values to the batik (rgb) color space
* @param opacity
* Opacity
* @param profileName
* ICC profile name
* @param profileHref
* the URI to the color profile
* @param profileCS
* ICC ColorSpace profile
* @param colorValues
* ICC color values
* @return the requested color object
*/
public static ColorExt createFromSvgIccColor( // CSOK: ParameterNumber
float red, float green,
float blue, float opacity, String profileName, String profileHref,
ColorSpace profileCS, float[] colorValues) {
//TODO this method is not referenced by FOP, can it be deleted?
ColorExt ce = new ColorExt(red, green, blue, opacity);
ce.rgbReplacementRed = -1;
ce.rgbReplacementGreen = -1;
ce.rgbReplacementBlue = -1;
ce.iccProfileName = profileName;
ce.iccProfileSrc = profileHref;
ce.colorSpace = profileCS;
ce.colorValues = colorValues;
return ce;

}

/** {@inheritDoc} */
@Override
public int hashCode() {
//implementation from the superclass should be good enough for our purposes
return super.hashCode();
}

/** {@inheritDoc} */
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!super.equals(obj)) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
ColorExt other = (ColorExt)obj;
//TODO maybe use super.getColorComponents() instead
if (!Arrays.equals(colorValues, other.colorValues)) {
return false;
}
if (iccProfileName == null) {
if (other.iccProfileName != null) {
return false;
}
} else if (!iccProfileName.equals(other.iccProfileName)) {
return false;
}
if (iccProfileSrc == null) {
if (other.iccProfileSrc != null) {
return false;
}
} else if (!iccProfileSrc.equals(other.iccProfileSrc)) {
return false;
}
if (Float.floatToIntBits(rgbReplacementBlue)
!= Float.floatToIntBits(other.rgbReplacementBlue)) {
return false;
}
if (Float.floatToIntBits(rgbReplacementGreen)
!= Float.floatToIntBits(other.rgbReplacementGreen)) {
return false;
}
if (Float.floatToIntBits(rgbReplacementRed)
!= Float.floatToIntBits(other.rgbReplacementRed)) {
return false;
}
return true;
}

/**
* Get ICC profile name
*
* @return ICC profile name
*/
public String getIccProfileName() {
return this.iccProfileName;
}

/**
* Get ICC profile source
*
* @return ICC profile source
*/
public String getIccProfileSrc() {
return this.iccProfileSrc;
}

/**
* @return the original ColorSpace
*/
public ColorSpace getOrigColorSpace() {
//TODO this method is probably unnecessary due to super.cs and getColorSpace()
return this.colorSpace;
}

/**
* Returns the original color values.
* @return the original color values
*/
public float[] getOriginalColorComponents() {
//TODO this method is probably unnecessary due to super.fvalue and getColorComponents()
float[] copy = new float[this.colorValues.length];
System.arraycopy(this.colorValues, 0, copy, 0, copy.length);
return copy;
}

/**
* Create string representation of fop-rgb-icc function call to map this
* ColorExt settings
* @return the string representing the internal fop-rgb-icc() function call
*/
public String toFunctionCall() {
StringBuffer sb = new StringBuffer(40);
sb.append("fop-rgb-icc(");
sb.append(this.rgbReplacementRed + ",");
sb.append(this.rgbReplacementGreen + ",");
sb.append(this.rgbReplacementBlue + ",");
sb.append(this.iccProfileName + ",");
if (this.iccProfileSrc != null) {
sb.append("\"" + this.iccProfileSrc + "\"");
}
float[] colorComponents = this.getColorComponents(null);
for (int ix = 0; ix < colorComponents.length; ix++) {
sb.append(",");
sb.append(colorComponents[ix]);
}
sb.append(")");
return sb.toString();
}

}
>>>>>>> .merge-right.r1069429

+ 13
- 6
src/java/org/apache/fop/util/ColorSpaceCache.java View File

@@ -20,7 +20,6 @@
package org.apache.fop.util;

import java.awt.color.ColorSpace;
import java.awt.color.ICC_ColorSpace;
import java.awt.color.ICC_Profile;
import java.util.Collections;
import java.util.Map;
@@ -33,6 +32,9 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.xmlgraphics.java2d.color.profile.ColorProfileUtil;

import org.apache.xmlgraphics.java2d.color.ICCColorSpaceWithIntent;
import org.apache.xmlgraphics.java2d.color.RenderingIntent;

/**
* Map with cached ICC based ColorSpace objects.
*/
@@ -61,13 +63,17 @@ public class ColorSpaceCache {
* The FOP URI resolver is used to try and locate the ICC file.
* If that fails null is returned.
*
* @param profileName the profile name
* @param base a base URI to resolve relative URIs
* @param iccProfileSrc ICC Profile source to return a ColorSpace for
* @param renderingIntent overriding rendering intent
* @return ICC ColorSpace object or null if ColorSpace could not be created
*/
public ColorSpace get(String base, String iccProfileSrc) {
public ColorSpace get(String profileName, String base, String iccProfileSrc,
RenderingIntent renderingIntent) {
String key = profileName + ":" + base + iccProfileSrc;
ColorSpace colorSpace = null;
if (!colorSpaceMap.containsKey(base + iccProfileSrc)) {
if (!colorSpaceMap.containsKey(key)) {
try {
ICC_Profile iccProfile = null;
// First attempt to use the FOP URI resolver to locate the ICC
@@ -88,7 +94,8 @@ public class ColorSpaceCache {
// iccProfile = ICC_Profile.getInstance(iccProfileSrc);
}
if (iccProfile != null) {
colorSpace = new ICC_ColorSpace(iccProfile);
colorSpace = new ICCColorSpaceWithIntent(iccProfile, renderingIntent,
profileName, iccProfileSrc);
}
} catch (Exception e) {
// Ignore exception - will be logged a bit further down
@@ -97,14 +104,14 @@ public class ColorSpaceCache {

if (colorSpace != null) {
// Put in cache (not when VM resolved it as we can't control
colorSpaceMap.put(base + iccProfileSrc, colorSpace);
colorSpaceMap.put(key, colorSpace);
} else {
// TODO To avoid an excessive amount of warnings perhaps
// register a null ColorMap in the colorSpaceMap
log.warn("Color profile '" + iccProfileSrc + "' not found.");
}
} else {
colorSpace = colorSpaceMap.get(base + iccProfileSrc);
colorSpace = colorSpaceMap.get(key);
}
return colorSpace;
}

+ 557
- 283
src/java/org/apache/fop/util/ColorUtil.java
File diff suppressed because it is too large
View File


+ 85
- 0
src/java/org/apache/fop/util/ColorWithFallback.java View File

@@ -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;
}

}

+ 6
- 0
status.xml View File

@@ -59,6 +59,12 @@
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">
Added support for CIE Lab colors (from XSL-FO 2.0 WD).
</action>
<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="Renderers" dev="JM" type="fix" fixes-bug="50705" due-to="Mehdi Houshmand">
Fix to preserve the order of AFP TLEs and NOPs as given in the XSL-FO document.
</action>

+ 5
- 6
test/java/org/apache/fop/traits/BorderPropsTestCase.java View File

@@ -23,11 +23,10 @@ import java.awt.Color;

import junit.framework.TestCase;

import org.apache.xmlgraphics.java2d.color.ColorSpaces;
import org.apache.xmlgraphics.java2d.color.ColorWithAlternatives;
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,10 @@ 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);
//Convert to sRGB with CMYK alternative as constructed by the cmyk() function
float[] rgb = col.getRGBColorComponents(null);
col = new ColorWithAlternatives(rgb[0], rgb[1], rgb[2], new Color[] {col});
b1 = new BorderProps(Constants.EN_INSET, 9999,
col, BorderProps.SEPARATE);
ser = b1.toString();

+ 141
- 31
test/java/org/apache/fop/util/ColorUtilTestCase.java View File

@@ -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,39 @@ 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.getColorSpaceCache().get(
"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
+ "\"" + sRGBLoc.toASCIIString() + "\",1.0,0.0,0.0)";
colActual = (ColorWithFallback)ColorUtil.parseColorString(ua, colSpec);
assertEquals(cs, colActual.getColorSpace());
assertEquals(255, colActual.getRed(), 2f); //Java 5: 253, Java 6: 255
assertEquals(0, colActual.getGreen(), 25f); //Java 5: 25, Java 6: 0
assertEquals(0, colActual.getBlue());
//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());
assertEquals(cs, colActual.getColorSpace());
float[] comps = colActual.getColorComponents(null);
assertEquals(3, comps.length);
assertEquals(1f, comps[0], 0);
assertEquals(0f, comps[1], 0);
assertEquals(0f, comps[2], 0);
assertEquals(0, colActual.getAlternativeColors().length);

Color fallback = colActual.getFallbackColor();
assertTrue(fallback.getColorSpace().isCS_sRGB());
assertEquals(255, fallback.getRed());
assertEquals(0, fallback.getGreen());
assertEquals(0, fallback.getBlue());

assertEquals(colSpec, ColorUtil.colorToString(colActual));

@@ -148,16 +163,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,12 +183,13 @@ 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);
@@ -181,12 +198,13 @@ public class ColorUtilTestCase extends TestCase {
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 +214,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);
@@ -209,6 +228,97 @@ public class ColorUtilTestCase extends TestCase {
assertEquals(0.5f, comps[3], 0);
assertEquals("fop-rgb-icc(0.5,0.5,0.5,#CMYK,,0.0,0.0,0.0,0.5)",
ColorUtil.colorToString(colActual));

//Verify that the cmyk() and fop-rgb-icc(#CMYK) functions have the same results
ColorWithAlternatives colCMYK = (ColorWithAlternatives)ColorUtil.parseColorString(
null, "cmyk(0,0,0,0.5)");
assertEquals(colCMYK.getAlternativeColors()[0], colActual.getAlternativeColors()[0]);
//The following doesn't work:
//assertEquals(colCMYK, colActual);
//java.awt.Color does not consistenly calculate the int RGB values:
//Color(ColorSpace cspace, float components[], float alpha): 0.5 --> 127
//Color(float r, float g, float b): 0.5 --> 128
if (!colCMYK.equals(colActual)) {
System.out.println("Info: java.awt.Color does not consistently calculate"
+ " int RGB values from float RGB values.");
}
}

/**
* 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(), 5);
assertEquals(204, colActual.getGreen(), 3);
assertEquals(0, colActual.getBlue(), 12);
//sRGB results differ between JDKs

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.getColorSpaceCache().get(
"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(), 2);
assertEquals(193, colActual.getGreen(), 2);
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));
}
}

BIN
test/resources/color/ncp-example.icc View File


Loading…
Cancel
Save