Merged latest trunk


git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/FOP-2393_gradient-rendering@1611658 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/build.xml b/build.xml
index ba9f2e0..b6c7f37 100644
--- a/build.xml
+++ b/build.xml
@@ -732,6 +732,7 @@
         <include name="**/*.fo"/>
         <include name="**/BidiTestData*.ser"/>
         <include name="**/*.afp"/>
+        <include name="**/*.ps"/>
         <include name="**/*.xsl"/>
       </fileset>
       <fileset dir="${build.dir}/test-gensrc">
diff --git a/src/java/org/apache/fop/pdf/PDFFactory.java b/src/java/org/apache/fop/pdf/PDFFactory.java
index b1e23f3..cad0a40 100644
--- a/src/java/org/apache/fop/pdf/PDFFactory.java
+++ b/src/java/org/apache/fop/pdf/PDFFactory.java
@@ -20,14 +20,12 @@
 package org.apache.fop.pdf;
 
 // Java
-import java.awt.Color;
 import java.awt.geom.Point2D;
 import java.awt.geom.Rectangle2D;
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.text.DecimalFormat;
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.BitSet;
 import java.util.Iterator;
@@ -41,7 +39,6 @@
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 
-import org.apache.xmlgraphics.java2d.color.ColorUtil;
 import org.apache.xmlgraphics.java2d.color.NamedColorSpace;
 import org.apache.xmlgraphics.xmp.Metadata;
 
@@ -244,188 +241,35 @@
     /* ========================= functions ================================= */
 
     /**
-     * Make a Type 0 sampled function
-     *
-     * @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 user
-     *                    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.
-     * @return the PDF function that was created
-     */
-    public PDFFunction makeFunction(int theFunctionType, List theDomain,
-            List theRange, List theSize,
-            int theBitsPerSample, int theOrder,
-            List theEncode, List theDecode,
-            StringBuffer theFunctionDataStream,
-            List theFilter) {
-        // Type 0 function
-        PDFFunction function = new PDFFunction(theFunctionType, theDomain,
-                                               theRange, theSize,
-                                               theBitsPerSample, theOrder,
-                                               theEncode, theDecode,
-                                               theFunctionDataStream,
-                                               theFilter);
-
-        function = registerFunction(function);
-        return (function);
-    }
-
-    /**
      * make a type Exponential interpolation function
      * (for shading usually)
-     *
-     * @param theDomain List objects of Double objects.
+     * @param domain 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.
+     * @param range 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
+     * @param cZero 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
+     * @param cOne 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.
+     * @param interpolationExponentN This is the inerpolation exponent.
      *
      * This attribute is required.
      * PDF Spec page 268
-     * @param theFunctionType The type of the function, which should be 2.
+     *
      * @return the PDF function that was created
      */
