git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@815938 13f79535-47bb-0310-9956-ffa450edef68tags/fop-1_0
@@ -226,6 +226,23 @@ to following pages. Here is an example of FO code creating such a table-header:< | |||
color space the CMYK value is converted to an sRGB value. | |||
</p> | |||
</section> | |||
<section id="pseudo-color-profiles"> | |||
<title>#CMYK pseudo-profile</title> | |||
<p><code>color rgb-icc(numeric, numeric, numeric, #CMYK, numeric, numeric, numeric, numeric)</code></p> | |||
<p> | |||
The <code>rgb-icc</code> function will respond to a pseudo-profile called "#CMYK" | |||
which indicates a device-specific CMYK color space. The "#CMYK" profile is implicitely | |||
available and doesn't have to be (and cannot be) defined through an | |||
<code>fo:color-profile</code> element. It is provided for compatibility with certain | |||
commercial XSL-FO implementations. Please note that this is not part of the official | |||
specification but rather a convention. The following two color specifications are | |||
equivalent: | |||
</p> | |||
<ul> | |||
<li><code>cmyk(0%,0%,20%,40%)</code></li> | |||
<li><code>rgb-icc(153, 153, 102, #CMYK, 0, 0, 0.2, 0.4)</code></li> | |||
</ul> | |||
</section> | |||
</section> | |||
<section id="prepress"> | |||
<title>Prepress Support</title> |
@@ -24,6 +24,7 @@ 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; | |||
import org.apache.fop.util.ColorUtil; | |||
/** | |||
* Implements the rgb-icc() function. | |||
@@ -63,13 +64,15 @@ class ICCColorFunction extends FunctionBase { | |||
} else { | |||
cp = decls.getColorProfile(colorProfileName); | |||
if (cp == null) { | |||
PropertyException pe = new PropertyException("The " + colorProfileName | |||
+ " color profile was not declared"); | |||
pe.setPropertyInfo(pInfo); | |||
throw pe; | |||
if (!ColorUtil.isPseudoProfile(colorProfileName)) { | |||
PropertyException pe = new PropertyException("The " + colorProfileName | |||
+ " color profile was not declared"); | |||
pe.setPropertyInfo(pInfo); | |||
throw pe; | |||
} | |||
} | |||
} | |||
String src = cp.getSrc(); | |||
String src = (cp != null ? cp.getSrc() : ""); | |||
float red = 0, green = 0, blue = 0; | |||
red = args[0].getNumber().floatValue(); |
@@ -243,15 +243,20 @@ class PropertyTokenizer { | |||
} | |||
private void nextColor () throws PropertyException { | |||
private void nextColor() throws PropertyException { | |||
if (exprIndex < exprLength | |||
&& isHexDigit(expr.charAt(exprIndex))) { | |||
++exprIndex; | |||
scanHexDigits(); | |||
currentToken = TOK_COLORSPEC; | |||
int len = exprIndex - currentTokenStartIndex - 1; | |||
if (len % 3 == 0) { | |||
currentToken = TOK_COLORSPEC; | |||
} else { | |||
scanRestOfName(); | |||
currentToken = TOK_NCNAME; | |||
} | |||
currentTokenValue = expr.substring(currentTokenStartIndex, | |||
exprIndex); | |||
// Probably should have some multiple of 3 for length! | |||
return; | |||
} else { | |||
throw new PropertyException("illegal character '#'"); | |||
@@ -263,11 +268,15 @@ class PropertyTokenizer { | |||
*/ | |||
private void scanName() { | |||
if (exprIndex < exprLength && isNameStartChar(expr.charAt(exprIndex))) { | |||
while (++exprIndex < exprLength | |||
&& isNameChar(expr.charAt(exprIndex))) { } | |||
scanRestOfName(); | |||
} | |||
} | |||
private void scanRestOfName() { | |||
while (++exprIndex < exprLength | |||
&& isNameChar(expr.charAt(exprIndex))) { } | |||
} | |||
/** | |||
* Attempt to recognize a valid sequence of decimal DIGITS in the | |||
* input expression. |
@@ -176,7 +176,9 @@ public final class ColorExt extends Color { | |||
sb.append(this.rgbReplacementGreen + ","); | |||
sb.append(this.rgbReplacementBlue + ","); | |||
sb.append(this.iccProfileName + ","); | |||
sb.append("\"" + this.iccProfileSrc + "\""); | |||
if (this.iccProfileSrc != null) { | |||
sb.append("\"" + this.iccProfileSrc + "\""); | |||
} | |||
float[] colorComponents = this.getColorComponents(null); | |||
for (int ix = 0; ix < colorComponents.length; ix++) { | |||
sb.append(","); |
@@ -38,6 +38,9 @@ import org.apache.fop.fo.expr.PropertyException; | |||
*/ | |||
public final class ColorUtil { | |||
/** The name for the uncalibrated CMYK pseudo-profile */ | |||
public static final String CMYK_PSEUDO_PROFILE = "#CMYK"; | |||
/** | |||
* | |||
* keeps all the predefined and parsed colors. | |||
@@ -319,26 +322,32 @@ public final class ColorUtil { | |||
if (iccProfileName == null || "".equals(iccProfileName)) { | |||
throw new PropertyException("ICC profile name missing"); | |||
} | |||
/* Get and verify ICC profile source */ | |||
String iccProfileSrc = args[4].trim(); | |||
if (iccProfileSrc == null || "".equals(iccProfileSrc)) { | |||
throw new PropertyException("ICC profile source missing"); | |||
} | |||
if (iccProfileSrc.startsWith("\"") || iccProfileSrc.startsWith("'")) { | |||
iccProfileSrc = iccProfileSrc.substring(1); | |||
} | |||
if (iccProfileSrc.endsWith("\"") || iccProfileSrc.endsWith("'")) { | |||
iccProfileSrc = iccProfileSrc.substring(0, iccProfileSrc.length() - 1); | |||
ColorSpace colorSpace = null; | |||
String iccProfileSrc = null; | |||
if (isPseudoProfile(iccProfileName)) { | |||
if (CMYK_PSEUDO_PROFILE.equalsIgnoreCase(iccProfileName)) { | |||
colorSpace = CMYKColorSpace.getInstance(); | |||
} else { | |||
assert false : "Incomplete implementation"; | |||
} | |||
} else { | |||
/* Get and verify ICC profile source */ | |||
iccProfileSrc = args[4].trim(); | |||
if (iccProfileSrc == null || "".equals(iccProfileSrc)) { | |||
throw new PropertyException("ICC profile source missing"); | |||
} | |||
if (iccProfileSrc.startsWith("\"") || iccProfileSrc.startsWith("'")) { | |||
iccProfileSrc = iccProfileSrc.substring(1); | |||
} | |||
if (iccProfileSrc.endsWith("\"") || iccProfileSrc.endsWith("'")) { | |||
iccProfileSrc = iccProfileSrc.substring(0, iccProfileSrc.length() - 1); | |||
} | |||
} | |||
/* ICC profile arguments */ | |||
float[] iccComponents = new float[args.length - 5]; | |||
for (int ix = 4; ++ix < args.length;) { | |||
iccComponents[ix - 5] = Float.parseFloat(args[ix].trim()); | |||
} | |||
/* Ask FOP factory to get ColorSpace for the specified ICC profile source */ | |||
ColorSpace colorSpace = (foUserAgent != null | |||
? foUserAgent.getFactory().getColorSpace( | |||
foUserAgent.getBaseURL(), iccProfileSrc) : null); | |||
float red = 0, green = 0, blue = 0; | |||
red = Float.parseFloat(args[0].trim()); | |||
@@ -352,6 +361,11 @@ public final class ColorUtil { | |||
+ "Fallback RGB arguments to fop-rgb-icc() must be [0..1]"); | |||
} | |||
/* Ask FOP factory to get ColorSpace for the specified ICC profile source */ | |||
if (foUserAgent != null && iccProfileSrc != null) { | |||
colorSpace = foUserAgent.getFactory().getColorSpace( | |||
foUserAgent.getBaseURL(), iccProfileSrc); | |||
} | |||
if (colorSpace != null) { | |||
// ColorSpace available - create ColorExt (keeps track of replacement rgb | |||
// values for possible later colorTOsRGBString call | |||
@@ -440,7 +454,7 @@ public final class ColorUtil { | |||
CMYKColorSpace cmykCs = CMYKColorSpace.getInstance(); | |||
float[] rgb = cmykCs.toRGB(cmyk); | |||
parsedColor = ColorExt.createFromFoRgbIcc(rgb[0], rgb[1], rgb[2], | |||
null, "#CMYK", cmykCs, cmyk); | |||
CMYK_PSEUDO_PROFILE, null, cmykCs, cmyk); | |||
} catch (PropertyException pe) { | |||
throw pe; | |||
} catch (Exception e) { | |||
@@ -465,13 +479,13 @@ public final class ColorUtil { | |||
*/ | |||
public static String colorToString(Color color) { | |||
ColorSpace cs = color.getColorSpace(); | |||
if (cs != null && cs.getType() == ColorSpace.TYPE_CMYK) { | |||
if (color instanceof ColorExt) { | |||
return ((ColorExt)color).toFunctionCall(); | |||
} else if (cs != null && cs.getType() == ColorSpace.TYPE_CMYK) { | |||
StringBuffer sbuf = new StringBuffer(24); | |||
float[] cmyk = color.getColorComponents(null); | |||
sbuf.append("cmyk(" + cmyk[0] + "," + cmyk[1] + "," + cmyk[2] + "," + cmyk[3] + ")"); | |||
return sbuf.toString(); | |||
} else if (color instanceof ColorExt) { | |||
return ((ColorExt)color).toFunctionCall(); | |||
} else { | |||
StringBuffer sbuf = new StringBuffer(); | |||
sbuf.append('#'); | |||
@@ -681,4 +695,35 @@ public final class ColorUtil { | |||
return new Color(cols[0], cols[1], cols[2], cols[3]); | |||
} | |||
/** | |||
* Indicates whether the given color profile name is one of the pseudo-profiles supported | |||
* by FOP (ex. #CMYK). | |||
* @param colorProfileName the color profile name to check | |||
* @return true if the color profile name is of a built-in pseudo-profile | |||
*/ | |||
public static boolean isPseudoProfile(String colorProfileName) { | |||
return CMYK_PSEUDO_PROFILE.equalsIgnoreCase(colorProfileName); | |||
} | |||
/** | |||
* Indicates whether the color is a gray value. | |||
* @param col the color | |||
* @return true if it is a gray value | |||
*/ | |||
public static boolean isGray(Color col) { | |||
return (col.getRed() == col.getBlue() && col.getRed() == col.getGreen()); | |||
} | |||
/** | |||
* Creates an uncalibrary CMYK color with the given gray value. | |||
* @param black the gray component (0 - 1) | |||
* @return the CMYK color | |||
*/ | |||
public static Color toCMYKGrayColor(float black) { | |||
float[] cmyk = new float[] {0f, 0f, 0f, 1.0f - black}; | |||
CMYKColorSpace cmykCs = CMYKColorSpace.getInstance(); | |||
float[] rgb = cmykCs.toRGB(cmyk); | |||
return ColorExt.createFromFoRgbIcc(rgb[0], rgb[1], rgb[2], | |||
CMYK_PSEUDO_PROFILE, null, cmykCs, cmyk); | |||
} | |||
} |
@@ -58,6 +58,10 @@ | |||
documents. Example: the fix of marks layering will be such a case when it's done. | |||
--> | |||
<release version="FOP Trunk" date="TBD"> | |||
<action context="Extensions" dev="JM" type="add"> | |||
Added support for the #CMYK pseudo-profile supported by some commercial XSL implementations | |||
on the rgb-icc() function. | |||
</action> | |||
<action context="Layout" dev="VH" type="add" importance="high"> | |||
Added limited support for pages of different inline-progression-dimensions within a | |||
page-sequence. |
@@ -154,7 +154,8 @@ public class ColorUtilTestCase extends TestCase { | |||
assertEquals(0f, comps[1], 0); | |||
assertEquals(1f, comps[2], 0); | |||
assertEquals(0f, comps[3], 0); | |||
assertEquals("cmyk(0.0,0.0,1.0,0.0)", ColorUtil.colorToString(colActual)); | |||
assertEquals("fop-rgb-icc(1.0,1.0,0.0,#CMYK,,0.0,0.0,1.0,0.0)", | |||
ColorUtil.colorToString(colActual)); | |||
colSpec = "cmyk(0.0274, 0.2196, 0.3216, 0.0)"; | |||
colActual = (ColorExt)ColorUtil.parseColorString(null, colSpec); | |||
@@ -167,7 +168,23 @@ public class ColorUtilTestCase extends TestCase { | |||
assertEquals(0.2196f, comps[1], 0.001); | |||
assertEquals(0.3216f, comps[2], 0.001); | |||
assertEquals(0f, comps[3], 0); | |||
assertEquals("cmyk(0.0274,0.2196,0.3216,0.0)", ColorUtil.colorToString(colActual)); | |||
assertEquals("fop-rgb-icc(0.9726,0.7804,0.67840004,#CMYK,,0.0274,0.2196,0.3216,0.0)", | |||
ColorUtil.colorToString(colActual)); | |||
colSpec = "fop-rgb-icc(1.0,1.0,0.0,#CMYK,,0.0,0.0,1.0,0.0)"; | |||
colActual = (ColorExt)ColorUtil.parseColorString(null, colSpec); | |||
assertEquals(255, colActual.getRed()); | |||
assertEquals(255, colActual.getGreen()); | |||
assertEquals(0, colActual.getBlue()); | |||
assertEquals(CMYKColorSpace.getInstance(), colActual.getColorSpace()); | |||
comps = colActual.getOriginalColorComponents(); | |||
assertEquals(4, comps.length); | |||
assertEquals(0f, comps[0], 0); | |||
assertEquals(0f, comps[1], 0); | |||
assertEquals(1f, comps[2], 0); | |||
assertEquals(0f, comps[3], 0); | |||
assertEquals("fop-rgb-icc(1.0,1.0,0.0,#CMYK,,0.0,0.0,1.0,0.0)", | |||
ColorUtil.colorToString(colActual)); | |||
} | |||
} |
@@ -41,9 +41,7 @@ | |||
<fo:block color="rgb-icc(100%,50%,0%, sRGB, 1, 0.5, 0)">color "rgb-icc(100%,50%,0%, sRGB, 1, 0.5, 0)"</fo:block> | |||
<fo:block color="rgb-icc(0%,100%,0%, unknown, 1, 0.5, 0)">color "rgb-icc(0%,100%,0%, unknown, 1, 0.5, 0)"</fo:block> | |||
<fo:block color="cmyk(0%,0%,20%,40%)">color "cmyk(0%,0%,20%,40%)" (Khaki)</fo:block> | |||
<!-- NYI | |||
<fo:block color="rgb-icc(153, 153, 102, #CMYK, 0, 0, 0.2, 0.4)">color "rgb-icc(153, 153, 102, #CMYK, 0, 0, 0.2, 0.4)" (Khaki)</fo:block> | |||
--> | |||
</fo:flow> | |||
</fo:page-sequence> | |||
</fo:root> | |||
@@ -58,6 +56,7 @@ | |||
<eval expected="#ff8000" xpath="//block[3]//text/@color"/> | |||
<eval expected="fop-rgb-icc(1.0,0.5,0.0,sRGB,"../../../src/java/org/apache/fop/pdf/sRGB Color Space Profile.icm",1.0,0.5,0.0)" xpath="//block[4]//text/@color"/> | |||
<eval expected="#00ff00" xpath="//block[5]//text/@color"/> | |||
<eval expected="cmyk(0.0,0.0,0.2,0.4)" xpath="//block[6]//text/@color"/> | |||
<eval expected="fop-rgb-icc(0.6,0.6,0.48000002,#CMYK,,0.0,0.0,0.2,0.4)" xpath="//block[6]//text/@color"/> | |||
<eval expected="fop-rgb-icc(0.6,0.6,0.48000002,#CMYK,,0.0,0.0,0.2,0.4)" xpath="//block[7]//text/@color"/> | |||
</checks> | |||
</testcase> |