]> source.dussan.org Git - poi.git/commitdiff
#60586 - Support embedding OLE1.0 package in XSSF / SS Common
authorAndreas Beeker <kiwiwings@apache.org>
Sun, 15 Jan 2017 02:04:57 +0000 (02:04 +0000)
committerAndreas Beeker <kiwiwings@apache.org>
Sun, 15 Jan 2017 02:04:57 +0000 (02:04 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1778869 13f79535-47bb-0310-9956-ffa450edef68

src/java/org/apache/poi/hssf/usermodel/HSSFPatriarch.java
src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java
src/java/org/apache/poi/ss/usermodel/Drawing.java
src/java/org/apache/poi/ss/usermodel/Workbook.java
src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFDrawing.java
src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFWorkbook.java
src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFDrawing.java
src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFObjectData.java
src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java
src/ooxml/testcases/org/apache/poi/ss/usermodel/TestEmbedOLEPackage.java [new file with mode: 0644]

index cc6805c5b82835438af60d653b017eaf013ea725..d7baeb58e0ab7ffac7d1fe683609b0a4be96cd3e 100644 (file)
@@ -48,6 +48,7 @@ import org.apache.poi.poifs.filesystem.DirectoryNode;
 import org.apache.poi.ss.usermodel.Chart;
 import org.apache.poi.ss.usermodel.ClientAnchor;
 import org.apache.poi.ss.usermodel.Drawing;
+import org.apache.poi.ss.usermodel.Workbook;
 import org.apache.poi.util.HexDump;
 import org.apache.poi.util.Internal;
 import org.apache.poi.util.NotImplemented;
@@ -137,6 +138,7 @@ public final class HSSFPatriarch implements HSSFShapeContainer, Drawing<HSSFShap
      * @param shape to be removed
      * @return true of shape is removed
      */
+    @Override
     public boolean removeShape(HSSFShape shape) {
         boolean  isRemoved = _mainSpgrContainer.removeChildRecord(shape.getEscherContainer());
         if (isRemoved){
@@ -214,22 +216,13 @@ public final class HSSFPatriarch implements HSSFShapeContainer, Drawing<HSSFShap
      *
      * @return newly created shape
      */
+    @Override
     public HSSFPicture createPicture(ClientAnchor anchor, int pictureIndex) {
         return createPicture((HSSFClientAnchor) anchor, pictureIndex);
     }
 
-    /**
-     * Adds a new OLE Package Shape 
-     * 
-     * @param anchor       the client anchor describes how this picture is
-     *                     attached to the sheet.
-     * @param storageId    the storageId returned by {@link HSSFWorkbook#addOlePackage(POIFSFileSystem,String,String,String)}
-     * @param pictureIndex the index of the picture (used as preview image) in the
-     *                     workbook collection of pictures.
-     *
-     * @return newly created shape
-     */
-    public HSSFObjectData createObjectData(HSSFClientAnchor anchor, int storageId, int pictureIndex) {
+    @Override
+    public HSSFObjectData createObjectData(ClientAnchor anchor, int storageId, int pictureIndex) {
         ObjRecord obj = new ObjRecord();
 
         CommonObjectDataSubRecord ftCmo = new CommonObjectDataSubRecord();
@@ -248,15 +241,15 @@ public final class HSSFPatriarch implements HSSFShapeContainer, Drawing<HSSFShap
         FtCfSubRecord ftCf = new FtCfSubRecord();
         HSSFPictureData pictData = getSheet().getWorkbook().getAllPictures().get(pictureIndex-1);
         switch (pictData.getFormat()) {
-               case HSSFWorkbook.PICTURE_TYPE_WMF:
-               case HSSFWorkbook.PICTURE_TYPE_EMF:
+               case Workbook.PICTURE_TYPE_WMF:
+               case Workbook.PICTURE_TYPE_EMF:
                        // this needs patch #49658 to be applied to actually work 
                    ftCf.setFlags(FtCfSubRecord.METAFILE_BIT);
                    break;
-               case HSSFWorkbook.PICTURE_TYPE_DIB:
-               case HSSFWorkbook.PICTURE_TYPE_PNG:
-               case HSSFWorkbook.PICTURE_TYPE_JPEG:
-               case HSSFWorkbook.PICTURE_TYPE_PICT:
+               case Workbook.PICTURE_TYPE_DIB:
+               case Workbook.PICTURE_TYPE_PNG:
+               case Workbook.PICTURE_TYPE_JPEG:
+               case Workbook.PICTURE_TYPE_PICT:
                    ftCf.setFlags(FtCfSubRecord.BITMAP_BIT);
                    break;
                default:
@@ -280,14 +273,16 @@ public final class HSSFPatriarch implements HSSFShapeContainer, Drawing<HSSFShap
         DirectoryEntry oleRoot;
         try {
             DirectoryNode dn = _sheet.getWorkbook().getDirectory();
-               if (dn == null) throw new FileNotFoundException();
+               if (dn == null) {
+                throw new FileNotFoundException();
+            }
                oleRoot = (DirectoryEntry)dn.getEntry(entryName);
         } catch (FileNotFoundException e) {
                throw new IllegalStateException("trying to add ole shape without actually adding data first - use HSSFWorkbook.addOlePackage first", e);
         }
         
         // create picture shape, which need to be minimal modified for oleshapes
-        HSSFPicture shape = new HSSFPicture(null, anchor);
+        HSSFPicture shape = new HSSFPicture(null, (HSSFClientAnchor)anchor);
         shape.setPictureIndex(pictureIndex);
         EscherContainerRecord spContainer = shape.getEscherContainer();
         EscherSpRecord spRecord = spContainer.getChildById(EscherSpRecord.RECORD_ID);
@@ -355,6 +350,7 @@ public final class HSSFPatriarch implements HSSFShapeContainer, Drawing<HSSFShap
         return shape;
     }
 
+    @Override
     public HSSFComment createCellComment(ClientAnchor anchor) {
         return createComment((HSSFAnchor) anchor);
     }
@@ -362,6 +358,7 @@ public final class HSSFPatriarch implements HSSFShapeContainer, Drawing<HSSFShap
     /**
      * Returns a unmodifiable list of all shapes contained by the patriarch.
      */
+    @Override
     public List<HSSFShape> getChildren() {
         return Collections.unmodifiableList(_shapes);
     }
@@ -369,6 +366,7 @@ public final class HSSFPatriarch implements HSSFShapeContainer, Drawing<HSSFShap
     /**
      * add a shape to this drawing
      */
+    @Override
     @Internal
     public void addShape(HSSFShape shape) {
         shape.setPatriarch(this);
@@ -405,6 +403,7 @@ public final class HSSFPatriarch implements HSSFShapeContainer, Drawing<HSSFShap
      * Sets the coordinate space of this group.  All children are constrained
      * to these coordinates.
      */
+    @Override
     public void setCoordinates(int x1, int y1, int x2, int y2) {
         _spgrRecord.setRectY1(y1);
         _spgrRecord.setRectY2(y2);
@@ -415,6 +414,7 @@ public final class HSSFPatriarch implements HSSFShapeContainer, Drawing<HSSFShap
     /**
      * remove all shapes inside patriarch
      */
+    @Override
     public void clear() {
         ArrayList <HSSFShape> copy = new ArrayList<HSSFShape>(_shapes);
         for (HSSFShape shape: copy){
@@ -469,6 +469,7 @@ public final class HSSFPatriarch implements HSSFShapeContainer, Drawing<HSSFShap
     /**
      * @return x coordinate of the left up corner
      */
+    @Override
     public int getX1() {
         return _spgrRecord.getRectX1();
     }
@@ -476,6 +477,7 @@ public final class HSSFPatriarch implements HSSFShapeContainer, Drawing<HSSFShap
     /**
      * @return y coordinate of the left up corner
      */
+    @Override
     public int getY1() {
         return _spgrRecord.getRectY1();
     }
@@ -483,6 +485,7 @@ public final class HSSFPatriarch implements HSSFShapeContainer, Drawing<HSSFShap
     /**
      * @return x coordinate of the right down corner
      */
+    @Override
     public int getX2() {
         return _spgrRecord.getRectX2();
     }
@@ -490,6 +493,7 @@ public final class HSSFPatriarch implements HSSFShapeContainer, Drawing<HSSFShap
     /**
      * @return y coordinate of the right down corner
      */
+    @Override
     public int getY2() {
         return _spgrRecord.getRectY2();
     }
@@ -517,10 +521,12 @@ public final class HSSFPatriarch implements HSSFShapeContainer, Drawing<HSSFShap
      * @param row2 the row (0 based) of the second cell.
      * @return the newly created client anchor
      */
+    @Override
     public HSSFClientAnchor createAnchor(int dx1, int dy1, int dx2, int dy2, int col1, int row1, int col2, int row2) {
         return new HSSFClientAnchor(dx1, dy1, dx2, dy2, (short) col1, row1, (short) col2, row2);
     }
 
+    @Override
     @NotImplemented
     public Chart createChart(ClientAnchor anchor) {
         throw new RuntimeException("NotImplemented");
index 2f3be03de90f0d105fe0be1268e5b0e1f5738a6e..55382236e06341ce7b28e0a4ed0bec5d617e4424 100644 (file)
@@ -2048,6 +2048,16 @@ public final class HSSFWorkbook extends POIDocument implements org.apache.poi.ss
        return olemap;
     }
 
+    /**
+     * Adds an OLE package manager object with the given POIFS to the sheet
+     *
+     * @param poiData an POIFS containing the embedded document, to be added
+     * @param label the label of the payload
+     * @param fileName the original filename
+     * @param command the command to open the payload
+     * @return the index of the added ole object
+     * @throws IOException if the object can't be embedded
+     */
     public int addOlePackage(POIFSFileSystem poiData, String label, String fileName, String command)
     throws IOException {
        DirectoryNode root = poiData.getRoot();
@@ -2064,6 +2074,7 @@ public final class HSSFWorkbook extends POIDocument implements org.apache.poi.ss
         return addOlePackage(bos.toByteArray(), label, fileName, command);
     }
 
+    @Override
     public int addOlePackage(byte[] oleData, String label, String fileName, String command)
     throws IOException {
        // check if we were created by POIFS otherwise create a new dummy POIFS for storing the package data
index e2d55d30a1f287546624ff6191db070b52692fc1..13a830ef9807779bc53094d30baf7e102c8d99b5 100644 (file)
@@ -62,4 +62,17 @@ public interface Drawing<T extends Shape> extends ShapeContainer<T> {
         * @return the newly created client anchor
         */
        ClientAnchor createAnchor(int dx1, int dy1, int dx2, int dy2, int col1, int row1, int col2, int row2);
+       
+    /**
+     * Adds a new OLE Package Shape 
+     * 
+     * @param anchor       the client anchor describes how this picture is
+     *                     attached to the sheet.
+     * @param storageId    the storageId returned by {@link Workbook#addOlePackage(byte[], String, String, String)}
+     * @param pictureIndex the index of the picture (used as preview image) in the
+     *                     workbook collection of pictures.
+     *
+     * @return newly created shape
+     */
+    ObjectData createObjectData(ClientAnchor anchor, int storageId, int pictureIndex);
 }
index f5043b727f326a0ea837ff61942e391fdcd07806..5273423de08a4fb263e305098221753449905052 100644 (file)
@@ -286,6 +286,7 @@ public interface Workbook extends Closeable, Iterable<Sheet> {
      * @return the font with the matched attributes or <code>null</code>
      * @deprecated POI 3.15 beta 2. Use {@link #findFont(boolean, short, short, String, boolean, boolean, short, byte)} instead.
      */
+    @Deprecated
     Font findFont(short boldWeight, short color, short fontHeight, String name, boolean italic, boolean strikeout, short typeOffset, byte underline);
     
     /**
@@ -635,4 +636,18 @@ public interface Workbook extends Closeable, Iterable<Sheet> {
      * @since 3.14 beta 2
      */
     SpreadsheetVersion getSpreadsheetVersion();
+
+    /**
+     * Adds an OLE package manager object with the given content to the sheet
+     *
+     * @param oleData the payload
+     * @param label the label of the payload
+     * @param fileName the original filename
+     * @param command the command to open the payload
+     * 
+     * @return the index of the added ole object, i.e. the storage id
+     * 
+     * @throws IOException if the object can't be embedded
+     */
+    int addOlePackage(byte[] oleData, String label, String fileName, String command) throws IOException;
 }
index 17f18710253aafa6a4fc7a5be6f009fd4b736b88..3a645f76768c8adfe4fc4b5eefd629e5ed1cb9cf 100644 (file)
@@ -23,6 +23,7 @@ import org.apache.poi.ss.usermodel.Chart;
 import org.apache.poi.ss.usermodel.ClientAnchor;
 import org.apache.poi.ss.usermodel.Comment;
 import org.apache.poi.ss.usermodel.Drawing;
+import org.apache.poi.ss.usermodel.ObjectData;
 import org.apache.poi.xssf.usermodel.XSSFDrawing;
 import org.apache.poi.xssf.usermodel.XSSFPicture;
 import org.apache.poi.xssf.usermodel.XSSFShape;
@@ -62,9 +63,15 @@ public class SXSSFDrawing implements Drawing<XSSFShape> {
         return _drawing.createAnchor(dx1, dy1, dx2, dy2, col1, row1, col2, row2);
     }
 
+    @Override
+    public ObjectData createObjectData(ClientAnchor anchor, int storageId, int pictureIndex) {
+        return _drawing.createObjectData(anchor, storageId, pictureIndex);
+    }
+
     @Override
     public Iterator<XSSFShape> iterator() {
         return _drawing.getShapes().iterator();
     }
+    
 }
 
index a11bef310094f8350650ce59d85553f9aeb6017c..611f16bc0c689cbf8bf48312d1c2a3969019ccab 100644 (file)
@@ -1391,5 +1391,10 @@ public class SXSSFWorkbook implements Workbook {
         return SpreadsheetVersion.EXCEL2007;
     }
 
+    @Override
+    public int addOlePackage(byte[] oleData, String label, String fileName, String command) throws IOException {
+        return _wb.addOlePackage(oleData, label, fileName, command);
+    }
+    
 //end of interface implementation
 }
index 5c7395ed3ebd673addb46e4ccbc22d1f18e6394e..35f71381c31c24772d7037f2da48da8ad1f2a851 100644 (file)
@@ -28,9 +28,16 @@ import java.util.List;
 
 import javax.xml.namespace.QName;
 
+import org.apache.poi.POIXMLDocument;
 import org.apache.poi.POIXMLDocumentPart;
+import org.apache.poi.POIXMLException;
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
 import org.apache.poi.openxml4j.opc.PackagePart;
+import org.apache.poi.openxml4j.opc.PackagePartName;
 import org.apache.poi.openxml4j.opc.PackageRelationship;
+import org.apache.poi.openxml4j.opc.PackageRelationshipTypes;
+import org.apache.poi.openxml4j.opc.PackagingURIHelper;
+import org.apache.poi.openxml4j.opc.TargetMode;
 import org.apache.poi.ss.usermodel.ClientAnchor;
 import org.apache.poi.ss.usermodel.Drawing;
 import org.apache.poi.ss.util.CellAddress;
@@ -45,7 +52,9 @@ import org.apache.xmlbeans.XmlException;
 import org.apache.xmlbeans.XmlObject;
 import org.apache.xmlbeans.XmlOptions;
 import org.apache.xmlbeans.impl.values.XmlAnyTypeImpl;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTBlipFillProperties;
 import org.openxmlformats.schemas.drawingml.x2006.main.CTGroupTransform2D;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTNonVisualDrawingProps;
 import org.openxmlformats.schemas.drawingml.x2006.main.CTPoint2D;
 import org.openxmlformats.schemas.drawingml.x2006.main.CTPositiveSize2D;
 import org.openxmlformats.schemas.drawingml.x2006.main.CTTransform2D;
@@ -59,19 +68,22 @@ import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTPicture;
 import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTShape;
 import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTTwoCellAnchor;
 import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.STEditAs;
+import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTOleObject;
+import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTOleObjects;
+import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTWorksheet;
 
 /**
  * Represents a SpreadsheetML drawing
  */
 public final class XSSFDrawing extends POIXMLDocumentPart implements Drawing<XSSFShape> {
     private static final POILogger LOG = POILogFactory.getLogger(XSSFDrawing.class);
-    
+
     /**
      * Root element of the SpreadsheetML Drawing part
      */
     private CTDrawing drawing;
     private long numOfGraphicFrames = 0L;
-    
+
     protected static final String NAMESPACE_A = XSSFRelation.NS_DRAWINGML;
     protected static final String NAMESPACE_C = XSSFRelation.NS_CHART;
 
@@ -90,7 +102,7 @@ public final class XSSFDrawing extends POIXMLDocumentPart implements Drawing<XSS
      *
      * @param part the package part holding the drawing data,
      * the content type must be <code>application/vnd.openxmlformats-officedocument.drawing+xml</code>
-     * 
+     *
      * @since POI 3.14-Beta1
      */
     public XSSFDrawing(PackagePart part) throws IOException, XmlException {
@@ -105,7 +117,7 @@ public final class XSSFDrawing extends POIXMLDocumentPart implements Drawing<XSS
             is.close();
         }
     }
-    
+
     /**
      * Construct a new CTDrawing bean. By default, it's just an empty placeholder for drawing objects
      *
@@ -194,7 +206,7 @@ public final class XSSFDrawing extends POIXMLDocumentPart implements Drawing<XSS
         shape.anchor = anchor;
         shape.setPictureReference(rel);
         ctShape.getSpPr().setXfrm(createXfrm(anchor));
-        
+
         return shape;
     }
 
@@ -319,7 +331,7 @@ public final class XSSFDrawing extends POIXMLDocumentPart implements Drawing<XSS
     @Override
     public XSSFComment createCellComment(ClientAnchor anchor) {
         XSSFClientAnchor ca = (XSSFClientAnchor)anchor;
-        XSSFSheet sheet = (XSSFSheet)getParent();
+        XSSFSheet sheet = getSheet();
 
         //create comments and vmlDrawing parts if they don't exist
         CommentsTable comments = sheet.getCommentsTable(true);
@@ -344,7 +356,7 @@ public final class XSSFDrawing extends POIXMLDocumentPart implements Drawing<XSS
         if(comments.findCellComment(ref) != null) {
             throw new IllegalArgumentException("Multiple cell comments in one cell are not allowed, cell: " + ref);
         }
-        
+
         return new XSSFComment(comments, comments.newComment(ref), vmlShape);
     }
 
@@ -368,7 +380,85 @@ public final class XSSFDrawing extends POIXMLDocumentPart implements Drawing<XSS
         graphicFrame.setName("Diagramm" + frameId);
         return graphicFrame;
     }
-    
+
+    @Override
+    public XSSFObjectData createObjectData(ClientAnchor anchor, int storageId, int pictureIndex) {
+        XSSFSheet sh = getSheet();
+        PackagePart sheetPart = sh.getPackagePart();
+        long shapeId = newShapeId();
+
+        // add reference to OLE part
+        PackagePartName olePN;
+        try {
+            olePN = PackagingURIHelper.createPartName( "/xl/embeddings/oleObject"+storageId+".bin" );
+        } catch (InvalidFormatException e) {
+            throw new POIXMLException(e);
+        }
+        PackageRelationship olePR = sheetPart.addRelationship( olePN, TargetMode.INTERNAL, POIXMLDocument.OLE_OBJECT_REL_TYPE );
+
+        // add reference to image part
+        XSSFPictureData imgPD = sh.getWorkbook().getAllPictures().get(pictureIndex);
+        PackagePartName imgPN = imgPD.getPackagePart().getPartName();
+        PackageRelationship imgSheetPR = sheetPart.addRelationship( imgPN, TargetMode.INTERNAL, PackageRelationshipTypes.IMAGE_PART );
+        PackageRelationship imgDrawPR = getPackagePart().addRelationship( imgPN, TargetMode.INTERNAL, PackageRelationshipTypes.IMAGE_PART );
+
+
+        // add OLE part metadata to sheet
+        CTWorksheet cwb = sh.getCTWorksheet();
+        CTOleObjects oo = cwb.isSetOleObjects() ? cwb.getOleObjects() : cwb.addNewOleObjects();
+
+        CTOleObject ole1 = oo.addNewOleObject();
+        ole1.setProgId("Package");
+        ole1.setShapeId(shapeId);
+        ole1.setId(olePR.getId());
+
+        XmlCursor cur1 = ole1.newCursor();
+        cur1.toEndToken();
+        cur1.beginElement("objectPr", XSSFRelation.NS_SPREADSHEETML);
+        cur1.insertAttributeWithValue("id", PackageRelationshipTypes.CORE_PROPERTIES_ECMA376_NS, imgSheetPR.getId());
+        cur1.insertAttributeWithValue("defaultSize", "0");
+        cur1.beginElement("anchor", XSSFRelation.NS_SPREADSHEETML);
+        cur1.insertAttributeWithValue("moveWithCells", "1");
+
+        CTTwoCellAnchor ctAnchor = createTwoCellAnchor((XSSFClientAnchor)anchor);
+        
+        XmlCursor cur2 = ctAnchor.newCursor();
+        cur2.copyXmlContents(cur1);
+        cur2.dispose();
+
+        cur1.toParent();
+        cur1.toFirstChild();
+        cur1.setName(new QName(XSSFRelation.NS_SPREADSHEETML, "from"));
+        cur1.toNextSibling();
+        cur1.setName(new QName(XSSFRelation.NS_SPREADSHEETML, "to"));
+
+        cur1.dispose();
+
+        // add a new shape and link OLE & image part
+        CTShape ctShape = ctAnchor.addNewSp();
+        ctShape.set(XSSFObjectData.prototype());
+        ctShape.getSpPr().setXfrm(createXfrm((XSSFClientAnchor)anchor));
+        
+        // workaround for not having the vmlDrawing filled
+        CTBlipFillProperties blipFill = ctShape.getSpPr().addNewBlipFill();
+        blipFill.addNewBlip().setEmbed(imgDrawPR.getId());
+        blipFill.addNewStretch().addNewFillRect();
+        
+        CTNonVisualDrawingProps cNvPr = ctShape.getNvSpPr().getCNvPr();
+        cNvPr.setId(shapeId);
+
+        XmlCursor extCur = cNvPr.getExtLst().getExtArray(0).newCursor();
+        extCur.toFirstChild();
+        extCur.setAttributeText(new QName("spid"), "_x0000_s"+shapeId);
+        extCur.dispose();
+
+        XSSFObjectData shape = new XSSFObjectData(this, ctShape);
+        shape.anchor = (XSSFClientAnchor)anchor;
+
+        return shape;
+    }
+
+
     /**
      * Returns all charts in this drawing.
      */
@@ -410,7 +500,7 @@ public final class XSSFDrawing extends POIXMLDocumentPart implements Drawing<XSS
         CTPoint2D off = xfrm.addNewOff();
         off.setX(anchor.getDx1());
         off.setY(anchor.getDy1());
-        XSSFSheet sheet = (XSSFSheet)getParent();
+        XSSFSheet sheet = getSheet();
         double widthPx = 0;
         for (int col=anchor.getCol1(); col<anchor.getCol2(); col++) {
             widthPx += sheet.getColumnWidthInPixels(col);
@@ -424,11 +514,11 @@ public final class XSSFDrawing extends POIXMLDocumentPart implements Drawing<XSS
         CTPositiveSize2D ext = xfrm.addNewExt();
         ext.setCx(width - anchor.getDx1() + anchor.getDx2());
         ext.setCy(height - anchor.getDy1() + anchor.getDy2());
-        
+
         // TODO: handle vflip/hflip
         return xfrm;
     }
-    
+
     private long newShapeId(){
         return drawing.sizeOfTwoCellAnchorArray() + 1;
     }
@@ -462,7 +552,7 @@ public final class XSSFDrawing extends POIXMLDocumentPart implements Drawing<XSS
         }
         return lst;
     }
-    
+
     private void addShapes(XmlCursor cur, List<XSSFShape> lst) {
         try {
             do {
@@ -470,7 +560,7 @@ public final class XSSFDrawing extends POIXMLDocumentPart implements Drawing<XSS
                 if (cur.toFirstChild()) {
                     do {
                         XmlObject obj = cur.getObject();
-    
+
                         XSSFShape shape;
                         if (obj instanceof CTMarker) {
                             // ignore anchor elements
@@ -480,7 +570,7 @@ public final class XSSFDrawing extends POIXMLDocumentPart implements Drawing<XSS
                         } else if(obj instanceof CTConnector) {
                             shape = new XSSFConnector(this, (CTConnector)obj) ;
                         } else if(obj instanceof CTShape) {
-                            shape = hasOleLink(obj) 
+                            shape = hasOleLink(obj)
                                 ? new XSSFObjectData(this, (CTShape)obj)
                                 : new XSSFSimpleShape(this, (CTShape)obj) ;
                         } else if(obj instanceof CTGraphicalObjectFrame) {
@@ -492,7 +582,7 @@ public final class XSSFDrawing extends POIXMLDocumentPart implements Drawing<XSS
                                     + "this unlinks the returned Shapes from the underlying xml content, "
                                     + "so those shapes can't be used to modify the drawing, "
                                     + "i.e. modifications will be ignored!");
-                            
+
                             // XmlAnyTypeImpl is returned for AlternateContent parts, which might contain a CTDrawing
                             cur.push();
                             cur.toFirstChild();
@@ -522,7 +612,7 @@ public final class XSSFDrawing extends POIXMLDocumentPart implements Drawing<XSS
                         assert(shape != null);
                         shape.anchor = getAnchorFromParent(obj);
                         lst.add(shape);
-                        
+
                     } while (cur.toNextSibling());
                 }
                 cur.pop();
@@ -575,4 +665,12 @@ public final class XSSFDrawing extends POIXMLDocumentPart implements Drawing<XSS
     public Iterator<XSSFShape> iterator() {
         return getShapes().iterator();
     }
+
+    /**
+     * @return the sheet associated with the drawing
+     */
+    public XSSFSheet getSheet() {
+        return (XSSFSheet)getParent();
+    }
+
 }
index ab51df81eec4411d03666e1543918f426b879f58..2221979411f992a46fa5739140028f4eccabae63 100644 (file)
@@ -36,7 +36,17 @@ import org.apache.poi.util.IOUtils;
 import org.apache.poi.util.POILogFactory;
 import org.apache.poi.util.POILogger;
 import org.apache.xmlbeans.XmlCursor;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTNonVisualDrawingProps;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTOfficeArtExtension;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTOfficeArtExtensionList;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTPoint2D;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTPositiveSize2D;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTPresetGeometry2D;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTShapeProperties;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTTransform2D;
+import org.openxmlformats.schemas.drawingml.x2006.main.STShapeType;
 import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTShape;
+import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTShapeNonVisual;
 import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTOleObject;
 
 /**
@@ -56,16 +66,57 @@ public class XSSFObjectData extends XSSFSimpleShape implements ObjectData {
         super(drawing, ctShape);
     }
 
+    /**
+     * Prototype with the default structure of a new auto-shape.
+     */
     /**
      * Prototype with the default structure of a new auto-shape.
      */
     protected static CTShape prototype() {
+        final String drawNS = "http://schemas.microsoft.com/office/drawing/2010/main";
+
         if(prototype == null) {
-            prototype = XSSFSimpleShape.prototype();
+            CTShape shape = CTShape.Factory.newInstance();
+
+            CTShapeNonVisual nv = shape.addNewNvSpPr();
+            CTNonVisualDrawingProps nvp = nv.addNewCNvPr();
+            nvp.setId(1);
+            nvp.setName("Shape 1");
+//            nvp.setHidden(true);
+            CTOfficeArtExtensionList extLst = nvp.addNewExtLst();
+            // https://msdn.microsoft.com/en-us/library/dd911027(v=office.12).aspx
+            CTOfficeArtExtension ext = extLst.addNewExt();
+            ext.setUri("{63B3BB69-23CF-44E3-9099-C40C66FF867C}");
+            XmlCursor cur = ext.newCursor();
+            cur.toEndToken();
+            cur.beginElement(new QName(drawNS, "compatExt", "a14"));
+            cur.insertNamespace("a14", drawNS);
+            cur.insertAttributeWithValue("spid", "_x0000_s1");
+            cur.dispose();
+            
+            nv.addNewCNvSpPr();
+
+            CTShapeProperties sp = shape.addNewSpPr();
+            CTTransform2D t2d = sp.addNewXfrm();
+            CTPositiveSize2D p1 = t2d.addNewExt();
+            p1.setCx(0);
+            p1.setCy(0);
+            CTPoint2D p2 = t2d.addNewOff();
+            p2.setX(0);
+            p2.setY(0);
+
+            CTPresetGeometry2D geom = sp.addNewPrstGeom();
+            geom.setPrst(STShapeType.RECT);
+            geom.addNewAvLst();
+
+            prototype = shape;
         }
         return prototype;
     }
 
+    
+    
+    
     @Override
     public String getOLE2ClassName() {
         return getOleObject().getProgId();
index c6df00599d64e8b3b3faebcf73b0345a2302f540..6aa2064c38b734d5dcbcab7055f3b7cada7da9e4 100644 (file)
@@ -47,6 +47,7 @@ import org.apache.poi.POIXMLDocument;
 import org.apache.poi.POIXMLDocumentPart;
 import org.apache.poi.POIXMLException;
 import org.apache.poi.POIXMLProperties;
+import org.apache.poi.hpsf.ClassID;
 import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
 import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
 import org.apache.poi.openxml4j.opc.OPCPackage;
@@ -58,6 +59,9 @@ import org.apache.poi.openxml4j.opc.PackageRelationshipTypes;
 import org.apache.poi.openxml4j.opc.PackagingURIHelper;
 import org.apache.poi.openxml4j.opc.TargetMode;
 import org.apache.poi.poifs.crypt.HashAlgorithm;
+import org.apache.poi.poifs.filesystem.DirectoryNode;
+import org.apache.poi.poifs.filesystem.Ole10Native;
+import org.apache.poi.poifs.filesystem.POIFSFileSystem;
 import org.apache.poi.ss.SpreadsheetVersion;
 import org.apache.poi.ss.formula.SheetNameFormatter;
 import org.apache.poi.ss.formula.udf.AggregatingUDFFinder;
@@ -2437,4 +2441,41 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook {
         }
         return null;
     }
+
+    @Override
+    public int addOlePackage(byte[] oleData, String label, String fileName, String command)
+    throws IOException {
+        // find an unused part name
+        OPCPackage opc = getPackage();
+        PackagePartName pnOLE;
+        int oleId=0;
+        do {
+            try {
+                pnOLE = PackagingURIHelper.createPartName( "/xl/embeddings/oleObject"+(++oleId)+".bin" );
+            } catch (InvalidFormatException e) {
+                throw new IOException("ole object name not recognized", e);
+            }
+        } while (opc.containPart(pnOLE));
+
+        PackagePart pp = opc.createPart( pnOLE, "application/vnd.openxmlformats-officedocument.oleObject" );
+        
+        Ole10Native ole10 = new Ole10Native(label, fileName, command, oleData);
+
+        ByteArrayOutputStream bos = new ByteArrayOutputStream(oleData.length+500);
+        ole10.writeOut(bos);
+        
+        POIFSFileSystem poifs = new POIFSFileSystem();
+        DirectoryNode root = poifs.getRoot();
+        root.createDocument(Ole10Native.OLE10_NATIVE, new ByteArrayInputStream(bos.toByteArray()));
+        root.setStorageClsid(ClassID.OLE10_PACKAGE);
+
+        // TODO: generate CombObj stream
+
+        OutputStream os = pp.getOutputStream();
+        poifs.writeFilesystem(os);
+        os.close();
+        poifs.close();
+
+        return oleId;
+    }
 }
diff --git a/src/ooxml/testcases/org/apache/poi/ss/usermodel/TestEmbedOLEPackage.java b/src/ooxml/testcases/org/apache/poi/ss/usermodel/TestEmbedOLEPackage.java
new file mode 100644 (file)
index 0000000..0664d64
--- /dev/null
@@ -0,0 +1,125 @@
+/* ====================================================================
+   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.ss.usermodel;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+
+import java.awt.image.BufferedImage;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.net.URL;
+
+import javax.imageio.ImageIO;
+
+import org.apache.poi.hssf.HSSFTestDataSamples;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.sl.usermodel.AutoShape;
+import org.apache.poi.sl.usermodel.ShapeType;
+import org.apache.poi.sl.usermodel.Slide;
+import org.apache.poi.sl.usermodel.SlideShow;
+import org.apache.poi.ss.extractor.EmbeddedData;
+import org.apache.poi.ss.extractor.EmbeddedExtractor;
+import org.apache.poi.xslf.usermodel.XMLSlideShow;
+import org.apache.poi.xssf.XSSFTestDataSamples;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+import org.junit.Test;
+
+public class TestEmbedOLEPackage {
+    @Test
+    public void embedXSSF() throws IOException {
+        Workbook wb1 = new XSSFWorkbook();
+        Sheet sh = wb1.createSheet();
+        int picIdx = wb1.addPicture(getSamplePng(), Workbook.PICTURE_TYPE_PNG);
+        byte samplePPTX[] = getSamplePPT(true);
+        int oleIdx = wb1.addOlePackage(samplePPTX, "dummy.pptx", "dummy.pptx", "dummy.pptx");
+
+        Drawing<?> pat = sh.createDrawingPatriarch();
+        ClientAnchor anchor = pat.createAnchor(0, 0, 0, 0, 1, 1, 3, 6);
+        pat.createObjectData(anchor, oleIdx, picIdx);
+
+        Workbook wb2 = XSSFTestDataSamples.writeOutAndReadBack(wb1);
+
+        pat = wb2.getSheetAt(0).getDrawingPatriarch();
+        assertTrue(pat.iterator().next() instanceof ObjectData);
+
+        EmbeddedExtractor ee = new EmbeddedExtractor();
+        EmbeddedData ed = ee.extractAll(wb2.getSheetAt(0)).get(0);
+        assertArrayEquals(samplePPTX, ed.getEmbeddedData());
+
+        wb2.close();
+        wb1.close();
+    }
+
+    @Test
+    public void embedHSSF() throws IOException {
+        try {
+            Class.forName("org.apache.poi.hslf.usermodel.HSLFSlideShow");
+        } catch (Exception e) {
+            assumeTrue(false);
+        }
+
+        Workbook wb1 = new HSSFWorkbook();
+        Sheet sh = wb1.createSheet();
+        int picIdx = wb1.addPicture(getSamplePng(), Workbook.PICTURE_TYPE_PNG);
+        byte samplePPT[] = getSamplePPT(false);
+        int oleIdx = wb1.addOlePackage(samplePPT, "dummy.ppt", "dummy.ppt", "dummy.ppt");
+
+        Drawing<?> pat = sh.createDrawingPatriarch();
+        ClientAnchor anchor = pat.createAnchor(0, 0, 0, 0, 1, 1, 3, 6);
+        pat.createObjectData(anchor, oleIdx, picIdx);
+
+        Workbook wb2 = HSSFTestDataSamples.writeOutAndReadBack((HSSFWorkbook)wb1);
+
+        pat = wb2.getSheetAt(0).getDrawingPatriarch();
+        assertTrue(pat.iterator().next() instanceof ObjectData);
+
+        EmbeddedExtractor ee = new EmbeddedExtractor();
+        EmbeddedData ed = ee.extractAll(wb2.getSheetAt(0)).get(0);
+        assertArrayEquals(samplePPT, ed.getEmbeddedData());
+
+        wb2.close();
+        wb1.close();
+    }
+
+    static byte[] getSamplePng() throws IOException {
+        ClassLoader cl = Thread.currentThread().getContextClassLoader();
+        URL imgUrl = cl.getResource("javax/swing/plaf/metal/icons/ocean/directory.gif");
+        BufferedImage img = ImageIO.read(imgUrl);
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        ImageIO.write(img, "PNG", bos);
+        return bos.toByteArray();
+    }
+
+    static byte[] getSamplePPT(boolean ooxml) throws IOException {
+        SlideShow<?,?> ppt = (ooxml) ? new XMLSlideShow() : new org.apache.poi.hslf.usermodel.HSLFSlideShow();
+        Slide<?,?> slide = ppt.createSlide();
+
+        AutoShape<?,?> sh1 = slide.createAutoShape();
+        sh1.setShapeType(ShapeType.STAR_32);
+        sh1.setAnchor(new java.awt.Rectangle(50, 50, 100, 200));
+        sh1.setFillColor(java.awt.Color.red);
+
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        ppt.write(bos);
+        ppt.close();
+
+        return bos.toByteArray();
+    }
+}