-    public PDFFunction makeFunction(int theFunctionType, List theDomain,
-                                    List theRange, List theCZero,
-                                    List theCOne,
-                                    double theInterpolationExponentN) {    // type 2
-        PDFFunction function = new PDFFunction(theFunctionType, theDomain,
-                                               theRange, theCZero, theCOne,
-                                               theInterpolationExponentN);
+    public PDFFunction makeFunction(List domain, List range, float[] cZero, float[] cOne,
+                                    double interpolationExponentN) {
+        PDFFunction function = new PDFFunction(domain, range, cZero, cOne, interpolationExponentN);
         function = registerFunction(function);
-        return (function);
-    }
-
-    /**
-     * Make a Type 3 Stitching function
-     *
-     * @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 An 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.
-     * @return the PDF function that was created
-     */
-    public PDFFunction makeFunction(int theFunctionType, List theDomain,
-                                    List theRange, List theFunctions,
-                                    List theBounds,
-                                    List theEncode) {
-        // Type 3
-
-        PDFFunction function = new PDFFunction(theFunctionType, theDomain,
-                                               theRange, theFunctions,
-                                               theBounds, theEncode);
-
-        function = registerFunction(function);
-        return (function);
-    }
-
-    /**
-     * make a postscript calculator function
-     *
-     * @param theNumber the PDF object number
-     * @param theFunctionType the type of function to make
-     * @param theDomain the domain values
-     * @param theRange the range values of the function
-     * @param theFunctionDataStream a string containing the pdf drawing
-     * @return the PDF function that was created
-     */
-    public PDFFunction makeFunction(int theNumber, int theFunctionType,
-                                    List theDomain, List theRange,
-                                    StringBuffer theFunctionDataStream) {
-        // Type 4
-        PDFFunction function = new PDFFunction(theFunctionType, theDomain,
-                                               theRange,
-                                               theFunctionDataStream);
-
-        function = registerFunction(function);
-        return (function);
-
+        return function;
     }
 
     /**
@@ -445,175 +289,6 @@
     /* ========================= shadings ================================== */
 
     /**
-     * make a function based shading object
-     *
-     * @param res the PDF resource context to add the shading, may be null
-     * @param theShadingType The type of shading object, which should be 1 for function
-     * based shading.
-     * @param theColorSpace The colorspace is 'DeviceRGB' or something similar.
-     * @param 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 Whether or not to anti-alias.
-     * @param theDomain Optional vector of Doubles specifying the domain.
-     * @param theMatrix List of Doubles specifying the matrix.
-     * If it's a pattern, then the matrix maps it to pattern space.
-     * If it's a shading, then it maps it to current user space.
-     * It's optional, the default is the identity matrix
-     * @param theFunction The PDF Function that maps an (x,y) location to a color
-     * @return the PDF shading that was created
-     */
-    public PDFShading makeShading(PDFResourceContext res, int theShadingType,
-            PDFDeviceColorSpace theColorSpace,
-            List theBackground, List theBBox,
-            boolean theAntiAlias, List theDomain,
-            List theMatrix,
-            PDFFunction theFunction) {
-        // make Shading of Type 1
-        PDFShading shading = new PDFShading(theShadingType,
-                                            theColorSpace, theBackground,
-                                            theBBox, theAntiAlias, theDomain,
-                                            theMatrix, theFunction);
-
-        shading = registerShading(res, shading);
-        return (shading);
-    }
-
-    /**
-     * Make an axial or radial shading object.
-     *
-     * @param res the PDF resource context to add the shading, may be null
-     * @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]
-     * @return the PDF shading that was created
-     */
-    public PDFShading makeShading(PDFResourceContext res, int theShadingType,
-            PDFDeviceColorSpace theColorSpace,
-            List theBackground, List theBBox,
-            boolean theAntiAlias, List theCoords,
-            List theDomain, PDFFunction theFunction,
-            List theExtend) {
-        // make Shading of Type 2 or 3
-        PDFShading shading = new PDFShading(theShadingType,
-                                            theColorSpace, theBackground,
-                                            theBBox, theAntiAlias, theCoords,
-                                            theDomain, theFunction,
-                                            theExtend);
-
-        shading = registerShading(res, shading);
-
-        return (shading);
-    }
-
-    /**
-     * Make a free-form gouraud shaded triangle mesh, coons patch mesh, or tensor patch mesh
-     * shading object
-     *
-     * @param res the PDF resource context to add the shading, may be null
-     * @param theShadingType 4, 6, or 7 depending on whether it's
-     * Free-form gouraud-shaded triangle meshes, coons patch meshes,
-     * or tensor product patch meshes, respectively.
-     * @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 theBitsPerCoordinate 1,2,4,8,12,16,24 or 32.
-     * @param theBitsPerComponent 1,2,4,8,12, and 16
-     * @param theBitsPerFlag 2,4,8.
-     * @param theDecode List of Doubles see PDF 1.3 spec pages 303 to 312.
-     * @param theFunction the PDFFunction
-     * @return the PDF shading that was created
-     */
-    public PDFShading makeShading(PDFResourceContext res, int theShadingType,
-            PDFDeviceColorSpace theColorSpace,
-            List theBackground, List theBBox,
-            boolean theAntiAlias,
-            int theBitsPerCoordinate,
-            int theBitsPerComponent,
-            int theBitsPerFlag, List theDecode,
-            PDFFunction theFunction) {
-        // make Shading of type 4,6 or 7
-        PDFShading shading = new PDFShading(theShadingType,
-                                            theColorSpace, theBackground,
-                                            theBBox, theAntiAlias,
-                                            theBitsPerCoordinate,
-                                            theBitsPerComponent,
-                                            theBitsPerFlag, theDecode,
-                                            theFunction);
-
-        shading = registerShading(res, shading);
-
-        return (shading);
-    }
-
-    /**
-     * make a Lattice-Form Gouraud mesh shading object
-     *
-     * @param res the PDF resource context to add the shading, may be null
-     * @param theShadingType 5 for lattice-Form Gouraud shaded-triangle mesh
-     * without spaces. "Shading1" or "Sh1" are good examples.
-     * @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 theBitsPerCoordinate 1,2,4,8,12,16, 24, or 32
-     * @param theBitsPerComponent 1,2,4,8,12,24,32
-     * @param theDecode List of Doubles. See page 305 in PDF 1.3 spec.
-     * @param theVerticesPerRow number of vertices in each "row" of the lattice.
-     * @param theFunction The PDFFunction that's mapped on to this shape
-     * @return the PDF shading that was created
-     */
-    public PDFShading makeShading(PDFResourceContext res, int theShadingType,
-            PDFDeviceColorSpace theColorSpace,
-            List theBackground, List theBBox,
-            boolean theAntiAlias,
-            int theBitsPerCoordinate,
-            int theBitsPerComponent, List theDecode,
-            int theVerticesPerRow,
-            PDFFunction theFunction) {
-        // make shading of Type 5
-        PDFShading shading = new PDFShading(theShadingType,
-                                            theColorSpace, theBackground,
-                                            theBBox, theAntiAlias,
-                                            theBitsPerCoordinate,
-                                            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
@@ -697,148 +372,6 @@
         return pattern;
     }
 
-    /**
-     * Make a smooth shading pattern
-     *
-     * @param res the PDF resource context to add the shading, may be null
-     * @param thePatternType the type of the pattern, which is 2, smooth shading
-     * @param theShading the PDF Shading object that comprises this pattern
-     * @param theXUID optional:the extended unique Identifier if used.
-     * @param theExtGState optional: the extended graphics state, if used.
-     * @param theMatrix Optional:List of Doubles that specify the matrix.
-     * @return the PDF pattern that was created
-     */
-    public PDFPattern makePattern(PDFResourceContext res,
-                                  int thePatternType, PDFShading theShading,
-                                  List theXUID, StringBuffer theExtGState,
-                                  List theMatrix) {
-        PDFPattern pattern = new PDFPattern(2, theShading,
-                                            theXUID, theExtGState, theMatrix);
-
-        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 gradient
-     *
-     * @param res the PDF resource context to add the shading, may be null
-     * @param radial if true a radial gradient will be created
-     * @param theColorspace the colorspace of the gradient
-     * @param theColors the list of colors for the gradient
-     * @param theBounds the list of bounds associated with the colors
-     * @param theCoords the coordinates for the gradient
-     * @param theMatrix the coordinate-transformation matrix
-     * @return the PDF pattern that was created
-     */
-    public PDFPattern makeGradient(PDFResourceContext res, boolean radial,
-                                   PDFDeviceColorSpace theColorspace,
-                                   List theColors, List theBounds,
-                                   List theCoords, List theMatrix) {
-        PDFShading myShad;
-        PDFFunction myfunky;
-        PDFFunction myfunc;
-        List theCzero;
-        List theCone;
-        PDFPattern myPattern;
-        //PDFColorSpace theColorSpace;
-        double interpolation = 1.000;
-        List theFunctions = new ArrayList();
-
-        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 = (Color)theColors.get(currentPosition);
-            Color nextColor = (Color)theColors.get(currentPosition + 1);
-
-            // colorspace must be consistent, so we simply convert to sRGB where necessary
-            if (!currentColor.getColorSpace().isCS_sRGB()) {
-                //Convert to sRGB
-                currentColor = ColorUtil.toSRGBColor(currentColor);
-                theColors.set(currentPosition, currentColor);
-            }
-            if (!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) {
-                myShad = makeShading(res, 3, getDocument().getPDFColorSpace(),
-                                     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 newCoords = new ArrayList();
-                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(new Double(0.0));
-
-                myShad = makeShading(res, 3, getDocument().getPDFColorSpace(),
-                                     null, null,
-                                     false, newCoords, null, myfunky,
-                                     null);
-
-            }
-        } else {
-            myShad = makeShading(res, 2, getDocument().getPDFColorSpace(),
-                                 null, null,
-                                 false, theCoords, null, myfunky,
-                                 null);
-
-        }
-
-        myPattern = makePattern(res, 2, myShad, null, null, theMatrix);
-
-        return (myPattern);
-    }
-
-    private List toColorVector(Color nextColor) {
-        List vector = new java.util.ArrayList();
-        float[] comps = nextColor.getColorComponents(null);
-        for (int i = 0, c = comps.length; i < c; i++) {
-            vector.add(new Double(comps[i]));
-        }
-        return vector;
-    }
 
     /* ============= named destinations and the name dictionary ============ */
 
@@ -1866,16 +1399,11 @@
         String colorName = ncs.getColorName();
         final Double zero = new Double(0d);
         final Double one = new Double(1d);
-        List theDomain = Arrays.asList(new Double[] {zero, one});
-        List theRange = Arrays.asList(new Double[] {zero, one, zero, one, zero, one});
-        List theCZero = Arrays.asList(new Double[] {one, one, one});
-        List theCOne = new ArrayList();
-        float[] comps = ncs.getRGBColor().getColorComponents(null);
-        for (int i = 0, c = comps.length; i < c; i++) {
-            theCOne.add(new Double(comps[i]));
-        }
-        PDFFunction tintFunction = makeFunction(2, theDomain, theRange,
-                theCZero, theCOne, 1.0d);
+        List domain = Arrays.asList(new Double[] {zero, one});
+        List range = Arrays.asList(new Double[] {zero, one, zero, one, zero, one});
+        float[] cZero = new float[] {1f, 1f, 1f};
+        float[] cOne = ncs.getRGBColor().getColorComponents(null);
+        PDFFunction tintFunction = makeFunction(domain, range, cZero, cOne, 1.0d);
         PDFSeparationColorSpace cs = new PDFSeparationColorSpace(colorName, tintFunction);
         getDocument().registerObject(cs);
         if (res != null) {
diff --git a/src/java/org/apache/fop/pdf/PDFFunction.java b/src/java/org/apache/fop/pdf/PDFFunction.java
index 09cbd97..fd860b0 100644
--- a/src/java/org/apache/fop/pdf/PDFFunction.java
+++ b/src/java/org/apache/fop/pdf/PDFFunction.java
@@ -19,12 +19,15 @@
 
 package org.apache.fop.pdf;
 
-// Java...
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
 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;
+import org.apache.fop.render.gradient.Function;
+import org.apache.fop.render.gradient.Function.SubFunctionRenderer;
+import org.apache.fop.render.gradient.GradientMaker;
+import org.apache.fop.render.gradient.GradientMaker.DoubleFormatter;
 
 /**
  * class representing a PDF Function.
@@ -37,75 +40,11 @@
  *
  * All PDF Functions have a FunctionType (0,2,3, or 4), a Domain, and a Range.
  */
-public class PDFFunction extends PDFObject implements Function {
+public class PDFFunction extends PDFObject {
 
-    private FunctionDelegate delegate;
+    private final Function function;
 
-    /**
-     * 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 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);
-    }
+    private final List<PDFFunction> pdfFunctions;
 
     /**
      * create an complete Function object of Type 2, an Exponential Interpolation function.
@@ -113,104 +52,44 @@
      * 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.
+     * @param domain 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.
+     * @param range 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
+     * @param cZero 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
+     * @param cOne 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.
+     * @param interpolationExponentN 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 PDFFunction(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);
+    public PDFFunction(List<Double> domain, List<Double> range, float[] cZero, float[] cOne,
+            double interpolationExponentN) {
+        this(new Function(domain, range, cZero, cOne, interpolationExponentN));
 
     }
 
-    /**
-     * 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 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);
-
+    @SuppressWarnings("unchecked")
+    public PDFFunction(Function function) {
+        this(function, Collections.EMPTY_LIST);
     }
 
-    /**
-     * 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 PDFFunction(int theFunctionType, List<Double> theDomain,
-                       List<Double> theRange, StringBuffer theFunctionDataStream) {
-        delegate = new FunctionDelegate(this, theFunctionType, theDomain, theRange,
-                theFunctionDataStream);
+    public PDFFunction(Function function, List<PDFFunction> pdfFunctions) {
+        this.function = function;
+        this.pdfFunctions = pdfFunctions;
+    }
+
+    public Function getFunction() {
+        return function;
     }
 
     /**
@@ -230,8 +109,25 @@
 
 
     public byte[] toByteString() {
-        FunctionPattern pattern = new FunctionPattern(this);
-        return encode(pattern.toWriteableString());
+        List<String> functionsStrings = new ArrayList<String>(function.getFunctions().size());
+        for (PDFFunction f : pdfFunctions) {
+            functionsStrings.add(f.referencePDF());
+        }
+        SubFunctionRenderer subFunctionRenderer = new SubFunctionRenderer() {
+
+            public void outputFunction(StringBuilder out, int functionIndex) {
+                out.append(pdfFunctions.get(functionIndex).referencePDF());
+            }
+        };
+        StringBuilder out = new StringBuilder();
+        GradientMaker.DoubleFormatter doubleFormatter = new DoubleFormatter() {
+
+            public String formatDouble(double d) {
+                return PDFNumber.doubleOut(d);
+            }
+        };
+        function.output(out, doubleFormatter, subFunctionRenderer);
+        return encode(out.toString());
     }
 
     /** {@inheritDoc} */
@@ -245,91 +141,79 @@
         if (!(obj instanceof PDFFunction)) {
             return false;
         }
-        PDFFunction func = (PDFFunction)obj;
-        if (delegate.getFunctionType() != func.getFunctionType()) {
+        Function func = ((PDFFunction) obj).function;
+        if (function.getFunctionType() != func.getFunctionType()) {
             return false;
         }
-        if (delegate.getBitsPerSample() != func.getBitsPerSample()) {
+        if (function.getBitsPerSample() != func.getBitsPerSample()) {
             return false;
         }
-        if (delegate.getOrder() != func.getOrder()) {
+        if (function.getOrder() != func.getOrder()) {
             return false;
         }
-        if (delegate.getInterpolationExponentN() != func.getInterpolationExponentN()) {
+        if (function.getInterpolationExponentN() != func.getInterpolationExponentN()) {
             return false;
         }
-        if (delegate.getDomain() != null) {
-            if (!delegate.getDomain().equals(func.getDomain())) {
+        if (function.getDomain() != null) {
+            if (!function.getDomain().equals(func.getDomain())) {
                 return false;
             }
         } else if (func.getDomain() != null) {
             return false;
         }
-        if (delegate.getRange() != null) {
-            if (!delegate.getRange().equals(func.getRange())) {
+        if (function.getRange() != null) {
+            if (!function.getRange().equals(func.getRange())) {
                 return false;
             }
         } else if (func.getRange() != null) {
             return false;
         }
-        if (delegate.getSize() != null) {
-            if (!delegate.getSize().equals(func.getSize())) {
+        if (function.getSize() != null) {
+            if (!function.getSize().equals(func.getSize())) {
                 return false;
             }
         } else if (func.getSize() != null) {
             return false;
         }
-        if (delegate.getEncode() != null) {
-            if (!delegate.getEncode().equals(func.getEncode())) {
+        if (function.getEncode() != null) {
+            if (!function.getEncode().equals(func.getEncode())) {
                 return false;
             }
         } else if (func.getEncode() != null) {
             return false;
         }
-        if (delegate.getDecode() != null) {
-            if (!delegate.getDecode().equals(func.getDecode())) {
+        if (function.getDecode() != null) {
+            if (!function.getDecode().equals(func.getDecode())) {
                 return false;
             }
         } else if (func.getDecode() != null) {
             return false;
         }
-        if (delegate.getDataStream() != null) {
-            if (!delegate.getDataStream().equals(func.getDataStream())) {
+        if (function.getDataStream() != null) {
+            if (!function.getDataStream().equals(func.getDataStream())) {
                 return false;
             }
         } else if (func.getDataStream() != null) {
             return false;
         }
-        if (delegate.getFilter() != null) {
-            if (!delegate.getFilter().equals(func.getFilter())) {
+        if (function.getFilter() != null) {
+            if (!function.getFilter().equals(func.getFilter())) {
                 return false;
             }
         } else if (func.getFilter() != null) {
             return false;
         }
-        if (delegate.getCZero() != null) {
-            if (!delegate.getCZero().equals(func.getCZero())) {
-                return false;
-            }
-        } else if (func.getCZero() != null) {
+        if (!Arrays.equals(function.getCZero(), func.getCZero())) {
             return false;
         }
-        if (delegate.getCOne() != null) {
-            if (!delegate.getCOne().equals(func.getCOne())) {
-                return false;
-            }
-        } else if (func.getCOne() != null) {
+        if (!Arrays.equals(function.getCOne(), func.getCOne())) {
             return false;
         }
-        if (delegate.getFunctions() != null) {
-            if (!delegate.getFunctions().equals(func.getFunctions())) {
-                return false;
-            }
-        } else if (func.getFunctions() != null) {
+        if (!pdfFunctions.equals(((PDFFunction) obj).pdfFunctions)) {
             return false;
         }
-        if (delegate.getBounds() != null) {
-            if (!delegate.getBounds().equals(func.getBounds())) {
+        if (function.getBounds() != null) {
+            if (!function.getBounds().equals(func.getBounds())) {
                 return false;
             }
         } else if (func.getBounds() != null) {
@@ -338,63 +222,4 @@
         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();
-    }
 }
diff --git a/src/java/org/apache/fop/pdf/PDFPattern.java b/src/java/org/apache/fop/pdf/PDFPattern.java
index 9871d15..7acffa0 100644
--- a/src/java/org/apache/fop/pdf/PDFPattern.java
+++ b/src/java/org/apache/fop/pdf/PDFPattern.java
@@ -23,9 +23,6 @@
 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.
  *
@@ -36,7 +33,7 @@
  *
  * All PDF Functions have a FunctionType (0,2,3, or 4), a Domain, and a Range.
  */
-public class PDFPattern extends PDFPathPaint implements Pattern {
+public class PDFPattern extends PDFPathPaint {
 
     /**
      * The resources associated with this pattern
@@ -144,19 +141,18 @@
      * Create a type 2 pattern (smooth shading)
      *
      * @param thePatternType the type of the pattern, which is 2, smooth shading
-     * @param theShading the PDF Shading object that comprises this pattern
+     * @param shading the Shading object that comprises this pattern
      * @param theXUID optional:the extended unique Identifier if used.
      * @param theExtGState optional: the extended graphics state, if used.
      * @param theMatrix Optional:List of Doubles that specify the matrix.
      */
-    public PDFPattern(int thePatternType, Shading theShading,
+    public PDFPattern(int thePatternType, PDFShading shading,
                       List theXUID, StringBuffer theExtGState,
                       List theMatrix) {
         super();
 
         this.patternType = 2;             // thePatternType;
-        assert theShading instanceof PDFShading;
-        this.shading = (PDFShading)theShading;
+        this.shading = shading;
         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.
diff --git a/src/java/org/apache/fop/pdf/PDFShading.java b/src/java/org/apache/fop/pdf/PDFShading.java
index eaef78f..e1784c5 100644
--- a/src/java/org/apache/fop/pdf/PDFShading.java
+++ b/src/java/org/apache/fop/pdf/PDFShading.java
@@ -19,12 +19,12 @@
 
 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;
+import org.apache.fop.render.gradient.GradientMaker;
+import org.apache.fop.render.gradient.GradientMaker.DoubleFormatter;
+import org.apache.fop.render.gradient.Shading;
+
 
 /**
  * class representing a PDF Smooth Shading object.
@@ -36,7 +36,7 @@
  *
  * All PDF Functions have a shadingType (0,2,3, or 4), a Domain, and a Range.
  */
-public class PDFShading extends PDFObject implements Shading {
+public class PDFShading extends PDFObject {
     // Guts common to all function types
 
     /**
@@ -44,266 +44,27 @@
      */
     protected String shadingName;
 
-    /**
-     * Required: The Type of shading (1,2,3,4,5,6,7)
-     */
-    protected int shadingType = 3;    // Default
+    private final Shading shading;
 
+    private final PDFFunction pdfFunction;
     /**
      * A ColorSpace representing the colorspace. "DeviceRGB" is an example.
      */
     protected PDFDeviceColorSpace colorSpace;
 
     /**
-     * The background color. Since shading is opaque,
-     * this is very rarely used.
-     */
-    protected List background;
-
-    /**
-     * Optional: A List specifying the clipping rectangle
-     */
-    protected List bBox;
-
-    /**
-     * Optional: A flag whether or not to filter the shading function
-     * to prevent aliasing artifacts. Default is false.
-     */
-    protected boolean antiAlias;
-
-    /**
-     * 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;
-
-    /**
-     * Optional for Type 1: A transformation matrix
-     */
-    protected List matrix;
-
-    /**
-     * 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 PDFFunction function;
-
-    /**
-     * 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;
-
-    /**
-     * 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;
-
-    /**
-     * Required for Type 4,5,6, and 7: Specifies the number of bits used
-     * to represent each vertex coordinate.
-     * Allowed to be 1,2,4,8,12,16,24, or 32.
-     */
-    protected int bitsPerCoordinate;
-
-    /**
-     * Required for Type 4,5,6, and 7: Specifies the number of bits used
-     * to represent the edge flag for each vertex.
-     * Allowed to be 2,4,or 8, while the Edge flag itself is allowed to
-     * be 0,1 or 2.
-     */
-    protected int bitsPerFlag;
-
-    /**
-     * Required for Type 4,5,6, and 7: Array of Doubles which specifies
-     * how to decode coordinate and color component values.
-     * Each type has a differing number of decode array members, so check
-     * the spec.
-     * Page 303 in PDF Spec 1.3
-     */
-    protected List decode;
-
-    /**
-     * Required for Type 4,5,6, and 7: Specifies the number of bits used
-     * to represent each color coordinate.
-     * Allowed to be 1,2,4,8,12, or 16
-     */
-    protected int bitsPerComponent;
-
-    /**
-     * Required for Type 5:The number of vertices in each "row" of
-     * the lattice; it must be greater than or equal to 2.
-     */
-    protected int verticesPerRow;
-
-    /**
-     * Constructor for type function based shading
-     *
-     * @param theShadingType The type of shading object, which should be 1 for function
-     * based shading.
-     * @param theColorSpace The colorspace is 'DeviceRGB' or something similar.
-     * @param 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 Whether or not to anti-alias.
-     * @param theDomain Optional vector of Doubles specifying the domain.
-     * @param theMatrix List of Doubles specifying the matrix.
-     * If it's a pattern, then the matrix maps it to pattern space.
-     * If it's a shading, then it maps it to current user space.
-     * It's optional, the default is the identity matrix
-     * @param theFunction The PDF Function that maps an (x,y) location to a color
-     */
-    public PDFShading(int theShadingType, PDFDeviceColorSpace theColorSpace,
-            List theBackground, List theBBox,
-            boolean theAntiAlias, List theDomain,
-            List theMatrix, PDFFunction theFunction) {
-        super();
-        this.shadingType = theShadingType;    // 1
-        this.colorSpace = theColorSpace;
-        this.background = theBackground;
-        this.bBox = theBBox;
-        this.antiAlias = theAntiAlias;
-
-        this.domain = theDomain;
-        this.matrix = theMatrix;
-        this.function = theFunction;
-
-    }
-
-    /**
      * 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,
+     * @param shadingType 2 or 3 for axial or radial shading
+     * @param colorSpace "DeviceRGB" or similar.
+     * @param coords List of four (type 2) or 6 (type 3) Double
+     * @param pdfFunction 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 PDFShading(int theShadingType, PDFDeviceColorSpace theColorSpace,
-            List theBackground, List theBBox,
-            boolean theAntiAlias, List theCoords,
-            List theDomain, Function theFunction,
-            List theExtend) {
-        super();
-        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 PDFFunction;
-        this.function = (PDFFunction)theFunction;
-        this.extend = theExtend;
-
-    }
-
-    /**
-     * Constructor for Type 4,6, or 7
-     *
-     * @param theShadingType 4, 6, or 7 depending on whether it's
-     * Free-form gouraud-shaded triangle meshes, coons patch meshes,
-     * or tensor product patch meshes, respectively.
-     * @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 theBitsPerCoordinate 1,2,4,8,12,16,24 or 32.
-     * @param theBitsPerComponent 1,2,4,8,12, and 16
-     * @param theBitsPerFlag 2,4,8.
-     * @param theDecode List of Doubles see PDF 1.3 spec pages 303 to 312.
-     * @param theFunction the PDFFunction
-     */
-    public PDFShading(int theShadingType, PDFDeviceColorSpace theColorSpace,
-            List theBackground, List theBBox,
-            boolean theAntiAlias, int theBitsPerCoordinate,
-            int theBitsPerComponent, int theBitsPerFlag,
-            List theDecode, PDFFunction theFunction) {
-        super();
-
-        this.shadingType = theShadingType;    // 4,6 or 7
-        this.colorSpace = theColorSpace;
-        this.background = theBackground;
-        this.bBox = theBBox;
-        this.antiAlias = theAntiAlias;
-
-        this.bitsPerCoordinate = theBitsPerCoordinate;
-        this.bitsPerComponent = theBitsPerComponent;
-        this.bitsPerFlag = theBitsPerFlag;
-        this.decode = theDecode;
-        this.function = theFunction;
-    }
-
-    /**
-     * Constructor for type 5
-     *
-     * @param theShadingType 5 for lattice-Form Gouraud shaded-triangle mesh
-     * @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 theBitsPerCoordinate 1,2,4,8,12,16, 24, or 32
-     * @param theBitsPerComponent 1,2,4,8,12,24,32
-     * @param theDecode List of Doubles. See page 305 in PDF 1.3 spec.
-     * @param theVerticesPerRow number of vertices in each "row" of the lattice.
-     * @param theFunction The PDFFunction that's mapped on to this shape
-     */
-    public PDFShading(int theShadingType, PDFDeviceColorSpace theColorSpace,
-            List theBackground, List theBBox,
-            boolean theAntiAlias, int theBitsPerCoordinate,
-            int theBitsPerComponent, List theDecode,
-            int theVerticesPerRow, PDFFunction theFunction) {
-        super();
-        this.shadingType = theShadingType;    // 5
-        this.colorSpace = theColorSpace;
-        this.background = theBackground;
-        this.bBox = theBBox;
-        this.antiAlias = theAntiAlias;
-
-        this.bitsPerCoordinate = theBitsPerCoordinate;
-        this.bitsPerComponent = theBitsPerComponent;
-        this.decode = theDecode;
-        this.verticesPerRow = theVerticesPerRow;
-        this.function = theFunction;
-
+    public PDFShading(int shadingType, PDFDeviceColorSpace colorSpace,
+            List coords, PDFFunction pdfFunction) {
+        shading = new Shading(shadingType, colorSpace, coords, pdfFunction.getFunction());
+        this.pdfFunction = pdfFunction;
     }
 
     /**
@@ -340,8 +101,21 @@
      * @return the PDF string.
      */
     public String toPDFString() {
-        ShadingPattern pattern = new ShadingPattern(this);
-        return pattern.toString(colorSpace, shadingType, background, bBox, antiAlias);
+        Shading.FunctionRenderer functionRenderer = new Shading.FunctionRenderer() {
+
+            public void outputFunction(StringBuilder out) {
+                out.append(pdfFunction.referencePDF());
+            }
+        };
+        StringBuilder out = new StringBuilder();
+        GradientMaker.DoubleFormatter doubleFormatter = new DoubleFormatter() {
+
+            public String formatDouble(double d) {
+                return PDFNumber.doubleOut(d);
+            }
+        };
+        shading.output(out, doubleFormatter, functionRenderer);
+        return out.toString();
     }
 
     /** {@inheritDoc} */
@@ -355,257 +129,82 @@
         if (!(obj instanceof PDFShading)) {
             return false;
         }
-        PDFShading shad = (PDFShading)obj;
-        if (shadingType != shad.shadingType) {
+        Shading other = ((PDFShading) obj).shading;
+        if (shading.getShadingType() != other.getShadingType()) {
             return false;
         }
-        if (antiAlias != shad.antiAlias) {
+        if (shading.isAntiAlias() != other.isAntiAlias()) {
             return false;
         }
-        if (bitsPerCoordinate != shad.bitsPerCoordinate) {
+        if (shading.getBitsPerCoordinate() != other.getBitsPerCoordinate()) {
             return false;
         }
-        if (bitsPerFlag != shad.bitsPerFlag) {
+        if (shading.getBitsPerFlag() != other.getBitsPerFlag()) {
             return false;
         }
-        if (bitsPerComponent != shad.bitsPerComponent) {
+        if (shading.getBitsPerComponent() != other.getBitsPerComponent()) {
             return false;
         }
-        if (verticesPerRow != shad.verticesPerRow) {
+        if (shading.getVerticesPerRow() != other.getVerticesPerRow()) {
             return false;
         }
-        if (colorSpace != null) {
-            if (!colorSpace.equals(shad.colorSpace)) {
+        if (shading.getColorSpace() != null) {
+            if (!shading.getColorSpace().equals(other.getColorSpace())) {
                 return false;
             }
-        } else if (shad.colorSpace != null) {
+        } else if (other.getColorSpace() != null) {
             return false;
         }
-        if (background != null) {
-            if (!background.equals(shad.background)) {
+        if (shading.getBackground() != null) {
+            if (!shading.getBackground().equals(other.getBackground())) {
                 return false;
             }
-        } else if (shad.background != null) {
+        } else if (other.getBackground() != null) {
             return false;
         }
-        if (bBox != null) {
-            if (!bBox.equals(shad.bBox)) {
+        if (shading.getBBox() != null) {
+            if (!shading.getBBox().equals(other.getBBox())) {
                 return false;
             }
-        } else if (shad.bBox != null) {
+        } else if (other.getBBox() != null) {
             return false;
         }
-        if (domain != null) {
-            if (!domain.equals(shad.domain)) {
+        if (shading.getMatrix() != null) {
+            if (!shading.getMatrix().equals(other.getMatrix())) {
                 return false;
             }
-        } else if (shad.domain != null) {
+        } else if (other.getMatrix() != null) {
             return false;
         }
-        if (matrix != null) {
-            if (!matrix.equals(shad.matrix)) {
+        if (shading.getCoords() != null) {
+            if (!shading.getCoords().equals(other.getCoords())) {
                 return false;
             }
-        } else if (shad.matrix != null) {
+        } else if (other.getCoords() != null) {
             return false;
         }
-        if (coords != null) {
-            if (!coords.equals(shad.coords)) {
+        if (shading.getExtend() != null) {
+            if (!shading.getExtend().equals(other.getExtend())) {
                 return false;
             }
-        } else if (shad.coords != null) {
+        } else if (other.getExtend() != null) {
             return false;
         }
-        if (extend != null) {
-            if (!extend.equals(shad.extend)) {
+        if (shading.getDecode() != null) {
+            if (!shading.getDecode().equals(other.getDecode())) {
                 return false;
             }
-        } else if (shad.extend != null) {
+        } else if (other.getDecode() != null) {
             return false;
         }
-        if (decode != null) {
-            if (!decode.equals(shad.decode)) {
+        if (shading.getFunction() != null) {
+            if (!shading.getFunction().equals(other.getFunction())) {
                 return false;
             }
-        } else if (shad.decode != null) {
-            return false;
-        }
-        if (function != null) {
-            if (!function.equals(shad.function)) {
-                return false;
-            }
-        } else if (shad.function != null) {
+        } else if (other.getFunction() != null) {
             return false;
         }
         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;
-    }
 }
diff --git a/src/java/org/apache/fop/render/gradient/Function.java b/src/java/org/apache/fop/render/gradient/Function.java
new file mode 100644
index 0000000..38aec80
--- /dev/null
+++ b/src/java/org/apache/fop/render/gradient/Function.java
@@ -0,0 +1,506 @@
+/*
+ * 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.gradient;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.fop.render.gradient.GradientMaker.DoubleFormatter;
+
+public class Function {
+
+    public interface SubFunctionRenderer {
+
+        void outputFunction(StringBuilder out, int functionIndex);
+    }
+
+    /**
+     * Required: The Type of function (0,2,3,4) default is 0.
+     */
+    private int functionType;
+
+    /**
+     * Required: 2 * m Array of Double numbers which are possible inputs to the function
+     */
+    private List<Double> domain;
+
+    /**
+     * Required: 2 * n Array of Double numbers which are possible outputs to the function
+     */
+    private List<Double> range;
+
+    /* ********************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;
+
+    /**
+     * Required for Type 0: Number of Bits used to represent each sample value.
+     * Limited to 1,2,4,8,12,16,24, or 32
+     */
+    private int bitsPerSample = 1;
+
+    /**
+     * Optional for Type 0: order of interpolation between samples.
+     * Limited to linear (1) or cubic (3). Default is 1
+     */
+    private 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.
+     */
+    private List<Double> encode;
+
+    /**
+     * Optional for Type 0: A 2 * n array of Doubles which provides
+     * a linear mapping of sample values to the range. Defaults to Range.
+     */
+    private List<Double> decode;
+
+    /**
+     * 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
+     */
+    private StringBuffer functionDataStream;
+
+    /**
+     * 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.
+     */
+    private List<String> filter;
+    /* *************************TYPE 2************************** */
+
+    /**
+     * Required For Type 2: An Array of n Doubles defining
+     * the function result when x=0. Default is [0].
+     */
+    private float[] cZero;
+
+    /**
+     * Required For Type 2: An Array of n Doubles defining
+     * the function result when x=1. Default is [1].
+     */
+    private float[] cOne;
+
+    /**
+     * Required for Type 2: The interpolation exponent.
+     * Each value x will return n results.
+     * Must be greater than 0.
+     */
+    private 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.
+     */
+    private List<Function> functions;
+
+    /**
+     * 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.
+     */
+    private List<Float> bounds;
+
+    /**
+     * 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 domain List objects of Double objects.
+     * This is the domain of the function.
+     * See page 264 of the PDF 1.3 Spec.
+     * @param range List of Doubles that is the Range of the function.
+     * See page 264 of the PDF 1.3 Spec.
+     * @param cZero 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 cOne 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 interpolationExponentN This is the inerpolation exponent.
+     *
+     * This attribute is required.
+     * PDF Spec page 268
+     */
+    public Function(List<Double> domain, List<Double> range, float[] cZero, float[] cOne,
+            double interpolationExponentN) {
+        this(2, domain, range);
+        this.cZero = cZero;
+        this.cOne = cOne;
+        this.interpolationExponentN = interpolationExponentN;
+    }
+
+    /**
+     * 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 domain List objects of Double objects.
+     * This is the domain of the function.
+     * See page 264 of the PDF 1.3 Spec.
+     * @param range List objects of Double objects.
+     * This is the Range of the function.
+     * See page 264 of the PDF 1.3 Spec.
+     * @param functions 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 bounds 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 encode 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.
+     */
+    public Function(List<Double> domain, List<Double> range, List<Function> functions,
+                       List<Float> bounds, List<Double> encode) {
+        this(3, domain, range);
+        this.functions = functions;
+        this.bounds = bounds;
+        this.encode = makeEncode(encode);
+    }
+
+    private List<Double> makeEncode(List<Double> encode) {
+        if (encode != null) {
+            return encode;
+        } else {
+            encode = new ArrayList<Double>(functions.size() * 2);
+            for (int i = 0; i < functions.size(); i++) {
+                encode.add(0.0);
+                encode.add(1.0);
+            }
+            return encode;
+        }
+    }
+
+    private Function(int functionType, List<Double> domain, List<Double> range) {
+        this.functionType = functionType;
+        this.domain = (domain == null) ? Arrays.asList(0.0, 1.0) : domain;
+        this.range = range;
+    }
+
+    /**
+     * Gets the function type
+     */
+    public int getFunctionType() {
+        return functionType;
+    }
+
+    /**
+     * Gets the function bounds
+     */
+    public List<Float> 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() {
+        if (functions == null) {
+            return Collections.emptyList();
+        } else {
+            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 float[] getCZero() {
+        return cZero;
+    }
+
+    /**
+     * Gets the function C1 value (color for gradient)
+     */
+    public float[] getCOne() {
+        return cOne;
+    }
+
+    public String output(StringBuilder out, DoubleFormatter doubleFormatter,
+            SubFunctionRenderer subFunctionRenderer) {
+        out.append("<<\n/FunctionType " + functionType + "\n");
+        outputDomain(out, doubleFormatter);
+        if (this.functionType == 0) {
+            outputSize(out, doubleFormatter);
+            outputEncode(out, doubleFormatter);
+            outputBitsPerSample(out);
+            outputOrder(out);
+            outputRange(out, doubleFormatter);
+            outputDecode(out, doubleFormatter);
+            if (functionDataStream != null) {
+                out.append("/Length " + (functionDataStream.length() + 1) + "\n");
+            }
+            outputFilter(out);
+            out.append(">>");
+            if (functionDataStream != null) {
+                out.append("\nstream\n" + functionDataStream + "\nendstream");
+            }
+        } else if (functionType == 2) {
+            outputRange(out, doubleFormatter);
+            outputCZero(out, doubleFormatter);
+            outputCOne(out, doubleFormatter);
+            outputInterpolationExponentN(out, doubleFormatter);
+            out.append(">>");
+        } else if (functionType == 3) {
+            outputRange(out, doubleFormatter);
+            if (!functions.isEmpty()) {
+                out.append("/Functions [ ");
+                for (int i = 0; i < functions.size(); i++) {
+                    subFunctionRenderer.outputFunction(out, i);
+                    out.append(' ');
+                }
+                out.append("]\n");
+            }
+            outputEncode(out, doubleFormatter);
+            out.append("/Bounds ");
+            if (bounds != null) {
+                GradientMaker.outputDoubles(out, doubleFormatter, bounds);
+            } else if (!functions.isEmpty()) {
+                // 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 ]
+                int numberOfFunctions = functions.size();
+                String functionsFraction = doubleFormatter.formatDouble(1.0 / numberOfFunctions);
+                out.append("[ ");
+                for (int i = 0; i + 1 < numberOfFunctions; i++) {
+                    out.append(functionsFraction);
+                    out.append(" ");
+                }
+                out.append("]");
+            }
+            out.append("\n>>");
+        } else if (functionType == 4) {
+            outputRange(out, doubleFormatter);
+            if (functionDataStream != null) {
+                out.append("/Length " + (functionDataStream.length() + 1) + "\n");
+            }
+            out.append(">>");
+            if (functionDataStream != null) {
+                out.append("\nstream\n{ " + functionDataStream + " }\nendstream");
+            }
+        }
+        return out.toString();
+    }
+
+    private void outputDomain(StringBuilder p, DoubleFormatter doubleFormatter) {
+        p.append("/Domain ");
+        GradientMaker.outputDoubles(p, doubleFormatter, domain);
+        p.append("\n");
+    }
+
+    private void outputSize(StringBuilder out, DoubleFormatter doubleFormatter) {
+        if (size != null) {
+            out.append("/Size ");
+            GradientMaker.outputDoubles(out, doubleFormatter, size);
+            out.append("\n");
+        }
+    }
+
+    private void outputBitsPerSample(StringBuilder out) {
+        out.append("/BitsPerSample " + bitsPerSample + "\n");
+    }
+
+    private void outputOrder(StringBuilder out) {
+        if (order == 1 || order == 3) {
+            out.append("\n/Order " + order + "\n");
+        }
+    }
+
+    private void outputRange(StringBuilder out, DoubleFormatter doubleFormatter) {
+        if (range != null) {
+            out.append("/Range ");
+            GradientMaker.outputDoubles(out, doubleFormatter, range);
+            out.append("\n");
+        }
+    }
+
+    private void outputEncode(StringBuilder out, DoubleFormatter doubleFormatter) {
+        out.append("/Encode ");
+        GradientMaker.outputDoubles(out, doubleFormatter, encode);
+        out.append("\n");
+    }
+
+    private void outputDecode(StringBuilder out, DoubleFormatter doubleFormatter) {
+        if (decode != null) {
+            out.append("/Decode ");
+            GradientMaker.outputDoubles(out, doubleFormatter, decode);
+            out.append("\n");
+        }
+    }
+
+    private void outputFilter(StringBuilder out) {
+        if (filter != null) {
+            int size = filter.size();
+            out.append("/Filter ");
+            if (size == 1) {
+                out.append("/" + filter.get(0) + "\n");
+            } else {
+                out.append("[ ");
+                for (int i = 0; i < size; i++) {
+                    out.append("/" + filter.get(0) + " ");
+                }
+                out.append("]\n");
+            }
+        }
+    }
+
+    private void outputCZero(StringBuilder out, DoubleFormatter doubleFormatter) {
+        if (cZero != null) {
+            out.append("/C0 [ ");
+            for (float c : cZero) {
+                out.append(doubleFormatter.formatDouble(c));
+                out.append(" ");
+            }
+            out.append("]\n");
+        }
+    }
+
+    private void outputCOne(StringBuilder out, DoubleFormatter doubleFormatter) {
+        if (cOne != null) {
+            out.append("/C1 [ ");
+            for (float c : cOne) {
+                out.append(doubleFormatter.formatDouble(c));
+                out.append(" ");
+            }
+            out.append("]\n");
+        }
+    }
+
+    private void outputInterpolationExponentN(StringBuilder out, DoubleFormatter doubleFormatter) {
+        out.append("/N ");
+        out.append(doubleFormatter.formatDouble(interpolationExponentN));
+        out.append("\n");
+    }
+
+}
diff --git a/src/java/org/apache/fop/render/gradient/GradientMaker.java b/src/java/org/apache/fop/render/gradient/GradientMaker.java
new file mode 100644
index 0000000..4c8734d
--- /dev/null
+++ b/src/java/org/apache/fop/render/gradient/GradientMaker.java
@@ -0,0 +1,168 @@
+/*
+ * 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.gradient;
+
+import java.awt.Color;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Point2D;
+import java.util.ArrayList;
+import java.util.List;
+
+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.color.ColorUtil;
+
+import org.apache.fop.pdf.PDFDeviceColorSpace;
+
+public final class GradientMaker {
+
+    public interface DoubleFormatter {
+
+        String formatDouble(double d);
+    }
+
+    private GradientMaker() { }
+
+    public static Pattern makeLinearGradient(LinearGradientPaint gp,
+            AffineTransform baseTransform, AffineTransform transform) {
+        Point2D startPoint = gp.getStartPoint();
+        Point2D endPoint = gp.getEndPoint();
+        List<Double> coords = new java.util.ArrayList<Double>(4);
+        coords.add(new Double(startPoint.getX()));
+        coords.add(new Double(startPoint.getY()));
+        coords.add(new Double(endPoint.getX()));
+        coords.add(new Double(endPoint.getY()));
+        return makeGradient(gp, coords, baseTransform, transform);
+    }
+
+    public static Pattern makeRadialGradient(RadialGradientPaint gradient,
+            AffineTransform baseTransform, AffineTransform transform) {
+        double radius = gradient.getRadius();
+        Point2D center = gradient.getCenterPoint();
+        Point2D focus = gradient.getFocusPoint();
+        double dx = focus.getX() - center.getX();
+        double dy = focus.getY() - center.getY();
+        double d = Math.sqrt(dx * dx + dy * dy);
+        if (d > radius) {
+            // The center point must be within the circle with
+            // radius radius centered at center so limit it to that.
+            double scale = (radius * .9999) / d;
+            dx *= scale;
+            dy *= scale;
+        }
+        List<Double> coords = new java.util.ArrayList<Double>(6);
+        coords.add(Double.valueOf(center.getX() + dx));
+        coords.add(Double.valueOf(center.getY() + dy));
+        coords.add(Double.valueOf(0));
+        coords.add(Double.valueOf(center.getX()));
+        coords.add(Double.valueOf(center.getY()));
+        coords.add(Double.valueOf(radius));
+        return makeGradient(gradient, coords, baseTransform, transform);
+    }
+
+    private static Pattern makeGradient(MultipleGradientPaint gradient, List<Double> coords,
+            AffineTransform baseTransform, AffineTransform transform) {
+        List<Double> matrix = makeTransform(gradient, baseTransform, transform);
+        List<Float> bounds = makeBounds(gradient);
+        List<Function> functions = makeFunctions(gradient);
+        // Gradients are currently restricted to sRGB
+        PDFDeviceColorSpace colorSpace = new PDFDeviceColorSpace(PDFDeviceColorSpace.DEVICE_RGB);
+        Function function = new Function(null, null, functions, bounds, null);
+        int shadingType = gradient instanceof LinearGradientPaint ? 2 : 3;
+        Shading shading = new Shading(shadingType, colorSpace, coords, function);
+        return new Pattern(2, shading, matrix);
+    }
+
+    private static List<Double> makeTransform(MultipleGradientPaint gradient,
+            AffineTransform baseTransform, AffineTransform transform) {
+        AffineTransform gradientTransform = new AffineTransform(baseTransform);
+        gradientTransform.concatenate(transform);
+        gradientTransform.concatenate(gradient.getTransform());
+        List<Double> matrix = new ArrayList<Double>(6);
+        double[] m = new double[6];
+        gradientTransform.getMatrix(m);
+        for (double d : m) {
+            matrix.add(Double.valueOf(d));
+        }
+        return matrix;
+    }
+
+    private static Color getsRGBColor(Color c) {
+        // Color space must be consistent, so convert to sRGB if necessary
+        // TODO really?
+        return c.getColorSpace().isCS_sRGB() ? c : ColorUtil.toSRGBColor(c);
+    }
+
+    private static List<Float> makeBounds(MultipleGradientPaint gradient) {
+        float[] fractions = gradient.getFractions();
+        List<Float> bounds = new java.util.ArrayList<Float>(fractions.length);
+        for (float offset : fractions) {
+            if (0f < offset && offset < 1f) {
+                bounds.add(offset);
+            }
+        }
+        return bounds;
+    }
+
+    private static List<Function> makeFunctions(MultipleGradientPaint gradient) {
+        List<Color> colors = makeColors(gradient);
+        List<Function> functions = new ArrayList<Function>();
+        for (int currentPosition = 0, lastPosition = colors.size() - 1;
+                currentPosition < lastPosition;
+                currentPosition++) {
+            Color currentColor = colors.get(currentPosition);
+            Color nextColor = colors.get(currentPosition + 1);
+            float[] c0 = currentColor.getColorComponents(null);
+            float[] c1 = nextColor.getColorComponents(null);
+            Function function = new Function(null, null, c0, c1, 1.0);
+            functions.add(function);
+        }
+        return functions;
+    }
+
+    private static List<Color> makeColors(MultipleGradientPaint gradient) {
+        Color[] svgColors = gradient.getColors();
+        List<Color> gradientColors = new ArrayList<Color>(svgColors.length + 2);
+        float[] fractions = gradient.getFractions();
+        if (fractions[0] > 0f) {
+            gradientColors.add(getsRGBColor(svgColors[0]));
+        }
+        for (Color c : svgColors) {
+            gradientColors.add(getsRGBColor(c));
+        }
+        if (fractions[fractions.length - 1] < 1f) {
+            gradientColors.add(getsRGBColor(svgColors[svgColors.length - 1]));
+        }
+        return gradientColors;
+    }
+
+    static void outputDoubles(StringBuilder out, DoubleFormatter doubleFormatter,
+            List<? extends Number> numbers) {
+        out.append("[ ");
+        for (Number n : numbers) {
+            out.append(doubleFormatter.formatDouble(n.doubleValue()));
+            out.append(" ");
+        }
+        out.append("]");
+    }
+
+}
diff --git a/src/java/org/apache/fop/render/gradient/Pattern.java b/src/java/org/apache/fop/render/gradient/Pattern.java
new file mode 100644
index 0000000..cc244f5
--- /dev/null
+++ b/src/java/org/apache/fop/render/gradient/Pattern.java
@@ -0,0 +1,51 @@
+/*
+ * 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.gradient;
+
+import java.util.List;
+
+public class Pattern {
+
+    private final int patternType;
+
+    private final Shading shading;
+
+    private final List<Double> matrix;
+
+    Pattern(int patternType, Shading shading, List<Double> matrix) {
+        this.patternType = patternType;
+        this.shading = shading;
+        this.matrix = matrix;
+    }
+
+    /**
+     * Either one (1) for tiling, or two (2) for shading.
+     */
+    public int getPatternType() {
+        return patternType;
+    }
+
+    public Shading getShading() {
+        return shading;
+    }
+
+    public List<Double> getMatrix() {
+        return matrix;
+    }
+
+}
diff --git a/src/java/org/apache/fop/render/gradient/Shading.java b/src/java/org/apache/fop/render/gradient/Shading.java
new file mode 100644
index 0000000..53a6f7d
--- /dev/null
+++ b/src/java/org/apache/fop/render/gradient/Shading.java
@@ -0,0 +1,337 @@
+/*
+ * 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.gradient;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.fop.pdf.PDFDeviceColorSpace;
+import org.apache.fop.render.gradient.GradientMaker.DoubleFormatter;
+
+
+public class Shading {
+
+    public interface FunctionRenderer {
+
+        void outputFunction(StringBuilder out);
+    }
+
+    /**
+     * Required: The Type of shading (1,2,3,4,5,6,7)
+     */
+    private final int shadingType;
+
+    /**
+     * A ColorSpace representing the colorspace. "DeviceRGB" is an example.
+     */
+    private final PDFDeviceColorSpace colorSpace;
+
+    /**
+     * 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.
+     */
+    private final List<Double> coords;
+
+    /**
+     * 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.
+     */
+    private final Function function;
+
+    /**
+     * Optional: A List specifying the clipping rectangle
+     */
+    private final List<Double> bbox;
+
+    /**
+     * Optional for Type 1: A transformation matrix
+     */
+    private final List<Double> matrix;
+
+    /**
+     * The background color. Since shading is opaque,
+     * this is very rarely used.
+     */
+    private final List<Double> background;
+
+    /**
+     * Required for Type 4,5,6, and 7: Array of Doubles which specifies
+     * how to decode coordinate and color component values.
+     * Each type has a differing number of decode array members, so check
+     * the spec.
+     * Page 303 in PDF Spec 1.3
+     */
+    private final List<Double> decode;
+
+    /**
+     * 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.
+     */
+    private final List<Boolean> extend;
+
+    /**
+     * Required for Type 4,5,6, and 7: Specifies the number of bits used
+     * to represent each vertex coordinate.
+     * Allowed to be 1,2,4,8,12,16,24, or 32.
+     */
+    private final int bitsPerCoordinate;
+
+    /**
+     * Required for Type 4,5,6, and 7: Specifies the number of bits used
+     * to represent the edge flag for each vertex.
+     * Allowed to be 2,4,or 8, while the Edge flag itself is allowed to
+     * be 0,1 or 2.
+     */
+    private final int bitsPerFlag;
+
+    /**
+     * Optional: A flag whether or not to filter the shading function
+     * to prevent aliasing artifacts. Default is false.
+     */
+    private final boolean antiAlias;
+
+    /**
+     * Required for Type 4,5,6, and 7: Specifies the number of bits used
+     * to represent each color coordinate.
+     * Allowed to be 1,2,4,8,12, or 16
+     */
+    private final int bitsPerComponent;
+
+    /**
+     * Required for Type 5:The number of vertices in each "row" of
+     * the lattice; it must be greater than or equal to 2.
+     */
+    private final int verticesPerRow;
+
+    public Shading(int shadingType, PDFDeviceColorSpace colorSpace,
+            List<Double> coords, Function function) {
+        this.shadingType = shadingType;
+        this.colorSpace = colorSpace;
+        this.background = null;
+        this.bbox = null;
+        this.antiAlias = false;
+        this.coords = coords;
+        this.function = function;
+        this.extend = Arrays.asList(true, true);
+        this.matrix = null;
+        this.decode = null;
+        this.bitsPerCoordinate = 0;
+        this.bitsPerFlag = 0;
+        this.bitsPerComponent = 0;
+        this.verticesPerRow = 0;
+    }
+
+    public int getShadingType() {
+        return shadingType;
+    }
+
+    public PDFDeviceColorSpace getColorSpace() {
+        return colorSpace;
+    }
+
+    public List<Double> getCoords() {
+        return coords;
+    }
+
+    public Function getFunction() {
+        return function;
+    }
+
+    public List<Double> getBBox() {
+        return bbox;
+    }
+
+    public List<Double> getMatrix() {
+        return matrix;
+    }
+
+    public List<Double> getBackground() {
+        return background;
+    }
+
+    public List<Double> getDecode() {
+        return decode;
+    }
+
+    public List<Boolean> getExtend() {
+        return extend;
+    }
+
+    public int getBitsPerCoordinate() {
+        return bitsPerCoordinate;
+    }
+
+    public int getBitsPerFlag() {
+        return bitsPerFlag;
+    }
+
+    public boolean isAntiAlias() {
+        return antiAlias;
+    }
+
+    public int getBitsPerComponent() {
+        return bitsPerComponent;
+    }
+
+    public int getVerticesPerRow() {
+        return verticesPerRow;
+    }
+
+    public void output(StringBuilder out, DoubleFormatter doubleFormatter, FunctionRenderer functionRenderer) {
+        out.append("<<\n/ShadingType " + shadingType + "\n");
+        if (colorSpace != null) {
+            out.append("/ColorSpace /" + colorSpace.getName() + "\n");
+        }
+
+        if (background != null) {
+            out.append("/Background ");
+            GradientMaker.outputDoubles(out, doubleFormatter, background);
+            out.append("\n");
+        }
+
+        if (bbox != null) {
+            out.append("/BBox");
+            GradientMaker.outputDoubles(out, doubleFormatter, bbox);
+            out.append("\n");
+        }
+
+        if (antiAlias) {
+            out.append("/AntiAlias " + antiAlias + "\n");
+        }
+
+        switch (shadingType) {
+        // Function based shading
+        case 1: outputShadingType1(out, doubleFormatter, functionRenderer); break;
+        // Axial shading
+        case 2:
+        // Radial shading
+        case 3: outputShadingType2or3(out, doubleFormatter, functionRenderer); break;
+        // Free-form Gouraud-shaded triangle meshes
+        case 4:
+        // Coons patch meshes
+        case 6:
+        // Tensor product patch meshes
+        case 7: outputShadingType4or6or7(out, doubleFormatter, functionRenderer); break;
+        // Lattice Free form gouraud-shaded triangle mesh
+        case 5: outputShadingType5(out, doubleFormatter, functionRenderer); break;
+        default: throw new UnsupportedOperationException("Shading type " + shadingType);
+        }
+
+        out.append(">>");
+    }
+
+    private void outputShadingType1(StringBuilder out, DoubleFormatter doubleFormatter,
+            Shading.FunctionRenderer functionRenderer) {
+        if (matrix != null) {
+            out.append("/Matrix ");
+            GradientMaker.outputDoubles(out, doubleFormatter, matrix);
+            out.append("\n");
+        }
+        outputFunction(out, functionRenderer);
+    }
+
+    private void outputShadingType2or3(StringBuilder out, DoubleFormatter doubleFormatter,
+            Shading.FunctionRenderer functionRenderer) {
+        if (coords != null) {
+            out.append("/Coords ");
+            GradientMaker.outputDoubles(out, doubleFormatter, coords);
+            out.append("\n");
+        }
+
+        out.append("/Extend [ ");
+        for (Boolean b : extend) {
+            out.append(b);
+            out.append(" ");
+        }
+        out.append("]\n");
+
+        outputFunction(out, functionRenderer);
+    }
+
+    private void outputShadingType4or6or7(StringBuilder out, DoubleFormatter doubleFormatter,
+            Shading.FunctionRenderer functionRenderer) {
+        if (bitsPerCoordinate > 0) {
+            out.append("/BitsPerCoordinate " + bitsPerCoordinate + "\n");
+        } else {
+            out.append("/BitsPerCoordinate 1 \n");
+        }
+
+        if (bitsPerComponent > 0) {
+            out.append("/BitsPerComponent " + bitsPerComponent + "\n");
+        } else {
+            out.append("/BitsPerComponent 1 \n");
+        }
+
+        if (bitsPerFlag > 0) {
+            out.append("/BitsPerFlag " + bitsPerFlag + "\n");
+        } else {
+            out.append("/BitsPerFlag 2 \n");
+        }
+
+        if (decode != null) {
+            out.append("/Decode ");
+            GradientMaker.outputDoubles(out, doubleFormatter, decode);
+            out.append("\n");
+        }
+
+        outputFunction(out, functionRenderer);
+    }
+
+    private void outputShadingType5(StringBuilder out, DoubleFormatter doubleFormatter,
+            Shading.FunctionRenderer functionRenderer) {
+        if (bitsPerCoordinate > 0) {
+            out.append("/BitsPerCoordinate " + bitsPerCoordinate + "\n");
+        } else {
+            out.append("/BitsPerCoordinate 1 \n");
+        }
+
+        if (bitsPerComponent > 0) {
+            out.append("/BitsPerComponent " + bitsPerComponent + "\n");
+        } else {
+            out.append("/BitsPerComponent 1 \n");
+        }
+
+        if (decode != null) {
+            out.append("/Decode ");
+            GradientMaker.outputDoubles(out, doubleFormatter, decode);
+            out.append("\n");
+        }
+
+        outputFunction(out, functionRenderer);
+
+        if (verticesPerRow > 0) {
+            out.append("/VerticesPerRow " + verticesPerRow + "\n");
+        } else {
+            out.append("/VerticesPerRow 2 \n");
+        }
+    }
+
+    private void outputFunction(StringBuilder out, FunctionRenderer functionRenderer) {
+        if (function != null) {
+            out.append("/Function ");
+            functionRenderer.outputFunction(out);
+            out.append("\n");
+        }
+    }
+}
diff --git a/src/java/org/apache/fop/render/ps/PSImageHandlerSVG.java b/src/java/org/apache/fop/render/ps/PSImageHandlerSVG.java
index 4d21592..be479ec 100644
--- a/src/java/org/apache/fop/render/ps/PSImageHandlerSVG.java
+++ b/src/java/org/apache/fop/render/ps/PSImageHandlerSVG.java
@@ -33,8 +33,6 @@
 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;
 
@@ -314,19 +312,6 @@
                 }
             }
             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);
diff --git a/src/java/org/apache/fop/render/ps/svg/PSFunction.java b/src/java/org/apache/fop/render/ps/svg/PSFunction.java
deleted file mode 100644
index b03e0b5..0000000
--- a/src/java/org/apache/fop/render/ps/svg/PSFunction.java
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * 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();
-    }
-}
diff --git a/src/java/org/apache/fop/render/ps/svg/PSPattern.java b/src/java/org/apache/fop/render/ps/svg/PSPattern.java
deleted file mode 100644
index 57a5ad3..0000000
--- a/src/java/org/apache/fop/render/ps/svg/PSPattern.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * 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;
-
-    /**
-     * List of Integers represetning the Extended unique Identifier
-     */
-    protected List xUID;
-
-    /**
-     * TODO use PDFGState
-     * String representing the extended Graphics state.
-     * Probably will never be used like this.
-     */
-    protected StringBuffer extGState;
-
-    /**
-     * 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();
-    }
-}
diff --git a/src/java/org/apache/fop/render/ps/svg/PSSVGGraphics2D.java b/src/java/org/apache/fop/render/ps/svg/PSSVGGraphics2D.java
index 1c15e56..28fa4ec 100644
--- a/src/java/org/apache/fop/render/ps/svg/PSSVGGraphics2D.java
+++ b/src/java/org/apache/fop/render/ps/svg/PSSVGGraphics2D.java
@@ -19,34 +19,29 @@
 
 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;
+import org.apache.fop.render.gradient.Function;
+import org.apache.fop.render.gradient.Function.SubFunctionRenderer;
+import org.apache.fop.render.gradient.GradientMaker;
+import org.apache.fop.render.gradient.GradientMaker.DoubleFormatter;
+import org.apache.fop.render.gradient.Pattern;
+import org.apache.fop.render.gradient.Shading;
 
 
-public class PSSVGGraphics2D extends PSGraphics2D implements GradientRegistrar {
+public class PSSVGGraphics2D extends PSGraphics2D {
 
     private static final Log LOG = LogFactory.getLog(PSSVGGraphics2D.class);
 
@@ -79,167 +74,72 @@
 
     protected void applyPaint(Paint paint, boolean fill) {
         super.applyPaint(paint, fill);
-        if (paint instanceof RadialGradientPaint) {
-            RadialGradientPaint rgp = (RadialGradientPaint)paint;
+        if (paint instanceof LinearGradientPaint) {
+            Pattern pattern = GradientMaker.makeLinearGradient((LinearGradientPaint) paint,
+                    getBaseTransform(), getTransform());
             try {
-                handleRadialGradient(rgp, gen);
+                gen.write(outputPattern(pattern));
             } catch (IOException ioe) {
                 handleIOException(ioe);
             }
-        } else if (paint instanceof LinearGradientPaint) {
-            LinearGradientPaint lgp = (LinearGradientPaint)paint;
+        } else if (paint instanceof RadialGradientPaint) {
+            Pattern pattern = GradientMaker.makeRadialGradient((RadialGradientPaint) paint,
+                    getBaseTransform(), getTransform());
             try {
-                handleLinearGradient(lgp, gen);
+                gen.write(outputPattern(pattern));
             } 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;
+    private String outputPattern(Pattern pattern) {
+        StringBuilder p = new StringBuilder(64);
+        p.append("/Pattern setcolorspace\n");
+        p.append("<< \n/Type /Pattern \n");
+
+        p.append("/PatternType " + pattern.getPatternType() + " \n");
+
+        if (pattern.getShading() != null) {
+            p.append("/Shading ");
+            outputShading(p, pattern.getShading());
+            p.append(" \n");
         }
-        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]));
+        p.append(">> \n");
+        p.append("[ ");
+        for (double m : pattern.getMatrix()) {
+            p.append(getPSGenerator().formatDouble(m));
+            p.append(" ");
         }
+        p.append("] ");
+        p.append("makepattern setcolor\n");
 
-
-        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());
-
+        return p.toString();
     }
 
+    private void outputShading(StringBuilder out, Shading shading) {
+        final GradientMaker.DoubleFormatter doubleFormatter = new DoubleFormatter() {
 
-
-    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");
+            public String formatDouble(double d) {
+                return getPSGenerator().formatDouble(d);
             }
+        };
+        final Function function = shading.getFunction();
+        Shading.FunctionRenderer functionRenderer = new Shading.FunctionRenderer() {
 
-            someColors.add(cc);
-        }
+            public void outputFunction(StringBuilder out) {
+                SubFunctionRenderer subFunctionRenderer = new Function.SubFunctionRenderer() {
 
-        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;
+                    public void outputFunction(StringBuilder out, int functionIndex) {
+                        Function subFunction = function.getFunctions().get(functionIndex);
+                        assert subFunction.getFunctions().isEmpty();
+                        subFunction.output(out, doubleFormatter, null);
+                    }
+                };
+                function.output(out, doubleFormatter, subFunctionRenderer);
+            }
+        };
+        shading.output(out, doubleFormatter, functionRenderer);
     }
 
     protected AffineTransform getBaseTransform() {
@@ -259,36 +159,4 @@
         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;
-    }
 }
diff --git a/src/java/org/apache/fop/render/ps/svg/PSShading.java b/src/java/org/apache/fop/render/ps/svg/PSShading.java
deleted file mode 100644
index e6eba11..0000000
--- a/src/java/org/apache/fop/render/ps/svg/PSShading.java
+++ /dev/null
@@ -1,228 +0,0 @@
-/*
- * 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;
-
-    /**
-     * The background color. Since shading is opaque,
-     * this is very rarely used.
-     */
-    protected List background;
-
-    /**
-     * Optional: A List specifying the clipping rectangle
-     */
-    protected List bBox;
-
-    /**
-     * Optional: A flag whether or not to filter the shading function
-     * to prevent aliasing artifacts. Default is false.
-     */
-    protected boolean antiAlias;
-
-    /**
-     * 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;
-
-    /**
-     * 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;
-
-    /**
-     * 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;
-
-    /**
-     * 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;
-
-    /**
-     * 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;
-    }
-}
diff --git a/src/java/org/apache/fop/render/shading/Function.java b/src/java/org/apache/fop/render/shading/Function.java
deleted file mode 100644
index 5bd4408..0000000
--- a/src/java/org/apache/fop/render/shading/Function.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * 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();
-}
diff --git a/src/java/org/apache/fop/render/shading/FunctionDelegate.java b/src/java/org/apache/fop/render/shading/FunctionDelegate.java
deleted file mode 100644
index 66a8db9..0000000
--- a/src/java/org/apache/fop/render/shading/FunctionDelegate.java
+++ /dev/null
@@ -1,451 +0,0 @@
-/*
- * 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;    // Default
-
-    /**
-     * Required: 2 * m Array of Double numbers which are possible inputs to the function
-     */
-    protected List<Double> domain;
-
-    /**
-     * Required: 2 * n Array of Double numbers which are possible outputs to the function
-     */
-    protected List<Double> range;
-
-    /* ********************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;
-
-    /**
-     * 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;
-
-    /**
-     * 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;
-
-    /**
-     * 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;
-
-    /**
-     * 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;
-    /* *************************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;
-
-    /**
-     * Required For Type 2: An Array of n Doubles defining
-     * the function result when x=1. Default is [1].
-     */
-    protected List<Double> cOne;
-
-    /**
-     * 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;
-
-    /**
-     * 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;
-
-    /**
-     * 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();
-    }
-}
diff --git a/src/java/org/apache/fop/render/shading/FunctionPattern.java b/src/java/org/apache/fop/render/shading/FunctionPattern.java
deleted file mode 100644
index 044053a..0000000
--- a/src/java/org/apache/fop/render/shading/FunctionPattern.java
+++ /dev/null
@@ -1,363 +0,0 @@
-/*
- * 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();
-    }
-}
diff --git a/src/java/org/apache/fop/render/shading/GradientFactory.java b/src/java/org/apache/fop/render/shading/GradientFactory.java
deleted file mode 100644
index 87ac11c..0000000
--- a/src/java/org/apache/fop/render/shading/GradientFactory.java
+++ /dev/null
@@ -1,162 +0,0 @@
-/*
- * 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;
-    }
-}
diff --git a/src/java/org/apache/fop/render/shading/GradientRegistrar.java b/src/java/org/apache/fop/render/shading/GradientRegistrar.java
deleted file mode 100644
index 617fcd4..0000000
--- a/src/java/org/apache/fop/render/shading/GradientRegistrar.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * 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);
-}
diff --git a/src/java/org/apache/fop/render/shading/PDFGradientFactory.java b/src/java/org/apache/fop/render/shading/PDFGradientFactory.java
deleted file mode 100644
index 3b3dcab..0000000
--- a/src/java/org/apache/fop/render/shading/PDFGradientFactory.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * 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;
-    }
-
-}
diff --git a/src/java/org/apache/fop/render/shading/PSGradientFactory.java b/src/java/org/apache/fop/render/shading/PSGradientFactory.java
deleted file mode 100644
index cd47de9..0000000
--- a/src/java/org/apache/fop/render/shading/PSGradientFactory.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * 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);
-    }
-}
diff --git a/src/java/org/apache/fop/render/shading/Pattern.java b/src/java/org/apache/fop/render/shading/Pattern.java
deleted file mode 100644
index b66926e..0000000
--- a/src/java/org/apache/fop/render/shading/Pattern.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * 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 {
-
-}
diff --git a/src/java/org/apache/fop/render/shading/Shading.java b/src/java/org/apache/fop/render/shading/Shading.java
deleted file mode 100644
index 385cb11..0000000
--- a/src/java/org/apache/fop/render/shading/Shading.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * 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);
-}
diff --git a/src/java/org/apache/fop/render/shading/ShadingPattern.java b/src/java/org/apache/fop/render/shading/ShadingPattern.java
deleted file mode 100644
index 6dac65f..0000000
--- a/src/java/org/apache/fop/render/shading/ShadingPattern.java
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * 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());
-    }
-}
diff --git a/src/java/org/apache/fop/svg/PDFGraphics2D.java b/src/java/org/apache/fop/svg/PDFGraphics2D.java
index 6344e37..c9ec79d 100644
--- a/src/java/org/apache/fop/svg/PDFGraphics2D.java
+++ b/src/java/org/apache/fop/svg/PDFGraphics2D.java
@@ -36,7 +36,6 @@
 import java.awt.color.ColorSpace;
 import java.awt.geom.AffineTransform;
 import java.awt.geom.PathIterator;
-import java.awt.geom.Point2D;
 import java.awt.geom.Rectangle2D;
 import java.awt.image.BufferedImage;
 import java.awt.image.ColorModel;
@@ -50,6 +49,7 @@
 import java.io.IOException;
 import java.io.OutputStream;
 import java.io.StringWriter;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 
@@ -93,15 +93,13 @@
 import org.apache.fop.pdf.PDFShading;
 import org.apache.fop.pdf.PDFText;
 import org.apache.fop.pdf.PDFXObject;
+import org.apache.fop.render.gradient.Function;
+import org.apache.fop.render.gradient.GradientMaker;
+import org.apache.fop.render.gradient.Pattern;
+import org.apache.fop.render.gradient.Shading;
 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.
@@ -112,7 +110,7 @@
  *
  * @see org.apache.batik.ext.awt.g2d.AbstractGraphics2D
  */
-public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHandler, GradientRegistrar {
+public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHandler {
     private static final AffineTransform IDENTITY_TRANSFORM = new AffineTransform();
 
     /** The number of decimal places. */
@@ -818,159 +816,18 @@
                     new Color[] {gpaint.getColor1(), gpaint.getColor2()},
                     gpaint.isCyclic() ? LinearGradientPaint.REPEAT : LinearGradientPaint.NO_CYCLE);
         }
-        if (paint instanceof LinearGradientPaint) {
-            LinearGradientPaint gp = (LinearGradientPaint)paint;
-
-            // This code currently doesn't support 'repeat'.
-            // For linear gradients it is possible to construct
-            // a 'tile' that is repeated with a PDF pattern, but
-            // it would be very tricky as you would have to rotate
-            // the coordinate system so the repeat was axially
-            // aligned.  At this point I'm just going to rasterize it.
-            MultipleGradientPaint.CycleMethodEnum cycle = gp.getCycleMethod();
-            if (cycle != MultipleGradientPaint.NO_CYCLE) {
-                return false;
-            }
-
-            Color[] cols = gp.getColors();
-            float[] fractions = gp.getFractions();
-
-            // Build proper transform from gradient space to page space
-            // ('Patterns' don't get userspace transform).
-            AffineTransform transform;
-            transform = new AffineTransform(getBaseTransform());
-            transform.concatenate(getTransform());
-            transform.concatenate(gp.getTransform());
-
-            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(new Double(mat[idx]));
-            }
-
-            Point2D p1 = gp.getStartPoint();
-            Point2D p2 = gp.getEndPoint();
-            List<Double> theCoords = new java.util.ArrayList<Double>();
-            theCoords.add(new Double(p1.getX()));
-            theCoords.add(new Double(p1.getY()));
-            theCoords.add(new Double(p2.getX()));
-            theCoords.add(new Double(p2.getY()));
-
-            List<Boolean> theExtend = new java.util.ArrayList<Boolean>();
-            theExtend.add(Boolean.TRUE);
-            theExtend.add(Boolean.TRUE);
-
-            List<Double> theDomain = new java.util.ArrayList<Double>();
-            theDomain.add(new Double(0));
-            theDomain.add(new Double(1));
-
-            List<Double> theEncode = new java.util.ArrayList<Double>();
-            theEncode.add(new Double(0));
-            theEncode.add(new Double(1));
-            theEncode.add(new Double(0));
-            theEncode.add(new Double(1));
-
-            List<Double> theBounds = new java.util.ArrayList<Double>();
-
-            List<Color> someColors = new java.util.ArrayList<Color>();
-
-            for (int count = 0; count < cols.length; count++) {
-                Color c1 = cols[count];
-                if (c1.getAlpha() != 255) {
-                    return false;  // PDF can't do alpha
-                }
-
-                //PDFColor color1 = new PDFColor(c1.getRed(), c1.getGreen(),
-                //                               c1.getBlue());
-                someColors.add(c1);
-                if (count > 0 && count < cols.length - 1) {
-                    theBounds.add(new Double(fractions[count]));
-                }
-            }
-
-            //Gradients are currently restricted to sRGB
-            PDFDeviceColorSpace 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));
-
+        if (paint instanceof LinearGradientPaint && gradientSupported((LinearGradientPaint) paint)) {
+            Pattern pattern = GradientMaker.makeLinearGradient((LinearGradientPaint) paint,
+                    getBaseTransform(), getTransform());
+            PDFPattern pdfPattern = createPDFPattern(pattern);
+            currentStream.write(pdfPattern.getColorSpaceOut(fill));
             return true;
         }
-        if (paint instanceof RadialGradientPaint) {
-            RadialGradientPaint rgp = (RadialGradientPaint)paint;
-
-            // There is essentially no way to support repeats
-            // in PDF for radial gradients (the one option would
-            // be to 'grow' the outer circle until it fully covered
-            // the bounds and then grow the stops accordingly, the
-            // problem is that this may require an extremely large
-            // number of stops for cases where the focus is near
-            // the edge of the outer circle).  so we rasterize.
-            MultipleGradientPaint.CycleMethodEnum cycle = rgp.getCycleMethod();
-            if (cycle != MultipleGradientPaint.NO_CYCLE) {
-                return false;
-            }
-
-            AffineTransform transform;
-            transform = new AffineTransform(getBaseTransform());
-            transform.concatenate(getTransform());
-            transform.concatenate(rgp.getTransform());
-
-            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(new Double(mat[idx]));
-            }
-
-            double ar = rgp.getRadius();
-            Point2D ac = rgp.getCenterPoint();
-            Point2D af = rgp.getFocusPoint();
-
-            List<Double> theCoords = new java.util.ArrayList<Double>();
-            double dx = af.getX() - ac.getX();
-            double dy = af.getY() - ac.getY();
-            double d = Math.sqrt(dx * dx + dy * dy);
-            if (d > ar) {
-                // the center point af must be within the circle with
-                // radius ar centered at ac so limit it to that.
-                double scale = (ar * .9999) / d;
-                dx = dx * scale;
-                dy = dy * scale;
-            }
-
-            theCoords.add(new Double(ac.getX() + dx)); // Fx
-            theCoords.add(new Double(ac.getY() + dy)); // Fy
-            theCoords.add(new Double(0));
-            theCoords.add(new Double(ac.getX()));
-            theCoords.add(new Double(ac.getY()));
-            theCoords.add(new Double(ar));
-
-            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) {
-                    return false;  // PDF can't do alpha
-                }
-
-                someColors.add(cc);
-            }
-
-            float[] fractions = rgp.getFractions();
-            List<Double> theBounds = new java.util.ArrayList<Double>();
-            for (int count = 1; count < fractions.length - 1; count++) {
-                float offset = fractions[count];
-                theBounds.add(new Double(offset));
-            }
-            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));
-
+        if (paint instanceof RadialGradientPaint && gradientSupported((RadialGradientPaint) paint)) {
+            Pattern pattern = GradientMaker.makeRadialGradient((RadialGradientPaint) paint,
+                    getBaseTransform(), getTransform());
+            PDFPattern pdfPattern = createPDFPattern(pattern);
+            currentStream.write(pdfPattern.getColorSpaceOut(fill));
             return true;
         }
         if (paint instanceof PatternPaint) {
@@ -980,6 +837,47 @@
         return false; // unknown paint
     }
 
+    private PDFPattern createPDFPattern(Pattern pattern) {
+        Shading shading = pattern.getShading();
+        Function function = shading.getFunction();
+        List<PDFFunction> pdfFunctions = new ArrayList<PDFFunction>(function.getFunctions().size());
+        for (Function f : function.getFunctions()) {
+            pdfFunctions.add(registerFunction(new PDFFunction(f)));
+        }
+        PDFFunction pdfFunction = registerFunction(new PDFFunction(function, pdfFunctions));
+        PDFShading pdfShading = new PDFShading(shading.getShadingType(), shading.getColorSpace(), shading.getCoords(),
+                pdfFunction);
+        pdfShading = registerShading(pdfShading);
+        PDFPattern pdfPattern = new PDFPattern(pattern.getPatternType(), pdfShading, null, null, pattern.getMatrix());
+        return registerPattern(pdfPattern);
+    }
+
+    private boolean gradientSupported(MultipleGradientPaint gradient) {
+        return !(gradientContainsTransparency(gradient) || gradientIsRepeated(gradient));
+    }
+
+    private boolean gradientContainsTransparency(MultipleGradientPaint gradient) {
+        for (Color color : gradient.getColors()) {
+            if (color.getAlpha() != 255) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean gradientIsRepeated(MultipleGradientPaint gradient) {
+         // For linear gradients it is possible to construct a 'tile' that is repeated with
+         // a PDF pattern, but it would be very tricky as the coordinate system would have
+         // to be rotated so the repeat is axially aligned.
+
+         // For radial gradients there is essentially no way to support repeats in PDF (the
+         // one option would be to 'grow' the outer circle until it fully covers the
+         // bounds and then grow the stops accordingly, the problem is that this may
+         // require an extremely large number of stops for cases where the focus is near
+         // the edge of the outer circle).
+        return (gradient.getCycleMethod() != MultipleGradientPaint.NO_CYCLE);
+    }
+
     private boolean createPattern(PatternPaint pp, boolean fill) {
         preparePainting();
 
@@ -1883,8 +1781,8 @@
      * @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);
+    public PDFFunction registerFunction(PDFFunction function) {
+        return pdfDoc.getFactory().registerFunction(function);
     }
 
     /**
@@ -1893,9 +1791,8 @@
      * @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);
+    public PDFShading registerShading(PDFShading shading) {
+        return pdfDoc.getFactory().registerShading(resourceContext, shading);
     }
 
     /**
@@ -1904,9 +1801,8 @@
      * @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);
+    public PDFPattern registerPattern(PDFPattern pattern) {
+        return pdfDoc.getFactory().registerPattern(resourceContext, pattern);
     }
 
 }
diff --git a/test/java/org/apache/fop/render/gradient/GradientTestCase.java b/test/java/org/apache/fop/render/gradient/GradientTestCase.java
new file mode 100644
index 0000000..51a9c6c
--- /dev/null
+++ b/test/java/org/apache/fop/render/gradient/GradientTestCase.java
@@ -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.gradient;
+
+import java.awt.Color;
+import java.awt.geom.AffineTransform;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+import org.apache.batik.ext.awt.LinearGradientPaint;
+import org.apache.batik.ext.awt.RadialGradientPaint;
+
+public class GradientTestCase {
+
+    private static class PatternChecker {
+
+        private final Pattern pattern;
+
+        PatternChecker(Pattern pattern) {
+            this.pattern = pattern;
+        }
+
+        public PatternChecker type(int expectedType) {
+            assertEquals(expectedType, pattern.getPatternType());
+            return this;
+        }
+
+        public PatternChecker matrix(Double... expectedMatrix) {
+            assertArrayEquals(expectedMatrix, pattern.getMatrix().toArray());
+            return this;
+        }
+
+        public ShadingChecker shading() {
+            return new ShadingChecker(pattern.getShading());
+        }
+    }
+
+    private static class ShadingChecker {
+
+        private final Shading shading;
+
+        ShadingChecker(Shading shading) {
+            this.shading = shading;
+        }
+
+        ShadingChecker shadingType(int expectedShadingType) {
+            assertEquals(expectedShadingType, shading.getShadingType());
+            return this;
+        }
+
+        ShadingChecker coords(double... expectedCoords) {
+            double[] coords = new double[shading.getCoords().size()];
+            int index = 0;
+            for (Double d : shading.getCoords()) {
+                coords[index++] = d;
+            }
+            assertArrayEquals(expectedCoords, coords, 0.0001);
+            return this;
+        }
+
+        ShadingChecker extend(Boolean... expectedExtend) {
+            assertArrayEquals(expectedExtend, shading.getExtend().toArray());
+            return this;
+        }
+
+        FunctionChecker function() {
+            return new FunctionChecker(shading.getFunction());
+        }
+    }
+
+    private static class FunctionChecker {
+
+        private final Function function;
+
+        FunctionChecker(Function function) {
+            this.function = function;
+        }
+
+        FunctionChecker functionType(int expectedFunctionType) {
+            assertEquals(expectedFunctionType, function.getFunctionType());
+            return this;
+        }
+
+        FunctionChecker domain(Double... expectedDomain) {
+            assertArrayEquals(expectedDomain, function.getDomain().toArray());
+            return this;
+        }
+
+        FunctionChecker bounds(Float... expectedBounds) {
+            assertArrayEquals(expectedBounds, function.getBounds().toArray());
+            return this;
+        }
+
+        FunctionChecker encode(Double... expectedEncode) {
+            assertArrayEquals(expectedEncode, function.getEncode().toArray());
+            return this;
+        }
+
+        FunctionChecker cZero(float... expectedCZero) {
+            assertArrayEquals(expectedCZero, function.getCZero(), 0f);
+            return this;
+        }
+
+        FunctionChecker cOne(float... expectedCOne) {
+            assertArrayEquals(expectedCOne, function.getCOne(), 0f);
+            return this;
+        }
+
+        FunctionChecker functions(int expectedFunctionCount) {
+            assertEquals(expectedFunctionCount, function.getFunctions().size());
+            return this;
+        }
+
+        FunctionChecker function(int index) {
+            return new FunctionChecker(function.getFunctions().get(index));
+        }
+    }
+
+    @Test
+    public void simpleLinearGradient() {
+        LinearGradientPaint gradient = new LinearGradientPaint(0f, 0f, 100f, 100f,
+                fractions(0f, 1f), colors(Color.BLUE, Color.RED));
+        Pattern pattern = GradientMaker.makeLinearGradient(gradient,
+                AffineTransform.getTranslateInstance(10.0, 20.0),
+                AffineTransform.getScaleInstance(100.0, 1000.0));
+        PatternChecker patternChecker = new PatternChecker(pattern)
+                .type(2)
+                .matrix(100.0, 0.0, 0.0, 1000.0, 10.0, 20.0);
+        ShadingChecker shadingChecker = patternChecker.shading()
+                .shadingType(2)
+                .coords(0.0, 0.0, 100.0, 100.0)
+                .extend(true, true);
+        FunctionChecker functionChecker = shadingChecker.function()
+                .functionType(3)
+                .domain(0.0, 1.0)
+                .bounds()
+                .encode(0.0, 1.0)
+                .functions(1);
+        functionChecker.function(0)
+                .functionType(2)
+                .domain(0.0, 1.0)
+                .cZero(0f, 0f, 1f)
+                .cOne(1f, 0f, 0f)
+                .functions(0);
+    }
+
+    @Test
+    public void simpleRadialGradient() {
+        RadialGradientPaint gradient = new RadialGradientPaint(100, 200, 50,
+                fractions(0f, 1f), colors(Color.BLUE, Color.RED));
+        Pattern pattern = GradientMaker.makeRadialGradient(gradient, new AffineTransform(), new AffineTransform());
+        PatternChecker patternChecker = new PatternChecker(pattern).type(2);
+        ShadingChecker shadingChecker = patternChecker.shading()
+                .shadingType(3)
+                .coords(100.0, 200.0, 0.0, 100.0, 200.0, 50.0)
+                .extend(true, true);
+        FunctionChecker functionChecker = shadingChecker.function()
+                .functionType(3)
+                .domain(0.0, 1.0)
+                .bounds()
+                .encode(0.0, 1.0)
+                .functions(1);
+        functionChecker.function(0)
+                .functionType(2)
+                .domain(0.0, 1.0)
+                .cZero(0f, 0f, 1f)
+                .cOne(1f, 0f, 0f)
+                .functions(0);
+    }
+
+    @Test
+    public void threeColorLinearGradient() {
+        LinearGradientPaint gradient = new LinearGradientPaint(0f, 10f, 20f, 30f,
+                fractions(0f, 0.5f, 1f), colors(Color.BLUE, Color.RED, Color.GREEN));
+        Pattern pattern = GradientMaker.makeLinearGradient(gradient, new AffineTransform(), new AffineTransform());
+        PatternChecker patternChecker = new PatternChecker(pattern)
+                .type(2)
+                .matrix(1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
+        ShadingChecker shadingChecker = patternChecker.shading()
+                .shadingType(2)
+                .coords(0.0, 10.0, 20.0, 30.0)
+                .extend(true, true);
+        FunctionChecker functionChecker = shadingChecker.function()
+                .functionType(3)
+                .domain(0.0, 1.0)
+                .bounds(0.5f)
+                .encode(0.0, 1.0, 0.0, 1.0)
+                .functions(2);
+        functionChecker.function(0)
+                .functionType(2)
+                .domain(0.0, 1.0)
+                .cZero(0f, 0f, 1f)
+                .cOne(1f, 0f, 0f)
+                .functions(0);
+        functionChecker.function(1)
+                .functionType(2)
+                .domain(0.0, 1.0)
+                .cZero(1f, 0f, 0f)
+                .cOne(0f, 1f, 0f)
+                .functions(0);
+    }
+
+    @Test
+    public void fourColorRadialGradientNonZeroFirstStop() {
+        RadialGradientPaint gradient = new RadialGradientPaint(100, 200, 50, 110, 220,
+                fractions(0.2f, 0.5f, 0.7f, 1f), colors(Color.BLUE, Color.RED, Color.GREEN, Color.WHITE));
+        Pattern pattern = GradientMaker.makeRadialGradient(gradient, new AffineTransform(), new AffineTransform());
+        ShadingChecker shadingChecker = new PatternChecker(pattern).shading()
+                .coords(110.0, 220.0, 0.0, 100.0, 200.0, 50.0);
+        FunctionChecker functionChecker = shadingChecker.function()
+                .functionType(3)
+                .bounds(0.2f, 0.5f, 0.7f)
+                .encode(0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0)
+                .functions(4);
+        functionChecker.function(0)
+                .functionType(2)
+                .cZero(0f, 0f, 1f)
+                .cOne(0f, 0f, 1f);
+        functionChecker.function(1)
+                .functionType(2)
+                .cZero(0f, 0f, 1f)
+                .cOne(1f, 0f, 0f);
+        functionChecker.function(2)
+                .functionType(2)
+                .cZero(1f, 0f, 0f)
+                .cOne(0f, 1f, 0f);
+        functionChecker.function(3)
+                .functionType(2)
+                .cZero(0f, 1f, 0f)
+                .cOne(1f, 1f, 1f);
+    }
+
+    @Test
+    public void fourColorRadialGradientNonZeroLastStopFocalOut() {
+        RadialGradientPaint gradient = new RadialGradientPaint(0, 0, 100, 100, 100,
+                fractions(0f, 0.3f, 0.6f, 0.9f), colors(Color.WHITE, Color.RED, Color.GREEN, Color.BLUE));
+        Pattern pattern = GradientMaker.makeRadialGradient(gradient, new AffineTransform(), new AffineTransform());
+        ShadingChecker shadingChecker = new PatternChecker(pattern).shading()
+                .coords(70.7036, 70.7036, 0.0, 0.0, 0.0, 100.0);
+        FunctionChecker functionChecker = shadingChecker.function()
+                .functionType(3)
+                .bounds(0.3f, 0.6f, 0.9f)
+                .encode(0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0)
+                .functions(4);
+        functionChecker.function(0)
+                .functionType(2)
+                .cZero(1f, 1f, 1f)
+                .cOne(1f, 0f, 0f);
+        functionChecker.function(1)
+                .functionType(2)
+                .cZero(1f, 0f, 0f)
+                .cOne(0f, 1f, 0f);
+        functionChecker.function(2)
+                .functionType(2)
+                .cZero(0f, 1f, 0f)
+                .cOne(0f, 0f, 1f);
+        functionChecker.function(3)
+                .functionType(2)
+                .cZero(0f, 0f, 1f)
+                .cOne(0f, 0f, 1f);
+    }
+
+    private float[] fractions(float... fractions) {
+        return fractions;
+    }
+
+    private Color[] colors(Color... colors) {
+        return colors;
+    }
+
+}
diff --git a/test/java/org/apache/fop/render/ps/svg/GradientTestCase.java b/test/java/org/apache/fop/render/ps/svg/GradientTestCase.java
new file mode 100644
index 0000000..397dbed
--- /dev/null
+++ b/test/java/org/apache/fop/render/ps/svg/GradientTestCase.java
@@ -0,0 +1,75 @@
+/*
+ * 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.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+import org.apache.commons.io.IOUtils;
+
+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.GraphicContext;
+import org.apache.xmlgraphics.ps.PSGenerator;
+
+public class GradientTestCase {
+
+    @Test
+    public void testLinearGradient() throws IOException {
+        float[] fractions = {0f, 1f};
+        Color[] colors = {new Color(255, 255, 0), new Color(255, 0, 0)};
+        LinearGradientPaint gradient = new LinearGradientPaint(115f, 285f, 15f, 15f, fractions, colors);
+        testGradientRendering(gradient, "expected-linear-gradient.ps");
+    }
+
+    @Test
+    public void testRadialGradient() throws IOException {
+        float cx = 840f;
+        float cy = 180f;
+        float r = 16f;
+        float[] fractions = {0.2f, 0.6f, 0.8f, 1.0f};
+        Color[] colors = {
+                new Color(255, 255, 255),
+                new Color(200, 200, 200),
+                new Color(170, 170, 170),
+                new Color(140, 140, 140)};
+        RadialGradientPaint gradient = new RadialGradientPaint(cx, cy, r, fractions, colors);
+        testGradientRendering(gradient, "expected-radial-gradient.ps");
+    }
+
+    private void testGradientRendering(MultipleGradientPaint gradient, String expectedResourceName)
+            throws IOException {
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        PSSVGGraphics2D svgGraphics2D = new PSSVGGraphics2D(false, new PSGenerator(out));
+        svgGraphics2D.setGraphicContext(new GraphicContext());
+        svgGraphics2D.applyPaint(gradient, true);
+        byte[] actual = out.toByteArray();
+        byte[] expected = IOUtils.toByteArray(getClass().getResourceAsStream(expectedResourceName));
+        assertEquals(new String(expected, "US-ASCII"), new String(actual, "US-ASCII"));
+    }
+
+}
diff --git a/test/java/org/apache/fop/render/ps/svg/PSSVGGraphics2DTestCase.java b/test/java/org/apache/fop/render/ps/svg/PSSVGGraphics2DTestCase.java
deleted file mode 100644
index 29a3a7b..0000000
--- a/test/java/org/apache/fop/render/ps/svg/PSSVGGraphics2DTestCase.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * 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 static org.junit.Assert.assertEquals;
-
-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;
-
-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));
-    }
-}
diff --git a/test/java/org/apache/fop/render/ps/svg/PSSVGLinearGraphics2DTestCase.java b/test/java/org/apache/fop/render/ps/svg/PSSVGLinearGraphics2DTestCase.java
deleted file mode 100644
index 3a6db43..0000000
--- a/test/java/org/apache/fop/render/ps/svg/PSSVGLinearGraphics2DTestCase.java
+++ /dev/null
@@ -1,70 +0,0 @@
- /*
- * 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 static org.junit.Assert.assertEquals;
-
-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;
-
-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));
-    }
-}
diff --git a/test/java/org/apache/fop/render/ps/svg/axial-shading-expected.dat b/test/java/org/apache/fop/render/ps/svg/axial-shading-expected.dat
deleted file mode 100644
index fdf92cd..0000000
--- a/test/java/org/apache/fop/render/ps/svg/axial-shading-expected.dat
+++ /dev/null
@@ -1,26 +0,0 @@
-/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
diff --git a/test/java/org/apache/fop/render/ps/svg/expected-linear-gradient.ps b/test/java/org/apache/fop/render/ps/svg/expected-linear-gradient.ps
new file mode 100644
index 0000000..8ddc644
--- /dev/null
+++ b/test/java/org/apache/fop/render/ps/svg/expected-linear-gradient.ps
@@ -0,0 +1,25 @@
+/Pattern setcolorspace
+<< 
+/Type /Pattern 
+/PatternType 2 
+/Shading <<
+/ShadingType 2
+/ColorSpace /DeviceRGB
+/Coords [ 115 285 15 15 ]
+/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 [ ]
+>>
+>> 
+>> 
+[ 1 0 0 1 0 0 ] makepattern setcolor
diff --git a/test/java/org/apache/fop/render/ps/svg/expected-radial-gradient.ps b/test/java/org/apache/fop/render/ps/svg/expected-radial-gradient.ps
new file mode 100644
index 0000000..527f279
--- /dev/null
+++ b/test/java/org/apache/fop/render/ps/svg/expected-radial-gradient.ps
@@ -0,0 +1,43 @@
+/Pattern setcolorspace
+<< 
+/Type /Pattern 
+/PatternType 2 
+/Shading <<
+/ShadingType 3
+/ColorSpace /DeviceRGB
+/Coords [ 840 180 0 840 180 16 ]
+/Extend [ true true ]
+/Function <<
+/FunctionType 3
+/Domain [ 0 1 ]
+/Functions [ <<
+/FunctionType 2
+/Domain [ 0 1 ]
+/C0 [ 1 1 1 ]
+/C1 [ 1 1 1 ]
+/N 1
+>> <<
+/FunctionType 2
+/Domain [ 0 1 ]
+/C0 [ 1 1 1 ]
+/C1 [ 0.784 0.784 0.784 ]
+/N 1
+>> <<
+/FunctionType 2
+/Domain [ 0 1 ]
+/C0 [ 0.784 0.784 0.784 ]
+/C1 [ 0.667 0.667 0.667 ]
+/N 1
+>> <<
+/FunctionType 2
+/Domain [ 0 1 ]
+/C0 [ 0.667 0.667 0.667 ]
+/C1 [ 0.549 0.549 0.549 ]
+/N 1
+>> ]
+/Encode [ 0 1 0 1 0 1 0 1 ]
+/Bounds [ 0.2 0.6 0.8 ]
+>>
+>> 
+>> 
+[ 1 0 0 1 0 0 ] makepattern setcolor
diff --git a/test/java/org/apache/fop/render/ps/svg/expected.ps b/test/java/org/apache/fop/render/ps/svg/expected.ps
deleted file mode 100644
index 9977070..0000000
--- a/test/java/org/apache/fop/render/ps/svg/expected.ps
+++ /dev/null
@@ -1,38 +0,0 @@
-/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