Browse Source

FOP-2313: add support for svg gradients when generating PostScript; most code authored by Robert Meyer with a small contribution by Athanasios Giannimaras.

git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1541551 13f79535-47bb-0310-9956-ffa450edef68
pull/22/head
Luis Bernardo 10 years ago
parent
commit
8d0bc94dd4
26 changed files with 3009 additions and 854 deletions
  1. 1
    0
      build.xml
  2. BIN
      lib/xmlgraphics-commons-svn-trunk.jar
  3. 46
    60
      src/java/org/apache/fop/pdf/PDFFactory.java
  4. 127
    524
      src/java/org/apache/fop/pdf/PDFFunction.java
  5. 9
    5
      src/java/org/apache/fop/pdf/PDFPattern.java
  6. 179
    194
      src/java/org/apache/fop/pdf/PDFShading.java
  7. 269
    58
      src/java/org/apache/fop/render/ps/PSImageHandlerSVG.java
  8. 143
    0
      src/java/org/apache/fop/render/ps/svg/PSFunction.java
  9. 103
    0
      src/java/org/apache/fop/render/ps/svg/PSPattern.java
  10. 291
    0
      src/java/org/apache/fop/render/ps/svg/PSSVGGraphics2D.java
  11. 228
    0
      src/java/org/apache/fop/render/ps/svg/PSShading.java
  12. 39
    0
      src/java/org/apache/fop/render/shading/Function.java
  13. 451
    0
      src/java/org/apache/fop/render/shading/FunctionDelegate.java
  14. 363
    0
      src/java/org/apache/fop/render/shading/FunctionPattern.java
  15. 162
    0
      src/java/org/apache/fop/render/shading/GradientFactory.java
  16. 45
    0
      src/java/org/apache/fop/render/shading/GradientRegistrar.java
  17. 76
    0
      src/java/org/apache/fop/render/shading/PDFGradientFactory.java
  18. 70
    0
      src/java/org/apache/fop/render/shading/PSGradientFactory.java
  19. 22
    0
      src/java/org/apache/fop/render/shading/Pattern.java
  20. 26
    0
      src/java/org/apache/fop/render/shading/Shading.java
  21. 105
    0
      src/java/org/apache/fop/render/shading/ShadingPattern.java
  22. 49
    13
      src/java/org/apache/fop/svg/PDFGraphics2D.java
  23. 71
    0
      test/java/org/apache/fop/render/ps/svg/PSSVGGraphics2DTestCase.java
  24. 70
    0
      test/java/org/apache/fop/render/ps/svg/PSSVGLinearGraphics2DTestCase.java
  25. 26
    0
      test/java/org/apache/fop/render/ps/svg/axial-shading-expected.dat
  26. 38
    0
      test/java/org/apache/fop/render/ps/svg/expected.ps

+ 1
- 0
build.xml View File

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

BIN
lib/xmlgraphics-commons-svn-trunk.jar View File


+ 46
- 60
src/java/org/apache/fop/pdf/PDFFactory.java View File

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

+ 127
- 524
src/java/org/apache/fop/pdf/PDFFunction.java View File

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

+ 9
- 5
src/java/org/apache/fop/pdf/PDFPattern.java View File

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

+ 179
- 194
src/java/org/apache/fop/pdf/PDFShading.java View File

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

+ 269
- 58
src/java/org/apache/fop/render/ps/PSImageHandlerSVG.java View File

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

+ 143
- 0
src/java/org/apache/fop/render/ps/svg/PSFunction.java View File

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

+ 103
- 0
src/java/org/apache/fop/render/ps/svg/PSPattern.java View File

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

+ 291
- 0
src/java/org/apache/fop/render/ps/svg/PSSVGGraphics2D.java View File

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

+ 228
- 0
src/java/org/apache/fop/render/ps/svg/PSShading.java View File

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

+ 39
- 0
src/java/org/apache/fop/render/shading/Function.java View File

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

+ 451
- 0
src/java/org/apache/fop/render/shading/FunctionDelegate.java View File

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

+ 363
- 0
src/java/org/apache/fop/render/shading/FunctionPattern.java View File

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

+ 162
- 0
src/java/org/apache/fop/render/shading/GradientFactory.java View File

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

+ 45
- 0
src/java/org/apache/fop/render/shading/GradientRegistrar.java View File

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

+ 76
- 0
src/java/org/apache/fop/render/shading/PDFGradientFactory.java View File

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

}

+ 70
- 0
src/java/org/apache/fop/render/shading/PSGradientFactory.java View File

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

+ 22
- 0
src/java/org/apache/fop/render/shading/Pattern.java View File

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

}

+ 26
- 0
src/java/org/apache/fop/render/shading/Shading.java View File

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

+ 105
- 0
src/java/org/apache/fop/render/shading/ShadingPattern.java View File

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

+ 49
- 13
src/java/org/apache/fop/svg/PDFGraphics2D.java View File

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

}

+ 71
- 0
test/java/org/apache/fop/render/ps/svg/PSSVGGraphics2DTestCase.java View File

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

+ 70
- 0
test/java/org/apache/fop/render/ps/svg/PSSVGLinearGraphics2DTestCase.java View File

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

+ 26
- 0
test/java/org/apache/fop/render/ps/svg/axial-shading-expected.dat View File

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

+ 38
- 0
test/java/org/apache/fop/render/ps/svg/expected.ps View File

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

Loading…
Cancel
Save