git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1541551 13f79535-47bb-0310-9956-ffa450edef68pull/22/head
@@ -579,6 +579,7 @@ list of possible build targets. | |||
<include name="org/apache/fop/util/Finalizable.class"/> | |||
<include name="org/apache/fop/util/CharUtilities.class"/> | |||
<include name="org/apache/fop/util/DecimalFormatCache*.class"/> | |||
<include name="org/apache/fop/render/shading/**"/> | |||
</patternset> | |||
<!-- PDF transcoder --> | |||
<patternset> |
@@ -310,12 +310,7 @@ public class PDFFactory { | |||
theFunctionDataStream, | |||
theFilter); | |||
PDFFunction oldfunc = getDocument().findFunction(function); | |||
if (oldfunc == null) { | |||
getDocument().registerObject(function); | |||
} else { | |||
function = oldfunc; | |||
} | |||
function = registerFunction(function); | |||
return (function); | |||
} | |||
@@ -352,12 +347,7 @@ public class PDFFactory { | |||
PDFFunction function = new PDFFunction(theFunctionType, theDomain, | |||
theRange, theCZero, theCOne, | |||
theInterpolationExponentN); | |||
PDFFunction oldfunc = getDocument().findFunction(function); | |||
if (oldfunc == null) { | |||
getDocument().registerObject(function); | |||
} else { | |||
function = oldfunc; | |||
} | |||
function = registerFunction(function); | |||
return (function); | |||
} | |||
@@ -407,12 +397,7 @@ public class PDFFactory { | |||
theRange, theFunctions, | |||
theBounds, theEncode); | |||
PDFFunction oldfunc = getDocument().findFunction(function); | |||
if (oldfunc == null) { | |||
getDocument().registerObject(function); | |||
} else { | |||
function = oldfunc; | |||
} | |||
function = registerFunction(function); | |||
return (function); | |||
} | |||
@@ -434,14 +419,23 @@ public class PDFFactory { | |||
theRange, | |||
theFunctionDataStream); | |||
function = registerFunction(function); | |||
return (function); | |||
} | |||
/** | |||
* Registers a function against the document | |||
* @param function The function to register | |||
*/ | |||
public PDFFunction registerFunction(PDFFunction function) { | |||
PDFFunction oldfunc = getDocument().findFunction(function); | |||
if (oldfunc == null) { | |||
getDocument().registerObject(function); | |||
} else { | |||
function = oldfunc; | |||
} | |||
return (function); | |||
return function; | |||
} | |||
/* ========================= shadings ================================== */ | |||
@@ -481,20 +475,7 @@ public class PDFFactory { | |||
theBBox, theAntiAlias, theDomain, | |||
theMatrix, theFunction); | |||
PDFShading oldshad = getDocument().findShading(shading); | |||
if (oldshad == null) { | |||
getDocument().registerObject(shading); | |||
} else { | |||
shading = oldshad; | |||
} | |||
// add this shading to resources | |||
if (res != null) { | |||
res.getPDFResources().addShading(shading); | |||
} else { | |||
getDocument().getResources().addShading(shading); | |||
} | |||
shading = registerShading(res, shading); | |||
return (shading); | |||
} | |||
@@ -534,18 +515,7 @@ public class PDFFactory { | |||
theDomain, theFunction, | |||
theExtend); | |||
PDFShading oldshad = getDocument().findShading(shading); | |||
if (oldshad == null) { | |||
getDocument().registerObject(shading); | |||
} else { | |||
shading = oldshad; | |||
} | |||
if (res != null) { | |||
res.getPDFResources().addShading(shading); | |||
} else { | |||
getDocument().getResources().addShading(shading); | |||
} | |||
shading = registerShading(res, shading); | |||
return (shading); | |||
} | |||
@@ -591,18 +561,7 @@ public class PDFFactory { | |||
theBitsPerFlag, theDecode, | |||
theFunction); | |||
PDFShading oldshad = getDocument().findShading(shading); | |||
if (oldshad == null) { | |||
getDocument().registerObject(shading); | |||
} else { | |||
shading = oldshad; | |||
} | |||
if (res != null) { | |||
res.getPDFResources().addShading(shading); | |||
} else { | |||
getDocument().getResources().addShading(shading); | |||
} | |||
shading = registerShading(res, shading); | |||
return (shading); | |||
} | |||
@@ -645,6 +604,17 @@ public class PDFFactory { | |||
theBitsPerComponent, theDecode, | |||
theVerticesPerRow, theFunction); | |||
shading = registerShading(res, shading); | |||
return (shading); | |||
} | |||
/** | |||
* Registers a shading object against the document | |||
* @param res The PDF resource context | |||
* @param shading The shading object to be registered | |||
*/ | |||
public PDFShading registerShading(PDFResourceContext res, PDFShading shading) { | |||
PDFShading oldshad = getDocument().findShading(shading); | |||
if (oldshad == null) { | |||
getDocument().registerObject(shading); | |||
@@ -652,13 +622,13 @@ public class PDFFactory { | |||
shading = oldshad; | |||
} | |||
// add this shading to resources | |||
if (res != null) { | |||
res.getPDFResources().addShading(shading); | |||
} else { | |||
getDocument().getResources().addShading(shading); | |||
} | |||
return (shading); | |||
return shading; | |||
} | |||
/* ========================= patterns ================================== */ | |||
@@ -707,6 +677,22 @@ public class PDFFactory { | |||
return (pattern); | |||
} | |||
public PDFPattern registerPattern(PDFResourceContext res, PDFPattern pattern) { | |||
PDFPattern oldpatt = getDocument().findPattern(pattern); | |||
if (oldpatt == null) { | |||
getDocument().registerObject(pattern); | |||
} else { | |||
pattern = oldpatt; | |||
} | |||
if (res != null) { | |||
res.getPDFResources().addPattern(pattern); | |||
} else { | |||
getDocument().getResources().addPattern(pattern); | |||
} | |||
return pattern; | |||
} | |||
/** | |||
* Make a smooth shading pattern | |||
* |
@@ -22,6 +22,10 @@ package org.apache.fop.pdf; | |||
// Java... | |||
import java.util.List; | |||
import org.apache.fop.render.shading.Function; | |||
import org.apache.fop.render.shading.FunctionDelegate; | |||
import org.apache.fop.render.shading.FunctionPattern; | |||
/** | |||
* class representing a PDF Function. | |||
* | |||
@@ -33,126 +37,9 @@ import java.util.List; | |||
* | |||
* All PDF Functions have a FunctionType (0,2,3, or 4), a Domain, and a Range. | |||
*/ | |||
public class PDFFunction extends PDFObject { | |||
// Guts common to all function types | |||
/** | |||
* Required: The Type of function (0,2,3,4) default is 0. | |||
*/ | |||
protected int functionType = 0; // Default | |||
/** | |||
* Required: 2 * m Array of Double numbers which are possible inputs to the function | |||
*/ | |||
protected List domain = null; | |||
/** | |||
* Required: 2 * n Array of Double numbers which are possible outputs to the function | |||
*/ | |||
protected List range = null; | |||
/* ********************TYPE 0***************************** */ | |||
// FunctionType 0 specific function guts | |||
/** | |||
* Required: Array containing the Integer size of the Domain and Range, respectively. | |||
* Note: This is really more like two seperate integers, sizeDomain, and sizeRange, | |||
* but since they're expressed as an array in PDF, my implementation reflects that. | |||
*/ | |||
protected List size = null; | |||
/** | |||
* Required for Type 0: Number of Bits used to represent each sample value. | |||
* Limited to 1,2,4,8,12,16,24, or 32 | |||
*/ | |||
protected int bitsPerSample = 1; | |||
/** | |||
* Optional for Type 0: order of interpolation between samples. | |||
* Limited to linear (1) or cubic (3). Default is 1 | |||
*/ | |||
protected int order = 1; | |||
/** | |||
* Optional for Type 0: A 2 * m array of Doubles which provides a | |||
* linear mapping of input values to the domain. | |||
* | |||
* Required for Type 3: A 2 * k array of Doubles that, taken | |||
* in pairs, map each subset of the domain defined by Domain | |||
* and the Bounds array to the domain of the corresponding function. | |||
* Should be two values per function, usually (0,1), | |||
* as in [0 1 0 1] for 2 functions. | |||
*/ | |||
protected List encode = null; | |||
/** | |||
* Optional for Type 0: A 2 * n array of Doubles which provides | |||
* a linear mapping of sample values to the range. Defaults to Range. | |||
*/ | |||
protected List decode = null; | |||
/** | |||
* Optional For Type 0: A stream of sample values | |||
*/ | |||
/** | |||
* Required For Type 4: Postscript Calculator function | |||
* composed of arithmetic, boolean, and stack operators + boolean constants | |||
*/ | |||
protected StringBuffer functionDataStream = null; | |||
/** | |||
* Required (possibly) For Type 0: A vector of Strings for the | |||
* various filters to be used to decode the stream. | |||
* These are how the string is compressed. Flate, LZW, etc. | |||
*/ | |||
protected List filter = null; | |||
/* *************************TYPE 2************************** */ | |||
/** | |||
* Required For Type 2: An Array of n Doubles defining | |||
* the function result when x=0. Default is [0]. | |||
*/ | |||
protected List cZero = null; | |||
/** | |||
* Required For Type 2: An Array of n Doubles defining | |||
* the function result when x=1. Default is [1]. | |||
*/ | |||
protected List cOne = null; | |||
public class PDFFunction extends PDFObject implements Function { | |||
/** | |||
* Required for Type 2: The interpolation exponent. | |||
* Each value x will return n results. | |||
* Must be greater than 0. | |||
*/ | |||
protected double interpolationExponentN = 1; | |||
/* *************************TYPE 3************************** */ | |||
/** | |||
* Required for Type 3: An vector of PDFFunctions which | |||
* form an array of k single input functions making up | |||
* the stitching function. | |||
*/ | |||
protected List functions = null; | |||
/** | |||
* Optional for Type 3: An array of (k-1) Doubles that, | |||
* in combination with Domain, define the intervals to which | |||
* each function from the Functions array apply. Bounds | |||
* elements must be in order of increasing magnitude, | |||
* and each value must be within the value of Domain. | |||
* k is the number of functions. | |||
* If you pass null, it will output (1/k) in an array of k-1 elements. | |||
* This makes each function responsible for an equal amount of the stitching function. | |||
* It makes the gradient even. | |||
*/ | |||
protected List bounds = null; | |||
// See encode above, as it's also part of Type 3 Functions. | |||
/* *************************TYPE 4************************** */ | |||
// See 'data' above. | |||
private FunctionDelegate delegate; | |||
/** | |||
* create an complete Function object of Type 0, A Sampled function. | |||
@@ -211,26 +98,13 @@ public class PDFFunction extends PDFObject { | |||
* @param theFunctionType This is the type of function (0,2,3, or 4). | |||
* It should be 0 as this is the constructor for sampled functions. | |||
*/ | |||
public PDFFunction(int theFunctionType, List theDomain, | |||
List theRange, List theSize, int theBitsPerSample, | |||
int theOrder, List theEncode, List theDecode, | |||
StringBuffer theFunctionDataStream, List theFilter) { | |||
super(); | |||
this.functionType = 0; // dang well better be 0; | |||
this.size = theSize; | |||
this.bitsPerSample = theBitsPerSample; | |||
this.order = theOrder; // int | |||
this.encode = theEncode; // vector of int | |||
this.decode = theDecode; // vector of int | |||
this.functionDataStream = theFunctionDataStream; | |||
this.filter = theFilter; // vector of Strings | |||
// the domain and range are actually two dimensional arrays. | |||
// so if there's not an even number of items, bad stuff | |||
// happens. | |||
this.domain = theDomain; | |||
this.range = theRange; | |||
public PDFFunction(int theFunctionType, List<Double> theDomain, | |||
List<Double> theRange, List<Double> theSize, int theBitsPerSample, | |||
int theOrder, List<Double> theEncode, List<Double> theDecode, | |||
StringBuffer theFunctionDataStream, List<String> theFilter) { | |||
delegate = new FunctionDelegate(this, theFunctionType, theDomain, theRange, | |||
theSize, theBitsPerSample, theOrder, theEncode, theDecode, | |||
theFunctionDataStream, theFilter); | |||
} | |||
/** | |||
@@ -260,20 +134,11 @@ public class PDFFunction extends PDFObject { | |||
* PDF Spec page 268 | |||
* @param theFunctionType The type of the function, which should be 2. | |||
*/ | |||
public PDFFunction(int theFunctionType, List theDomain, | |||
List theRange, List theCZero, List theCOne, | |||
public PDFFunction(int theFunctionType, List<Double> theDomain, | |||
List<Double> theRange, List<Double> theCZero, List<Double> theCOne, | |||
double theInterpolationExponentN) { | |||
super(); | |||
this.functionType = 2; // dang well better be 2; | |||
this.cZero = theCZero; | |||
this.cOne = theCOne; | |||
this.interpolationExponentN = theInterpolationExponentN; | |||
this.domain = theDomain; | |||
this.range = theRange; | |||
delegate = new FunctionDelegate(this, theFunctionType, theDomain, theRange, | |||
theCZero, theCOne, theInterpolationExponentN); | |||
} | |||
@@ -312,18 +177,11 @@ public class PDFFunction extends PDFObject { | |||
* @param theFunctionType This is the function type. It should be 3, | |||
* for a stitching function. | |||
*/ | |||
public PDFFunction(int theFunctionType, List theDomain, | |||
List theRange, List theFunctions, | |||
List theBounds, List theEncode) { | |||
super(); | |||
this.functionType = 3; // dang well better be 3; | |||
this.functions = theFunctions; | |||
this.bounds = theBounds; | |||
this.encode = theEncode; | |||
this.domain = theDomain; | |||
this.range = theRange; | |||
public PDFFunction(int theFunctionType, List<Double> theDomain, | |||
List<Double> theRange, List<Function> theFunctions, | |||
List<Double> theBounds, List<Double> theEncode) { | |||
delegate = new FunctionDelegate(this, theFunctionType, theDomain, theRange, | |||
theFunctions, theBounds, theEncode); | |||
} | |||
@@ -349,20 +207,12 @@ public class PDFFunction extends PDFObject { | |||
* @param theFunctionType The type of function which should be 4, as this is | |||
* a Postscript calculator function | |||
*/ | |||
public PDFFunction(int theFunctionType, List theDomain, | |||
List theRange, StringBuffer theFunctionDataStream) { | |||
super(); | |||
this.functionType = 4; // dang well better be 4; | |||
this.functionDataStream = theFunctionDataStream; | |||
this.domain = theDomain; | |||
this.range = theRange; | |||
public PDFFunction(int theFunctionType, List<Double> theDomain, | |||
List<Double> theRange, StringBuffer theFunctionDataStream) { | |||
delegate = new FunctionDelegate(this, theFunctionType, theDomain, theRange, | |||
theFunctionDataStream); | |||
} | |||
/** | |||
* represent as PDF. Whatever the FunctionType is, the correct | |||
* representation spits out. The sets of required and optional | |||
@@ -375,319 +225,13 @@ public class PDFFunction extends PDFObject { | |||
* @return the PDF string. | |||
*/ | |||
public byte[] toPDF() { | |||
int vectorSize = 0; | |||
int numberOfFunctions = 0; | |||
int tempInt = 0; | |||
StringBuffer p = new StringBuffer(256); | |||
p.append("<< \n/FunctionType " + this.functionType + " \n"); | |||
// FunctionType 0 | |||
if (this.functionType == 0) { | |||
if (this.domain != null) { | |||
// DOMAIN | |||
p.append("/Domain [ "); | |||
vectorSize = this.domain.size(); | |||
for (tempInt = 0; tempInt < vectorSize; tempInt++) { | |||
p.append(PDFNumber.doubleOut((Double)this.domain.get(tempInt)) | |||
+ " "); | |||
} | |||
p.append("] \n"); | |||
} else { | |||
p.append("/Domain [ 0 1 ] \n"); | |||
} | |||
// SIZE | |||
if (this.size != null) { | |||
p.append("/Size [ "); | |||
vectorSize = this.size.size(); | |||
for (tempInt = 0; tempInt < vectorSize; tempInt++) { | |||
p.append(PDFNumber.doubleOut((Double)this.size.get(tempInt)) | |||
+ " "); | |||
} | |||
p.append("] \n"); | |||
} | |||
// ENCODE | |||
if (this.encode != null) { | |||
p.append("/Encode [ "); | |||
vectorSize = this.encode.size(); | |||
for (tempInt = 0; tempInt < vectorSize; tempInt++) { | |||
p.append(PDFNumber.doubleOut((Double)this.encode.get(tempInt)) | |||
+ " "); | |||
} | |||
p.append("] \n"); | |||
} else { | |||
p.append("/Encode [ "); | |||
vectorSize = this.functions.size(); | |||
for (tempInt = 0; tempInt < vectorSize; tempInt++) { | |||
p.append("0 1 "); | |||
} | |||
p.append("] \n"); | |||
} | |||
// BITSPERSAMPLE | |||
p.append("/BitsPerSample " + this.bitsPerSample); | |||
// ORDER (optional) | |||
if (this.order == 1 || this.order == 3) { | |||
p.append(" \n/Order " + this.order + " \n"); | |||
} | |||
// RANGE | |||
if (this.range != null) { | |||
p.append("/Range [ "); | |||
vectorSize = this.range.size(); | |||
for (tempInt = 0; tempInt < vectorSize; tempInt++) { | |||
p.append(PDFNumber.doubleOut((Double)this.range.get(tempInt)) | |||
+ " "); | |||
} | |||
p.append("] \n"); | |||
} | |||
// DECODE | |||
if (this.decode != null) { | |||
p.append("/Decode [ "); | |||
vectorSize = this.decode.size(); | |||
for (tempInt = 0; tempInt < vectorSize; tempInt++) { | |||
p.append(PDFNumber.doubleOut((Double)this.decode.get(tempInt)) | |||
+ " "); | |||
} | |||
p.append("] \n"); | |||
} | |||
// LENGTH | |||
if (this.functionDataStream != null) { | |||
p.append("/Length " + (this.functionDataStream.length() + 1) | |||
+ " \n"); | |||
} | |||
// FILTER? | |||
if (this.filter != null) { // if there's a filter | |||
vectorSize = this.filter.size(); | |||
p.append("/Filter "); | |||
if (vectorSize == 1) { | |||
p.append("/" + ((String)this.filter.get(0)) | |||
+ " \n"); | |||
} else { | |||
p.append("[ "); | |||
for (tempInt = 0; tempInt < vectorSize; tempInt++) { | |||
p.append("/" + ((String)this.filter.get(0)) | |||
+ " "); | |||
} | |||
p.append("] \n"); | |||
} | |||
} | |||
p.append(">>"); | |||
// stream representing the function | |||
if (this.functionDataStream != null) { | |||
p.append("\nstream\n" + this.functionDataStream | |||
+ "\nendstream"); | |||
} | |||
// end of if FunctionType 0 | |||
} else if (this.functionType == 2) { | |||
// DOMAIN | |||
if (this.domain != null) { | |||
p.append("/Domain [ "); | |||
vectorSize = this.domain.size(); | |||
for (tempInt = 0; tempInt < vectorSize; tempInt++) { | |||
p.append(PDFNumber.doubleOut((Double)this.domain.get(tempInt)) | |||
+ " "); | |||
} | |||
p.append("] \n"); | |||
} else { | |||
p.append("/Domain [ 0 1 ] \n"); | |||
} | |||
// RANGE | |||
if (this.range != null) { | |||
p.append("/Range [ "); | |||
vectorSize = this.range.size(); | |||
for (tempInt = 0; tempInt < vectorSize; tempInt++) { | |||
p.append(PDFNumber.doubleOut((Double)this.range.get(tempInt)) | |||
+ " "); | |||
} | |||
p.append("] \n"); | |||
} | |||
// FunctionType, C0, C1, N are required in PDF | |||
// C0 | |||
if (this.cZero != null) { | |||
p.append("/C0 [ "); | |||
vectorSize = this.cZero.size(); | |||
for (tempInt = 0; tempInt < vectorSize; tempInt++) { | |||
p.append(PDFNumber.doubleOut((Double)this.cZero.get(tempInt)) | |||
+ " "); | |||
} | |||
p.append("] \n"); | |||
} | |||
// C1 | |||
if (this.cOne != null) { | |||
p.append("/C1 [ "); | |||
vectorSize = this.cOne.size(); | |||
for (tempInt = 0; tempInt < vectorSize; tempInt++) { | |||
p.append(PDFNumber.doubleOut((Double)this.cOne.get(tempInt)) | |||
+ " "); | |||
} | |||
p.append("] \n"); | |||
} | |||
// N: The interpolation Exponent | |||
p.append("/N " | |||
+ PDFNumber.doubleOut(new Double(this.interpolationExponentN)) | |||
+ " \n"); | |||
p.append(">>"); | |||
} else if (this.functionType | |||
== 3) { // fix this up when my eyes uncross | |||
// DOMAIN | |||
if (this.domain != null) { | |||
p.append("/Domain [ "); | |||
vectorSize = this.domain.size(); | |||
for (tempInt = 0; tempInt < vectorSize; tempInt++) { | |||
p.append(PDFNumber.doubleOut((Double)this.domain.get(tempInt)) | |||
+ " "); | |||
} | |||
p.append("] \n"); | |||
} else { | |||
p.append("/Domain [ 0 1 ] \n"); | |||
} | |||
// RANGE | |||
if (this.range != null) { | |||
p.append("/Range [ "); | |||
vectorSize = this.range.size(); | |||
for (tempInt = 0; tempInt < vectorSize; tempInt++) { | |||
p.append(PDFNumber.doubleOut((Double)this.range.get(tempInt)) | |||
+ " "); | |||
} | |||
p.append("] \n"); | |||
} | |||
// FUNCTIONS | |||
if (this.functions != null) { | |||
p.append("/Functions [ "); | |||
numberOfFunctions = this.functions.size(); | |||
for (tempInt = 0; tempInt < numberOfFunctions; tempInt++) { | |||
p.append(((PDFFunction)this.functions.get(tempInt)).referencePDF() | |||
+ " "); | |||
} | |||
p.append("] \n"); | |||
} | |||
// ENCODE | |||
if (this.encode != null) { | |||
p.append("/Encode [ "); | |||
vectorSize = this.encode.size(); | |||
for (tempInt = 0; tempInt < vectorSize; tempInt++) { | |||
p.append(PDFNumber.doubleOut((Double)this.encode.get(tempInt)) | |||
+ " "); | |||
} | |||
p.append("] \n"); | |||
} else { | |||
p.append("/Encode [ "); | |||
vectorSize = this.functions.size(); | |||
for (tempInt = 0; tempInt < vectorSize; tempInt++) { | |||
p.append("0 1 "); | |||
} | |||
p.append("] \n"); | |||
} | |||
// BOUNDS, required, but can be empty | |||
p.append("/Bounds [ "); | |||
if (this.bounds != null) { | |||
vectorSize = this.bounds.size(); | |||
for (tempInt = 0; tempInt < vectorSize; tempInt++) { | |||
p.append(PDFNumber.doubleOut((Double)this.bounds.get(tempInt)) | |||
+ " "); | |||
} | |||
} else { | |||
if (this.functions != null) { | |||
// if there are n functions, | |||
// there must be n-1 bounds. | |||
// so let each function handle an equal portion | |||
// of the whole. e.g. if there are 4, then [ 0.25 0.25 0.25 ] | |||
String functionsFraction = PDFNumber.doubleOut(new Double(1.0 | |||
/ ((double)numberOfFunctions))); | |||
for (tempInt = 0; tempInt + 1 < numberOfFunctions; | |||
tempInt++) { | |||
p.append(functionsFraction + " "); | |||
} | |||
functionsFraction = null; // clean reference. | |||
} | |||
} | |||
p.append("]\n>>"); | |||
} else if (this.functionType | |||
== 4) { // fix this up when my eyes uncross | |||
// DOMAIN | |||
if (this.domain != null) { | |||
p.append("/Domain [ "); | |||
vectorSize = this.domain.size(); | |||
for (tempInt = 0; tempInt < vectorSize; tempInt++) { | |||
p.append(PDFNumber.doubleOut((Double)this.domain.get(tempInt)) | |||
+ " "); | |||
} | |||
p.append("] \n"); | |||
} else { | |||
p.append("/Domain [ 0 1 ] \n"); | |||
} | |||
// RANGE | |||
if (this.range != null) { | |||
p.append("/Range [ "); | |||
vectorSize = this.range.size(); | |||
for (tempInt = 0; tempInt < vectorSize; tempInt++) { | |||
p.append(PDFNumber.doubleOut((Double)this.range.get(tempInt)) | |||
+ " "); | |||
} | |||
p.append("] \n"); | |||
} | |||
// LENGTH | |||
if (this.functionDataStream != null) { | |||
p.append("/Length " + (this.functionDataStream.length() + 1) | |||
+ " \n"); | |||
} | |||
p.append(">>"); | |||
// stream representing the function | |||
if (this.functionDataStream != null) { | |||
p.append("\nstream\n{ " + this.functionDataStream | |||
+ " }\nendstream"); | |||
} | |||
} | |||
return toByteString(); | |||
} | |||
return encode(p.toString()); | |||
public byte[] toByteString() { | |||
FunctionPattern pattern = new FunctionPattern(this); | |||
return encode(pattern.toWriteableString()); | |||
} | |||
/** {@inheritDoc} */ | |||
@@ -702,96 +246,155 @@ public class PDFFunction extends PDFObject { | |||
return false; | |||
} | |||
PDFFunction func = (PDFFunction)obj; | |||
if (functionType != func.functionType) { | |||
if (delegate.getFunctionType() != func.getFunctionType()) { | |||
return false; | |||
} | |||
if (bitsPerSample != func.bitsPerSample) { | |||
if (delegate.getBitsPerSample() != func.getBitsPerSample()) { | |||
return false; | |||
} | |||
if (order != func.order) { | |||
if (delegate.getOrder() != func.getOrder()) { | |||
return false; | |||
} | |||
if (interpolationExponentN != func.interpolationExponentN) { | |||
if (delegate.getInterpolationExponentN() != func.getInterpolationExponentN()) { | |||
return false; | |||
} | |||
if (domain != null) { | |||
if (!domain.equals(func.domain)) { | |||
if (delegate.getDomain() != null) { | |||
if (!delegate.getDomain().equals(func.getDomain())) { | |||
return false; | |||
} | |||
} else if (func.domain != null) { | |||
} else if (func.getDomain() != null) { | |||
return false; | |||
} | |||
if (range != null) { | |||
if (!range.equals(func.range)) { | |||
if (delegate.getRange() != null) { | |||
if (!delegate.getRange().equals(func.getRange())) { | |||
return false; | |||
} | |||
} else if (func.range != null) { | |||
} else if (func.getRange() != null) { | |||
return false; | |||
} | |||
if (size != null) { | |||
if (!size.equals(func.size)) { | |||
if (delegate.getSize() != null) { | |||
if (!delegate.getSize().equals(func.getSize())) { | |||
return false; | |||
} | |||
} else if (func.size != null) { | |||
} else if (func.getSize() != null) { | |||
return false; | |||
} | |||
if (encode != null) { | |||
if (!encode.equals(func.encode)) { | |||
if (delegate.getEncode() != null) { | |||
if (!delegate.getEncode().equals(func.getEncode())) { | |||
return false; | |||
} | |||
} else if (func.encode != null) { | |||
} else if (func.getEncode() != null) { | |||
return false; | |||
} | |||
if (decode != null) { | |||
if (!decode.equals(func.decode)) { | |||
if (delegate.getDecode() != null) { | |||
if (!delegate.getDecode().equals(func.getDecode())) { | |||
return false; | |||
} | |||
} else if (func.decode != null) { | |||
} else if (func.getDecode() != null) { | |||
return false; | |||
} | |||
if (functionDataStream != null) { | |||
if (!functionDataStream.equals(func.functionDataStream)) { | |||
if (delegate.getDataStream() != null) { | |||
if (!delegate.getDataStream().equals(func.getDataStream())) { | |||
return false; | |||
} | |||
} else if (func.functionDataStream != null) { | |||
} else if (func.getDataStream() != null) { | |||
return false; | |||
} | |||
if (filter != null) { | |||
if (!filter.equals(func.filter)) { | |||
if (delegate.getFilter() != null) { | |||
if (!delegate.getFilter().equals(func.getFilter())) { | |||
return false; | |||
} | |||
} else if (func.filter != null) { | |||
} else if (func.getFilter() != null) { | |||
return false; | |||
} | |||
if (cZero != null) { | |||
if (!cZero.equals(func.cZero)) { | |||
if (delegate.getCZero() != null) { | |||
if (!delegate.getCZero().equals(func.getCZero())) { | |||
return false; | |||
} | |||
} else if (func.cZero != null) { | |||
} else if (func.getCZero() != null) { | |||
return false; | |||
} | |||
if (cOne != null) { | |||
if (!cOne.equals(func.cOne)) { | |||
if (delegate.getCOne() != null) { | |||
if (!delegate.getCOne().equals(func.getCOne())) { | |||
return false; | |||
} | |||
} else if (func.cOne != null) { | |||
} else if (func.getCOne() != null) { | |||
return false; | |||
} | |||
if (functions != null) { | |||
if (!functions.equals(func.functions)) { | |||
if (delegate.getFunctions() != null) { | |||
if (!delegate.getFunctions().equals(func.getFunctions())) { | |||
return false; | |||
} | |||
} else if (func.functions != null) { | |||
} else if (func.getFunctions() != null) { | |||
return false; | |||
} | |||
if (bounds != null) { | |||
if (!bounds.equals(func.bounds)) { | |||
if (delegate.getBounds() != null) { | |||
if (!delegate.getBounds().equals(func.getBounds())) { | |||
return false; | |||
} | |||
} else if (func.bounds != null) { | |||
} else if (func.getBounds() != null) { | |||
return false; | |||
} | |||
return true; | |||
} | |||
public int getFunctionType() { | |||
return delegate.getFunctionType(); | |||
} | |||
public List<Double> getBounds() { | |||
return delegate.getBounds(); | |||
} | |||
public List<Double> getDomain() { | |||
return delegate.getDomain(); | |||
} | |||
public List<Double> getSize() { | |||
return delegate.getSize(); | |||
} | |||
public List<String> getFilter() { | |||
return delegate.getFilter(); | |||
} | |||
public List<Double> getEncode() { | |||
return delegate.getEncode(); | |||
} | |||
public List<Function> getFunctions() { | |||
return delegate.getFunctions(); | |||
} | |||
public int getBitsPerSample() { | |||
return delegate.getBitsPerSample(); | |||
} | |||
public double getInterpolationExponentN() { | |||
return delegate.getInterpolationExponentN(); | |||
} | |||
public int getOrder() { | |||
return delegate.getOrder(); | |||
} | |||
public List<Double> getRange() { | |||
return delegate.getRange(); | |||
} | |||
public List<Double> getDecode() { | |||
return delegate.getDecode(); | |||
} | |||
public StringBuffer getDataStream() { | |||
return delegate.getDataStream(); | |||
} | |||
public List<Double> getCZero() { | |||
return delegate.getCZero(); | |||
} | |||
public List<Double> getCOne() { | |||
return delegate.getCOne(); | |||
} | |||
} |
@@ -23,6 +23,9 @@ import java.io.IOException; | |||
import java.io.OutputStream; | |||
import java.util.List; | |||
import org.apache.fop.render.shading.Pattern; | |||
import org.apache.fop.render.shading.Shading; | |||
/** | |||
* class representing a PDF Function. | |||
* | |||
@@ -33,7 +36,7 @@ import java.util.List; | |||
* | |||
* All PDF Functions have a FunctionType (0,2,3, or 4), a Domain, and a Range. | |||
*/ | |||
public class PDFPattern extends PDFPathPaint { | |||
public class PDFPattern extends PDFPathPaint implements Pattern { | |||
/** | |||
* The resources associated with this pattern | |||
@@ -146,13 +149,14 @@ public class PDFPattern extends PDFPathPaint { | |||
* @param theExtGState optional: the extended graphics state, if used. | |||
* @param theMatrix Optional:List of Doubles that specify the matrix. | |||
*/ | |||
public PDFPattern(int thePatternType, PDFShading theShading, | |||
public PDFPattern(int thePatternType, Shading theShading, | |||
List theXUID, StringBuffer theExtGState, | |||
List theMatrix) { | |||
super(); | |||
this.patternType = 2; // thePatternType; | |||
this.shading = theShading; | |||
assert theShading instanceof PDFShading; | |||
this.shading = (PDFShading)theShading; | |||
this.xUID = theXUID; | |||
// this isn't really implemented, so it should always be null. | |||
// I just don't want to have to add a new parameter once it is implemented. | |||
@@ -259,7 +263,7 @@ public class PDFPattern extends PDFPathPaint { | |||
vectorSize = this.xUID.size(); | |||
p.append("/XUID [ "); | |||
for (tempInt = 0; tempInt < vectorSize; tempInt++) { | |||
p.append(((Integer)this.xUID.get(tempInt)) + " "); | |||
p.append((this.xUID.get(tempInt)) + " "); | |||
} | |||
p.append("] \n"); | |||
} | |||
@@ -290,7 +294,7 @@ public class PDFPattern extends PDFPathPaint { | |||
vectorSize = this.xUID.size(); | |||
p.append("/XUID [ "); | |||
for (tempInt = 0; tempInt < vectorSize; tempInt++) { | |||
p.append(((Integer)this.xUID.get(tempInt)) + " "); | |||
p.append((this.xUID.get(tempInt)) + " "); | |||
} | |||
p.append("] \n"); | |||
} |
@@ -22,6 +22,10 @@ package org.apache.fop.pdf; | |||
// Java... | |||
import java.util.List; | |||
import org.apache.fop.render.shading.Function; | |||
import org.apache.fop.render.shading.Shading; | |||
import org.apache.fop.render.shading.ShadingPattern; | |||
/** | |||
* class representing a PDF Smooth Shading object. | |||
* | |||
@@ -32,7 +36,7 @@ import java.util.List; | |||
* | |||
* All PDF Functions have a shadingType (0,2,3, or 4), a Domain, and a Range. | |||
*/ | |||
public class PDFShading extends PDFObject { | |||
public class PDFShading extends PDFObject implements Shading { | |||
// Guts common to all function types | |||
/** | |||
@@ -205,7 +209,7 @@ public class PDFShading extends PDFObject { | |||
public PDFShading(int theShadingType, PDFDeviceColorSpace theColorSpace, | |||
List theBackground, List theBBox, | |||
boolean theAntiAlias, List theCoords, | |||
List theDomain, PDFFunction theFunction, | |||
List theDomain, Function theFunction, | |||
List theExtend) { | |||
super(); | |||
this.shadingType = theShadingType; // 2 or 3 | |||
@@ -216,7 +220,8 @@ public class PDFShading extends PDFObject { | |||
this.coords = theCoords; | |||
this.domain = theDomain; | |||
this.function = theFunction; | |||
assert theFunction instanceof PDFFunction; | |||
this.function = (PDFFunction)theFunction; | |||
this.extend = theExtend; | |||
} | |||
@@ -335,197 +340,8 @@ public class PDFShading extends PDFObject { | |||
* @return the PDF string. | |||
*/ | |||
public String toPDFString() { | |||
int vectorSize; | |||
int tempInt; | |||
StringBuffer p = new StringBuffer(128); | |||
p.append("<<\n/ShadingType " + this.shadingType + " \n"); | |||
if (this.colorSpace != null) { | |||
p.append("/ColorSpace /" | |||
+ this.colorSpace.getName() + " \n"); | |||
} | |||
if (this.background != null) { | |||
p.append("/Background [ "); | |||
vectorSize = this.background.size(); | |||
for (tempInt = 0; tempInt < vectorSize; tempInt++) { | |||
p.append(PDFNumber.doubleOut((Double)this.background.get(tempInt)) | |||
+ " "); | |||
} | |||
p.append("] \n"); | |||
} | |||
if (this.bBox | |||
!= null) { // I've never seen an example, so I guess this is right. | |||
p.append("/BBox [ "); | |||
vectorSize = this.bBox.size(); | |||
for (tempInt = 0; tempInt < vectorSize; tempInt++) { | |||
p.append(PDFNumber.doubleOut((Double)this.bBox.get(tempInt)) | |||
+ " "); | |||
} | |||
p.append("] \n"); | |||
} | |||
if (this.antiAlias) { | |||
p.append("/AntiAlias " + this.antiAlias + " \n"); | |||
} | |||
// Here's where we differentiate based on what type it is. | |||
if (this.shadingType == 1) { // function based shading | |||
if (this.domain != null) { | |||
p.append("/Domain [ "); | |||
vectorSize = this.domain.size(); | |||
for (tempInt = 0; tempInt < vectorSize; tempInt++) { | |||
p.append(PDFNumber.doubleOut((Double)this.domain.get(tempInt)) | |||
+ " "); | |||
} | |||
p.append("] \n"); | |||
} else { | |||
p.append("/Domain [ 0 1 ] \n"); | |||
} | |||
if (this.matrix != null) { | |||
p.append("/Matrix [ "); | |||
vectorSize = this.matrix.size(); | |||
for (tempInt = 0; tempInt < vectorSize; tempInt++) { | |||
p.append(PDFNumber.doubleOut((Double)this.matrix.get(tempInt)) | |||
+ " "); | |||
} | |||
p.append("] \n"); | |||
} | |||
if (this.function != null) { | |||
p.append("/Function "); | |||
p.append(this.function.referencePDF() + " \n"); | |||
} | |||
} else if ((this.shadingType == 2) | |||
|| (this.shadingType | |||
== 3)) { // 2 is axial shading (linear gradient) | |||
// 3 is radial shading (circular gradient) | |||
if (this.coords != null) { | |||
p.append("/Coords [ "); | |||
vectorSize = this.coords.size(); | |||
for (tempInt = 0; tempInt < vectorSize; tempInt++) { | |||
p.append(PDFNumber.doubleOut((Double)this.coords.get(tempInt)) | |||
+ " "); | |||
} | |||
p.append("] \n"); | |||
} | |||
// DOMAIN | |||
if (this.domain != null) { | |||
p.append("/Domain [ "); | |||
vectorSize = this.domain.size(); | |||
for (tempInt = 0; tempInt < vectorSize; tempInt++) { | |||
p.append(PDFNumber.doubleOut((Double)this.domain.get(tempInt)) | |||
+ " "); | |||
} | |||
p.append("] \n"); | |||
} else { | |||
p.append("/Domain [ 0 1 ] \n"); | |||
} | |||
if (this.extend != null) { | |||
p.append("/Extend [ "); | |||
vectorSize = this.extend.size(); | |||
for (tempInt = 0; tempInt < vectorSize; tempInt++) { | |||
p.append(((Boolean)this.extend.get(tempInt)) + " "); | |||
} | |||
p.append("] \n"); | |||
} else { | |||
p.append("/Extend [ true true ] \n"); | |||
} | |||
if (this.function != null) { | |||
p.append("/Function "); | |||
p.append(this.function.referencePDF() + " \n"); | |||
} | |||
} else if ((this.shadingType == 4) || (this.shadingType == 6) | |||
|| (this.shadingType | |||
== 7)) { // 4:Free-form Gouraud-shaded triangle meshes | |||
// 6:coons patch meshes | |||
// 7://tensor product patch meshes (which no one ever uses) | |||
if (this.bitsPerCoordinate > 0) { | |||
p.append("/BitsPerCoordinate " + this.bitsPerCoordinate | |||
+ " \n"); | |||
} else { | |||
p.append("/BitsPerCoordinate 1 \n"); | |||
} | |||
if (this.bitsPerComponent > 0) { | |||
p.append("/BitsPerComponent " + this.bitsPerComponent | |||
+ " \n"); | |||
} else { | |||
p.append("/BitsPerComponent 1 \n"); | |||
} | |||
if (this.bitsPerFlag > 0) { | |||
p.append("/BitsPerFlag " + this.bitsPerFlag + " \n"); | |||
} else { | |||
p.append("/BitsPerFlag 2 \n"); | |||
} | |||
if (this.decode != null) { | |||
p.append("/Decode [ "); | |||
vectorSize = this.decode.size(); | |||
for (tempInt = 0; tempInt < vectorSize; tempInt++) { | |||
p.append(((Boolean)this.decode.get(tempInt)) + " "); | |||
} | |||
p.append("] \n"); | |||
} | |||
if (this.function != null) { | |||
p.append("/Function "); | |||
p.append(this.function.referencePDF() + " \n"); | |||
} | |||
} else if (this.shadingType | |||
== 5) { // Lattice Free form gouraud-shaded triangle mesh | |||
if (this.bitsPerCoordinate > 0) { | |||
p.append("/BitsPerCoordinate " + this.bitsPerCoordinate | |||
+ " \n"); | |||
} else { | |||
p.append("/BitsPerCoordinate 1 \n"); | |||
} | |||
if (this.bitsPerComponent > 0) { | |||
p.append("/BitsPerComponent " + this.bitsPerComponent | |||
+ " \n"); | |||
} else { | |||
p.append("/BitsPerComponent 1 \n"); | |||
} | |||
if (this.decode != null) { | |||
p.append("/Decode [ "); | |||
vectorSize = this.decode.size(); | |||
for (tempInt = 0; tempInt < vectorSize; tempInt++) { | |||
p.append(((Boolean)this.decode.get(tempInt)) + " "); | |||
} | |||
p.append("] \n"); | |||
} | |||
if (this.function != null) { | |||
p.append("/Function "); | |||
p.append(this.function.referencePDF() + " \n"); | |||
} | |||
if (this.verticesPerRow > 0) { | |||
p.append("/VerticesPerRow " + this.verticesPerRow + " \n"); | |||
} else { | |||
p.append("/VerticesPerRow 2 \n"); | |||
} | |||
} | |||
p.append(">>"); | |||
return (p.toString()); | |||
ShadingPattern pattern = new ShadingPattern(this); | |||
return pattern.toString(colorSpace, shadingType, background, bBox, antiAlias); | |||
} | |||
/** {@inheritDoc} */ | |||
@@ -623,4 +439,173 @@ public class PDFShading extends PDFObject { | |||
} | |||
return true; | |||
} | |||
/** | |||
* A method to write a type 1 shading object | |||
* @param p The StringBuffer to write the shading object | |||
* @return Returns the StringBuffer to which the shading object was written | |||
*/ | |||
public StringBuffer handleShadingType1(StringBuffer p) { | |||
if (this.domain != null) { | |||
p.append("/Domain [ "); | |||
for (int domainIndex = 0; domainIndex < domain.size(); domainIndex++) { | |||
p.append(PDFNumber.doubleOut((Double)this.domain.get(domainIndex)) | |||
+ " "); | |||
} | |||
p.append("] \n"); | |||
} else { | |||
p.append("/Domain [ 0 1 ] \n"); | |||
} | |||
if (this.matrix != null) { | |||
p.append("/Matrix [ "); | |||
for (int matrixIndex = 0; matrixIndex < matrix.size(); matrixIndex++) { | |||
p.append(PDFNumber.doubleOut((Double)this.matrix.get(matrixIndex)) | |||
+ " "); | |||
} | |||
p.append("] \n"); | |||
} | |||
if (this.function != null) { | |||
p.append("/Function "); | |||
p.append(this.function.referencePDF() + " \n"); | |||
} | |||
return p; | |||
} | |||
/** | |||
* A method to write a type 2 or 3 shading object | |||
* @param p The StringBuffer to write the shading object | |||
* @return Returns the StringBuffer to which the shading object was written | |||
*/ | |||
public StringBuffer handleShadingType2or3(StringBuffer p) { | |||
// 3 is radial shading (circular gradient) | |||
if (this.coords != null) { | |||
p.append("/Coords [ "); | |||
for (int coordIndex = 0; coordIndex < coords.size(); coordIndex++) { | |||
p.append(PDFNumber.doubleOut((Double)this.coords.get(coordIndex)) | |||
+ " "); | |||
} | |||
p.append("] \n"); | |||
} | |||
// DOMAIN | |||
if (this.domain != null) { | |||
p.append("/Domain [ "); | |||
for (int domainIndex = 0; domainIndex < domain.size(); domainIndex++) { | |||
p.append(PDFNumber.doubleOut((Double)this.domain.get(domainIndex)) | |||
+ " "); | |||
} | |||
p.append("] \n"); | |||
} else { | |||
p.append("/Domain [ 0 1 ] \n"); | |||
} | |||
if (this.extend != null) { | |||
p.append("/Extend [ "); | |||
for (int extendIndex = 0; extendIndex < extend.size(); extendIndex++) { | |||
p.append((this.extend.get(extendIndex)) + " "); | |||
} | |||
p.append("] \n"); | |||
} else { | |||
p.append("/Extend [ true true ] \n"); | |||
} | |||
if (this.function != null) { | |||
p.append("/Function "); | |||
p.append(this.function.referencePDF() + " \n"); | |||
} | |||
return p; | |||
} | |||
/** | |||
* A method to write a type 4, 6 or 7 shading object | |||
* @param p The StringBuffer to write the shading object | |||
* @return Returns the StringBuffer to which the shading object was written | |||
*/ | |||
public StringBuffer handleShadingType4or6or7(StringBuffer p) { | |||
// 6:coons patch meshes | |||
// 7://tensor product patch meshes (which no one ever uses) | |||
if (this.bitsPerCoordinate > 0) { | |||
p.append("/BitsPerCoordinate " + this.bitsPerCoordinate | |||
+ " \n"); | |||
} else { | |||
p.append("/BitsPerCoordinate 1 \n"); | |||
} | |||
if (this.bitsPerComponent > 0) { | |||
p.append("/BitsPerComponent " + this.bitsPerComponent | |||
+ " \n"); | |||
} else { | |||
p.append("/BitsPerComponent 1 \n"); | |||
} | |||
if (this.bitsPerFlag > 0) { | |||
p.append("/BitsPerFlag " + this.bitsPerFlag + " \n"); | |||
} else { | |||
p.append("/BitsPerFlag 2 \n"); | |||
} | |||
if (this.decode != null) { | |||
p.append("/Decode [ "); | |||
for (int decodeIndex = 0; decodeIndex < decode.size(); decodeIndex++) { | |||
p.append((this.decode.get(decodeIndex)) + " "); | |||
} | |||
p.append("] \n"); | |||
} | |||
if (this.function != null) { | |||
p.append("/Function "); | |||
p.append(this.function.referencePDF() + " \n"); | |||
} | |||
return p; | |||
} | |||
/** | |||
* A method to write a type 5 shading object | |||
* @param p The StringBuffer to write the shading object | |||
* @return Returns the StringBuffer to which the shading object was written | |||
*/ | |||
public StringBuffer handleShadingType5(StringBuffer p) { | |||
if (this.bitsPerCoordinate > 0) { | |||
p.append("/BitsPerCoordinate " + this.bitsPerCoordinate | |||
+ " \n"); | |||
} else { | |||
p.append("/BitsPerCoordinate 1 \n"); | |||
} | |||
if (this.bitsPerComponent > 0) { | |||
p.append("/BitsPerComponent " + this.bitsPerComponent | |||
+ " \n"); | |||
} else { | |||
p.append("/BitsPerComponent 1 \n"); | |||
} | |||
if (this.decode != null) { | |||
p.append("/Decode [ "); | |||
for (int decodeIndex = 0; decodeIndex < decode.size(); decodeIndex++) { | |||
p.append((this.decode.get(decodeIndex)) + " "); | |||
} | |||
p.append("] \n"); | |||
} | |||
if (this.function != null) { | |||
p.append("/Function "); | |||
p.append(this.function.referencePDF() + " \n"); | |||
} | |||
if (this.verticesPerRow > 0) { | |||
p.append("/VerticesPerRow " + this.verticesPerRow + " \n"); | |||
} else { | |||
p.append("/VerticesPerRow 2 \n"); | |||
} | |||
return p; | |||
} | |||
} |
@@ -19,26 +19,50 @@ | |||
package org.apache.fop.render.ps; | |||
import java.awt.Color; | |||
import java.awt.Dimension; | |||
import java.awt.Rectangle; | |||
import java.awt.geom.AffineTransform; | |||
import java.awt.geom.Rectangle2D; | |||
import java.awt.image.BufferedImage; | |||
import java.awt.image.ColorModel; | |||
import java.io.ByteArrayInputStream; | |||
import java.io.ByteArrayOutputStream; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.util.ArrayList; | |||
import java.util.HashMap; | |||
import java.util.List; | |||
import java.util.regex.Matcher; | |||
import java.util.regex.Pattern; | |||
import javax.imageio.ImageIO; | |||
import org.w3c.dom.Document; | |||
import org.w3c.dom.Node; | |||
import org.w3c.dom.NodeList; | |||
import org.apache.batik.bridge.BridgeContext; | |||
import org.apache.batik.bridge.GVTBuilder; | |||
import org.apache.batik.gvt.GraphicsNode; | |||
import org.apache.batik.transcoder.SVGAbstractTranscoder; | |||
import org.apache.batik.transcoder.TranscoderException; | |||
import org.apache.batik.transcoder.TranscoderInput; | |||
import org.apache.batik.transcoder.TranscoderOutput; | |||
import org.apache.batik.transcoder.image.PNGTranscoder; | |||
import org.apache.xmlgraphics.image.loader.Image; | |||
import org.apache.xmlgraphics.image.loader.ImageFlavor; | |||
import org.apache.xmlgraphics.image.loader.impl.ImageXMLDOM; | |||
import org.apache.xmlgraphics.java2d.ps.PSGraphics2D; | |||
import org.apache.xmlgraphics.ps.ImageEncoder; | |||
import org.apache.xmlgraphics.ps.ImageEncodingHelper; | |||
import org.apache.xmlgraphics.ps.PSGenerator; | |||
import org.apache.fop.image.loader.batik.BatikImageFlavors; | |||
import org.apache.fop.image.loader.batik.BatikUtil; | |||
import org.apache.fop.render.ImageHandler; | |||
import org.apache.fop.render.RenderingContext; | |||
import org.apache.fop.render.ps.svg.PSSVGGraphics2D; | |||
import org.apache.fop.svg.SVGEventProducer; | |||
import org.apache.fop.svg.SVGUserAgent; | |||
@@ -47,6 +71,9 @@ import org.apache.fop.svg.SVGUserAgent; | |||
*/ | |||
public class PSImageHandlerSVG implements ImageHandler { | |||
private static final Color FALLBACK_COLOR = new Color(255, 33, 117); | |||
private HashMap<String, String> gradientsFound = new HashMap<String, String>(); | |||
private static final ImageFlavor[] FLAVORS = new ImageFlavor[] { | |||
BatikImageFlavors.SVG_DOM | |||
}; | |||
@@ -58,78 +85,262 @@ public class PSImageHandlerSVG implements ImageHandler { | |||
PSGenerator gen = psContext.getGenerator(); | |||
ImageXMLDOM imageSVG = (ImageXMLDOM)image; | |||
//Controls whether text painted by Batik is generated using text or path operations | |||
boolean strokeText = false; | |||
//TODO Configure text stroking | |||
if (shouldRaster(imageSVG)) { | |||
InputStream is = renderSVGToInputStream(context, imageSVG); | |||
float x = (float) pos.getX() / 1000f; | |||
float y = (float) pos.getY() / 1000f; | |||
float w = (float) pos.getWidth() / 1000f; | |||
float h = (float) pos.getHeight() / 1000f; | |||
Rectangle2D targetRect = new Rectangle2D.Double(x, y, w, h); | |||
MaskedImage mi = convertToRGB(ImageIO.read(is)); | |||
BufferedImage ri = mi.getImage(); | |||
ImageEncoder encoder = ImageEncodingHelper.createRenderedImageEncoder(ri); | |||
Dimension imgDim = new Dimension(ri.getWidth(), ri.getHeight()); | |||
String imgDescription = ri.getClass().getName(); | |||
ImageEncodingHelper helper = new ImageEncodingHelper(ri); | |||
ColorModel cm = helper.getEncodedColorModel(); | |||
PSImageUtils.writeImage(encoder, imgDim, imgDescription, targetRect, cm, gen, ri, mi.getMaskColor()); | |||
} else { | |||
//Controls whether text painted by Batik is generated using text or path operations | |||
boolean strokeText = false; | |||
//TODO Configure text stroking | |||
SVGUserAgent ua | |||
= new SVGUserAgent(context.getUserAgent(), new AffineTransform()); | |||
PSSVGGraphics2D graphics = new PSSVGGraphics2D(strokeText, gen); | |||
graphics.setGraphicContext(new org.apache.xmlgraphics.java2d.GraphicContext()); | |||
BridgeContext ctx = new PSBridgeContext(ua, | |||
(strokeText ? null : psContext.getFontInfo()), | |||
context.getUserAgent().getImageManager(), | |||
context.getUserAgent().getImageSessionContext()); | |||
//Cloning SVG DOM as Batik attaches non-thread-safe facilities (like the CSS engine) | |||
//to it. | |||
Document clonedDoc = BatikUtil.cloneSVGDocument(imageSVG.getDocument()); | |||
GraphicsNode root; | |||
try { | |||
GVTBuilder builder = new GVTBuilder(); | |||
root = builder.build(ctx, clonedDoc); | |||
} catch (Exception e) { | |||
SVGEventProducer eventProducer = SVGEventProducer.Provider.get( | |||
context.getUserAgent().getEventBroadcaster()); | |||
eventProducer.svgNotBuilt(this, e, image.getInfo().getOriginalURI()); | |||
return; | |||
} | |||
// get the 'width' and 'height' attributes of the SVG document | |||
float w = (float)ctx.getDocumentSize().getWidth() * 1000f; | |||
float h = (float)ctx.getDocumentSize().getHeight() * 1000f; | |||
SVGUserAgent ua | |||
= new SVGUserAgent(context.getUserAgent(), new AffineTransform()); | |||
float sx = pos.width / w; | |||
float sy = pos.height / h; | |||
PSGraphics2D graphics = new PSGraphics2D(strokeText, gen); | |||
graphics.setGraphicContext(new org.apache.xmlgraphics.java2d.GraphicContext()); | |||
ctx = null; | |||
BridgeContext ctx = new PSBridgeContext(ua, | |||
(strokeText ? null : psContext.getFontInfo()), | |||
context.getUserAgent().getImageManager(), | |||
context.getUserAgent().getImageSessionContext()); | |||
gen.commentln("%FOPBeginSVG"); | |||
gen.saveGraphicsState(); | |||
final boolean clip = false; | |||
if (clip) { | |||
/* | |||
* Clip to the svg area. | |||
* Note: To have the svg overlay (under) a text area then use | |||
* an fo:block-container | |||
*/ | |||
gen.writeln("newpath"); | |||
gen.defineRect(pos.getMinX() / 1000f, pos.getMinY() / 1000f, | |||
pos.width / 1000f, pos.height / 1000f); | |||
gen.writeln("clip"); | |||
} | |||
//Cloning SVG DOM as Batik attaches non-thread-safe facilities (like the CSS engine) | |||
//to it. | |||
Document clonedDoc = BatikUtil.cloneSVGDocument(imageSVG.getDocument()); | |||
// transform so that the coordinates (0,0) is from the top left | |||
// and positive is down and to the right. (0,0) is where the | |||
// viewBox puts it. | |||
gen.concatMatrix(sx, 0, 0, sy, pos.getMinX() / 1000f, pos.getMinY() / 1000f); | |||
GraphicsNode root; | |||
AffineTransform transform = new AffineTransform(); | |||
// scale to viewbox | |||
transform.translate(pos.getMinX(), pos.getMinY()); | |||
gen.getCurrentState().concatMatrix(transform); | |||
try { | |||
root.paint(graphics); | |||
} catch (Exception e) { | |||
SVGEventProducer eventProducer = SVGEventProducer.Provider.get( | |||
context.getUserAgent().getEventBroadcaster()); | |||
eventProducer.svgRenderingError(this, e, image.getInfo().getOriginalURI()); | |||
} | |||
gen.restoreGraphicsState(); | |||
gen.commentln("%FOPEndSVG"); | |||
} | |||
} | |||
private InputStream renderSVGToInputStream(RenderingContext context, ImageXMLDOM imageSVG) throws IOException { | |||
PNGTranscoder png = new PNGTranscoder(); | |||
Float width = getDimension(imageSVG.getDocument(), "width") * 8; | |||
png.addTranscodingHint(SVGAbstractTranscoder.KEY_WIDTH, width); | |||
Float height = getDimension(imageSVG.getDocument(), "height") * 8; | |||
png.addTranscodingHint(SVGAbstractTranscoder.KEY_HEIGHT, height); | |||
TranscoderInput input = new TranscoderInput(imageSVG.getDocument()); | |||
ByteArrayOutputStream os = new ByteArrayOutputStream(); | |||
TranscoderOutput output = new TranscoderOutput(os); | |||
try { | |||
GVTBuilder builder = new GVTBuilder(); | |||
root = builder.build(ctx, clonedDoc); | |||
} catch (Exception e) { | |||
png.transcode(input, output); | |||
} catch (TranscoderException ex) { | |||
SVGEventProducer eventProducer = SVGEventProducer.Provider.get( | |||
context.getUserAgent().getEventBroadcaster()); | |||
eventProducer.svgNotBuilt(this, e, image.getInfo().getOriginalURI()); | |||
return; | |||
eventProducer.svgRenderingError(this, ex, imageSVG.getInfo().getOriginalURI()); | |||
} finally { | |||
os.flush(); | |||
os.close(); | |||
} | |||
return new ByteArrayInputStream(os.toByteArray()); | |||
} | |||
private MaskedImage convertToRGB(BufferedImage alphaImage) { | |||
int[] red = new int[256]; | |||
int[] green = new int[256]; | |||
int[] blue = new int[256]; | |||
BufferedImage rgbImage = new BufferedImage(alphaImage.getWidth(), | |||
alphaImage.getHeight(), BufferedImage.TYPE_INT_RGB); | |||
//Count occurances of each colour in image | |||
for (int cx = 0; cx < alphaImage.getWidth(); cx++) { | |||
for (int cy = 0; cy < alphaImage.getHeight(); cy++) { | |||
int pixelValue = alphaImage.getRGB(cx, cy); | |||
Color pixelColor = new Color(pixelValue); | |||
red[pixelColor.getRed()]++; | |||
green[pixelColor.getGreen()]++; | |||
blue[pixelColor.getBlue()]++; | |||
} | |||
} | |||
//Find colour not in image | |||
Color alphaSwap = null; | |||
for (int i = 0; i < 256; i++) { | |||
if (red[i] == 0) { | |||
alphaSwap = new Color(i, 0, 0); | |||
break; | |||
} else if (green[i] == 0) { | |||
alphaSwap = new Color(0, i, 0); | |||
break; | |||
} else if (blue[i] == 0) { | |||
alphaSwap = new Color(0, 0, i); | |||
break; | |||
} | |||
} | |||
// get the 'width' and 'height' attributes of the SVG document | |||
float w = (float)ctx.getDocumentSize().getWidth() * 1000f; | |||
float h = (float)ctx.getDocumentSize().getHeight() * 1000f; | |||
float sx = pos.width / w; | |||
float sy = pos.height / h; | |||
ctx = null; | |||
gen.commentln("%FOPBeginSVG"); | |||
gen.saveGraphicsState(); | |||
final boolean clip = false; | |||
if (clip) { | |||
/* | |||
* Clip to the svg area. | |||
* Note: To have the svg overlay (under) a text area then use | |||
* an fo:block-container | |||
*/ | |||
gen.writeln("newpath"); | |||
gen.defineRect(pos.getMinX() / 1000f, pos.getMinY() / 1000f, | |||
pos.width / 1000f, pos.height / 1000f); | |||
gen.writeln("clip"); | |||
//Check if all variations are used in all three colours | |||
if (alphaSwap == null) { | |||
//Fallback colour is no unique colour channel can be found | |||
alphaSwap = FALLBACK_COLOR; | |||
} | |||
//Replace alpha channel with the new mask colour | |||
for (int cx = 0; cx < alphaImage.getWidth(); cx++) { | |||
for (int cy = 0; cy < alphaImage.getHeight(); cy++) { | |||
int pixelValue = alphaImage.getRGB(cx, cy); | |||
if (pixelValue == 0) { | |||
rgbImage.setRGB(cx, cy, alphaSwap.getRGB()); | |||
} else { | |||
rgbImage.setRGB(cx, cy, alphaImage.getRGB(cx, cy)); | |||
} | |||
} | |||
} | |||
return new MaskedImage(rgbImage, alphaSwap); | |||
} | |||
private static class MaskedImage { | |||
private Color maskColor = new Color(0, 0, 0); | |||
private BufferedImage image; | |||
public MaskedImage(BufferedImage image, Color maskColor) { | |||
this.image = image; | |||
this.maskColor = maskColor; | |||
} | |||
public Color getMaskColor() { | |||
return maskColor; | |||
} | |||
public BufferedImage getImage() { | |||
return image; | |||
} | |||
} | |||
// transform so that the coordinates (0,0) is from the top left | |||
// and positive is down and to the right. (0,0) is where the | |||
// viewBox puts it. | |||
gen.concatMatrix(sx, 0, 0, sy, pos.getMinX() / 1000f, pos.getMinY() / 1000f); | |||
private Float getDimension(Document document, String dimension) { | |||
if (document.getFirstChild().getAttributes().getNamedItem(dimension) != null) { | |||
String width = document.getFirstChild().getAttributes().getNamedItem(dimension).getNodeValue(); | |||
width = width.replaceAll("[^\\d.]", ""); | |||
return Float.parseFloat(width); | |||
} | |||
return null; | |||
} | |||
AffineTransform transform = new AffineTransform(); | |||
// scale to viewbox | |||
transform.translate(pos.getMinX(), pos.getMinY()); | |||
gen.getCurrentState().concatMatrix(transform); | |||
private boolean shouldRaster(ImageXMLDOM image) { | |||
//A list of objects on which to check opacity | |||
try { | |||
root.paint(graphics); | |||
} catch (Exception e) { | |||
SVGEventProducer eventProducer = SVGEventProducer.Provider.get( | |||
context.getUserAgent().getEventBroadcaster()); | |||
eventProducer.svgRenderingError(this, e, image.getInfo().getOriginalURI()); | |||
List<String> gradMatches = new ArrayList<String>(); | |||
gradMatches.add("radialGradient"); | |||
gradMatches.add("linearGradient"); | |||
return recurseSVGElements(image.getDocument().getChildNodes(), gradMatches, false); | |||
} finally { | |||
gradientsFound.clear(); | |||
} | |||
} | |||
gen.restoreGraphicsState(); | |||
gen.commentln("%FOPEndSVG"); | |||
private boolean recurseSVGElements(NodeList childNodes, List<String> gradMatches, boolean isMatched) { | |||
boolean opacityFound = false; | |||
for (int i = 0; i < childNodes.getLength(); i++) { | |||
Node curNode = childNodes.item(i); | |||
if (isMatched && curNode.getLocalName() != null && curNode.getLocalName().equals("stop")) { | |||
if (curNode.getAttributes().getNamedItem("style") != null) { | |||
String[] stylePairs = curNode.getAttributes().getNamedItem("style").getNodeValue() | |||
.split(";"); | |||
for (int styleAtt = 0; styleAtt < stylePairs.length; styleAtt++) { | |||
String[] style = stylePairs[styleAtt].split(":"); | |||
if (style[0].equalsIgnoreCase("stop-opacity")) { | |||
if (Double.parseDouble(style[1]) < 1) { | |||
return true; | |||
} | |||
} | |||
} | |||
} | |||
if (curNode.getAttributes().getNamedItem("stop-opacity") != null) { | |||
String opacityValue = curNode.getAttributes().getNamedItem("stop-opacity").getNodeValue(); | |||
if (Double.parseDouble(opacityValue) < 1) { | |||
return true; | |||
} | |||
} | |||
} | |||
String nodeName = curNode.getLocalName(); | |||
//Special case where rasterization needed for radial gradients | |||
if (nodeName != null && nodeName.equals("ellipse")) { | |||
String found = ""; | |||
String ellipseFill = curNode.getAttributes().getNamedItem("fill").getNodeValue(); | |||
Pattern pattern = Pattern.compile("#(.*?)\\)"); | |||
Matcher matcher = pattern.matcher(ellipseFill); | |||
if (matcher.find()) { | |||
found = matcher.group(1); | |||
} | |||
if (gradientsFound.get(found) != null) { | |||
return true; | |||
} | |||
} | |||
boolean inMatch = false; | |||
if (!isMatched) { | |||
inMatch = nodeName != null && gradMatches.contains(nodeName); | |||
if (inMatch) { | |||
gradientsFound.put(curNode.getAttributes().getNamedItem("id").getNodeValue(), nodeName); | |||
} | |||
} else { | |||
inMatch = true; | |||
} | |||
opacityFound = recurseSVGElements(curNode.getChildNodes(), gradMatches, inMatch); | |||
if (opacityFound) { | |||
return true; | |||
} | |||
} | |||
return opacityFound; | |||
} | |||
/** {@inheritDoc} */ |
@@ -0,0 +1,143 @@ | |||
/* | |||
* 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.render.ps.svg; | |||
import java.io.UnsupportedEncodingException; | |||
import java.util.List; | |||
import org.apache.fop.render.shading.Function; | |||
import org.apache.fop.render.shading.FunctionDelegate; | |||
import org.apache.fop.render.shading.FunctionPattern; | |||
public class PSFunction implements Function { | |||
private FunctionDelegate delegate; | |||
/** | |||
* Creates a Postscript function dictionary | |||
* @param theFunctionType The function type (0 = Sampled, 2 = Exponential | |||
* Interpolation, 3 = Stitching) | |||
* @param theDomain The function domain | |||
* @param theRange Range used for clipping | |||
* @param theFunctions An array of sub-functions such as determining the | |||
* colour values used in a gradient. | |||
* @param theBounds Bounds determines where each boundary exists for whatever | |||
* the function is mean't. In a gradient case, it would be the point between | |||
* colours. | |||
* @param theEncode The function encoding | |||
*/ | |||
public PSFunction(int theFunctionType, List<Double> theDomain, | |||
List<Double> theRange, List<Function> theFunctions, | |||
List<Double> theBounds, List<Double> theEncode) { | |||
delegate = new FunctionDelegate(this, theFunctionType, theDomain, theRange, theFunctions, | |||
theBounds, theEncode); | |||
} | |||
/** | |||
* Creates a Postscript function dictionary | |||
* @param theFunctionType The function type (0 = Sampled, 2 = Exponential | |||
* Interpolation, 3 = Stitching) | |||
* @param theDomain The function domain | |||
* @param theRange Range used for clipping | |||
* @param theCZero In a gradient, this would be the first colour | |||
* @param theCOne In a gradient, this would be the second colour | |||
* @param theInterpolationExponentN Determines the number of values | |||
* the function returns. | |||
*/ | |||
public PSFunction(int theFunctionType, List<Double> theDomain, | |||
List<Double> theRange, List<Double> theCZero, List<Double> theCOne, | |||
double theInterpolationExponentN) { | |||
delegate = new FunctionDelegate(this, theFunctionType, theDomain, theRange, theCZero, | |||
theCOne, theInterpolationExponentN); | |||
} | |||
/** | |||
* Outputs the function to a byte array | |||
*/ | |||
public byte[] toByteString() { | |||
FunctionPattern pattern = new FunctionPattern(this); | |||
try { | |||
return pattern.toWriteableString().getBytes("UTF-8"); | |||
} catch (UnsupportedEncodingException ex) { | |||
//This should have been made an enum type to avoid throwing exceptions. | |||
return new byte[0]; | |||
} | |||
} | |||
public int getFunctionType() { | |||
return delegate.getFunctionType(); | |||
} | |||
public List<Double> getBounds() { | |||
return delegate.getBounds(); | |||
} | |||
public List<Double> getDomain() { | |||
return delegate.getDomain(); | |||
} | |||
public List<Double> getSize() { | |||
return delegate.getSize(); | |||
} | |||
public List<String> getFilter() { | |||
return delegate.getFilter(); | |||
} | |||
public List<Double> getEncode() { | |||
return delegate.getEncode(); | |||
} | |||
public List<Function> getFunctions() { | |||
return delegate.getFunctions(); | |||
} | |||
public int getBitsPerSample() { | |||
return delegate.getBitsPerSample(); | |||
} | |||
public double getInterpolationExponentN() { | |||
return delegate.getInterpolationExponentN(); | |||
} | |||
public int getOrder() { | |||
return delegate.getOrder(); | |||
} | |||
public List<Double> getRange() { | |||
return delegate.getRange(); | |||
} | |||
public List<Double> getDecode() { | |||
return delegate.getDecode(); | |||
} | |||
public StringBuffer getDataStream() { | |||
return delegate.getDataStream(); | |||
} | |||
public List<Double> getCZero() { | |||
return delegate.getCZero(); | |||
} | |||
public List<Double> getCOne() { | |||
return delegate.getCOne(); | |||
} | |||
} |
@@ -0,0 +1,103 @@ | |||
/* | |||
* 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.render.ps.svg; | |||
import java.util.List; | |||
import org.apache.fop.render.shading.Pattern; | |||
import org.apache.fop.render.shading.Shading; | |||
public class PSPattern implements Pattern { | |||
/** | |||
* Either one (1) for tiling, or two (2) for shading. | |||
*/ | |||
protected int patternType = 2; // Default | |||
/** | |||
* The Shading object comprising the Type 2 pattern | |||
*/ | |||
protected PSShading shading = null; | |||
/** | |||
* List of Integers represetning the Extended unique Identifier | |||
*/ | |||
protected List xUID = null; | |||
/** | |||
* TODO use PDFGState | |||
* String representing the extended Graphics state. | |||
* Probably will never be used like this. | |||
*/ | |||
protected StringBuffer extGState = null; | |||
/** | |||
* Creates a radial or axial shading pattern | |||
* @param thePatternType The pattern type which will be 3 for radial and 2 for axial | |||
* @param theShading The shading object to determine how the gradient | |||
* is drawn | |||
* @param theXUID The XUID | |||
* @param theExtGState The exit state | |||
*/ | |||
public PSPattern(int thePatternType, Shading theShading, List theXUID, | |||
StringBuffer theExtGState) { | |||
this.patternType = 2; // thePatternType; | |||
assert theShading instanceof PSShading; | |||
this.shading = (PSShading)theShading; | |||
this.xUID = theXUID; | |||
this.extGState = theExtGState; // always null | |||
} | |||
/** | |||
* Outputs the radial or axial pattern as a string dictionary to insert | |||
* into a postscript document. | |||
*/ | |||
public String toString() { | |||
int vectorSize = 0; | |||
int tempInt = 0; | |||
StringBuffer p = new StringBuffer(64); | |||
p.append("/Pattern setcolorspace\n"); | |||
p.append("<< \n/Type /Pattern \n"); | |||
p.append("/PatternType " + this.patternType + " \n"); | |||
if (this.shading != null) { | |||
p.append("/Shading " + this.shading.toString() + " \n"); | |||
} | |||
if (this.xUID != null) { | |||
vectorSize = this.xUID.size(); | |||
p.append("/XUID [ "); | |||
for (tempInt = 0; tempInt < vectorSize; tempInt++) { | |||
p.append((this.xUID.get(tempInt)) + " "); | |||
} | |||
p.append("] \n"); | |||
} | |||
if (this.extGState != null) { | |||
p.append("/ExtGState " + this.extGState + " \n"); | |||
} | |||
p.append(">> \n"); | |||
p.append("matrix makepattern setcolor\n"); | |||
return p.toString(); | |||
} | |||
} |
@@ -0,0 +1,291 @@ | |||
/* | |||
* 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.render.ps.svg; | |||
import java.awt.Color; | |||
import java.awt.Graphics; | |||
import java.awt.Paint; | |||
import java.awt.geom.AffineTransform; | |||
import java.io.IOException; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
import org.apache.commons.logging.Log; | |||
import org.apache.commons.logging.LogFactory; | |||
import org.apache.batik.ext.awt.LinearGradientPaint; | |||
import org.apache.batik.ext.awt.MultipleGradientPaint; | |||
import org.apache.batik.ext.awt.RadialGradientPaint; | |||
import org.apache.xmlgraphics.java2d.ps.PSGraphics2D; | |||
import org.apache.xmlgraphics.ps.PSGenerator; | |||
import org.apache.fop.pdf.PDFDeviceColorSpace; | |||
import org.apache.fop.render.shading.Function; | |||
import org.apache.fop.render.shading.GradientFactory; | |||
import org.apache.fop.render.shading.GradientRegistrar; | |||
import org.apache.fop.render.shading.PSGradientFactory; | |||
import org.apache.fop.render.shading.Pattern; | |||
import org.apache.fop.render.shading.Shading; | |||
public class PSSVGGraphics2D extends PSGraphics2D implements GradientRegistrar { | |||
private static final Log LOG = LogFactory.getLog(PSSVGGraphics2D.class); | |||
/** | |||
* Create a new Graphics2D that generates PostScript code. | |||
* @param textAsShapes True if text should be rendered as graphics | |||
* @see org.apache.xmlgraphics.java2d.AbstractGraphics2D#AbstractGraphics2D(boolean) | |||
*/ | |||
public PSSVGGraphics2D(boolean textAsShapes) { | |||
super(textAsShapes); | |||
} | |||
/** | |||
* Create a new Graphics2D that generates PostScript code. | |||
* @param textAsShapes True if text should be rendered as graphics | |||
* @param gen PostScript generator to use for output | |||
* @see org.apache.xmlgraphics.java2d.AbstractGraphics2D#AbstractGraphics2D(boolean) | |||
*/ | |||
public PSSVGGraphics2D(boolean textAsShapes, PSGenerator gen) { | |||
super(textAsShapes, gen); | |||
} | |||
/** | |||
* Constructor for creating copies | |||
* @param g parent PostScript Graphics2D | |||
*/ | |||
public PSSVGGraphics2D(PSGraphics2D g) { | |||
super(g); | |||
} | |||
protected void applyPaint(Paint paint, boolean fill) { | |||
super.applyPaint(paint, fill); | |||
if (paint instanceof RadialGradientPaint) { | |||
RadialGradientPaint rgp = (RadialGradientPaint)paint; | |||
try { | |||
handleRadialGradient(rgp, gen); | |||
} catch (IOException ioe) { | |||
handleIOException(ioe); | |||
} | |||
} else if (paint instanceof LinearGradientPaint) { | |||
LinearGradientPaint lgp = (LinearGradientPaint)paint; | |||
try { | |||
handleLinearGradient(lgp, gen); | |||
} catch (IOException ioe) { | |||
handleIOException(ioe); | |||
} | |||
} | |||
} | |||
private void handleLinearGradient(LinearGradientPaint lgp, PSGenerator gen) throws IOException { | |||
MultipleGradientPaint.CycleMethodEnum cycle = lgp.getCycleMethod(); | |||
if (cycle != MultipleGradientPaint.NO_CYCLE) { | |||
return; | |||
} | |||
float[] fractions = lgp.getFractions(); | |||
Color[] cols = lgp.getColors(); | |||
AffineTransform transform = new AffineTransform(getBaseTransform()); | |||
transform.concatenate(getTransform()); | |||
transform.concatenate(lgp.getTransform()); | |||
List theMatrix = new ArrayList(); | |||
double [] mat = new double[6]; | |||
transform.getMatrix(mat); | |||
for (int idx = 0; idx < mat.length; idx++) { | |||
theMatrix.add(Double.valueOf(mat[idx])); | |||
} | |||
List<Double> theCoords = new java.util.ArrayList<Double>(); | |||
AffineTransform start = applyTransform(lgp.getTransform(), lgp.getStartPoint().getX(), lgp.getStartPoint().getY()); | |||
AffineTransform end = applyTransform(lgp.getTransform(), lgp.getEndPoint().getX(), lgp.getEndPoint().getY()); | |||
double startX = start.getTranslateX(); | |||
double startY = start.getTranslateY(); | |||
double endX = end.getTranslateX(); | |||
double endY = end.getTranslateY(); | |||
double width = endX - startX; | |||
double height = endY - startY; | |||
startX = startX + width * fractions[0]; | |||
endX = endX - width * (1 - fractions[fractions.length - 1]); | |||
startY = startY + (height * fractions[0]); | |||
endY = endY - height * (1 - fractions[fractions.length - 1]); | |||
theCoords.add(startX); | |||
theCoords.add(startY); | |||
theCoords.add(endX); | |||
theCoords.add(endY); | |||
List<Color> someColors = new java.util.ArrayList<Color>(); | |||
for (int count = 0; count < cols.length; count++) { | |||
Color c1 = cols[count]; | |||
if (c1.getAlpha() != 255) { | |||
LOG.warn("Opacity is not currently supported for Postscript output"); | |||
} | |||
someColors.add(c1); | |||
} | |||
List<Double> theBounds = new java.util.ArrayList<Double>(); | |||
for (int count = 1; count < fractions.length - 1; count++) { | |||
float offset = fractions[count]; | |||
theBounds.add(Double.valueOf(offset)); | |||
} | |||
PDFDeviceColorSpace colSpace; | |||
colSpace = new PDFDeviceColorSpace(PDFDeviceColorSpace.DEVICE_RGB); | |||
PSGradientFactory gradientFactory = (PSGradientFactory)GradientFactory.newInstance(this); | |||
PSPattern myPattern = gradientFactory.createGradient(false, colSpace, | |||
someColors, theBounds, theCoords, theMatrix); | |||
gen.write(myPattern.toString()); | |||
} | |||
private void handleRadialGradient(RadialGradientPaint rgp, PSGenerator gen) throws IOException { | |||
MultipleGradientPaint.CycleMethodEnum cycle = rgp.getCycleMethod(); | |||
if (cycle != MultipleGradientPaint.NO_CYCLE) { | |||
return; | |||
} | |||
AffineTransform transform; | |||
transform = new AffineTransform(getBaseTransform()); | |||
transform.concatenate(getTransform()); | |||
transform.concatenate(rgp.getTransform()); | |||
AffineTransform resultCentre = applyTransform(rgp.getTransform(), rgp.getCenterPoint().getX(), rgp.getCenterPoint().getY()); | |||
AffineTransform resultFocus = applyTransform(rgp.getTransform(), rgp.getFocusPoint().getX(), rgp.getFocusPoint().getY()); | |||
double scale = Math.sqrt(rgp.getTransform().getDeterminant()); | |||
double radius = rgp.getRadius() * scale; | |||
double centreX = resultCentre.getTranslateX(); | |||
double centreY = resultCentre.getTranslateY(); | |||
double focusX = resultFocus.getTranslateX(); | |||
double focusY = resultFocus.getTranslateY(); | |||
List<Double> theMatrix = new java.util.ArrayList<Double>(); | |||
double [] mat = new double[6]; | |||
transform.getMatrix(mat); | |||
for (int idx = 0; idx < mat.length; idx++) { | |||
theMatrix.add(Double.valueOf(mat[idx])); | |||
} | |||
List<Double> theCoords = new java.util.ArrayList<Double>(); | |||
float[] fractions = rgp.getFractions(); | |||
theCoords.add(centreX); | |||
theCoords.add(centreY); | |||
theCoords.add(radius * rgp.getFractions()[0]); | |||
theCoords.add(focusX); | |||
theCoords.add(focusY); | |||
theCoords.add(radius * fractions[fractions.length - 1]); | |||
Color[] cols = rgp.getColors(); | |||
List<Color> someColors = new java.util.ArrayList<Color>(); | |||
for (int count = 0; count < cols.length; count++) { | |||
Color cc = cols[count]; | |||
if (cc.getAlpha() != 255) { | |||
/* This should never happen because radial gradients with opacity should now | |||
* be rasterized in the PSImageHandlerSVG class. Please see the shouldRaster() | |||
* method for more information. */ | |||
LOG.warn("Opacity is not currently supported for Postscript output"); | |||
} | |||
someColors.add(cc); | |||
} | |||
List<Double> theBounds = new java.util.ArrayList<Double>(); | |||
for (int count = 1; count < fractions.length - 1; count++) { | |||
float offset = fractions[count]; | |||
theBounds.add(Double.valueOf(offset)); | |||
} | |||
PDFDeviceColorSpace colSpace; | |||
colSpace = new PDFDeviceColorSpace(PDFDeviceColorSpace.DEVICE_RGB); | |||
PSGradientFactory gradientFactory = (PSGradientFactory)GradientFactory.newInstance(this); | |||
PSPattern myPattern = gradientFactory.createGradient(true, colSpace, | |||
someColors, theBounds, theCoords, theMatrix); | |||
gen.write(myPattern.toString()); | |||
} | |||
private AffineTransform applyTransform(AffineTransform base, double posX, double posY) { | |||
AffineTransform result = AffineTransform.getTranslateInstance(posX, posY); | |||
AffineTransform orig = base; | |||
orig.concatenate(result); | |||
return orig; | |||
} | |||
protected AffineTransform getBaseTransform() { | |||
AffineTransform at = new AffineTransform(this.getTransform()); | |||
return at; | |||
} | |||
/** | |||
* Creates a new <code>Graphics</code> object that is | |||
* a copy of this <code>Graphics</code> object. | |||
* @return a new graphics context that is a copy of | |||
* this graphics context. | |||
*/ | |||
@Override | |||
public Graphics create() { | |||
preparePainting(); | |||
return new PSSVGGraphics2D(this); | |||
} | |||
/** | |||
* Registers a function object against the output format document | |||
* @param function The function object to register | |||
* @return Returns either the function which has already been registered | |||
* or the current new registered object. | |||
*/ | |||
public Function registerFunction(Function function) { | |||
//Objects aren't needed to be registered in Postscript | |||
return function; | |||
} | |||
/** | |||
* Registers a shading object against the otuput format document | |||
* @param shading The shading object to register | |||
* @return Returs either the shading which has already been registered | |||
* or the current new registered object | |||
*/ | |||
public Shading registerShading(Shading shading) { | |||
//Objects aren't needed to be registered in Postscript | |||
return shading; | |||
} | |||
/** | |||
* Registers a pattern object against the output format document | |||
* @param pattern The pattern object to register | |||
* @return Returns either the pattern which has already been registered | |||
* or the current new registered object | |||
*/ | |||
public Pattern registerPattern(Pattern pattern) { | |||
// TODO Auto-generated method stub | |||
return pattern; | |||
} | |||
} |
@@ -0,0 +1,228 @@ | |||
/* | |||
* 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.render.ps.svg; | |||
import java.io.UnsupportedEncodingException; | |||
import java.util.List; | |||
import org.apache.fop.pdf.PDFDeviceColorSpace; | |||
import org.apache.fop.pdf.PDFNumber; | |||
import org.apache.fop.render.shading.Function; | |||
import org.apache.fop.render.shading.Shading; | |||
import org.apache.fop.render.shading.ShadingPattern; | |||
public class PSShading implements Shading { | |||
/** | |||
* Required: The Type of shading (1,2,3,4,5,6,7) | |||
*/ | |||
protected int shadingType = 3; // Default | |||
/** | |||
* A ColorSpace representing the colorspace. "DeviceRGB" is an example. | |||
*/ | |||
protected PDFDeviceColorSpace colorSpace = null; | |||
/** | |||
* The background color. Since shading is opaque, | |||
* this is very rarely used. | |||
*/ | |||
protected List background = null; | |||
/** | |||
* Optional: A List specifying the clipping rectangle | |||
*/ | |||
protected List bBox = null; | |||
/** | |||
* Optional: A flag whether or not to filter the shading function | |||
* to prevent aliasing artifacts. Default is false. | |||
*/ | |||
protected boolean antiAlias = false; | |||
/** | |||
* Optional for Type 1: Array of four numbers, xmin, xmax, ymin, ymax. | |||
* Default is [0 1 0 1] | |||
* Optional for Type 2: An array of two numbers between which the blend | |||
* varies between start and end points. Default is 0, 1. | |||
* Optional for Type 3: An array of two numbers between which the blend | |||
* varies between start and end points. Default is 0, 1. | |||
*/ | |||
protected List domain = null; | |||
/** | |||
* Required for Type 1, 2, and 3: | |||
* The object of the color mapping function (usually type 2 or 3). | |||
* Optional for Type 4,5,6, and 7: When it's nearly the same thing. | |||
*/ | |||
protected PSFunction function = null; | |||
/** | |||
* Required for Type 2: An Array of four numbers specifying | |||
* the starting and ending coordinate pairs | |||
* Required for Type 3: An Array of six numbers [x0,y0,r0,x1,y1,r1] | |||
* specifying the centers and radii of | |||
* the starting and ending circles. | |||
*/ | |||
protected List coords = null; | |||
/** | |||
* Required for Type 2+3: An Array of two boolean values specifying | |||
* whether to extend the start and end colors past the start | |||
* and end points, respectively. | |||
* Default is false, false. | |||
*/ | |||
protected List extend = null; | |||
/** | |||
* Constructor for Type 2 and 3 | |||
* | |||
* @param theShadingType 2 or 3 for axial or radial shading | |||
* @param theColorSpace "DeviceRGB" or similar. | |||
* @param theBackground theBackground An array of color components appropriate to the | |||
* colorspace key specifying a single color value. | |||
* This key is used by the f operator buy ignored by the sh operator. | |||
* @param theBBox List of double's representing a rectangle | |||
* in the coordinate space that is current at the | |||
* time of shading is imaged. Temporary clipping | |||
* boundary. | |||
* @param theAntiAlias Default is false | |||
* @param theCoords List of four (type 2) or 6 (type 3) Double | |||
* @param theDomain List of Doubles specifying the domain | |||
* @param theFunction the Stitching (PDFfunction type 3) function, | |||
* even if it's stitching a single function | |||
* @param theExtend List of Booleans of whether to extend the start | |||
* and end colors past the start and end points | |||
* The default is [false, false] | |||
*/ | |||
public PSShading(int theShadingType, PDFDeviceColorSpace theColorSpace, | |||
List<Double> theBackground, List<Double> theBBox, | |||
boolean theAntiAlias, List<Double> theCoords, | |||
List<Double> theDomain, Function theFunction, | |||
List<Integer> theExtend) { | |||
this.shadingType = theShadingType; // 2 or 3 | |||
this.colorSpace = theColorSpace; | |||
this.background = theBackground; | |||
this.bBox = theBBox; | |||
this.antiAlias = theAntiAlias; | |||
this.coords = theCoords; | |||
this.domain = theDomain; | |||
assert theFunction instanceof PSFunction; | |||
this.function = (PSFunction)theFunction; | |||
this.extend = theExtend; | |||
} | |||
/** | |||
* represent as PS. Whatever the shadingType is, the correct | |||
* representation spits out. The sets of required and optional | |||
* attributes are different for each type, but if a required | |||
* attribute's object was constructed as null, then no error | |||
* is raised. Instead, the malformed PS that was requested | |||
* by the construction is dutifully output. | |||
* This policy should be reviewed. | |||
* | |||
* @return the PDF string. | |||
*/ | |||
public String toString() { | |||
ShadingPattern pattern = new ShadingPattern(this); | |||
return pattern.toString(colorSpace, shadingType, background, bBox, antiAlias); | |||
} | |||
/** | |||
* A method to write a type 2 or 3 shading object | |||
* @param p The StringBuffer to write the shading object | |||
* @return Returns the StringBuffer to which the shading object was written | |||
*/ | |||
public StringBuffer handleShadingType2or3(StringBuffer p) { | |||
if (this.coords != null) { | |||
p.append("\t/Coords [ "); | |||
for (int coordIndex = 0; coordIndex < coords.size(); coordIndex++) { | |||
p.append(PDFNumber.doubleOut((Double)this.coords.get(coordIndex)) | |||
+ " "); | |||
} | |||
p.append("] \n"); | |||
} | |||
// DOMAIN | |||
if (this.domain != null) { | |||
p.append("\t/Domain [ "); | |||
for (int domainIndex = 0; domainIndex < domain.size(); domainIndex++) { | |||
p.append(PDFNumber.doubleOut((Double)this.domain.get(domainIndex)) | |||
+ " "); | |||
} | |||
p.append("] \n"); | |||
} else { | |||
p.append("\t/Domain [ 0 1 ] \n"); | |||
} | |||
if (this.extend != null) { | |||
p.append("\t/Extend [ "); | |||
for (int extendIndex = 0; extendIndex < extend.size(); extendIndex++) { | |||
p.append((this.extend.get(extendIndex)) + " "); | |||
} | |||
p.append("] \n"); | |||
} else { | |||
p.append("\t/Extend [ true true ] \n"); | |||
} | |||
if (this.function != null) { | |||
p.append("\t/Function "); | |||
try { | |||
p.append(new String(this.function.toByteString(), "UTF-8") + " \n"); | |||
} catch (UnsupportedEncodingException ex) { | |||
//This should have been made an enum type to avoid throwing exceptions. | |||
} | |||
} | |||
return p; | |||
} | |||
/** | |||
* A method to write a type 1 shading object | |||
* @param p The StringBuffer to write the shading object | |||
* @return Returns the StringBuffer to which the shading object was written | |||
*/ | |||
public StringBuffer handleShadingType1(StringBuffer p) { | |||
// TODO Auto-generated method stub | |||
return null; | |||
} | |||
/** | |||
* A method to write a type 4, 6 or 7 shading object | |||
* @param p The StringBuffer to write the shading object | |||
* @return Returns the StringBuffer to which the shading object was written | |||
*/ | |||
public StringBuffer handleShadingType4or6or7(StringBuffer p) { | |||
// TODO Auto-generated method stub | |||
return null; | |||
} | |||
/** | |||
* A method to write a type 5 shading object | |||
* @param p The StringBuffer to write the shading object | |||
* @return Returns the StringBuffer to which the shading object was written | |||
*/ | |||
public StringBuffer handleShadingType5(StringBuffer p) { | |||
// TODO Auto-generated method stub | |||
return null; | |||
} | |||
} |
@@ -0,0 +1,39 @@ | |||
/* | |||
* 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. | |||
*/ | |||
package org.apache.fop.render.shading; | |||
import java.util.List; | |||
public interface Function { | |||
int getFunctionType(); | |||
List<Double> getBounds(); | |||
List<Double> getDomain(); | |||
List<Double> getSize(); | |||
List<String> getFilter(); | |||
List<Double> getEncode(); | |||
List<Function> getFunctions(); | |||
int getBitsPerSample(); | |||
double getInterpolationExponentN(); | |||
int getOrder(); | |||
List<Double> getRange(); | |||
List<Double> getDecode(); | |||
StringBuffer getDataStream(); | |||
List<Double> getCZero(); | |||
List<Double> getCOne(); | |||
byte[] toByteString(); | |||
} |
@@ -0,0 +1,451 @@ | |||
/* | |||
* 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. | |||
*/ | |||
package org.apache.fop.render.shading; | |||
import java.util.List; | |||
public class FunctionDelegate implements Function { | |||
private Function parentFunction; | |||
/** | |||
* Required: The Type of function (0,2,3,4) default is 0. | |||
*/ | |||
protected int functionType = 0; // Default | |||
/** | |||
* Required: 2 * m Array of Double numbers which are possible inputs to the function | |||
*/ | |||
protected List<Double> domain = null; | |||
/** | |||
* Required: 2 * n Array of Double numbers which are possible outputs to the function | |||
*/ | |||
protected List<Double> range = null; | |||
/* ********************TYPE 0***************************** */ | |||
// FunctionType 0 specific function guts | |||
/** | |||
* Required: Array containing the Integer size of the Domain and Range, respectively. | |||
* Note: This is really more like two seperate integers, sizeDomain, and sizeRange, | |||
* but since they're expressed as an array in PDF, my implementation reflects that. | |||
*/ | |||
protected List<Double> size = null; | |||
/** | |||
* Required for Type 0: Number of Bits used to represent each sample value. | |||
* Limited to 1,2,4,8,12,16,24, or 32 | |||
*/ | |||
protected int bitsPerSample = 1; | |||
/** | |||
* Optional for Type 0: order of interpolation between samples. | |||
* Limited to linear (1) or cubic (3). Default is 1 | |||
*/ | |||
protected int order = 1; | |||
/** | |||
* Optional for Type 0: A 2 * m array of Doubles which provides a | |||
* linear mapping of input values to the domain. | |||
* | |||
* Required for Type 3: A 2 * k array of Doubles that, taken | |||
* in pairs, map each subset of the domain defined by Domain | |||
* and the Bounds array to the domain of the corresponding function. | |||
* Should be two values per function, usually (0,1), | |||
* as in [0 1 0 1] for 2 functions. | |||
*/ | |||
protected List<Double> encode = null; | |||
/** | |||
* Optional for Type 0: A 2 * n array of Doubles which provides | |||
* a linear mapping of sample values to the range. Defaults to Range. | |||
*/ | |||
protected List<Double> decode = null; | |||
/** | |||
* Optional For Type 0: A stream of sample values | |||
*/ | |||
/** | |||
* Required For Type 4: Postscript Calculator function | |||
* composed of arithmetic, boolean, and stack operators + boolean constants | |||
*/ | |||
protected StringBuffer functionDataStream = null; | |||
/** | |||
* Required (possibly) For Type 0: A vector of Strings for the | |||
* various filters to be used to decode the stream. | |||
* These are how the string is compressed. Flate, LZW, etc. | |||
*/ | |||
protected List<String> filter = null; | |||
/* *************************TYPE 2************************** */ | |||
/** | |||
* Required For Type 2: An Array of n Doubles defining | |||
* the function result when x=0. Default is [0]. | |||
*/ | |||
protected List<Double> cZero = null; | |||
/** | |||
* Required For Type 2: An Array of n Doubles defining | |||
* the function result when x=1. Default is [1]. | |||
*/ | |||
protected List<Double> cOne = null; | |||
/** | |||
* Required for Type 2: The interpolation exponent. | |||
* Each value x will return n results. | |||
* Must be greater than 0. | |||
*/ | |||
protected double interpolationExponentN = 1; | |||
/* *************************TYPE 3************************** */ | |||
/** | |||
* Required for Type 3: An vector of PDFFunctions which | |||
* form an array of k single input functions making up | |||
* the stitching function. | |||
*/ | |||
protected List<Function> functions = null; | |||
/** | |||
* Optional for Type 3: An array of (k-1) Doubles that, | |||
* in combination with Domain, define the intervals to which | |||
* each function from the Functions array apply. Bounds | |||
* elements must be in order of increasing magnitude, | |||
* and each value must be within the value of Domain. | |||
* k is the number of functions. | |||
* If you pass null, it will output (1/k) in an array of k-1 elements. | |||
* This makes each function responsible for an equal amount of the stitching function. | |||
* It makes the gradient even. | |||
*/ | |||
protected List<Double> bounds = null; | |||
/** | |||
* create an complete Function object of Type 0, A Sampled function. | |||
* | |||
* Use null for an optional object parameter if you choose not to use it. | |||
* For optional int parameters, pass the default. | |||
* | |||
* @param theDomain List objects of Double objects. | |||
* This is the domain of the function. | |||
* See page 264 of the PDF 1.3 Spec. | |||
* @param theRange List objects of Double objects. | |||
* This is the Range of the function. | |||
* See page 264 of the PDF 1.3 Spec. | |||
* @param theSize A List object of Integer objects. | |||
* This is the number of samples in each input dimension. | |||
* I can't imagine there being more or less than two input dimensions, | |||
* so maybe this should be an array of length 2. | |||
* | |||
* See page 265 of the PDF 1.3 Spec. | |||
* @param theBitsPerSample An int specifying the number of bits | |||
used to represent each sample value. | |||
* Limited to 1,2,4,8,12,16,24 or 32. | |||
* See page 265 of the 1.3 PDF Spec. | |||
* @param theOrder The order of interpolation between samples. Default is 1 (one). Limited | |||
* to 1 (one) or 3, which means linear or cubic-spline interpolation. | |||
* | |||
* This attribute is optional. | |||
* | |||
* See page 265 in the PDF 1.3 spec. | |||
* @param theEncode List objects of Double objects. | |||
* This is the linear mapping of input values intop the domain | |||
* of the function's sample table. Default is hard to represent in | |||
* ascii, but basically [0 (Size0 1) 0 (Size1 1)...]. | |||
* This attribute is optional. | |||
* | |||
* See page 265 in the PDF 1.3 spec. | |||
* @param theDecode List objects of Double objects. | |||
* This is a linear mapping of sample values into the range. | |||
* The default is just the range. | |||
* | |||
* This attribute is optional. | |||
* Read about it on page 265 of the PDF 1.3 spec. | |||
* @param theFunctionDataStream The sample values that specify | |||
* the function are provided in a stream. | |||
* | |||
* This is optional, but is almost always used. | |||
* | |||
* Page 265 of the PDF 1.3 spec has more. | |||
* @param theFilter This is a vector of String objects which are the various filters that | |||
* have are to be applied to the stream to make sense of it. Order matters, | |||
* so watch out. | |||
* | |||
* This is not documented in the Function section of the PDF 1.3 spec, | |||
* it was deduced from samples that this is sometimes used, even if we may never | |||
* use it in FOP. It is added for completeness sake. | |||
* @param theFunctionType This is the type of function (0,2,3, or 4). | |||
* It should be 0 as this is the constructor for sampled functions. | |||
*/ | |||
public FunctionDelegate(Function parentFunction, int theFunctionType, List<Double> theDomain, | |||
List<Double> theRange, List<Double> theSize, int theBitsPerSample, | |||
int theOrder, List<Double> theEncode, List<Double> theDecode, | |||
StringBuffer theFunctionDataStream, List<String> theFilter) { | |||
this.parentFunction = parentFunction; | |||
this.functionType = 0; // dang well better be 0; | |||
this.size = theSize; | |||
this.bitsPerSample = theBitsPerSample; | |||
this.order = theOrder; // int | |||
this.encode = theEncode; // vector of int | |||
this.decode = theDecode; // vector of int | |||
this.functionDataStream = theFunctionDataStream; | |||
this.filter = theFilter; // vector of Strings | |||
// the domain and range are actually two dimensional arrays. | |||
// so if there's not an even number of items, bad stuff | |||
// happens. | |||
this.domain = theDomain; | |||
this.range = theRange; | |||
} | |||
/** | |||
* create an complete Function object of Type 2, an Exponential Interpolation function. | |||
* | |||
* Use null for an optional object parameter if you choose not to use it. | |||
* For optional int parameters, pass the default. | |||
* | |||
* @param theDomain List objects of Double objects. | |||
* This is the domain of the function. | |||
* See page 264 of the PDF 1.3 Spec. | |||
* @param theRange List of Doubles that is the Range of the function. | |||
* See page 264 of the PDF 1.3 Spec. | |||
* @param theCZero This is a vector of Double objects which defines the function result | |||
* when x=0. | |||
* | |||
* This attribute is optional. | |||
* It's described on page 268 of the PDF 1.3 spec. | |||
* @param theCOne This is a vector of Double objects which defines the function result | |||
* when x=1. | |||
* | |||
* This attribute is optional. | |||
* It's described on page 268 of the PDF 1.3 spec. | |||
* @param theInterpolationExponentN This is the inerpolation exponent. | |||
* | |||
* This attribute is required. | |||
* PDF Spec page 268 | |||
* @param theFunctionType The type of the function, which should be 2. | |||
*/ | |||
public FunctionDelegate(Function parentFunction, int theFunctionType, List<Double> theDomain, | |||
List<Double> theRange, List<Double> theCZero, List<Double> theCOne, | |||
double theInterpolationExponentN) { | |||
this.parentFunction = parentFunction; | |||
this.functionType = 2; // dang well better be 2; | |||
this.cZero = theCZero; | |||
this.cOne = theCOne; | |||
this.interpolationExponentN = theInterpolationExponentN; | |||
this.domain = theDomain; | |||
this.range = theRange; | |||
} | |||
/** | |||
* create an complete Function object of Type 3, a Stitching function. | |||
* | |||
* Use null for an optional object parameter if you choose not to use it. | |||
* For optional int parameters, pass the default. | |||
* | |||
* @param theDomain List objects of Double objects. | |||
* This is the domain of the function. | |||
* See page 264 of the PDF 1.3 Spec. | |||
* @param theRange List objects of Double objects. | |||
* This is the Range of the function. | |||
* See page 264 of the PDF 1.3 Spec. | |||
* @param theFunctions A List of the PDFFunction objects that the stitching function stitches. | |||
* | |||
* This attributed is required. | |||
* It is described on page 269 of the PDF spec. | |||
* @param theBounds This is a vector of Doubles representing the numbers that, | |||
* in conjunction with Domain define the intervals to which each function from | |||
* the 'functions' object applies. It must be in order of increasing magnitude, | |||
* and each must be within Domain. | |||
* | |||
* It basically sets how much of the gradient each function handles. | |||
* | |||
* This attributed is required. | |||
* It's described on page 269 of the PDF 1.3 spec. | |||
* @param theEncode List objects of Double objects. | |||
* This is the linear mapping of input values intop the domain | |||
* of the function's sample table. Default is hard to represent in | |||
* ascii, but basically [0 (Size0 1) 0 (Size1 1)...]. | |||
* This attribute is required. | |||
* | |||
* See page 270 in the PDF 1.3 spec. | |||
* @param theFunctionType This is the function type. It should be 3, | |||
* for a stitching function. | |||
*/ | |||
public FunctionDelegate(Function parentFunction, int theFunctionType, List<Double> theDomain, | |||
List<Double> theRange, List<Function> theFunctions, | |||
List<Double> theBounds, List<Double> theEncode) { | |||
this.parentFunction = parentFunction; | |||
this.functionType = 3; // dang well better be 3; | |||
this.functions = theFunctions; | |||
this.bounds = theBounds; | |||
this.encode = theEncode; | |||
this.domain = theDomain; | |||
this.range = theRange; | |||
} | |||
/** | |||
* create an complete Function object of Type 4, a postscript calculator function. | |||
* | |||
* Use null for an optional object parameter if you choose not to use it. | |||
* For optional int parameters, pass the default. | |||
* | |||
* @param theDomain List object of Double objects. | |||
* This is the domain of the function. | |||
* See page 264 of the PDF 1.3 Spec. | |||
* @param theRange List object of Double objects. | |||
* This is the Range of the function. | |||
* See page 264 of the PDF 1.3 Spec. | |||
* @param theFunctionDataStream This is a stream of arithmetic, | |||
* boolean, and stack operators and boolean constants. | |||
* I end up enclosing it in the '{' and '}' braces for you, so don't do it | |||
* yourself. | |||
* | |||
* This attribute is required. | |||
* It's described on page 269 of the PDF 1.3 spec. | |||
* @param theFunctionType The type of function which should be 4, as this is | |||
* a Postscript calculator function | |||
*/ | |||
public FunctionDelegate(Function parentFunction, int theFunctionType, List<Double> theDomain, | |||
List<Double> theRange, StringBuffer theFunctionDataStream) { | |||
this.parentFunction = parentFunction; | |||
this.functionType = 4; // dang well better be 4; | |||
this.functionDataStream = theFunctionDataStream; | |||
this.domain = theDomain; | |||
this.range = theRange; | |||
} | |||
/** | |||
* Gets the function type | |||
*/ | |||
public int getFunctionType() { | |||
return functionType; | |||
} | |||
/** | |||
* Gets the function bounds | |||
*/ | |||
public List<Double> getBounds() { | |||
return bounds; | |||
} | |||
/** | |||
* The function domain | |||
*/ | |||
public List<Double> getDomain() { | |||
return domain; | |||
} | |||
/** | |||
* The function size | |||
*/ | |||
public List<Double> getSize() { | |||
return size; | |||
} | |||
/** | |||
* Gets the function encoding | |||
*/ | |||
public List<Double> getEncode() { | |||
return encode; | |||
} | |||
/** | |||
* Gets the sub-functions | |||
*/ | |||
public List<Function> getFunctions() { | |||
return functions; | |||
} | |||
/** | |||
* Gets the function filter | |||
*/ | |||
public List<String> getFilter() { | |||
return filter; | |||
} | |||
/** | |||
* Gets the bits per sample of the function | |||
*/ | |||
public int getBitsPerSample() { | |||
return bitsPerSample; | |||
} | |||
/** | |||
* Gets the interpolation exponent of the function | |||
*/ | |||
public double getInterpolationExponentN() { | |||
return interpolationExponentN; | |||
} | |||
/** | |||
* Gets the function order | |||
*/ | |||
public int getOrder() { | |||
return order; | |||
} | |||
/** | |||
* Gets the function range | |||
*/ | |||
public List<Double> getRange() { | |||
return range; | |||
} | |||
/** | |||
* Gets the function decoding | |||
*/ | |||
public List<Double> getDecode() { | |||
return decode; | |||
} | |||
/** | |||
* Gets the function data stream | |||
*/ | |||
public StringBuffer getDataStream() { | |||
return functionDataStream; | |||
} | |||
/** | |||
* Gets the function C0 value (color for gradient) | |||
*/ | |||
public List<Double> getCZero() { | |||
return cZero; | |||
} | |||
/** | |||
* Gets the function C1 value (color for gradient) | |||
*/ | |||
public List<Double> getCOne() { | |||
return cOne; | |||
} | |||
public byte[] toByteString() { | |||
return parentFunction.toByteString(); | |||
} | |||
} |
@@ -0,0 +1,363 @@ | |||
/* | |||
* 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.render.shading; | |||
import java.io.UnsupportedEncodingException; | |||
import org.apache.fop.pdf.PDFFunction; | |||
import org.apache.fop.pdf.PDFNumber; | |||
import org.apache.fop.render.ps.svg.PSFunction; | |||
/** | |||
* A class for writing function objects for different output formats | |||
*/ | |||
public class FunctionPattern { | |||
private Function function; | |||
/** | |||
* Constructor | |||
* @param function The function from which to write the output | |||
*/ | |||
public FunctionPattern(Function function) { | |||
this.function = function; | |||
} | |||
/** | |||
* Outputs the function to a byte array | |||
*/ | |||
public String toWriteableString() { | |||
int vectorSize = 0; | |||
int numberOfFunctions = 0; | |||
int tempInt = 0; | |||
StringBuffer p = new StringBuffer(256); | |||
p.append("<< \n/FunctionType " + function.getFunctionType() + " \n"); | |||
// FunctionType 0 | |||
if (this.function.getFunctionType() == 0) { | |||
if (function.getDomain() != null) { | |||
// DOMAIN | |||
p.append("/Domain [ "); | |||
vectorSize = function.getDomain().size(); | |||
for (tempInt = 0; tempInt < vectorSize; tempInt++) { | |||
p.append(PDFNumber.doubleOut(function.getDomain().get(tempInt)) | |||
+ " "); | |||
} | |||
p.append("] \n"); | |||
} else { | |||
p.append("/Domain [ 0 1 ] \n"); | |||
} | |||
// SIZE | |||
if (function.getSize() != null) { | |||
p.append("/Size [ "); | |||
vectorSize = function.getSize().size(); | |||
for (tempInt = 0; tempInt < vectorSize; tempInt++) { | |||
p.append(PDFNumber.doubleOut(function.getSize().get(tempInt)) | |||
+ " "); | |||
} | |||
p.append("] \n"); | |||
} | |||
// ENCODE | |||
if (function.getEncode() != null) { | |||
p.append("/Encode [ "); | |||
vectorSize = function.getEncode().size(); | |||
for (tempInt = 0; tempInt < vectorSize; tempInt++) { | |||
p.append(PDFNumber.doubleOut(function.getEncode().get(tempInt)) | |||
+ " "); | |||
} | |||
p.append("] \n"); | |||
} else { | |||
p.append("/Encode [ "); | |||
vectorSize = function.getFunctions().size(); | |||
for (tempInt = 0; tempInt < vectorSize; tempInt++) { | |||
p.append("0 1 "); | |||
} | |||
p.append("] \n"); | |||
} | |||
// BITSPERSAMPLE | |||
p.append("/BitsPerSample " + function.getBitsPerSample()); | |||
// ORDER (optional) | |||
if (function.getOrder() == 1 || function.getOrder() == 3) { | |||
p.append(" \n/Order " + function.getOrder() + " \n"); | |||
} | |||
// RANGE | |||
if (function.getRange() != null) { | |||
p.append("/Range [ "); | |||
vectorSize = function.getRange().size(); | |||
for (tempInt = 0; tempInt < vectorSize; tempInt++) { | |||
p.append(PDFNumber.doubleOut(function.getRange().get(tempInt)) | |||
+ " "); | |||
} | |||
p.append("] \n"); | |||
} | |||
// DECODE | |||
if (function.getDecode() != null) { | |||
p.append("/Decode [ "); | |||
vectorSize = function.getDecode().size(); | |||
for (tempInt = 0; tempInt < vectorSize; tempInt++) { | |||
p.append(PDFNumber.doubleOut(function.getDecode().get(tempInt)) | |||
+ " "); | |||
} | |||
p.append("] \n"); | |||
} | |||
// LENGTH | |||
if (function.getDataStream() != null) { | |||
p.append("/Length " + (function.getDataStream().length() + 1) | |||
+ " \n"); | |||
} | |||
// FILTER? | |||
if (function.getFilter() != null) { // if there's a filter | |||
vectorSize = function.getFilter().size(); | |||
p.append("/Filter "); | |||
if (vectorSize == 1) { | |||
p.append("/" + (function.getFilter().get(0)) | |||
+ " \n"); | |||
} else { | |||
p.append("[ "); | |||
for (tempInt = 0; tempInt < vectorSize; tempInt++) { | |||
p.append("/" + (function.getFilter().get(0)) | |||
+ " "); | |||
} | |||
p.append("] \n"); | |||
} | |||
} | |||
p.append(">>"); | |||
// stream representing the function | |||
if (function.getDataStream() != null) { | |||
p.append("\nstream\n" + function.getDataStream() | |||
+ "\nendstream"); | |||
} | |||
// end of if FunctionType 0 | |||
} else if (function.getFunctionType() == 2) { | |||
// DOMAIN | |||
if (function.getDomain() != null) { | |||
p.append("/Domain [ "); | |||
vectorSize = function.getDomain().size(); | |||
for (tempInt = 0; tempInt < vectorSize; tempInt++) { | |||
p.append(PDFNumber.doubleOut(function.getDomain().get(tempInt)) | |||
+ " "); | |||
} | |||
p.append("] \n"); | |||
} else { | |||
p.append("/Domain [ 0 1 ] \n"); | |||
} | |||
// RANGE | |||
if (function.getRange() != null) { | |||
p.append("/Range [ "); | |||
vectorSize = function.getRange().size(); | |||
for (tempInt = 0; tempInt < vectorSize; tempInt++) { | |||
p.append(PDFNumber.doubleOut(function.getRange().get(tempInt)) | |||
+ " "); | |||
} | |||
p.append("] \n"); | |||
} | |||
// FunctionType, C0, C1, N are required in PDF | |||
// C0 | |||
if (function.getCZero() != null) { | |||
p.append("/C0 [ "); | |||
vectorSize = function.getCZero().size(); | |||
for (tempInt = 0; tempInt < vectorSize; tempInt++) { | |||
p.append(PDFNumber.doubleOut(function.getCZero().get(tempInt)) | |||
+ " "); | |||
} | |||
p.append("] \n"); | |||
} | |||
// C1 | |||
if (function.getCOne() != null) { | |||
p.append("/C1 [ "); | |||
vectorSize = function.getCOne().size(); | |||
for (tempInt = 0; tempInt < vectorSize; tempInt++) { | |||
p.append(PDFNumber.doubleOut(function.getCOne().get(tempInt)) | |||
+ " "); | |||
} | |||
p.append("] \n"); | |||
} | |||
// N: The interpolation Exponent | |||
p.append("/N " | |||
+ PDFNumber.doubleOut(Double.valueOf(function.getInterpolationExponentN())) | |||
+ " \n"); | |||
p.append(">>"); | |||
} else if (function.getFunctionType() | |||
== 3) { // fix this up when my eyes uncross | |||
// DOMAIN | |||
if (function.getDomain() != null) { | |||
p.append("/Domain [ "); | |||
vectorSize = function.getDomain().size(); | |||
for (tempInt = 0; tempInt < vectorSize; tempInt++) { | |||
p.append(PDFNumber.doubleOut(function.getDomain().get(tempInt)) | |||
+ " "); | |||
} | |||
p.append("] \n"); | |||
} else { | |||
p.append("/Domain [ 0 1 ] \n"); | |||
} | |||
// RANGE | |||
if (function.getRange() != null) { | |||
p.append("/Range [ "); | |||
vectorSize = function.getRange().size(); | |||
for (tempInt = 0; tempInt < vectorSize; tempInt++) { | |||
p.append(PDFNumber.doubleOut(function.getRange().get(tempInt)) | |||
+ " "); | |||
} | |||
p.append("] \n"); | |||
} | |||
// FUNCTIONS | |||
if (function.getFunctions() != null) { | |||
p.append("/Functions [ "); | |||
numberOfFunctions = function.getFunctions().size(); | |||
for (tempInt = 0; tempInt < numberOfFunctions; tempInt++) { | |||
try { | |||
if (function instanceof PSFunction) { | |||
p.append(new String(function.getFunctions().get(tempInt).toByteString(), "UTF-8") | |||
+ " "); | |||
} else { | |||
p.append(((PDFFunction)function.getFunctions().get(tempInt)).referencePDF() | |||
+ " "); | |||
} | |||
} catch (UnsupportedEncodingException ex) { | |||
//This should have been made an enum type to avoid throwing exceptions. | |||
} | |||
} | |||
p.append("] \n"); | |||
} | |||
// ENCODE | |||
if (function.getEncode() != null) { | |||
p.append("/Encode [ "); | |||
vectorSize = function.getEncode().size(); | |||
for (tempInt = 0; tempInt < vectorSize; tempInt++) { | |||
p.append(PDFNumber.doubleOut(function.getEncode().get(tempInt)) | |||
+ " "); | |||
} | |||
p.append("] \n"); | |||
} else { | |||
p.append("/Encode [ "); | |||
vectorSize = function.getFunctions().size(); | |||
for (tempInt = 0; tempInt < vectorSize; tempInt++) { | |||
p.append("0 1 "); | |||
} | |||
p.append("] \n"); | |||
} | |||
// BOUNDS, required, but can be empty | |||
p.append("/Bounds [ "); | |||
if (function.getBounds() != null) { | |||
vectorSize = function.getBounds().size(); | |||
for (tempInt = 0; tempInt < vectorSize; tempInt++) { | |||
p.append(PDFNumber.doubleOut(function.getBounds().get(tempInt)) | |||
+ " "); | |||
} | |||
} else { | |||
if (function.getFunctions() != null) { | |||
// if there are n functions, | |||
// there must be n-1 bounds. | |||
// so let each function handle an equal portion | |||
// of the whole. e.g. if there are 4, then [ 0.25 0.25 0.25 ] | |||
String functionsFraction = PDFNumber.doubleOut(Double.valueOf(1.0 | |||
/ (numberOfFunctions))); | |||
for (tempInt = 0; tempInt + 1 < numberOfFunctions; | |||
tempInt++) { | |||
p.append(functionsFraction + " "); | |||
} | |||
} | |||
} | |||
p.append("]\n>>"); | |||
} else if (function.getFunctionType() | |||
== 4) { // fix this up when my eyes uncross | |||
// DOMAIN | |||
if (function.getDomain() != null) { | |||
p.append("/Domain [ "); | |||
vectorSize = function.getDomain().size(); | |||
for (tempInt = 0; tempInt < vectorSize; tempInt++) { | |||
p.append(PDFNumber.doubleOut(function.getDomain().get(tempInt)) | |||
+ " "); | |||
} | |||
p.append("] \n"); | |||
} else { | |||
p.append("/Domain [ 0 1 ] \n"); | |||
} | |||
// RANGE | |||
if (function.getRange() != null) { | |||
p.append("/Range [ "); | |||
vectorSize = function.getRange().size(); | |||
for (tempInt = 0; tempInt < vectorSize; tempInt++) { | |||
p.append(PDFNumber.doubleOut(function.getRange().get(tempInt)) | |||
+ " "); | |||
} | |||
p.append("] \n"); | |||
} | |||
// LENGTH | |||
if (function.getDataStream() != null) { | |||
p.append("/Length " + (function.getDataStream().length() + 1) | |||
+ " \n"); | |||
} | |||
p.append(">>"); | |||
// stream representing the function | |||
if (function.getDataStream() != null) { | |||
p.append("\nstream\n{ " + function.getDataStream() | |||
+ " }\nendstream"); | |||
} | |||
} | |||
return p.toString(); | |||
} | |||
} |
@@ -0,0 +1,162 @@ | |||
/* | |||
* 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.render.shading; | |||
import java.awt.Color; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
import org.apache.xmlgraphics.java2d.color.ColorUtil; | |||
import org.apache.fop.pdf.PDFDeviceColorSpace; | |||
import org.apache.fop.render.ps.svg.PSSVGGraphics2D; | |||
public abstract class GradientFactory { | |||
static GradientRegistrar registrar; | |||
/** | |||
* Constructor | |||
* @param registrar The object used to register new embedded objects in the | |||
* output format. | |||
*/ | |||
public static GradientFactory newInstance(GradientRegistrar theRegistrar) { | |||
registrar = theRegistrar; | |||
if (registrar instanceof PSSVGGraphics2D) { | |||
return new PSGradientFactory(); | |||
} else { | |||
return new PDFGradientFactory(); | |||
} | |||
} | |||
/** | |||
* Creates a new gradient | |||
* @param radial Determines whether the gradient is radial | |||
* @param theColorspace The colorspace used in PDF and Postscript | |||
* @param theColors The colors to be used in the gradient | |||
* @param theBounds The bounds of each color | |||
* @param theCoords The co-ordinates of the gradient | |||
* @param theMatrix The matrix for any transformations | |||
* @return Returns the Pattern object of the gradient | |||
*/ | |||
public abstract Pattern createGradient(boolean radial, | |||
PDFDeviceColorSpace theColorspace, List<Color> theColors, List<Double> theBounds, | |||
List<Double> theCoords, List<Double> theMatrix); | |||
protected Pattern makeGradient(boolean radial, PDFDeviceColorSpace theColorspace, | |||
List<Color> theColors, List<Double> theBounds, | |||
List<Double> theCoords, List<Double> theMatrix) { | |||
Shading myShad; | |||
Function myfunky; | |||
Function myfunc; | |||
List<Double> theCzero; | |||
List<Double> theCone; | |||
double interpolation = 1.000; | |||
List<Function> theFunctions = new ArrayList<Function>(); | |||
int currentPosition; | |||
int lastPosition = theColors.size() - 1; | |||
// if 5 elements, the penultimate element is 3. | |||
// do not go beyond that, because you always need | |||
// to have a next color when creating the function. | |||
for (currentPosition = 0; currentPosition < lastPosition; | |||
currentPosition++) { // for every consecutive color pair | |||
Color currentColor = theColors.get(currentPosition); | |||
Color nextColor = 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 (!nextColor.getColorSpace().isCS_sRGB()) { | |||
//Convert to sRGB | |||
nextColor = ColorUtil.toSRGBColor(nextColor); | |||
theColors.set(currentPosition + 1, nextColor); | |||
} | |||
theCzero = toColorVector(currentColor); | |||
theCone = toColorVector(nextColor); | |||
myfunc = makeFunction(2, null, null, theCzero, theCone, | |||
interpolation); | |||
theFunctions.add(myfunc); | |||
} // end of for every consecutive color pair | |||
myfunky = makeFunction(3, null, null, theFunctions, theBounds, | |||
null); | |||
if (radial) { | |||
if (theCoords.size() == 6) { | |||
// make Shading of Type 2 or 3 | |||
myShad = makeShading(3, theColorspace, null, null, false, theCoords, | |||
null, myfunky, null); | |||
} else { // if the center x, center y, and radius specifiy | |||
// the gradient, then assume the same center x, center y, | |||
// and radius of zero for the other necessary component | |||
List<Double> newCoords = new ArrayList<Double>(); | |||
newCoords.add(theCoords.get(0)); | |||
newCoords.add(theCoords.get(1)); | |||
newCoords.add(theCoords.get(2)); | |||
newCoords.add(theCoords.get(0)); | |||
newCoords.add(theCoords.get(1)); | |||
newCoords.add(Double.valueOf(0.0)); | |||
myShad = makeShading(3, theColorspace, null, null, false, newCoords, | |||
null, myfunky, null); | |||
} | |||
} else { | |||
myShad = makeShading(2, theColorspace, null, null, false, theCoords, | |||
null, myfunky, null); | |||
} | |||
return makePattern(2, myShad, null, null, theMatrix); | |||
} | |||
public abstract Function makeFunction(int functionType, List<Double> theDomain, | |||
List<Double> theRange, List<Function> theFunctions, | |||
List<Double> theBounds, List<Double> theEncode); | |||
public abstract Function makeFunction(int functionType, List<Double> theDomain, | |||
List<Double> theRange, List<Double> theCZero, List<Double> theCOne, | |||
double theInterpolationExponentN); | |||
public abstract Shading makeShading(int theShadingType, | |||
PDFDeviceColorSpace theColorSpace, List<Double> theBackground, List<Double> theBBox, | |||
boolean theAntiAlias, List<Double> theCoords, List<Double> theDomain, | |||
Function theFunction, List<Integer> theExtend); | |||
public abstract Pattern makePattern(int thePatternType, Shading theShading, List theXUID, | |||
StringBuffer theExtGState, List<Double> theMatrix); | |||
private List<Double> toColorVector(Color nextColor) { | |||
List<Double> vector = new java.util.ArrayList<Double>(); | |||
float[] comps = nextColor.getColorComponents(null); | |||
for (int i = 0, c = comps.length; i < c; i++) { | |||
vector.add(Double.valueOf(comps[i])); | |||
} | |||
return vector; | |||
} | |||
} |
@@ -0,0 +1,45 @@ | |||
/* | |||
* 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. | |||
*/ | |||
package org.apache.fop.render.shading; | |||
public interface GradientRegistrar { | |||
/** | |||
* Registers a function object against the output format document | |||
* @param function The function object to register | |||
* @return Returns either the function which has already been registered | |||
* or the current new registered object. | |||
*/ | |||
Function registerFunction(Function function); | |||
/** | |||
* Registers a shading object against the output format document | |||
* @param shading The shading object to register | |||
* @return Returns either the shading which has already been registered | |||
* or the current new registered object | |||
*/ | |||
Shading registerShading(Shading shading); | |||
/** | |||
* Registers a pattern object against the output format document | |||
* @param pattern The pattern object to register | |||
* @return Returns either the pattern which has already been registered | |||
* or the current new registered object | |||
*/ | |||
Pattern registerPattern(Pattern pattern); | |||
} |
@@ -0,0 +1,76 @@ | |||
/* | |||
* 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. | |||
*/ | |||
package org.apache.fop.render.shading; | |||
import java.awt.Color; | |||
import java.util.List; | |||
import org.apache.fop.pdf.PDFDeviceColorSpace; | |||
import org.apache.fop.pdf.PDFFunction; | |||
import org.apache.fop.pdf.PDFPattern; | |||
import org.apache.fop.pdf.PDFShading; | |||
public class PDFGradientFactory extends GradientFactory { | |||
@Override | |||
public PDFPattern createGradient(boolean radial, PDFDeviceColorSpace theColorspace, List<Color> theColors, | |||
List<Double> theBounds, List<Double> theCoords, List<Double> theMatrix) { | |||
return (PDFPattern)makeGradient(radial, theColorspace, theColors, theBounds, | |||
theCoords, theMatrix); | |||
} | |||
@Override | |||
public Function makeFunction(int functionType, List<Double> theDomain, | |||
List<Double> theRange, List<Function> theFunctions, | |||
List<Double> theBounds, List<Double> theEncode) { | |||
Function newFunction = new PDFFunction(functionType, theDomain, theRange, theFunctions, | |||
theBounds, theEncode); | |||
newFunction = registrar.registerFunction(newFunction); | |||
return newFunction; | |||
} | |||
public Function makeFunction(int functionType, List<Double> theDomain, | |||
List<Double> theRange, List<Double> theCZero, List<Double> theCOne, | |||
double theInterpolationExponentN) { | |||
Function newFunction = new PDFFunction(functionType, theDomain, theRange, theCZero, | |||
theCOne, theInterpolationExponentN); | |||
newFunction = registrar.registerFunction(newFunction); | |||
return newFunction; | |||
} | |||
@Override | |||
public Shading makeShading(int theShadingType, | |||
PDFDeviceColorSpace theColorSpace, List<Double> theBackground, List<Double> theBBox, | |||
boolean theAntiAlias, List<Double> theCoords, List<Double> theDomain, | |||
Function theFunction, List<Integer> theExtend) { | |||
Shading newShading = new PDFShading(theShadingType, theColorSpace, theBackground, | |||
theBBox, theAntiAlias, theCoords, theDomain, theFunction, theExtend); | |||
newShading = registrar.registerShading(newShading); | |||
return newShading; | |||
} | |||
@Override | |||
public Pattern makePattern(int thePatternType, Shading theShading, List theXUID, | |||
StringBuffer theExtGState, List<Double> theMatrix) { | |||
Pattern newPattern = new PDFPattern(thePatternType, theShading, theXUID, theExtGState, | |||
theMatrix); | |||
newPattern = registrar.registerPattern(newPattern); | |||
return newPattern; | |||
} | |||
} |
@@ -0,0 +1,70 @@ | |||
/* | |||
* 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. | |||
*/ | |||
package org.apache.fop.render.shading; | |||
import java.awt.Color; | |||
import java.util.List; | |||
import org.apache.fop.pdf.PDFDeviceColorSpace; | |||
import org.apache.fop.render.ps.svg.PSFunction; | |||
import org.apache.fop.render.ps.svg.PSPattern; | |||
import org.apache.fop.render.ps.svg.PSShading; | |||
public class PSGradientFactory extends GradientFactory { | |||
@Override | |||
public PSPattern createGradient(boolean radial, PDFDeviceColorSpace theColorspace, | |||
List<Color> theColors, List<Double> theBounds, List<Double> theCoords, | |||
List<Double> theMatrix) { | |||
return (PSPattern)makeGradient(radial, theColorspace, theColors, theBounds, | |||
theCoords, theMatrix); | |||
} | |||
public Function makeFunction(int functionType, List<Double> theDomain, | |||
List<Double> theRange, List<Function> theFunctions, | |||
List<Double> theBounds, List<Double> theEncode) { | |||
Function newFunction = new PSFunction(functionType, theDomain, theRange, theFunctions, | |||
theBounds, theEncode); | |||
return newFunction; | |||
} | |||
@Override | |||
public Function makeFunction(int functionType, List<Double> theDomain, | |||
List<Double> theRange, List<Double> theCZero, List<Double> theCOne, | |||
double theInterpolationExponentN) { | |||
Function newFunction = new PSFunction(functionType, theDomain, theRange, theCZero, | |||
theCOne, theInterpolationExponentN); | |||
return newFunction; | |||
} | |||
@Override | |||
public Shading makeShading(int theShadingType, | |||
PDFDeviceColorSpace theColorSpace, List<Double> theBackground, List<Double> theBBox, | |||
boolean theAntiAlias, List<Double> theCoords, List<Double> theDomain, | |||
Function theFunction, List<Integer> theExtend) { | |||
Shading newShading = new PSShading(theShadingType, theColorSpace, theBackground, theBBox, | |||
theAntiAlias, theCoords, theDomain, theFunction, theExtend); | |||
return newShading; | |||
} | |||
@Override | |||
public Pattern makePattern(int thePatternType, Shading theShading, List theXUID, | |||
StringBuffer theExtGState, List<Double> theMatrix) { | |||
return new PSPattern(thePatternType, theShading, theXUID, theExtGState); | |||
} | |||
} |
@@ -0,0 +1,22 @@ | |||
/* | |||
* 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. | |||
*/ | |||
package org.apache.fop.render.shading; | |||
public interface Pattern { | |||
} |
@@ -0,0 +1,26 @@ | |||
/* | |||
* 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. | |||
*/ | |||
package org.apache.fop.render.shading; | |||
public interface Shading { | |||
StringBuffer handleShadingType1(StringBuffer p); | |||
StringBuffer handleShadingType2or3(StringBuffer p); | |||
StringBuffer handleShadingType4or6or7(StringBuffer p); | |||
StringBuffer handleShadingType5(StringBuffer p); | |||
} |
@@ -0,0 +1,105 @@ | |||
/* | |||
* 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. | |||
*/ | |||
package org.apache.fop.render.shading; | |||
import java.util.List; | |||
import org.apache.fop.pdf.PDFDeviceColorSpace; | |||
import org.apache.fop.pdf.PDFNumber; | |||
/** | |||
* A class for writing shading objects for different output formats | |||
*/ | |||
public class ShadingPattern { | |||
private Shading shading; | |||
/** | |||
* Constructor | |||
* @param shading The shading object from which to write the output | |||
*/ | |||
public ShadingPattern(Shading shading) { | |||
this.shading = shading; | |||
} | |||
/** | |||
* Outputs the given shading object to a String | |||
* @param colorSpace The Colospace (PDF and Postscript) | |||
* @param shadingType The shading type | |||
* @param background The background | |||
* @param bBox The bounding box | |||
* @param antiAlias Anti-aliasing | |||
* @return Returns the output string | |||
*/ | |||
public String toString(PDFDeviceColorSpace colorSpace, int shadingType, List background, | |||
List bBox, boolean antiAlias) { | |||
StringBuffer p = new StringBuffer(128); | |||
p.append("<<\n/ShadingType " + shadingType + " \n"); | |||
if (colorSpace != null) { | |||
p.append("/ColorSpace /" | |||
+ colorSpace.getName() + " \n"); | |||
} | |||
if (background != null) { | |||
p.append("/Background [ "); | |||
for (int bgIndex = 0; bgIndex < background.size(); bgIndex++) { | |||
p.append(PDFNumber.doubleOut((Double)background.get(bgIndex)) | |||
+ " "); | |||
} | |||
p.append("] \n"); | |||
} | |||
if (bBox | |||
!= null) { // I've never seen an example, so I guess this is right. | |||
p.append("/BBox [ "); | |||
for (int bboxIndex = 0; bboxIndex < bBox.size(); bboxIndex++) { | |||
p.append(PDFNumber.doubleOut((Double)bBox.get(bboxIndex)) | |||
+ " "); | |||
} | |||
p.append("] \n"); | |||
} | |||
if (antiAlias) { | |||
p.append("/AntiAlias " + antiAlias + " \n"); | |||
} | |||
// Here's where we differentiate based on what type it is. | |||
switch (shadingType) { | |||
//Function based shading | |||
case 1: p = shading.handleShadingType1(p); break; | |||
//Axial shading | |||
case 2: | |||
//Radial shading | |||
case 3: p = shading.handleShadingType2or3(p); break; | |||
//Free-form Gouraud-shaded triangle meshes | |||
case 4: | |||
//Coons patch meshes | |||
case 6: | |||
//Tensor product patch meshes | |||
case 7: p = shading.handleShadingType4or6or7(p); break; | |||
//Lattice Free form gouraud-shaded triangle mesh | |||
case 5: p = shading.handleShadingType5(p); break; | |||
default: //Shading pattern outside expecting values | |||
break; | |||
} | |||
p.append(">>"); | |||
return (p.toString()); | |||
} | |||
} |
@@ -80,6 +80,7 @@ import org.apache.fop.pdf.PDFColorHandler; | |||
import org.apache.fop.pdf.PDFConformanceException; | |||
import org.apache.fop.pdf.PDFDeviceColorSpace; | |||
import org.apache.fop.pdf.PDFDocument; | |||
import org.apache.fop.pdf.PDFFunction; | |||
import org.apache.fop.pdf.PDFGState; | |||
import org.apache.fop.pdf.PDFImage; | |||
import org.apache.fop.pdf.PDFImageXObject; | |||
@@ -89,11 +90,18 @@ import org.apache.fop.pdf.PDFPaintingState; | |||
import org.apache.fop.pdf.PDFPattern; | |||
import org.apache.fop.pdf.PDFResourceContext; | |||
import org.apache.fop.pdf.PDFResources; | |||
import org.apache.fop.pdf.PDFShading; | |||
import org.apache.fop.pdf.PDFText; | |||
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.render.shading.Function; | |||
import org.apache.fop.render.shading.GradientFactory; | |||
import org.apache.fop.render.shading.GradientRegistrar; | |||
import org.apache.fop.render.shading.PDFGradientFactory; | |||
import org.apache.fop.render.shading.Pattern; | |||
import org.apache.fop.render.shading.Shading; | |||
/** | |||
* <p>PDF Graphics 2D. | |||
@@ -104,7 +112,7 @@ import org.apache.fop.render.pdf.ImageRenderedAdapter; | |||
* | |||
* @see org.apache.batik.ext.awt.g2d.AbstractGraphics2D | |||
*/ | |||
public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHandler { | |||
public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHandler, GradientRegistrar { | |||
private static final AffineTransform IDENTITY_TRANSFORM = new AffineTransform(); | |||
/** The number of decimal places. */ | |||
@@ -868,11 +876,10 @@ public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHand | |||
} | |||
//Gradients are currently restricted to sRGB | |||
PDFDeviceColorSpace aColorSpace; | |||
aColorSpace = new PDFDeviceColorSpace(PDFDeviceColorSpace.DEVICE_RGB); | |||
PDFPattern myPat = pdfDoc.getFactory().makeGradient( | |||
resourceContext, false, aColorSpace, | |||
someColors, theBounds, theCoords, theMatrix); | |||
PDFDeviceColorSpace colSpace = new PDFDeviceColorSpace(PDFDeviceColorSpace.DEVICE_RGB); | |||
PDFGradientFactory gradientFactory = (PDFGradientFactory)GradientFactory.newInstance(this); | |||
PDFPattern myPat = gradientFactory.createGradient(false, colSpace, someColors, theBounds, | |||
theCoords, theMatrix); | |||
currentStream.write(myPat.getColorSpaceOut(fill)); | |||
return true; | |||
@@ -944,13 +951,10 @@ public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHand | |||
float offset = fractions[count]; | |||
theBounds.add(new Double(offset)); | |||
} | |||
PDFDeviceColorSpace colSpace; | |||
colSpace = new PDFDeviceColorSpace(PDFDeviceColorSpace.DEVICE_RGB); | |||
PDFPattern myPat = pdfDoc.getFactory().makeGradient( | |||
resourceContext, true, colSpace, | |||
someColors, theBounds, theCoords, theMatrix); | |||
PDFDeviceColorSpace colSpace = new PDFDeviceColorSpace(PDFDeviceColorSpace.DEVICE_RGB); | |||
PDFGradientFactory gradientFactory = (PDFGradientFactory) GradientFactory.newInstance(this); | |||
PDFPattern myPat = gradientFactory.createGradient(true, colSpace, someColors, theBounds, | |||
theCoords, theMatrix); | |||
currentStream.write(myPat.getColorSpaceOut(fill)); | |||
return true; | |||
@@ -1856,4 +1860,36 @@ public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHand | |||
//NYI | |||
} | |||
/** | |||
* Registers a function object against the output format document | |||
* @param function The function object to register | |||
* @return Returns either the function which has already been registered | |||
* or the current new registered object. | |||
*/ | |||
public Function registerFunction(Function function) { | |||
return pdfDoc.getFactory().registerFunction((PDFFunction)function); | |||
} | |||
/** | |||
* Registers a shading object against the otuput format document | |||
* @param shading The shading object to register | |||
* @return Returs either the shading which has already been registered | |||
* or the current new registered object | |||
*/ | |||
public Shading registerShading(Shading shading) { | |||
assert shading instanceof PDFShading; | |||
return pdfDoc.getFactory().registerShading(resourceContext, (PDFShading)shading); | |||
} | |||
/** | |||
* Registers a pattern object against the output format document | |||
* @param pattern The pattern object to register | |||
* @return Returns either the pattern which has already been registered | |||
* or the current new registered object | |||
*/ | |||
public Pattern registerPattern(Pattern pattern) { | |||
assert pattern instanceof PDFPattern; | |||
return pdfDoc.getFactory().registerPattern(resourceContext, (PDFPattern)pattern); | |||
} | |||
} |
@@ -0,0 +1,71 @@ | |||
/* | |||
* 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.render.ps.svg; | |||
import java.awt.Color; | |||
import java.awt.geom.AffineTransform; | |||
import java.awt.geom.Rectangle2D; | |||
import java.awt.geom.Rectangle2D.Float; | |||
import java.io.ByteArrayOutputStream; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import org.junit.Test; | |||
import org.apache.commons.io.FileUtils; | |||
import org.apache.batik.ext.awt.RadialGradientPaint; | |||
import org.apache.xmlgraphics.java2d.GraphicContext; | |||
import org.apache.xmlgraphics.ps.PSGenerator; | |||
import static org.junit.Assert.assertEquals; | |||
public class PSSVGGraphics2DTestCase { | |||
float cx = 841.891f; | |||
float cy = 178.583f; | |||
float r = 16.4331f; | |||
float[] fractions = {0.2f, 0.6012f, 0.8094f, 1.0f}; | |||
/** | |||
* Tests a radial gradient generated pattern with certain inputs against | |||
* an expected output. | |||
* @throws IOException | |||
*/ | |||
@Test | |||
public void testApplyPaint() throws IOException { | |||
ByteArrayOutputStream os = new ByteArrayOutputStream(); | |||
PSGenerator gen = new PSGenerator(os); | |||
PSSVGGraphics2D svgGraphics2D = new PSSVGGraphics2D(false, gen); | |||
svgGraphics2D.setGraphicContext(new GraphicContext()); | |||
svgGraphics2D.setTransform(new AffineTransform()); | |||
Color[] colors = {new Color(255, 255, 255), new Color(200, 200, 200), | |||
new Color(170, 170, 170), new Color(140, 140, 140)}; | |||
RadialGradientPaint paint = new RadialGradientPaint(cx, cy, r, fractions, colors); | |||
Float s = new Rectangle2D.Float(7.0f, 3.0f, 841.891f, 178.583f); | |||
svgGraphics2D.applyPaint(paint, true); | |||
byte[] test = os.toByteArray(); | |||
byte[] expected = FileUtils.readFileToByteArray( | |||
new File("test/java/org/apache/fop/render/ps/svg/expected.ps")); | |||
assertEquals(new String(test), new String(expected)); | |||
} | |||
} |
@@ -0,0 +1,70 @@ | |||
/* | |||
* 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.render.ps.svg; | |||
import java.awt.Color; | |||
import java.awt.geom.AffineTransform; | |||
import java.awt.geom.Rectangle2D; | |||
import java.awt.geom.Rectangle2D.Float; | |||
import java.io.ByteArrayOutputStream; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import org.junit.Test; | |||
import org.apache.commons.io.FileUtils; | |||
import org.apache.batik.ext.awt.LinearGradientPaint; | |||
import org.apache.xmlgraphics.java2d.GraphicContext; | |||
import org.apache.xmlgraphics.ps.PSGenerator; | |||
import static org.junit.Assert.assertEquals; | |||
public class PSSVGLinearGraphics2DTestCase { | |||
float startX = 115f; | |||
float endX = 15f; | |||
float startY = 285f; | |||
float endY=15f; | |||
float[] fractions = {0.0f, 1.0f}; | |||
/** | |||
* Tests a linear gradient generated pattern with certain inputs against | |||
* an expected output. | |||
* @throws IOException | |||
*/ | |||
@Test | |||
public void testApplyPaint() throws IOException { | |||
ByteArrayOutputStream os = new ByteArrayOutputStream(); | |||
PSGenerator gen = new PSGenerator(os); | |||
PSSVGGraphics2D svgGraphics2D = new PSSVGGraphics2D(false, gen); | |||
svgGraphics2D.setGraphicContext(new GraphicContext()); | |||
svgGraphics2D.setTransform(new AffineTransform()); | |||
Color[] colors = {new Color(255, 255, 0), new Color(255, 0, 0)}; | |||
LinearGradientPaint paint = new LinearGradientPaint(startX, startY, endX, endY, fractions, colors); | |||
Float s = new Rectangle2D.Float(115.0f, 15.0f, 170f, 110f); | |||
svgGraphics2D.applyPaint(paint, true); | |||
byte[] test = os.toByteArray(); | |||
byte[] expected = FileUtils.readFileToByteArray( | |||
new File("test/java/org/apache/fop/render/ps/svg/axial-shading-expected.dat")); | |||
assertEquals(new String(test), new String(expected)); | |||
} | |||
} |
@@ -0,0 +1,26 @@ | |||
/Pattern setcolorspace | |||
<< | |||
/Type /Pattern | |||
/PatternType 2 | |||
/Shading << | |||
/ShadingType 2 | |||
/ColorSpace /DeviceRGB | |||
/Coords [ 115 285 15 15 ] | |||
/Domain [ 0 1 ] | |||
/Extend [ true true ] | |||
/Function << | |||
/FunctionType 3 | |||
/Domain [ 0 1 ] | |||
/Functions [ << | |||
/FunctionType 2 | |||
/Domain [ 0 1 ] | |||
/C0 [ 1 1 0 ] | |||
/C1 [ 1 0 0 ] | |||
/N 1 | |||
>> ] | |||
/Encode [ 0 1 ] | |||
/Bounds [ ] | |||
>> | |||
>> | |||
>> | |||
matrix makepattern setcolor |
@@ -0,0 +1,38 @@ | |||
/Pattern setcolorspace | |||
<< | |||
/Type /Pattern | |||
/PatternType 2 | |||
/Shading << | |||
/ShadingType 3 | |||
/ColorSpace /DeviceRGB | |||
/Coords [ 841.890991 178.582993 3.28662 841.890991 178.582993 16.4331 ] | |||
/Domain [ 0 1 ] | |||
/Extend [ true true ] | |||
/Function << | |||
/FunctionType 3 | |||
/Domain [ 0 1 ] | |||
/Functions [ << | |||
/FunctionType 2 | |||
/Domain [ 0 1 ] | |||
/C0 [ 1 1 1 ] | |||
/C1 [ 0.784314 0.784314 0.784314 ] | |||
/N 1 | |||
>> << | |||
/FunctionType 2 | |||
/Domain [ 0 1 ] | |||
/C0 [ 0.784314 0.784314 0.784314 ] | |||
/C1 [ 0.666667 0.666667 0.666667 ] | |||
/N 1 | |||
>> << | |||
/FunctionType 2 | |||
/Domain [ 0 1 ] | |||
/C0 [ 0.666667 0.666667 0.666667 ] | |||
/C1 [ 0.54902 0.54902 0.54902 ] | |||
/N 1 | |||
>> ] | |||
/Encode [ 0 1 0 1 0 1 ] | |||
/Bounds [ 0.6012 0.8094 ] | |||
>> | |||
>> | |||
>> | |||
matrix makepattern setcolor |