From 150a00654eaab6ddd58c571da73421beba01f8a3 Mon Sep 17 00:00:00 2001 From: Andreas Beeker Date: Sun, 16 Dec 2018 21:02:01 +0000 Subject: [PATCH] #62365 - SVG image support in XSLF git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1849046 13f79535-47bb-0310-9956-ffa450edef68 --- .../org/apache/poi/hslf/examples/PPT2PNG.java | 71 ++----------------- .../apache/poi/stress/SlideShowHandler.java | 4 +- .../apache/poi/sl/draw/DrawPictureShape.java | 35 +++++---- src/java/org/apache/poi/sl/draw/Drawable.java | 9 ++- .../apache/poi/sl/usermodel/PictureShape.java | 10 +++ .../poi/xslf/draw/SVGImageRenderer.java | 11 +++ .../poi/xslf/usermodel/XSLFPictureShape.java | 34 +++++++++ .../org/apache/poi/xslf/util/PPTX2PNG.java | 4 +- .../xslf/usermodel/TestXSLFPictureShape.java | 22 +++++- 9 files changed, 113 insertions(+), 87 deletions(-) diff --git a/src/examples/src/org/apache/poi/hslf/examples/PPT2PNG.java b/src/examples/src/org/apache/poi/hslf/examples/PPT2PNG.java index 7979c805c5..2a97610256 100644 --- a/src/examples/src/org/apache/poi/hslf/examples/PPT2PNG.java +++ b/src/examples/src/org/apache/poi/hslf/examples/PPT2PNG.java @@ -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 -slide ] ppt"); diff --git a/src/integrationtest/org/apache/poi/stress/SlideShowHandler.java b/src/integrationtest/org/apache/poi/stress/SlideShowHandler.java index 20460cc2db..374afa40af 100644 --- a/src/integrationtest/org/apache/poi/stress/SlideShowHandler.java +++ b/src/integrationtest/org/apache/poi/stress/SlideShowHandler.java @@ -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); diff --git a/src/java/org/apache/poi/sl/draw/DrawPictureShape.java b/src/java/org/apache/poi/sl/draw/DrawPictureShape.java index 70194bdcd7..84e7c91b33 100644 --- a/src/java/org/apache/poi/sl/draw/DrawPictureShape.java +++ b/src/java/org/apache/poi/sl/draw/DrawPictureShape.java @@ -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 diff --git a/src/java/org/apache/poi/sl/draw/Drawable.java b/src/java/org/apache/poi/sl/draw/Drawable.java index 4ec653ae50..d75b46dc40 100644 --- a/src/java/org/apache/poi/sl/draw/Drawable.java +++ b/src/java/org/apache/poi/sl/draw/Drawable.java @@ -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. * diff --git a/src/java/org/apache/poi/sl/usermodel/PictureShape.java b/src/java/org/apache/poi/sl/usermodel/PictureShape.java index c7fb629410..03b2f642a4 100644 --- a/src/java/org/apache/poi/sl/usermodel/PictureShape.java +++ b/src/java/org/apache/poi/sl/usermodel/PictureShape.java @@ -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%). diff --git a/src/ooxml/java/org/apache/poi/xslf/draw/SVGImageRenderer.java b/src/ooxml/java/org/apache/poi/xslf/draw/SVGImageRenderer.java index a40c169516..4fc144f570 100644 --- a/src/ooxml/java/org/apache/poi/xslf/draw/SVGImageRenderer.java +++ b/src/ooxml/java/org/apache/poi/xslf/draw/SVGImageRenderer.java @@ -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 { diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFPictureShape.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFPictureShape.java index b8de623683..01f903e193 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFPictureShape.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFPictureShape.java @@ -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(img)); graphics.scale(scale, scale); diff --git a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFPictureShape.java b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFPictureShape.java index 1c1fdf57fc..82d1587fe6 100644 --- a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFPictureShape.java +++ b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFPictureShape.java @@ -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 -- 2.39.5