diff options
author | Andreas Beeker <kiwiwings@apache.org> | 2017-01-15 02:04:57 +0000 |
---|---|---|
committer | Andreas Beeker <kiwiwings@apache.org> | 2017-01-15 02:04:57 +0000 |
commit | 9c2820add6a681f5775601fccb8270b85cd72920 (patch) | |
tree | d3131c2107184047d278d6eba14c2fc4fe445ac3 | |
parent | 4ce3e5def53c893012d6ba709e9beff95a149d48 (diff) | |
download | poi-9c2820add6a681f5775601fccb8270b85cd72920.tar.gz poi-9c2820add6a681f5775601fccb8270b85cd72920.zip |
#60586 - Support embedding OLE1.0 package in XSSF / SS Common
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1778869 13f79535-47bb-0310-9956-ffa450edef68
10 files changed, 409 insertions, 37 deletions
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFPatriarch.java b/src/java/org/apache/poi/hssf/usermodel/HSSFPatriarch.java index cc6805c5b8..d7baeb58e0 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFPatriarch.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFPatriarch.java @@ -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"); diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java b/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java index 2f3be03de9..55382236e0 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java @@ -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 diff --git a/src/java/org/apache/poi/ss/usermodel/Drawing.java b/src/java/org/apache/poi/ss/usermodel/Drawing.java index e2d55d30a1..13a830ef98 100644 --- a/src/java/org/apache/poi/ss/usermodel/Drawing.java +++ b/src/java/org/apache/poi/ss/usermodel/Drawing.java @@ -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); } diff --git a/src/java/org/apache/poi/ss/usermodel/Workbook.java b/src/java/org/apache/poi/ss/usermodel/Workbook.java index f5043b727f..5273423de0 100644 --- a/src/java/org/apache/poi/ss/usermodel/Workbook.java +++ b/src/java/org/apache/poi/ss/usermodel/Workbook.java @@ -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; } diff --git a/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFDrawing.java b/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFDrawing.java index 17f1871025..3a645f7676 100644 --- a/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFDrawing.java +++ b/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFDrawing.java @@ -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; @@ -63,8 +64,14 @@ public class SXSSFDrawing implements Drawing<XSSFShape> { } @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(); } + } diff --git a/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFWorkbook.java b/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFWorkbook.java index a11bef3100..611f16bc0c 100644 --- a/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFWorkbook.java +++ b/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFWorkbook.java @@ -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 } diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFDrawing.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFDrawing.java index 5c7395ed3e..35f71381c3 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFDrawing.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFDrawing.java @@ -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(); + } + } diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFObjectData.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFObjectData.java index ab51df81ee..2221979411 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFObjectData.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFObjectData.java @@ -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; /** @@ -59,13 +69,54 @@ public class XSSFObjectData extends XSSFSimpleShape implements ObjectData { /** * 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(); diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java index c6df00599d..6aa2064c38 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java @@ -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 index 0000000000..0664d64b10 --- /dev/null +++ b/src/ooxml/testcases/org/apache/poi/ss/usermodel/TestEmbedOLEPackage.java @@ -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(); + } +} |