FOP-2553: Support PDF shading to PS

git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop-pdf-images/trunk@1720524 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/lib/fontbox-2.0.0-SNAPSHOT.jar b/lib/fontbox-2.0.0-SNAPSHOT.jar
index eba4f81..7245755 100644
--- a/lib/fontbox-2.0.0-SNAPSHOT.jar
+++ b/lib/fontbox-2.0.0-SNAPSHOT.jar
Binary files differ
diff --git a/lib/fop.jar b/lib/fop.jar
index 55e34ab..d1bb409 100644
--- a/lib/fop.jar
+++ b/lib/fop.jar
Binary files differ
diff --git a/lib/pdfbox-2.0.0-SNAPSHOT.jar b/lib/pdfbox-2.0.0-SNAPSHOT.jar
index cdcce15..dce403a 100644
--- a/lib/pdfbox-2.0.0-SNAPSHOT.jar
+++ b/lib/pdfbox-2.0.0-SNAPSHOT.jar
Binary files differ
diff --git a/src/java/org/apache/fop/render/pdf/pdfbox/FOPPDFSingleByteFont.java b/src/java/org/apache/fop/render/pdf/pdfbox/FOPPDFSingleByteFont.java
index 77add78..6dcd95f 100644
--- a/src/java/org/apache/fop/render/pdf/pdfbox/FOPPDFSingleByteFont.java
+++ b/src/java/org/apache/fop/render/pdf/pdfbox/FOPPDFSingleByteFont.java
@@ -38,7 +38,6 @@
 import org.apache.pdfbox.cos.COSName;
 import org.apache.pdfbox.cos.COSNumber;
 import org.apache.pdfbox.encoding.Encoding;
-import org.apache.pdfbox.encoding.EncodingManager;
 import org.apache.pdfbox.pdmodel.common.PDStream;
 import org.apache.pdfbox.pdmodel.font.PDFont;
 import org.apache.pdfbox.pdmodel.font.PDFontDescriptorDictionary;
@@ -117,7 +116,7 @@
         addEncoding(font);
     }
 
