]> source.dussan.org Git - poi.git/commitdiff
PPTX2PNG - fix SVG gradients
authorAndreas Beeker <kiwiwings@apache.org>
Mon, 17 Feb 2020 22:29:29 +0000 (22:29 +0000)
committerAndreas Beeker <kiwiwings@apache.org>
Mon, 17 Feb 2020 22:29:29 +0000 (22:29 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1874150 13f79535-47bb-0310-9956-ffa450edef68

src/java/org/apache/poi/sl/draw/PathGradientPaint.java
src/ooxml/java/org/apache/poi/xslf/draw/SVGPOIGraphics2D.java [new file with mode: 0644]
src/ooxml/java/org/apache/poi/xslf/draw/SVGRenderExtension.java [new file with mode: 0644]
src/ooxml/java/org/apache/poi/xslf/util/OutputFormat.java [new file with mode: 0644]
src/ooxml/java/org/apache/poi/xslf/util/PPTX2PNG.java

index a26abadd2ef6b774ba21b75c6b0af751acfe8636..42d81956eafc89d6b537f56cd854eb71381b1a94 100644 (file)
@@ -42,7 +42,7 @@ import java.awt.image.WritableRaster;
 import org.apache.poi.util.Internal;
 
 @Internal
-class PathGradientPaint implements Paint {
+public class PathGradientPaint implements Paint {
 
     // http://asserttrue.blogspot.de/2010/01/how-to-iimplement-custom-paint-in-50.html
     private final Color[] colors;
@@ -51,11 +51,11 @@ class PathGradientPaint implements Paint {
     private final int joinStyle;
     private final int transparency;
 
-    
+
     PathGradientPaint(float[] fractions, Color[] colors) {
         this(fractions,colors,BasicStroke.CAP_ROUND,BasicStroke.JOIN_ROUND);
     }
-    
+
     private PathGradientPaint(float[] fractions, Color[] colors, int capStyle, int joinStyle) {
         this.colors = colors.clone();
         this.fractions = fractions.clone();
@@ -71,27 +71,28 @@ class PathGradientPaint implements Paint {
         }
         this.transparency = opaque ? OPAQUE : TRANSLUCENT;
     }
-    
-    public PaintContext createContext(ColorModel cm,
+
+    @Override
+    public PathGradientContext createContext(ColorModel cm,
         Rectangle deviceBounds,
         Rectangle2D userBounds,
         AffineTransform transform,
         RenderingHints hints) {
         return new PathGradientContext(cm, deviceBounds, userBounds, transform, hints);
     }
-    
+
     public int getTransparency() {
         return transparency;
     }
 
-    class PathGradientContext implements PaintContext {
+    public class PathGradientContext implements PaintContext {
         final Rectangle deviceBounds;
         final Rectangle2D userBounds;
         protected final AffineTransform xform;
         final RenderingHints hints;
 
         /**
-         * for POI: the shape will be only known when the subclasses determines the concrete implementation 
+         * for POI: the shape will be only known when the subclasses determines the concrete implementation
          * in the draw/-content method, so we need to postpone the setting/creation as long as possible
          **/
         protected final Shape shape;
@@ -121,7 +122,7 @@ class PathGradientPaint implements Paint {
             Point2D start = new Point2D.Double(0, 0);
             Point2D end = new Point2D.Double(gradientSteps, 0);
             LinearGradientPaint gradientPaint = new LinearGradientPaint(start, end, fractions, colors, CycleMethod.NO_CYCLE, ColorSpaceType.SRGB, new AffineTransform());
-            
+
             Rectangle bounds = new Rectangle(0, 0, gradientSteps, 1);
             pCtx = gradientPaint.createContext(cm, bounds, bounds, new AffineTransform(), hints);
         }
@@ -134,7 +135,7 @@ class PathGradientPaint implements Paint {
 
         public Raster getRaster(int xOffset, int yOffset, int w, int h) {
             ColorModel cm = getColorModel();
-            if (raster == null) createRaster();
+            raster = createRaster();
 
             // TODO: eventually use caching here
             WritableRaster childRaster = cm.createCompatibleWritableRaster(w, h);
@@ -143,7 +144,7 @@ class PathGradientPaint implements Paint {
                 // usually doesn't happen ...
                 return childRaster;
             }
-            
+
             Rectangle2D destRect = new Rectangle2D.Double();
             Rectangle2D.intersect(childRect, deviceBounds, destRect);
             int dx = (int)(destRect.getX()-deviceBounds.getX());
@@ -154,7 +155,7 @@ class PathGradientPaint implements Paint {
             dx = (int)(destRect.getX()-childRect.getX());
             dy = (int)(destRect.getY()-childRect.getY());
             childRaster.setDataElements(dx, dy, dw, dh, data);
-            
+
             return childRaster;
         }
 
@@ -174,10 +175,12 @@ class PathGradientPaint implements Paint {
             }
             return upper;
         }
-        
-        
-        
-        void createRaster() {
+
+        public WritableRaster createRaster() {
+            if (raster != null) {
+                return raster;
+            }
+
             ColorModel cm = getColorModel();
             raster = cm.createCompatibleWritableRaster((int)deviceBounds.getWidth(), (int)deviceBounds.getHeight());
             BufferedImage img = new BufferedImage(cm, raster, false, null);
@@ -203,8 +206,9 @@ class PathGradientPaint implements Paint {
                 }
                 graphics.draw(shape);
             }
-            
+
             graphics.dispose();
+            return raster;
         }
     }
 }
diff --git a/src/ooxml/java/org/apache/poi/xslf/draw/SVGPOIGraphics2D.java b/src/ooxml/java/org/apache/poi/xslf/draw/SVGPOIGraphics2D.java
new file mode 100644 (file)
index 0000000..a057d6d
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ *  ====================================================================
+ *    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.poi.xslf.draw;
+
+import java.awt.RenderingHints;
+import java.util.Map;
+
+import org.apache.batik.svggen.SVGGeneratorContext;
+import org.apache.batik.svggen.SVGGeneratorContext.GraphicContextDefaults;
+import org.apache.batik.svggen.SVGGraphics2D;
+import org.apache.poi.util.Internal;
+import org.w3c.dom.Document;
+
+/**
+ * Wrapper class to pass changes to rendering hints down to the svg graphics context defaults
+ * which can be read by the extension handlers
+ */
+@Internal
+public class SVGPOIGraphics2D extends SVGGraphics2D {
+
+    private final RenderingHints hints;
+
+    public SVGPOIGraphics2D(Document document) {
+        super(getCtx(document), false);
+        hints = getGeneratorContext().getGraphicContextDefaults().getRenderingHints();
+    }
+
+    private static SVGGeneratorContext getCtx(Document document) {
+        SVGGeneratorContext context = SVGGeneratorContext.createDefault(document);
+        context.setExtensionHandler(new SVGRenderExtension());
+        GraphicContextDefaults defs = new GraphicContextDefaults();
+        defs.setRenderingHints(new RenderingHints(null));
+        context.setGraphicContextDefaults(defs);
+        return context;
+    }
+
+    @Override
+    public void setRenderingHint(RenderingHints.Key hintKey, Object hintValue) {
+        hints.put(hintKey, hintValue);
+        super.setRenderingHint(hintKey, hintValue);
+    }
+
+    @Override
+    public void setRenderingHints(Map hints) {
+        this.hints.clear();
+        this.hints.putAll(hints);
+        super.setRenderingHints(hints);
+    }
+
+    @Override
+    public void addRenderingHints(Map hints) {
+        this.hints.putAll(hints);
+        super.addRenderingHints(hints);
+    }
+}
diff --git a/src/ooxml/java/org/apache/poi/xslf/draw/SVGRenderExtension.java b/src/ooxml/java/org/apache/poi/xslf/draw/SVGRenderExtension.java
new file mode 100644 (file)
index 0000000..3553671
--- /dev/null
@@ -0,0 +1,185 @@
+/* ====================================================================
+   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.poi.xslf.draw;
+
+import static java.awt.MultipleGradientPaint.ColorSpaceType.LINEAR_RGB;
+import static org.apache.batik.util.SVGConstants.*;
+
+import java.awt.Color;
+import java.awt.LinearGradientPaint;
+import java.awt.MultipleGradientPaint;
+import java.awt.Paint;
+import java.awt.RadialGradientPaint;
+import java.awt.RenderingHints;
+import java.awt.Shape;
+import java.awt.TexturePaint;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Point2D;
+import java.awt.image.BufferedImage;
+import java.awt.image.ColorModel;
+import java.awt.image.WritableRaster;
+
+import org.apache.batik.svggen.DefaultExtensionHandler;
+import org.apache.batik.svggen.SVGColor;
+import org.apache.batik.svggen.SVGGeneratorContext;
+import org.apache.batik.svggen.SVGPaintDescriptor;
+import org.apache.batik.svggen.SVGTexturePaint;
+import org.apache.poi.sl.draw.Drawable;
+import org.apache.poi.sl.draw.PathGradientPaint;
+import org.apache.poi.sl.draw.PathGradientPaint.PathGradientContext;
+import org.apache.poi.util.Internal;
+import org.w3c.dom.Element;
+
+
+/**
+ * Extension of Batik's DefaultExtensionHandler which handles different kinds of Paint objects
+ * <p>
+ * Taken (with permission) from https://gist.github.com/msteiger/4509119,
+ * including the fixes that are discussed in the comments
+ *
+ * @author Martin Steiger
+ *
+ * @see <a href="https://stackoverflow.com/questions/14258206/">Gradient paints not working in Apache Batik's svggen</a>
+ * @see <a href="https://issues.apache.org/jira/browse/BATIK-1032">BATIK-1032</a>
+ */
+@Internal
+public class SVGRenderExtension extends DefaultExtensionHandler {
+    @Override
+    public SVGPaintDescriptor handlePaint(Paint paint, SVGGeneratorContext generatorContext) {
+        if (paint instanceof LinearGradientPaint) {
+            return getLgpDescriptor((LinearGradientPaint)paint, generatorContext);
+        }
+
+        if (paint instanceof RadialGradientPaint) {
+            return getRgpDescriptor((RadialGradientPaint)paint, generatorContext);
+        }
+
+        if (paint instanceof PathGradientPaint) {
+            return getPathDescriptor((PathGradientPaint)paint, generatorContext);
+        }
+
+        return super.handlePaint(paint, generatorContext);
+    }
+
+    private SVGPaintDescriptor getPathDescriptor(PathGradientPaint gradient, SVGGeneratorContext genCtx) {
+        RenderingHints hints = genCtx.getGraphicContextDefaults().getRenderingHints();
+        Shape shape = (Shape)hints.get(Drawable.GRADIENT_SHAPE);
+        if (shape == null) {
+            return null;
+        }
+
+        PathGradientContext context = gradient.createContext(ColorModel.getRGBdefault(), shape.getBounds(), shape.getBounds2D(), new AffineTransform(), hints);
+        WritableRaster raster = context.createRaster();
+        BufferedImage img = new BufferedImage(context.getColorModel(), raster, false, null);
+
+        SVGTexturePaint texturePaint = new SVGTexturePaint(genCtx);
+        TexturePaint tp = new TexturePaint(img, shape.getBounds2D());
+        return texturePaint.toSVG(tp);
+    }
+
+
+    private SVGPaintDescriptor getRgpDescriptor(RadialGradientPaint gradient, SVGGeneratorContext genCtx) {
+        Element gradElem = genCtx.getDOMFactory().createElementNS(SVG_NAMESPACE_URI, SVG_RADIAL_GRADIENT_TAG);
+
+        // Create and set unique XML id
+        String id = genCtx.getIDGenerator().generateID("gradient");
+        gradElem.setAttribute(SVG_ID_ATTRIBUTE, id);
+
+        // Set x,y pairs
+        setPoint(gradElem, gradient.getCenterPoint(), "cx", "cy");
+        setPoint(gradElem, gradient.getFocusPoint(), "fx", "fy");
+
+        gradElem.setAttribute("r", String.valueOf(gradient.getRadius()));
+
+        addMgpAttributes(gradElem, genCtx, gradient);
+
+        return new SVGPaintDescriptor("url(#" + id + ")", SVG_OPAQUE_VALUE, gradElem);
+    }
+
+    private SVGPaintDescriptor getLgpDescriptor(LinearGradientPaint gradient, SVGGeneratorContext genCtx) {
+        Element gradElem = genCtx.getDOMFactory().createElementNS(SVG_NAMESPACE_URI, SVG_LINEAR_GRADIENT_TAG);
+
+        // Create and set unique XML id
+        String id = genCtx.getIDGenerator().generateID("gradient");
+        gradElem.setAttribute(SVG_ID_ATTRIBUTE, id);
+
+        // Set x,y pairs
+        setPoint(gradElem, gradient.getStartPoint(), "x1", "y1");
+        setPoint(gradElem, gradient.getEndPoint(), "x2", "y2");
+
+        addMgpAttributes(gradElem, genCtx, gradient);
+
+        return new SVGPaintDescriptor("url(#" + id + ")", SVG_OPAQUE_VALUE, gradElem);
+    }
+
+    private void addMgpAttributes(Element gradElem, SVGGeneratorContext genCtx, MultipleGradientPaint gradient) {
+        gradElem.setAttribute(SVG_GRADIENT_UNITS_ATTRIBUTE, SVG_USER_SPACE_ON_USE_VALUE);
+
+        // Set cycle method
+        final String cycleVal;
+        switch (gradient.getCycleMethod()) {
+            case REFLECT:
+                cycleVal = SVG_REFLECT_VALUE;
+                break;
+            case REPEAT:
+                cycleVal = SVG_REPEAT_VALUE;
+                break;
+            case NO_CYCLE:
+            default:
+                cycleVal = SVG_PAD_VALUE;
+                break;
+        }
+        gradElem.setAttribute(SVG_SPREAD_METHOD_ATTRIBUTE, cycleVal);
+
+        // Set color space
+        final String colorSpace = (gradient.getColorSpace() == LINEAR_RGB) ? SVG_LINEAR_RGB_VALUE : SVG_SRGB_VALUE;
+        gradElem.setAttribute(SVG_COLOR_INTERPOLATION_ATTRIBUTE, colorSpace);
+
+        // Set transform matrix if not identity
+        AffineTransform tf = gradient.getTransform();
+        if (!tf.isIdentity()) {
+            String matrix = "matrix(" + tf.getScaleX() + " " + tf.getShearY()
+                    + " " + tf.getShearX() + " " + tf.getScaleY() + " "
+                    + tf.getTranslateX() + " " + tf.getTranslateY() + ")";
+            gradElem.setAttribute(SVG_GRADIENT_TRANSFORM_ATTRIBUTE, matrix);
+        }
+
+        // Convert gradient stops
+        Color[] colors = gradient.getColors();
+        float[] fracs = gradient.getFractions();
+
+        for (int i = 0; i < colors.length; i++) {
+            Element stop = genCtx.getDOMFactory().createElementNS(SVG_NAMESPACE_URI, SVG_STOP_TAG);
+            SVGPaintDescriptor pd = SVGColor.toSVG(colors[i], genCtx);
+
+            stop.setAttribute(SVG_OFFSET_ATTRIBUTE, (int) (fracs[i] * 100.0f) + "%");
+            stop.setAttribute(SVG_STOP_COLOR_ATTRIBUTE, pd.getPaintValue());
+
+            if (colors[i].getAlpha() != 255) {
+                stop.setAttribute(SVG_STOP_OPACITY_ATTRIBUTE, pd.getOpacityValue());
+            }
+
+            gradElem.appendChild(stop);
+        }
+    }
+
+    private static void setPoint(Element gradElem, Point2D point, String x, String y) {
+        gradElem.setAttribute(x, Double.toString(point.getX()));
+        gradElem.setAttribute(y, Double.toString(point.getY()));
+    }
+}
diff --git a/src/ooxml/java/org/apache/poi/xslf/util/OutputFormat.java b/src/ooxml/java/org/apache/poi/xslf/util/OutputFormat.java
new file mode 100644 (file)
index 0000000..9f0a6f7
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ *  ====================================================================
+ *    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.poi.xslf.util;
+
+import java.awt.Dimension;
+import java.awt.Graphics2D;
+import java.awt.image.BufferedImage;
+import java.io.Closeable;
+import java.io.File;
+import java.io.IOException;
+import java.lang.ref.WeakReference;
+
+import javax.imageio.ImageIO;
+
+import org.apache.batik.dom.GenericDOMImplementation;
+import org.apache.batik.svggen.SVGGraphics2D;
+import org.apache.poi.sl.draw.Drawable;
+import org.apache.poi.util.Internal;
+import org.apache.poi.xslf.draw.SVGPOIGraphics2D;
+import org.w3c.dom.DOMImplementation;
+import org.w3c.dom.Document;
+
+/**
+ * Output formats for PPTX2PNG
+ */
+@Internal
+interface OutputFormat extends Closeable {
+
+    Graphics2D getGraphics2D(double width, double height);
+
+    void writeOut(MFProxy proxy, File outFile) throws IOException;
+
+    class SVGFormat implements OutputFormat {
+        static final String svgNS = "http://www.w3.org/2000/svg";
+        private SVGGraphics2D svgGenerator;
+
+        @Override
+        public Graphics2D getGraphics2D(double width, double height) {
+            // Get a DOMImplementation.
+            DOMImplementation domImpl = GenericDOMImplementation.getDOMImplementation();
+
+            // Create an instance of org.w3c.dom.Document.
+            Document document = domImpl.createDocument(svgNS, "svg", null);
+            svgGenerator = new SVGPOIGraphics2D(document);
+            svgGenerator.setSVGCanvasSize(new Dimension((int)width, (int)height));
+            return svgGenerator;
+        }
+
+        @Override
+        public void writeOut(MFProxy proxy, File outFile) throws IOException {
+            svgGenerator.stream(outFile.getCanonicalPath(), true);
+        }
+
+        @Override
+        public void close() throws IOException {
+            svgGenerator.dispose();
+        }
+    }
+
+    class BitmapFormat implements OutputFormat {
+        private final String format;
+        private BufferedImage img;
+        private Graphics2D graphics;
+
+        BitmapFormat(String format) {
+            this.format = format;
+        }
+
+        @Override
+        public Graphics2D getGraphics2D(double width, double height) {
+            img = new BufferedImage((int)width, (int)height, BufferedImage.TYPE_INT_ARGB);
+            graphics = img.createGraphics();
+            graphics.setRenderingHint(Drawable.BUFFERED_IMAGE, new WeakReference<>(img));
+            return graphics;
+        }
+
+        @Override
+        public void writeOut(MFProxy proxy, File outFile) throws IOException {
+            if (!"null".equals(format)) {
+                ImageIO.write(img, format, outFile);
+            }
+        }
+
+        @Override
+        public void close() throws IOException {
+            if (graphics != null) {
+                graphics.dispose();
+                img.flush();
+            }
+        }
+    }
+}
index bb01debd23abd24a09b84dda8be30aaed2d99336..1e4c92a5b1360dafca761bb583d4bfb077768b7e 100644 (file)
 package org.apache.poi.xslf.util;
 
 import java.awt.AlphaComposite;
-import java.awt.Dimension;
 import java.awt.Graphics2D;
 import java.awt.RenderingHints;
 import java.awt.geom.Dimension2D;
-import java.awt.image.BufferedImage;
-import java.io.Closeable;
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.lang.ref.WeakReference;
 import java.util.Locale;
 import java.util.Set;
 import java.util.regex.Pattern;
 
-import javax.imageio.ImageIO;
-
-import org.apache.batik.dom.GenericDOMImplementation;
-import org.apache.batik.svggen.SVGGraphics2D;
 import org.apache.poi.common.usermodel.GenericRecord;
 import org.apache.poi.poifs.filesystem.FileMagic;
-import org.apache.poi.sl.draw.Drawable;
 import org.apache.poi.sl.draw.EmbeddedExtractor.EmbeddedPart;
 import org.apache.poi.util.Dimension2DDouble;
 import org.apache.poi.util.GenericRecordJsonWriter;
-import org.w3c.dom.DOMImplementation;
-import org.w3c.dom.Document;
+import org.apache.poi.xslf.util.OutputFormat.BitmapFormat;
+import org.apache.poi.xslf.util.OutputFormat.SVGFormat;
 
 /**
  * An utility to convert slides of a .pptx slide show to a PNG image
@@ -266,7 +257,7 @@ public final class PPTX2PNG {
                     // draw stuff
                     proxy.draw(graphics);
 
-                    outputFormat.writeOut(proxy, slideNo);
+                    outputFormat.writeOut(proxy, new File(outdir, calcOutFile(proxy, slideNo)));
                 }
             }
         } catch (NoScratchpadException e) {
@@ -395,70 +386,4 @@ public final class PPTX2PNG {
             super(cause);
         }
     }
-
-    private interface OutputFormat extends Closeable {
-        Graphics2D getGraphics2D(double width, double height);
-        void writeOut(MFProxy proxy, int slideNo) throws IOException;
-    }
-
-    private class SVGFormat implements OutputFormat {
-        static final String svgNS = "http://www.w3.org/2000/svg";
-        private SVGGraphics2D svgGenerator;
-
-        @Override
-        public Graphics2D getGraphics2D(double width, double height) {
-            // Get a DOMImplementation.
-            DOMImplementation domImpl = GenericDOMImplementation.getDOMImplementation();
-
-            // Create an instance of org.w3c.dom.Document.
-            Document document = domImpl.createDocument(svgNS, "svg", null);
-            svgGenerator = new SVGGraphics2D(document);
-            svgGenerator.setSVGCanvasSize(new Dimension((int)width, (int)height));
-            return svgGenerator;
-        }
-
-        @Override
-        public void writeOut(MFProxy proxy, int slideNo) throws IOException {
-            File outfile = new File(outdir, calcOutFile(proxy, slideNo));
-            svgGenerator.stream(outfile.getCanonicalPath(), true);
-        }
-
-        @Override
-        public void close() throws IOException {
-            svgGenerator.dispose();
-        }
-    }
-
-    private class BitmapFormat implements OutputFormat {
-        private final String format;
-        private BufferedImage img;
-        private Graphics2D graphics;
-
-        BitmapFormat(String format) {
-            this.format = format;
-        }
-
-        @Override
-        public Graphics2D getGraphics2D(double width, double height) {
-            img = new BufferedImage((int)width, (int)height, BufferedImage.TYPE_INT_ARGB);
-            graphics = img.createGraphics();
-            graphics.setRenderingHint(Drawable.BUFFERED_IMAGE, new WeakReference<>(img));
-            return graphics;
-        }
-
-        @Override
-        public void writeOut(MFProxy proxy, int slideNo) throws IOException {
-            if (!"null".equals(format)) {
-                ImageIO.write(img, format, new File(outdir, calcOutFile(proxy, slideNo)));
-            }
-        }
-
-        @Override
-        public void close() throws IOException {
-            if (graphics != null) {
-                graphics.dispose();
-                img.flush();
-            }
-        }
-    }
 }