From 6663c6322ce96e1d74ac449dc6c1965bd43f3b1e Mon Sep 17 00:00:00 2001 From: Ritesh Sood Date: Sun, 13 May 2018 00:46:24 -0700 Subject: [PATCH] Added support for adding SVG images to XSLF. A PNG image is also generated using Batik and included in the .pptx --- build.xml | 4 +- .../org/apache/poi/sl/usermodel/PictureData.java | 6 +- .../org/apache/poi/xslf/usermodel/XSLFDrawing.java | 4 +- .../apache/poi/xslf/usermodel/XSLFGroupShape.java | 5 +- .../apache/poi/xslf/usermodel/XSLFPictureData.java | 5 +- .../poi/xslf/usermodel/XSLFPictureShape.java | 60 ++++++++++++++- .../apache/poi/xslf/usermodel/XSLFRelation.java | 6 ++ .../org/apache/poi/xslf/usermodel/XSLFSheet.java | 86 +++++++++++++++++++++- 8 files changed, 163 insertions(+), 13 deletions(-) diff --git a/build.xml b/build.xml index db73341..3f5e7f0 100644 --- a/build.xml +++ b/build.xml @@ -52,6 +52,7 @@ under the License. + @@ -264,7 +265,7 @@ under the License. - + @@ -324,6 +325,7 @@ under the License. + diff --git a/src/java/org/apache/poi/sl/usermodel/PictureData.java b/src/java/org/apache/poi/sl/usermodel/PictureData.java index 60e6266..c66c9ac 100644 --- a/src/java/org/apache/poi/sl/usermodel/PictureData.java +++ b/src/java/org/apache/poi/sl/usermodel/PictureData.java @@ -46,7 +46,9 @@ public interface PictureData { /** WordPerfect graphics (.wpg) */ WPG(-1,12,"image/x-wpg",".wpg"), /** Microsoft Windows Media Photo image (.wdp) */ - WDP(-1,13,"image/vnd.ms-photo",".wdp"); + WDP(-1,13,"image/vnd.ms-photo",".wdp"), + /** Scalable Vector Graphics (.svg) */ + SVG(14,14,"image/svg+xml",".svg"); public final int nativeId, ooxmlId; public final String contentType,extension; @@ -118,4 +120,4 @@ public interface PictureData { * @see PictureData#getImageDimension() */ Dimension getImageDimensionInPixels(); -} \ No newline at end of file +} diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFDrawing.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFDrawing.java index 42e375a..6dbfe44 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFDrawing.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFDrawing.java @@ -105,9 +105,9 @@ public class XSLFDrawing { return shape; } - public XSLFPictureShape createPicture(String rel){ + public XSLFPictureShape createPicture(String rel, String rel2){ CTPicture obj = _spTree.addNewPic(); - obj.set(XSLFPictureShape.prototype(_shapeId++, rel)); + obj.set(XSLFPictureShape.prototype(_shapeId++, rel, rel2)); XSLFPictureShape shape = new XSLFPictureShape(obj, _sheet); shape.setAnchor(new Rectangle2D.Double()); return shape; diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFGroupShape.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFGroupShape.java index e6425d0..7e2eb83 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFGroupShape.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFGroupShape.java @@ -269,8 +269,9 @@ implements XSLFShapeContainer, GroupShape { PackageRelationship rel = getSheet().getPackagePart().addRelationship( pic.getPartName(), TargetMode.INTERNAL, XSLFRelation.IMAGES.getRelation()); - - XSLFPictureShape sh = getDrawing().createPicture(rel.getId()); + + String relId2 = null; + XSLFPictureShape sh = getDrawing().createPicture(rel.getId(), relId2); new DrawPictureShape(sh).resize(); _shapes.add(sh); sh.setParent(this); diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFPictureData.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFPictureData.java index bfff18d..610220d 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFPictureData.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFPictureData.java @@ -218,6 +218,8 @@ public final class XSLFPictureData extends POIXMLDocumentPart implements Picture return PictureType.WDP; } else if (XSLFRelation.IMAGE_TIFF.getContentType().equals(ct)) { return PictureType.TIFF; + } else if (XSLFRelation.IMAGE_SVG.getContentType().equals(ct)) { + return PictureType.SVG; } else { return null; } @@ -237,6 +239,7 @@ public final class XSLFPictureData extends POIXMLDocumentPart implements Picture case WPG: return XSLFRelation.IMAGE_WPG; case WDP: return XSLFRelation.IMAGE_WDP; case TIFF: return XSLFRelation.IMAGE_TIFF; + case SVG: return XSLFRelation.IMAGE_SVG; default: return null; } } @@ -254,4 +257,4 @@ public final class XSLFPictureData extends POIXMLDocumentPart implements Picture public void setIndex(int index) { this.index = index; } -} \ No newline at end of file +} 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 da3cf45..d2a62bb 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFPictureShape.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFPictureShape.java @@ -35,6 +35,7 @@ import org.apache.xmlbeans.XmlException; import org.apache.xmlbeans.XmlObject; import org.openxmlformats.schemas.drawingml.x2006.main.CTBlip; import org.openxmlformats.schemas.drawingml.x2006.main.CTBlipFillProperties; +import org.openxmlformats.schemas.drawingml.x2006.main.CTGraphicalObjectData; import org.openxmlformats.schemas.drawingml.x2006.main.CTNonVisualDrawingProps; import org.openxmlformats.schemas.drawingml.x2006.main.CTOfficeArtExtension; import org.openxmlformats.schemas.drawingml.x2006.main.CTOfficeArtExtensionList; @@ -43,15 +44,24 @@ import org.openxmlformats.schemas.drawingml.x2006.main.CTRelativeRect; import org.openxmlformats.schemas.drawingml.x2006.main.CTShapeProperties; import org.openxmlformats.schemas.drawingml.x2006.main.STShapeType; import org.openxmlformats.schemas.presentationml.x2006.main.CTApplicationNonVisualDrawingProps; +import org.openxmlformats.schemas.presentationml.x2006.main.CTGraphicalObjectFrame; +import org.openxmlformats.schemas.presentationml.x2006.main.CTGraphicalObjectFrameNonVisual; import org.openxmlformats.schemas.presentationml.x2006.main.CTPicture; import org.openxmlformats.schemas.presentationml.x2006.main.CTPictureNonVisual; + + /** * Represents a picture shape */ @Beta public class XSLFPictureShape extends XSLFSimpleShape implements PictureShape { + + /* package */ static final String SVG_URI = "http://schemas.microsoft.com/office/drawing/2016/SVG/main"; + /* package */ static final String REL_URI = "http://schemas.openxmlformats.org/officeDocument/2006/relationships"; + /* package */ static final String DML_URI = "http://schemas.microsoft.com/office/drawing/2010/main"; + private XSLFPictureData _data; /*package*/ XSLFPictureShape(CTPicture shape, XSLFSheet sheet) { @@ -63,7 +73,15 @@ public class XSLFPictureShape extends XSLFSimpleShape * @param shapeId 1-based shapeId * @param rel relationship to the picture data in the ooxml package */ - static CTPicture prototype(int shapeId, String rel) { + static CTPicture prototype(int shapeId, String rel, String rel2) { + + CTGraphicalObjectFrame frame = CTGraphicalObjectFrame.Factory.newInstance(); + CTGraphicalObjectFrameNonVisual nvGr = frame.addNewNvGraphicFramePr(); + + // add empty property elements otherwise Powerpoint doesn't load the file ... + nvGr.addNewCNvGraphicFramePr(); + nvGr.addNewNvPr(); + CTPicture ct = CTPicture.Factory.newInstance(); CTPictureNonVisual nvSpPr = ct.addNewNvPicPr(); CTNonVisualDrawingProps cnv = nvSpPr.addNewCNvPr(); @@ -74,7 +92,42 @@ public class XSLFPictureShape extends XSLFSimpleShape CTBlipFillProperties blipFill = ct.addNewBlipFill(); CTBlip blip = blipFill.addNewBlip(); - blip.setEmbed(rel); + + /* + frame.addNewXfrm(); + CTGraphicalObjectData gr = frame.addNewGraphic().addNewGraphicData(); + gr.setUri(SVG_URI); + XmlCursor grCur = gr.newCursor(); + grCur.toEndToken(); + grCur.beginElement(new QName(SVG_URI, "svgBlip", "asvg")); + grCur.insertAttributeWithValue(new QName(PML_NS, "embed", "r"), "rID3"); + */ + + if (rel2 == null) { + blip.setEmbed(rel); + } + else { + blip.setEmbed(rel2); + // add PNG and SVG + CTOfficeArtExtensionList extLst = blip.addNewExtLst(); + + // add PNG + CTOfficeArtExtension ext = extLst.addNewExt(); + ext.setUri("{28A0092B-C50C-407E-A947-70E740481C1C}"); + XmlCursor cur = ext.newCursor(); + cur.toEndToken(); + cur.beginElement(new QName(DML_URI, "useLocalDpi", "a14")); + cur.insertAttributeWithValue("val", "0"); + + // add SVG image + ext = extLst.addNewExt(); + ext.setUri("{96DAC541-7B7A-43D3-8B79-37D633B846F1}"); + cur = ext.newCursor(); + cur.toEndToken(); + cur.beginElement(new QName(SVG_URI, "svgBlip", "asvg")); + cur.insertAttributeWithValue(new QName(REL_URI, "embed", "rel"), rel); + } + blipFill.addNewStretch().addNewFillRect(); CTShapeProperties spPr = ct.addNewSpPr(); @@ -84,7 +137,8 @@ public class XSLFPictureShape extends XSLFSimpleShape return ct; } - + + /** * Is this an internal picture (image data included within * the PowerPoint file), or an external linked picture diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFRelation.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFRelation.java index 38b28df..430cb61 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFRelation.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFRelation.java @@ -207,6 +207,12 @@ public class XSLFRelation extends POIXMLRelation { "/ppt/media/image#.eps", XSLFPictureData.class ); + public static final XSLFRelation IMAGE_SVG = new XSLFRelation( + PictureType.SVG.contentType, + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image", + "/ppt/media/image#.svg", + XSLFPictureData.class + ); public static final XSLFRelation IMAGE_BMP = new XSLFRelation( PictureType.BMP.contentType, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image", diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSheet.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSheet.java index e8b4f2c..89c17ce 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSheet.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSheet.java @@ -24,6 +24,7 @@ import java.awt.Graphics2D; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.io.ByteArrayOutputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; @@ -31,6 +32,11 @@ import java.util.List; import java.util.Map; import java.util.Optional; +import org.apache.batik.transcoder.image.JPEGTranscoder; +import org.apache.batik.transcoder.image.PNGTranscoder; +import org.apache.batik.transcoder.TranscoderInput; +import org.apache.batik.transcoder.TranscoderOutput; + import org.apache.poi.POIXMLDocumentPart; import org.apache.poi.POIXMLException; import org.apache.poi.openxml4j.exceptions.InvalidFormatException; @@ -234,16 +240,92 @@ implements XSLFShapeContainer, Sheet { } XSLFPictureData xPictureData = (XSLFPictureData)pictureData; PackagePart pic = xPictureData.getPackagePart(); - + RelationPart rp = addRelation(null, XSLFRelation.IMAGES, new XSLFPictureData(pic)); + String relId = rp.getRelationship().getId(); + + String relId2 = null; + if (xPictureData.getType() == PictureData.PictureType.SVG) { + // convert SVG to PNG, add to document, and create relationship + + InputStream pd_svg = null; + try { + pd_svg = xPictureData.getInputStream(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + ByteArrayOutputStream pd_png = new ByteArrayOutputStream(); + try { + generateJPEG(pd_svg, pd_png); + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } - XSLFPictureShape sh = getDrawing().createPicture(rp.getRelationship().getId()); + PictureData pictureData2 = getSlideShow().addPicture(pd_png.toByteArray(), XSLFPictureData.PictureType.PNG); + XSLFPictureData xPictureData2 = (XSLFPictureData)pictureData2; + PackagePart pic2 = xPictureData2.getPackagePart(); + + RelationPart rp2 = addRelation(null, XSLFRelation.IMAGES, new XSLFPictureData(pic2)); + relId2 = rp2.getRelationship().getId(); + } + + + XSLFPictureShape sh = getDrawing().createPicture(relId, relId2); new DrawPictureShape(sh).resize(); getShapes().add(sh); sh.setParent(this); return sh; } + static void generateJPEG(InputStream pd_in, OutputStream pd_out) throws Exception { + + // Create a JPEG transcoder + JPEGTranscoder t = new JPEGTranscoder(); + + // Set the transcoding hints. + t.addTranscodingHint(JPEGTranscoder.KEY_QUALITY, + new Float(.8)); + + // Create the transcoder input. + TranscoderInput input = new TranscoderInput(pd_in); + + // Create the transcoder output. + TranscoderOutput output = new TranscoderOutput(pd_out); + + // Save the image. + t.transcode(input, output); + + // Flush and close the stream. + pd_out.flush(); + pd_out.close(); + } + + static void generatePNG(InputStream pd_in, OutputStream pd_out) throws Exception { + + // Create a JPEG transcoder + PNGTranscoder t = new PNGTranscoder(); + + // Set the transcoding hints. + //t.addTranscodingHint(PNGTranscoder.KEY_QUALITY, + // new Float(.8)); + + // Create the transcoder input. + TranscoderInput input = new TranscoderInput(pd_in); + + // Create the transcoder output. + TranscoderOutput output = new TranscoderOutput(pd_out); + + // Save the image. + t.transcode(input, output); + + // Flush and close the stream. + pd_out.flush(); + pd_out.close(); + } + public XSLFTable createTable(){ XSLFTable sh = getDrawing().createTable(); getShapes().add(sh); -- 2.8.1