-    private Map<Integer, String> getCodeToName(Encoding encoding) {
+    private Map<Integer, String> getCodeToName(Encoding encoding) throws IOException {
         Map<Integer, String> codeToName = new HashMap<Integer, String>();
         if (encoding != null) {
             COSBase cos = encoding.getCOSObject();
@@ -125,12 +124,8 @@
                 COSDictionary enc = (COSDictionary) cos;
                 COSName baseEncodingName = (COSName) enc.getDictionaryObject(COSName.BASE_ENCODING);
                 if (baseEncodingName != null) {
-                    try {
-                        Encoding baseEncoding = EncodingManager.INSTANCE.getEncoding(baseEncodingName);
-                        codeToName.putAll(baseEncoding.getCodeToNameMap());
-                    } catch (IOException e) {
-                        throw new RuntimeException(e);
-                    }
+                    Encoding baseEncoding = Encoding.getInstance(baseEncodingName);
+                    codeToName.putAll(baseEncoding.getCodeToNameMap());
                 }
                 COSArray differences = (COSArray)enc.getDictionaryObject(COSName.DIFFERENCES);
                 int currentIndex = -1;
@@ -359,7 +354,7 @@
         return null;
     }
 
-    private void addEncoding(PDFont fontForEnc) {
+    private void addEncoding(PDFont fontForEnc) throws IOException {
         List<String> added = new ArrayList<String>(encodingMap.values());
         Map<Integer, String> codeToName = getCodeToName(fontForEnc.getFontEncoding());
         for (int i = fontForEnc.getFirstChar(); i <= fontForEnc.getLastChar(); i++) {
diff --git a/src/java/org/apache/fop/render/pdf/pdfbox/ImageConverterPDF2G2D.java b/src/java/org/apache/fop/render/pdf/pdfbox/ImageConverterPDF2G2D.java
index eeed02a..1c0b269 100644
--- a/src/java/org/apache/fop/render/pdf/pdfbox/ImageConverterPDF2G2D.java
+++ b/src/java/org/apache/fop/render/pdf/pdfbox/ImageConverterPDF2G2D.java
@@ -134,8 +134,8 @@
                         area.getHeight() / pageDimension.height);
                 g2d.transform(at);
 
-                PageDrawer drawer = new PageDrawer(null);
-                drawer.drawPage(g2d, page, mediaBox);
+                PageDrawer drawer = new PageDrawer(null, page);
+                drawer.drawPage(g2d, mediaBox);
             } catch (IOException ioe) {
                 //TODO Better exception handling
                 throw new RuntimeException("I/O error while painting PDF page", ioe);
diff --git a/src/java/org/apache/fop/render/pdf/pdfbox/MergeFontsPDFWriter.java b/src/java/org/apache/fop/render/pdf/pdfbox/MergeFontsPDFWriter.java
index d6846b5..6ea51bc 100644
--- a/src/java/org/apache/fop/render/pdf/pdfbox/MergeFontsPDFWriter.java
+++ b/src/java/org/apache/fop/render/pdf/pdfbox/MergeFontsPDFWriter.java
@@ -48,7 +48,7 @@
 import org.apache.pdfbox.pdmodel.font.PDTrueTypeFont;
 import org.apache.pdfbox.pdmodel.font.PDType0Font;
 import org.apache.pdfbox.pdmodel.font.PDType1Font;
-import org.apache.pdfbox.util.operator.PDFOperator;
+import org.apache.pdfbox.util.operator.Operator;
 
 import org.apache.fop.fonts.FontInfo;
 import org.apache.fop.fonts.SingleByteFont;
@@ -85,7 +85,7 @@
         return txt;
     }
 
-    protected void readPDFArguments(PDFOperator op, Collection<COSBase> arguments) throws IOException {
+    protected void readPDFArguments(Operator op, Collection<COSBase> arguments) throws IOException {
         for (COSBase c : arguments) {
             if (c instanceof COSName) {
                 COSName cn = (COSName)c;
diff --git a/src/java/org/apache/fop/render/pdf/pdfbox/PDFWriter.java b/src/java/org/apache/fop/render/pdf/pdfbox/PDFWriter.java
index a734c6d..5622ab4 100644
--- a/src/java/org/apache/fop/render/pdf/pdfbox/PDFWriter.java
+++ b/src/java/org/apache/fop/render/pdf/pdfbox/PDFWriter.java
@@ -36,7 +36,7 @@
 import org.apache.pdfbox.cos.COSString;
 import org.apache.pdfbox.pdfparser.PDFStreamParser;
 import org.apache.pdfbox.pdmodel.common.PDStream;
-import org.apache.pdfbox.util.operator.PDFOperator;
+import org.apache.pdfbox.util.operator.Operator;
 
 public class PDFWriter {
     protected StringBuilder s = new StringBuilder();
@@ -55,8 +55,8 @@
         List<COSBase> arguments = new ArrayList<COSBase>();
         while (it.hasNext()) {
             Object o = it.next();
-            if (o instanceof PDFOperator) {
-                PDFOperator op = (PDFOperator)o;
+            if (o instanceof Operator) {
+                Operator op = (Operator)o;
                 readPDFArguments(op, arguments);
                 s.append(op.getOperation() + "\n");
                 arguments.clear();
@@ -77,13 +77,13 @@
         return s.toString();
     }
 
-    protected void readPDFArguments(PDFOperator op, Collection<COSBase> arguments) throws IOException {
+    protected void readPDFArguments(Operator op, Collection<COSBase> arguments) throws IOException {
         for (COSBase c : arguments) {
             processArg(op, c);
         }
     }
 
-    protected void processArg(PDFOperator op, COSBase c) throws IOException {
+    protected void processArg(Operator op, COSBase c) throws IOException {
         if (c instanceof COSInteger) {
             s.append(((COSInteger) c).intValue());
             s.append(" ");
diff --git a/src/java/org/apache/fop/render/pdf/pdfbox/PSPDFGraphics2D.java b/src/java/org/apache/fop/render/pdf/pdfbox/PSPDFGraphics2D.java
index 1934a44..068b9b3 100644
--- a/src/java/org/apache/fop/render/pdf/pdfbox/PSPDFGraphics2D.java
+++ b/src/java/org/apache/fop/render/pdf/pdfbox/PSPDFGraphics2D.java
@@ -24,7 +24,10 @@
 import java.awt.Dimension;
 import java.awt.Graphics2D;
 import java.awt.Image;
+import java.awt.Paint;
+import java.awt.PaintContext;
 import java.awt.Rectangle;
+import java.awt.geom.AffineTransform;
 import java.awt.image.BufferedImage;
 import java.awt.image.DataBufferInt;
 import java.awt.image.ImageObserver;
@@ -33,7 +36,28 @@
 import java.io.DataOutputStream;
 import java.io.IOException;
 import java.net.URI;
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.List;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.pdfbox.cos.COSArray;
+import org.apache.pdfbox.cos.COSBase;
+import org.apache.pdfbox.cos.COSDictionary;
+import org.apache.pdfbox.cos.COSInteger;
+import org.apache.pdfbox.cos.COSName;
+import org.apache.pdfbox.cos.COSStream;
+import org.apache.pdfbox.pdmodel.common.function.PDFunction;
+import org.apache.pdfbox.pdmodel.common.function.PDFunctionType0;
+import org.apache.pdfbox.pdmodel.common.function.PDFunctionType2;
+import org.apache.pdfbox.pdmodel.common.function.PDFunctionType3;
+import org.apache.pdfbox.pdmodel.graphics.color.PDColorSpace;
+import org.apache.pdfbox.pdmodel.graphics.pattern.TilingPaint;
+import org.apache.pdfbox.pdmodel.graphics.shading.AxialShadingContext;
+import org.apache.pdfbox.pdmodel.graphics.shading.AxialShadingPaint;
+import org.apache.pdfbox.pdmodel.graphics.shading.RadialShadingContext;
+import org.apache.pdfbox.pdmodel.graphics.shading.RadialShadingPaint;
+
 
 import org.apache.xmlgraphics.image.loader.ImageInfo;
 import org.apache.xmlgraphics.image.loader.ImageSize;
@@ -42,6 +66,13 @@
 import org.apache.xmlgraphics.ps.PSGenerator;
 import org.apache.xmlgraphics.ps.PSResource;
 
+import org.apache.fop.pdf.PDFDeviceColorSpace;
+import org.apache.fop.render.gradient.Function;
+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;
+import org.apache.fop.render.ps.Gradient;
 import org.apache.fop.render.ps.PSDocumentHandler;
 import org.apache.fop.render.ps.PSImageUtils;
 
@@ -59,6 +90,127 @@
         super(textAsShapes, gen);
     }
 
+    private final GradientMaker.DoubleFormatter doubleFormatter = new DoubleFormatter() {
+
+        public String formatDouble(double d) {
+            return getPSGenerator().formatDouble(d);
+        }
+    };
+
+    protected void applyPaint(Paint paint, boolean fill) {
+        preparePainting();
+        if (paint instanceof AxialShadingPaint || paint instanceof RadialShadingPaint) {
+            PaintContext paintContext = paint.createContext(null, null, null, new AffineTransform(),
+                    getRenderingHints());
+            PDColorSpace pdcs;
+            int deviceColorSpace = PDFDeviceColorSpace.DEVICE_RGB;
+            if (paint instanceof AxialShadingPaint) {
+                try {
+                    AxialShadingContext asc = (AxialShadingContext) paintContext;
+                    float[] fCoords = asc.getCoords();
+                    PDFunction function = asc.getFunction();
+                    Function targetFT = getFunction(function);
+                    if (targetFT != null) {
+                        if (targetFT.getFunctions().size() == 5
+                                && targetFT.getFunctions().get(0).getFunctionType() == 0) {
+                            return;
+                        }
+                        List<Double> dCoords = floatArrayToDoubleList(fCoords);
+                        PDFDeviceColorSpace colSpace = new PDFDeviceColorSpace(deviceColorSpace);
+                        Shading shading = new Shading(2, colSpace, dCoords, targetFT);
+                        Pattern pattern = new Pattern(2, shading, null);
+                        gen.write(Gradient.outputPattern(pattern, doubleFormatter));
+                    }
+                } catch (IOException ioe) {
+                    handleIOException(ioe);
+                }
+            } else if (paint instanceof RadialShadingPaint) {
+                try {
+                    RadialShadingContext rsc = (RadialShadingContext) paintContext;
+                    float[] fCoords = rsc.getCoords();
+                    PDFunction function = rsc.getFunction();
+                    Function targetFT3 = getFunction(function);
+                    List<Double> dCoords = floatArrayToDoubleList(fCoords);
+                    PDFDeviceColorSpace colSpace = new PDFDeviceColorSpace(deviceColorSpace);
+                    Shading shading = new Shading(3, colSpace, dCoords, targetFT3);
+                    Pattern pattern = new Pattern(2, shading, null);
+                    gen.write(Gradient.outputPattern(pattern, doubleFormatter));
+                } catch (IOException ioe) {
+                    handleIOException(ioe);
+                }
+            }
+        }
+        if (paint instanceof TilingPaint) {
+            super.applyPaint(paint, fill);
+        }
+    }
+
+    private static Function getFunction(PDFunction f) throws IOException {
+        if (f instanceof PDFunctionType3) {
+            PDFunctionType3 sourceFT3 = (PDFunctionType3) f;
+            float[] bounds = sourceFT3.getBounds().toFloatArray();
+            COSArray sourceFunctions = sourceFT3.getFunctions();
+            List<Function> targetFunctions = new ArrayList<Function>();
+            for (int j = 0; j < sourceFunctions.size(); j++) {
+                targetFunctions.add(getFunction(PDFunction.create(sourceFunctions.get(j))));
+            }
+            return new Function(null, null, targetFunctions, toList(bounds), null);
+        } else if (f instanceof PDFunctionType2) {
+            PDFunctionType2 sourceFT2 = (PDFunctionType2) f;
+            double interpolation = (double)sourceFT2.getN();
+            float[] c0 = sourceFT2.getC0().toFloatArray();
+            float[] c1 = sourceFT2.getC1().toFloatArray();
+            return new Function(null, null, c0, c1, interpolation);
+        } else if (f instanceof PDFunctionType0) {
+            COSDictionary s = f.getDictionary();
+            assert s instanceof COSStream;
+            COSStream stream = (COSStream) s;
+            COSArray encode = (COSArray) s.getDictionaryObject(COSName.ENCODE);
+            COSArray domain = (COSArray) s.getDictionaryObject(COSName.DOMAIN);
+            COSArray range = (COSArray) s.getDictionaryObject(COSName.RANGE);
+            int bits = ((COSInteger)s.getDictionaryObject(COSName.BITS_PER_SAMPLE)).intValue();
+            COSArray size = (COSArray) s.getDictionaryObject(COSName.SIZE);
+            byte[] x = IOUtils.toByteArray(stream.getUnfilteredStream());
+            for (byte y : x) {
+                if (y != 0) {
+                    return new Function(floatArrayToDoubleList(domain.toFloatArray()),
+                            floatArrayToDoubleList(range.toFloatArray()),
+                            floatArrayToDoubleList(encode.toFloatArray()),
+                            x,
+                            bits,
+                            toList(size)
+                    );
+                }
+            }
+            return null;
+        }
+        throw new IOException("Unsupported " + f.toString());
+    }
+
+    private static List<Float> toList(float[] array) {
+        List<Float> list = new ArrayList<Float>(array.length);
+        for (float f : array) {
+            list.add(f);
+        }
+        return list;
+    }
+
+    private static List<Integer> toList(COSArray array) {
+        List<Integer> list = new ArrayList<Integer>();
+        for (COSBase i : array) {
+            list.add(((COSInteger)i).intValue());
+        }
+        return list;
+    }
+
+    private static List<Double> floatArrayToDoubleList(float[] floatArray) {
+        List<Double> doubleList = new ArrayList<Double>();
+        for (float f : floatArray) {
+            doubleList.add((double) f);
+        }
+        return doubleList;
+    }
+
     @Override
     public boolean drawImage(Image img, int x1, int y1, ImageObserver observer) {
         PSGenerator tmp = gen;