]> source.dussan.org Git - poi.git/commitdiff
#62365 - SVG image support in XSLF
authorAndreas Beeker <kiwiwings@apache.org>
Sun, 16 Dec 2018 21:02:01 +0000 (21:02 +0000)
committerAndreas Beeker <kiwiwings@apache.org>
Sun, 16 Dec 2018 21:02:01 +0000 (21:02 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1849046 13f79535-47bb-0310-9956-ffa450edef68

src/examples/src/org/apache/poi/hslf/examples/PPT2PNG.java
src/integrationtest/org/apache/poi/stress/SlideShowHandler.java
src/java/org/apache/poi/sl/draw/DrawPictureShape.java
src/java/org/apache/poi/sl/draw/Drawable.java
src/java/org/apache/poi/sl/usermodel/PictureShape.java
src/ooxml/java/org/apache/poi/xslf/draw/SVGImageRenderer.java
src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFPictureShape.java
src/ooxml/java/org/apache/poi/xslf/util/PPTX2PNG.java
src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFPictureShape.java

index 7979c805c52ebd7c7d10b75f33be1d74ed834e67..2a9761025651edef6b3b3a2bd7adeb337c8968d3 100644 (file)
@@ -26,82 +26,19 @@ import java.awt.image.BufferedImage;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.lang.ref.WeakReference;
 
 import javax.imageio.ImageIO;
 
 import org.apache.poi.hslf.usermodel.HSLFSlide;
 import org.apache.poi.hslf.usermodel.HSLFSlideShow;
+import org.apache.poi.sl.draw.Drawable;
+import org.apache.poi.xslf.util.PPTX2PNG;
 
 /**
  * Demonstrates how you can use HSLF to convert each slide into a PNG image
  */
-public final class PPT2PNG {
-
-    public static void main(String args[]) throws IOException {
-
-        if (args.length == 0) {
-            usage();
-            return;
-        }
-
-        int slidenum = -1;
-        float scale = 1;
-        String file = null;
-
-        for (int i = 0; i < args.length; i++) {
-            if (args[i].startsWith("-")) {
-                if ("-scale".equals(args[i])){
-                    scale = Float.parseFloat(args[++i]);
-                } else if ("-slide".equals(args[i])) {
-                    slidenum = Integer.parseInt(args[++i]);
-                }
-            } else {
-                file = args[i];
-            }
-        }
-        if(file == null){
-            usage();
-            return;
-        }
-
-        FileInputStream is = new FileInputStream(file);
-        HSLFSlideShow ppt = new HSLFSlideShow(is);
-        is.close();
-
-        Dimension pgsize = ppt.getPageSize();
-        int width = (int)(pgsize.width*scale);
-        int height = (int)(pgsize.height*scale);
-
-        for (HSLFSlide slide : ppt.getSlides()) {
-            if (slidenum != -1 && slidenum != slide.getSlideNumber()) {
-                continue;
-            }
-
-            String title = slide.getTitle();
-            System.out.println("Rendering slide "+slide.getSlideNumber() + (title == null ? "" : ": " + title));
-
-            BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
-            Graphics2D graphics = img.createGraphics();
-            graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
-            graphics.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
-            graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
-            graphics.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
-
-            graphics.setPaint(Color.white);
-            graphics.fill(new Rectangle2D.Float(0, 0, width, height));
-
-            graphics.scale((double)width/pgsize.width, (double)height/pgsize.height);
-
-            slide.draw(graphics);
-
-            String fname = file.replaceAll("\\.ppt", "-" + slide.getSlideNumber() + ".png");
-            FileOutputStream out = new FileOutputStream(fname);
-            ImageIO.write(img, "png", out);
-            out.close();
-        }
-        
-        ppt.close();
-    }
+public final class PPT2PNG extends PPTX2PNG {
 
     private static void usage(){
         System.out.println("Usage: PPT2PNG [-scale <scale> -slide <num>] ppt");
index 20460cc2db7ae3a77cb450a1eead1312b63a78ae..374afa40af03df38bfc45c6066c703c150613b35 100644 (file)
@@ -26,8 +26,9 @@ import java.awt.image.BufferedImage;
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
+import java.lang.ref.WeakReference;
 
-import org.apache.poi.sl.draw.DrawFactory;
+import org.apache.poi.sl.draw.Drawable;
 import org.apache.poi.sl.usermodel.PictureData;
 import org.apache.poi.sl.usermodel.Shape;
 import org.apache.poi.sl.usermodel.ShapeContainer;
@@ -111,6 +112,7 @@ public abstract class SlideShowHandler extends POIFSFileHandler {
             graphics.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
             graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
             graphics.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
+            graphics.setRenderingHint(Drawable.BUFFERED_IMAGE, new WeakReference<>(img));
     
             // draw stuff
             s.draw(graphics);
index 70194bdcd71a3bc5b516adb5e79d5678aa18dd26..84e7c91b3349f45e8f94d6035e54cf769eb22ad3 100644 (file)
@@ -44,20 +44,29 @@ public class DrawPictureShape extends DrawSimpleShape {
     
     @Override
     public void drawContent(Graphics2D graphics) {
-        PictureData data = getShape().getPictureData();
-        if(data == null) return;
-
-        Rectangle2D anchor = getAnchor(graphics, getShape());
-        Insets insets = getShape().getClipping();
-
-        try {
-            ImageRenderer renderer = getImageRenderer(graphics, data.getContentType());
-            renderer.loadImage(data.getData(), data.getContentType());
-            renderer.drawImage(graphics, anchor, insets);
-        } catch (IOException e) {
-            LOG.log(POILogger.ERROR, "image can't be loaded/rendered.", e);
+        PictureShape<?,?> ps = getShape();
+
+        Rectangle2D anchor = getAnchor(graphics, ps);
+        Insets insets = ps.getClipping();
+
+        PictureData[] pics = { ps.getAlternativePictureData(), ps.getPictureData() };
+        for (PictureData data : pics) {
+            if (data == null) {
+                continue;
+            }
+
+            try {
+                ImageRenderer renderer = getImageRenderer(graphics, data.getContentType());
+                if (renderer.canRender(data.getContentType())) {
+                    renderer.loadImage(data.getData(), data.getContentType());
+                    renderer.drawImage(graphics, anchor, insets);
+                    return;
+                }
+            } catch (IOException e) {
+                LOG.log(POILogger.ERROR, "image can't be loaded/rendered.", e);
+            }
         }
-    }    
+    }
 
     /**
      * Returns an ImageRenderer for the PictureData
index 4ec653ae50e64a761f39fc131b90d20fb6237012..d75b46dc4020fe08642e417514ad09bc75e714e0 100644 (file)
@@ -136,8 +136,13 @@ public interface Drawable {
      * are printed. In this situation we need to have a way to access the current slide
      */
     DrawableHint CURRENT_SLIDE = new DrawableHint(12);
-    
-    
+
+    /**
+     * Stores a reference (WEAK_REFERENCE) to the buffered image, if the rendering is
+     * based on a buffered image
+     */
+    DrawableHint BUFFERED_IMAGE = new DrawableHint(13);
+
     /**
      * Apply 2-D transforms before drawing this shape. This includes rotation and flipping.
      *
index c7fb629410e84ae61da75832d8e3df39221e0656..03b2f642a4264f9801047f3e37953d142a3a54b4 100644 (file)
@@ -30,6 +30,16 @@ public interface PictureShape<
      */
     PictureData getPictureData();
 
+       /**
+        * Returns an alternative picture data, e.g. an embedded SVG image
+        *
+        * @return an alternative picture data
+        *
+        * @since POI 4.1.0
+        */
+    default PictureData getAlternativePictureData() { return null; }
+
+
        /**
         * Returns the clipping values as percent ratio relatively to the image size.
         * The clipping are returned as insets converted/scaled to 100000 (=100%).
index a40c169516ddc5c2665dcbf4420af39550ca89a4..4fc144f57091fe1cee251491a0b9e1a65b506ce8 100644 (file)
@@ -21,6 +21,7 @@ import java.awt.Dimension;
 import java.awt.Graphics2D;
 import java.awt.Insets;
 import java.awt.RenderingHints;
+import java.awt.geom.AffineTransform;
 import java.awt.geom.Rectangle2D;
 import java.awt.image.BufferedImage;
 import java.io.ByteArrayInputStream;
@@ -38,6 +39,7 @@ import org.apache.batik.ext.awt.RenderingHintsKeyExt;
 import org.apache.batik.ext.awt.image.renderable.ClipRable8Bit;
 import org.apache.batik.gvt.GraphicsNode;
 import org.apache.batik.util.XMLResourceDescriptor;
+import org.apache.poi.sl.draw.Drawable;
 import org.apache.poi.sl.draw.ImageRenderer;
 import org.apache.poi.sl.usermodel.PictureData;
 import org.w3c.dom.Document;
@@ -118,6 +120,15 @@ public class SVGImageRenderer implements ImageRenderer {
 
     @Override
     public boolean drawImage(Graphics2D graphics, Rectangle2D anchor, Insets clip) {
+        graphics.setRenderingHint(RenderingHintsKeyExt.KEY_BUFFERED_IMAGE, graphics.getRenderingHint(Drawable.BUFFERED_IMAGE));
+
+        Dimension bounds = getDimension();
+
+        AffineTransform at = new AffineTransform();
+        at.translate(anchor.getX(), anchor.getY());
+        at.scale(anchor.getWidth()/bounds.getWidth(), anchor.getHeight()/bounds.getHeight());
+        svgRoot.setTransform(at);
+
         if (clip == null) {
             svgRoot.setClip(null);
         } else {
index b8de62368325a6c9448e068297dde619bad818cb..01f903e193565a0c4165198c44d077efde271f0d 100644 (file)
@@ -36,6 +36,7 @@ import javax.xml.namespace.QName;
 
 import org.apache.poi.openxml4j.opc.PackagePart;
 import org.apache.poi.openxml4j.opc.PackageRelationship;
+import org.apache.poi.sl.usermodel.PictureData;
 import org.apache.poi.sl.usermodel.PictureData.PictureType;
 import org.apache.poi.sl.usermodel.PictureShape;
 import org.apache.poi.sl.usermodel.Placeholder;
@@ -217,6 +218,8 @@ public class XSLFPictureShape extends XSLFSimpleShape
     /**
      * Add a SVG image reference
      * @param svgPic a previously imported svg image
+     *
+     * @since POI 4.1.0
      */
     public void setSvgImage(XSLFPictureData svgPic) {
         CTBlip blip = getBlip();
@@ -255,6 +258,35 @@ public class XSLFPictureShape extends XSLFSimpleShape
         cur.dispose();
     }
 
+    @Override
+    public PictureData getAlternativePictureData() {
+        return getSvgImage();
+    }
+
+    public XSLFPictureData getSvgImage() {
+        CTBlip blip = getBlip();
+        CTOfficeArtExtensionList extLst = blip.getExtLst();
+        if (extLst == null) {
+            return null;
+        }
+
+        int size = extLst.sizeOfExtArray();
+        for (int i=0; i<size; i++) {
+            XmlCursor cur = extLst.getExtArray(i).newCursor();
+            try {
+                if (cur.toChild(SVG_NS, "svgBlip")) {
+                    String svgRelId = cur.getAttributeText(new QName(CORE_PROPERTIES_ECMA376_NS, "embed"));
+                    return (svgRelId != null) ? (XSLFPictureData)getSheet().getRelationById(svgRelId) : null;
+                }
+            } finally {
+                cur.dispose();
+            }
+        }
+
+        return null;
+    }
+
+
     /**
      * Convienence method for adding SVG images, which generates the preview image
      * @param sheet the sheet to add
@@ -262,6 +294,8 @@ public class XSLFPictureShape extends XSLFSimpleShape
      * @param previewType the preview picture type or null (defaults to PNG) - currently only JPEG,GIF,PNG are allowed
      * @param anchor the image anchor (for calculating the preview image size) or
      *               null (the preview size is taken from the svg picture bounds)
+     *
+     * @since POI 4.1.0
      */
     public static XSLFPictureShape addSvgImage(XSLFSheet sheet, XSLFPictureData svgPic, PictureType previewType, Rectangle2D anchor) throws IOException {
 
index 2ae12931136871175e5712148587be166b0801b3..52d878098901244dacef171aaeb78799b9de8d80 100644 (file)
@@ -24,6 +24,7 @@ import java.awt.Graphics2D;
 import java.awt.RenderingHints;
 import java.awt.image.BufferedImage;
 import java.io.File;
+import java.lang.ref.WeakReference;
 import java.util.List;
 import java.util.Locale;
 import java.util.Set;
@@ -31,7 +32,7 @@ import java.util.TreeSet;
 
 import javax.imageio.ImageIO;
 
-import org.apache.poi.sl.draw.DrawFactory;
+import org.apache.poi.sl.draw.Drawable;
 import org.apache.poi.sl.usermodel.Slide;
 import org.apache.poi.sl.usermodel.SlideShow;
 import org.apache.poi.sl.usermodel.SlideShowFactory;
@@ -145,6 +146,7 @@ public class PPTX2PNG {
                 graphics.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
                 graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
                 graphics.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
+                graphics.setRenderingHint(Drawable.BUFFERED_IMAGE, new WeakReference<>(img));
 
                 graphics.scale(scale, scale);
 
index 1c1fdf57fc1a75bf44d7bf13e057efd38a8bd021..82d1587fe6a58bb26ba97dd810f05d1d96e8e521 100644 (file)
@@ -27,6 +27,7 @@ import java.awt.geom.Rectangle2D;
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.File;
+import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.HashMap;
@@ -36,7 +37,9 @@ import java.util.Map;
 import org.apache.poi.POIDataSamples;
 import org.apache.poi.sl.usermodel.PictureData.PictureType;
 import org.apache.poi.util.IOUtils;
+import org.apache.poi.util.TempFile;
 import org.apache.poi.xslf.XSLFTestDataSamples;
+import org.apache.poi.xslf.util.PPTX2PNG;
 import org.junit.Test;
 import org.openxmlformats.schemas.presentationml.x2006.main.CTPicture;
 
@@ -266,8 +269,21 @@ public class TestXSLFPictureShape {
         anchor.setRect(100, 100, anchor.getWidth(), anchor.getHeight());
         shape.setAnchor(anchor);
 
-//        try (FileOutputStream fos = new FileOutputStream("svgtest.pptx")) {
-//            ppt.write(fos);
-//        }
+        assertNotNull(shape.getSvgImage());
+
+        final File tmpFile = TempFile.createTempFile("svgtest", ".pptx");
+        System.out.println(tmpFile);
+        try (FileOutputStream fos = new FileOutputStream(tmpFile)) {
+            ppt.write(fos);
+        }
+
+        String[] args = {
+                "-format", "png", // png,gif,jpg or null for test
+                "-slide", "-1", // -1 for all
+                "-outdir", tmpFile.getParentFile().getCanonicalPath(),
+                "-quiet",
+                tmpFile.getAbsolutePath()
+        };
+        PPTX2PNG.main(args);
     }
 }
\ No newline at end of file