From 95bbbe56781a39687064c8dc7394fa4c113e031f Mon Sep 17 00:00:00 2001 From: Andreas Beeker Date: Sun, 31 Dec 2017 01:14:08 +0000 Subject: [PATCH] #61797 - Embed Excel / Ole objects into powerpoint git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1819710 13f79535-47bb-0310-9956-ffa450edef68 --- .../poi/hslf/examples/DataExtraction.java | 14 +- .../apache/poi/sl/usermodel/ObjectData.java | 102 ++++++ .../poi/sl/usermodel/ObjectMetaData.java | 96 ++++++ .../apache/poi/sl/usermodel/ObjectShape.java | 152 +++++++++ .../poi/sl/usermodel/ShapeContainer.java | 7 + .../poi/xslf/usermodel/XSLFDrawing.java | 24 +- .../poi/xslf/usermodel/XSLFGraphicFrame.java | 18 +- .../poi/xslf/usermodel/XSLFGroupShape.java | 25 ++ .../poi/xslf/usermodel/XSLFObjectData.java | 93 +++++ .../poi/xslf/usermodel/XSLFObjectShape.java | 323 ++++++++++++++++++ .../poi/xslf/usermodel/XSLFRelation.java | 8 + .../apache/poi/xslf/usermodel/XSLFSheet.java | 37 +- .../apache/poi/xslf/usermodel/XSLFTable.java | 37 +- .../org/apache/poi/sl/TestOleShape.java | 220 ++++++++++++ .../hslf/extractor/PowerPointExtractor.java | 10 +- .../org/apache/poi/hslf/record/ExEmbed.java | 81 +++-- .../poi/hslf/usermodel/HSLFGroupShape.java | 13 +- .../poi/hslf/usermodel/HSLFObjectData.java | 44 ++- .../HSLFObjectShape.java} | 141 +++++--- .../hslf/usermodel/HSLFShapeContainer.java | 3 +- .../poi/hslf/usermodel/HSLFShapeFactory.java | 5 +- .../apache/poi/hslf/usermodel/HSLFSheet.java | 11 + .../poi/hslf/extractor/TestExtractor.java | 12 +- .../poi/hslf/model/TestOleEmbedding.java | 19 +- .../apache/poi/hslf/usermodel/TestBugs.java | 2 +- 25 files changed, 1355 insertions(+), 142 deletions(-) create mode 100644 src/java/org/apache/poi/sl/usermodel/ObjectData.java create mode 100644 src/java/org/apache/poi/sl/usermodel/ObjectMetaData.java create mode 100644 src/java/org/apache/poi/sl/usermodel/ObjectShape.java create mode 100644 src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFObjectData.java create mode 100644 src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFObjectShape.java create mode 100644 src/ooxml/testcases/org/apache/poi/sl/TestOleShape.java rename src/scratchpad/src/org/apache/poi/hslf/{model/OLEShape.java => usermodel/HSLFObjectShape.java} (60%) diff --git a/src/examples/src/org/apache/poi/hslf/examples/DataExtraction.java b/src/examples/src/org/apache/poi/hslf/examples/DataExtraction.java index 3cc995d15..97b9cd3d1 100644 --- a/src/examples/src/org/apache/poi/hslf/examples/DataExtraction.java +++ b/src/examples/src/org/apache/poi/hslf/examples/DataExtraction.java @@ -21,8 +21,8 @@ import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStream; -import org.apache.poi.hslf.model.OLEShape; import org.apache.poi.hslf.usermodel.HSLFObjectData; +import org.apache.poi.hslf.usermodel.HSLFObjectShape; import org.apache.poi.hslf.usermodel.HSLFPictureData; import org.apache.poi.hslf.usermodel.HSLFPictureShape; import org.apache.poi.hslf.usermodel.HSLFShape; @@ -67,19 +67,19 @@ public final class DataExtraction { for (HSLFSlide slide : ppt.getSlides()) { //extract embedded OLE documents for (HSLFShape shape : slide.getShapes()) { - if (shape instanceof OLEShape) { + if (shape instanceof HSLFObjectShape) { oleIdx++; - OLEShape ole = (OLEShape) shape; + HSLFObjectShape ole = (HSLFObjectShape) shape; HSLFObjectData data = ole.getObjectData(); String name = ole.getInstanceName(); if ("Worksheet".equals(name)) { //read xls @SuppressWarnings({ "unused", "resource" }) - HSSFWorkbook wb = new HSSFWorkbook(data.getData()); + HSSFWorkbook wb = new HSSFWorkbook(data.getInputStream()); } else if ("Document".equals(name)) { - HWPFDocument doc = new HWPFDocument(data.getData()); + HWPFDocument doc = new HWPFDocument(data.getInputStream()); //read the word document Range r = doc.getRange(); for(int k = 0; k < r.numParagraphs(); k++) { @@ -93,8 +93,8 @@ public final class DataExtraction { out.close(); doc.close(); } else { - FileOutputStream out = new FileOutputStream(ole.getProgID() + "-"+(oleIdx+1)+".dat"); - InputStream dis = data.getData(); + FileOutputStream out = new FileOutputStream(ole.getProgId() + "-"+(oleIdx+1)+".dat"); + InputStream dis = data.getInputStream(); byte[] chunk = new byte[2048]; int count; while ((count = dis.read(chunk)) >= 0) { diff --git a/src/java/org/apache/poi/sl/usermodel/ObjectData.java b/src/java/org/apache/poi/sl/usermodel/ObjectData.java new file mode 100644 index 000000000..db30a9b24 --- /dev/null +++ b/src/java/org/apache/poi/sl/usermodel/ObjectData.java @@ -0,0 +1,102 @@ +/* ==================================================================== + 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.sl.usermodel; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import org.apache.poi.poifs.filesystem.DirectoryEntry; +import org.apache.poi.poifs.filesystem.FileMagic; +import org.apache.poi.poifs.filesystem.POIFSFileSystem; +import org.apache.poi.util.IOUtils; +import org.apache.poi.util.POILogFactory; +import org.apache.poi.util.POILogger; + +/** + * Common interface for OLE shapes, i.e. shapes linked to embedded documents + * + * @since POI 4.0.0 + */ +public interface ObjectData { + + /** + * Gets an input stream which returns the binary of the embedded data. + * + * @return the input stream which will contain the binary of the embedded data. + */ + InputStream getInputStream() throws IOException; + + + /** + * @return the object data as stream (for writing) + */ + OutputStream getOutputStream() throws IOException; + + /** + * Convenience method to get the embedded data as byte array. + * + * @return the embedded data. + */ + default byte[] getBytes() throws IOException { + try (InputStream is = getInputStream()) { + return IOUtils.toByteArray(is); + } + } + + /** + * @return does this ObjectData have an associated POIFS Directory Entry? + * (Not all do, those that don't have a data portion) + */ + default boolean hasDirectoryEntry() { + try (final InputStream is = FileMagic.prepareToCheckMagic(getInputStream())) { + FileMagic fm = FileMagic.valueOf(is); + return fm == FileMagic.OLE2; + } catch (IOException e) { + POILogger LOG = POILogFactory.getLogger(ObjectData.class); + LOG.log(POILogger.WARN, "Can't determine filemagic of ole stream", e); + return false; + } + } + + /** + * Gets the object data. Only call for ones that have + * data though. See {@link #hasDirectoryEntry()}. + * The caller has to close the corresponding POIFSFileSystem + * + * @return the object data as an OLE2 directory. + * @throws IOException if there was an error reading the data. + */ + @SuppressWarnings("resource") + default DirectoryEntry getDirectory() throws IOException { + try (final InputStream is = getInputStream()) { + return new POIFSFileSystem(is).getRoot(); + } + } + + /** + * @return the OLE2 Class Name of the object + */ + String getOLE2ClassName(); + + /** + * @return a filename suggestion - inspecting/interpreting the Directory object probably gives a better result + */ + String getFileName(); + +} diff --git a/src/java/org/apache/poi/sl/usermodel/ObjectMetaData.java b/src/java/org/apache/poi/sl/usermodel/ObjectMetaData.java new file mode 100644 index 000000000..1851b0c8b --- /dev/null +++ b/src/java/org/apache/poi/sl/usermodel/ObjectMetaData.java @@ -0,0 +1,96 @@ +/* ==================================================================== + 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.sl.usermodel; + +import org.apache.poi.hpsf.ClassID; +import org.apache.poi.hpsf.ClassIDPredefined; +import org.apache.poi.util.Beta; + +@Beta +public interface ObjectMetaData { + enum Application { + EXCEL_V8("Worksheet", "Excel.Sheet.8", "Package", ClassIDPredefined.EXCEL_V8), + EXCEL_V12("Worksheet", "Excel.Sheet.12", "Package", ClassIDPredefined.EXCEL_V12), + WORD_V8("Document", "Word.Document.8", "Package", ClassIDPredefined.WORD_V8), + WORD_V12("Document", "Word.Document.12", "Package", ClassIDPredefined.WORD_V12), + PDF("PDF", "AcroExch.Document", "Contents", ClassIDPredefined.PDF), + CUSTOM(null, null, null, null); + + String objectName; + String progId; + String oleEntry; + ClassID classId; + + Application(String objectName, String progId, String oleEntry, ClassIDPredefined classId) { + this.objectName = objectName; + this.progId = progId; + this.classId = (classId == null) ? null : classId.getClassID(); + this.oleEntry = oleEntry; + } + + public static Application lookup(String progId) { + for (Application a : values()) { + if (a.progId != null && a.progId.equals(progId)) { + return a; + } + } + return null; + } + + + public ObjectMetaData getMetaData() { + return new ObjectMetaData() { + public String getObjectName() { + return objectName; + } + + public String getProgId() { + return progId; + } + + public String getOleEntry() { + return oleEntry; + } + + public ClassID getClassID() { + return classId; + } + }; + } + } + + /** + * @return the name of the OLE shape + */ + String getObjectName(); + + /** + * @return the program id assigned to the OLE container application + */ + String getProgId(); + + /** + * @return the storage classid of the OLE entry + */ + ClassID getClassID(); + + /** + * @return the name of the OLE entry inside the oleObject#.bin + */ + String getOleEntry(); +} diff --git a/src/java/org/apache/poi/sl/usermodel/ObjectShape.java b/src/java/org/apache/poi/sl/usermodel/ObjectShape.java new file mode 100644 index 000000000..c7d421950 --- /dev/null +++ b/src/java/org/apache/poi/sl/usermodel/ObjectShape.java @@ -0,0 +1,152 @@ +/* ==================================================================== + 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.sl.usermodel; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import org.apache.poi.poifs.filesystem.DirectoryNode; +import org.apache.poi.poifs.filesystem.FileMagic; +import org.apache.poi.poifs.filesystem.POIFSFileSystem; +import org.apache.poi.sl.usermodel.ObjectMetaData.Application; +import org.apache.poi.util.IOUtils; + +/** + * An shape which references an embedded OLE object + * + * @since POI 4.0.0 + */ +public interface ObjectShape< + S extends Shape, + P extends TextParagraph +> extends Shape, PlaceableShape { + + /** + * Returns the picture data for this picture. + * + * @return the picture data for this picture. + */ + PictureData getPictureData(); + + /** + * Returns the ProgID that stores the OLE Programmatic Identifier. + * A ProgID is a string that uniquely identifies a given object, for example, + * "Word.Document.8" or "Excel.Sheet.8". + * + * @return the ProgID + */ + String getProgId(); + + /** + * Returns the full name of the embedded object, + * e.g. "Microsoft Word Document" or "Microsoft Office Excel Worksheet". + * + * @return the full name of the embedded object + */ + String getFullName(); + + /** + * Updates the ole data. If there wasn't an object registered before, a new + * ole embedding is registered in the parent slideshow.

+ * + * For HSLF this needs to be a {@link POIFSFileSystem} stream. + * + * @param application a preset application enum + * @param metaData or a custom metaData object, can be {@code null} if the application has been set + * + * @return an {@link OutputStream} which receives the new data, the data will be persisted on {@code close()} + * + * @throws IOException if the linked object data couldn't be found or a new object data couldn't be initialized + */ + OutputStream updateObjectData(ObjectMetaData.Application application, ObjectMetaData metaData) throws IOException; + + /** + * Reads the ole data as stream - the application specific stream is served + * The {@link #readObjectDataRaw() raw data} serves the outer/wrapped object, which is usually a + * {@link POIFSFileSystem} stream, whereas this method return the unwrapped entry + * + * @return an {@link InputStream} which serves the object data + * + * @throws IOException if the linked object data couldn't be found + */ + default InputStream readObjectData() throws IOException { + final String progId = getProgId(); + if (progId == null) { + throw new IllegalStateException( + "Ole object hasn't been initialized or provided in the source xml. " + + "use updateObjectData() first or check the corresponding slideXXX.xml"); + } + + final Application app = Application.lookup(progId); + + final ByteArrayOutputStream bos = new ByteArrayOutputStream(50000); + try (final InputStream is = FileMagic.prepareToCheckMagic(readObjectDataRaw())) { + final FileMagic fm = FileMagic.valueOf(is); + if (fm == FileMagic.OLE2) { + try (final POIFSFileSystem poifs = new POIFSFileSystem(is)) { + String[] names = { + (app == null) ? null : app.getMetaData().getOleEntry(), + // fallback to the usual suspects + "Package", + "Contents", + "CONTENTS", + "CONTENTSV30", + }; + final DirectoryNode root = poifs.getRoot(); + String entryName = null; + for (String n : names) { + if (root.hasEntry(n)) { + entryName = n; + break; + } + } + if (entryName == null) { + poifs.writeFilesystem(bos); + } else { + try (final InputStream is2 = poifs.createDocumentInputStream(entryName)) { + IOUtils.copy(is2, bos); + } + } + } + } else { + IOUtils.copy(is, bos); + } + } + + return new ByteArrayInputStream(bos.toByteArray()); + } + + /** + * Convenience method to return the raw data as {@code InputStream} + * + * @return the raw data stream + * + * @throws IOException if the data couldn't be retrieved + */ + default InputStream readObjectDataRaw() throws IOException { + return getObjectData().getInputStream(); + } + + /** + * @return the data object + */ + ObjectData getObjectData(); +} diff --git a/src/java/org/apache/poi/sl/usermodel/ShapeContainer.java b/src/java/org/apache/poi/sl/usermodel/ShapeContainer.java index ba02e4b05..7706828c5 100644 --- a/src/java/org/apache/poi/sl/usermodel/ShapeContainer.java +++ b/src/java/org/apache/poi/sl/usermodel/ShapeContainer.java @@ -84,4 +84,11 @@ public interface ShapeContainer< * @param numCols the number of columns */ TableShape createTable(int numRows, int numCols); + + /** + * Create a new OLE object shape with the given pictureData as preview image + * + * @param pictureData the preview image + */ + ObjectShape createOleShape(PictureData pictureData); } 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 058507c01..42e375ae0 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFDrawing.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFDrawing.java @@ -18,20 +18,30 @@ package org.apache.poi.xslf.usermodel; import java.awt.Color; import java.awt.geom.Rectangle2D; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import org.apache.poi.POIXMLDocumentPart.RelationPart; +import org.apache.poi.hpsf.ClassID; +import org.apache.poi.openxml4j.exceptions.InvalidFormatException; +import org.apache.poi.openxml4j.opc.OPCPackage; +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.PackagingURIHelper; +import org.apache.poi.poifs.filesystem.POIFSFileSystem; import org.apache.poi.util.Beta; import org.apache.xmlbeans.XmlObject; import org.openxmlformats.schemas.drawingml.x2006.main.CTNonVisualDrawingProps; import org.openxmlformats.schemas.presentationml.x2006.main.CTConnector; import org.openxmlformats.schemas.presentationml.x2006.main.CTGraphicalObjectFrame; import org.openxmlformats.schemas.presentationml.x2006.main.CTGroupShape; +import org.openxmlformats.schemas.presentationml.x2006.main.CTOleObject; import org.openxmlformats.schemas.presentationml.x2006.main.CTPicture; import org.openxmlformats.schemas.presentationml.x2006.main.CTShape; -/** - * @author Yegor Kozlov - */ @Beta public class XSLFDrawing { private XSLFSheet _sheet; @@ -110,4 +120,12 @@ public class XSLFDrawing { shape.setAnchor(new Rectangle2D.Double()); return shape; } + + public XSLFObjectShape createOleShape(String pictureRel) { + CTGraphicalObjectFrame obj = _spTree.addNewGraphicFrame(); + obj.set(XSLFObjectShape.prototype(_shapeId++, pictureRel)); + XSLFObjectShape shape = new XSLFObjectShape(obj, _sheet); + shape.setAnchor(new Rectangle2D.Double()); + return shape; + } } diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFGraphicFrame.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFGraphicFrame.java index f08e1d1de..b2cf29e43 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFGraphicFrame.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFGraphicFrame.java @@ -37,6 +37,7 @@ import org.apache.poi.util.Units; import org.apache.xmlbeans.XmlCursor; import org.apache.xmlbeans.XmlException; import org.apache.xmlbeans.XmlObject; +import org.openxmlformats.schemas.drawingml.x2006.main.CTGraphicalObject; import org.openxmlformats.schemas.drawingml.x2006.main.CTGraphicalObjectData; import org.openxmlformats.schemas.drawingml.x2006.main.CTPoint2D; import org.openxmlformats.schemas.drawingml.x2006.main.CTPositiveSize2D; @@ -86,13 +87,24 @@ public class XSLFGraphicFrame extends XSLFShape implements GraphicalFrame { return sh; } + @Override + public XSLFObjectShape createOleShape(PictureData pictureData) { + if (!(pictureData instanceof XSLFPictureData)) { + throw new IllegalArgumentException("pictureData needs to be of type XSLFPictureData"); + } + XSLFPictureData xPictureData = (XSLFPictureData)pictureData; + PackagePart pic = xPictureData.getPackagePart(); + + PackageRelationship rel = getSheet().getPackagePart().addRelationship( + pic.getPartName(), TargetMode.INTERNAL, XSLFRelation.IMAGES.getRelation()); + + XSLFObjectShape sh = getDrawing().createOleShape(rel.getId()); + CTOleObject oleObj = sh.getCTOleObject(); + Dimension dim = pictureData.getImageDimension(); + oleObj.setImgW(Units.toEMU(dim.getWidth())); + oleObj.setImgH(Units.toEMU(dim.getHeight())); + + + getShapes().add(sh); + sh.setParent(this); + return sh; + } + public XSLFTable createTable(){ XSLFTable sh = getDrawing().createTable(); _shapes.add(sh); diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFObjectData.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFObjectData.java new file mode 100644 index 000000000..1532dae83 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFObjectData.java @@ -0,0 +1,93 @@ +/* + * ==================================================================== + * 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.xslf.usermodel; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import org.apache.poi.POIXMLDocumentPart; +import org.apache.poi.openxml4j.opc.PackagePart; +import org.apache.poi.poifs.filesystem.DirectoryEntry; +import org.apache.poi.sl.usermodel.ObjectData; +import org.apache.poi.util.Beta; + +/** + * An XSLFOleData instance holds the ole binary stream/object + */ +@Beta +public final class XSLFObjectData extends POIXMLDocumentPart implements ObjectData { + + /** + * Create a new XSLFOleData node + */ + protected XSLFObjectData() { + super(); + } + + /** + * Construct XSLFOleData from a package part + * + * @param part the package part holding the ole data + * + * @since POI 3.14-Beta1 + */ + public XSLFObjectData(final PackagePart part) { + super(part); + } + + @Override + public InputStream getInputStream() throws IOException { + return getPackagePart().getInputStream(); + } + + @Override + public OutputStream getOutputStream() throws IOException { + final PackagePart pp = getPackagePart(); + pp.clear(); + return pp.getOutputStream(); + } + + /** + * *PictureData objects store the actual content in the part directly without keeping a + * copy like all others therefore we need to handle them differently. + */ + @Override + protected void prepareForCommit() { + // do not clear the part here + } + + + public void setData(final byte[] data) throws IOException { + try (final OutputStream os = getPackagePart().getOutputStream()) { + os.write(data); + } + } + + @Override + public String getOLE2ClassName() { + return null; + } + + @Override + public String getFileName() { + return null; + } +} \ No newline at end of file diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFObjectShape.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFObjectShape.java new file mode 100644 index 000000000..ffa6b9eb1 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFObjectShape.java @@ -0,0 +1,323 @@ +/* + * ==================================================================== + * 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.xslf.usermodel; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import javax.xml.namespace.QName; + +import org.apache.poi.POIXMLDocumentPart.RelationPart; +import org.apache.poi.POIXMLException; +import org.apache.poi.hpsf.ClassID; +import org.apache.poi.openxml4j.exceptions.InvalidFormatException; +import org.apache.poi.openxml4j.opc.OPCPackage; +import org.apache.poi.openxml4j.opc.PackagePart; +import org.apache.poi.openxml4j.opc.PackageRelationship; +import org.apache.poi.poifs.filesystem.DirectoryNode; +import org.apache.poi.poifs.filesystem.FileMagic; +import org.apache.poi.poifs.filesystem.Ole10Native; +import org.apache.poi.poifs.filesystem.POIFSFileSystem; +import org.apache.poi.sl.usermodel.ObjectMetaData; +import org.apache.poi.sl.usermodel.ObjectMetaData.Application; +import org.apache.poi.sl.usermodel.ObjectShape; +import org.apache.poi.util.IOUtils; +import org.apache.poi.util.Internal; +import org.apache.xmlbeans.XmlCursor; +import org.apache.xmlbeans.XmlException; +import org.apache.xmlbeans.XmlObject; +import org.apache.xmlbeans.impl.values.XmlAnyTypeImpl; +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.CTPoint2D; +import org.openxmlformats.schemas.drawingml.x2006.main.CTPositiveSize2D; +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.presentationml.x2006.main.CTGraphicalObjectFrame; +import org.openxmlformats.schemas.presentationml.x2006.main.CTGraphicalObjectFrameNonVisual; +import org.openxmlformats.schemas.presentationml.x2006.main.CTGroupShape; +import org.openxmlformats.schemas.presentationml.x2006.main.CTOleObject; +import org.openxmlformats.schemas.presentationml.x2006.main.CTPicture; +import org.openxmlformats.schemas.presentationml.x2006.main.CTPictureNonVisual; + +public class XSLFObjectShape extends XSLFGraphicFrame implements ObjectShape { + + /* package */ static final String OLE_URI = "http://schemas.openxmlformats.org/presentationml/2006/ole"; + + private CTOleObject _oleObject; + private XSLFPictureData _data; + + /*package*/ XSLFObjectShape(CTGraphicalObjectFrame shape, XSLFSheet sheet){ + super(shape, sheet); + + CTGraphicalObjectData god = shape.getGraphic().getGraphicData(); + XmlCursor xc = god.newCursor(); + // select oleObj potentially under AlternateContent + // usually the mc:Choice element will be selected first + xc.selectPath("declare namespace p='"+PML_NS+"' .//p:oleObj"); + try { + if (!xc.toNextSelection()) { + throw new IllegalStateException("p:oleObj element was not found in\n " + god); + } + + XmlObject xo = xc.getObject(); + // Pesky XmlBeans bug - see Bugzilla #49934 + // it never happens when using the full ooxml-schemas jar but may happen with the abridged poi-ooxml-schemas + if (xo instanceof XmlAnyTypeImpl){ + String errStr = + "Schemas (*.xsb) for CTOleObject can't be loaded - usually this happens when OSGI " + + "loading is used and the thread context classloader has no reference to " + + "the xmlbeans classes - use POIXMLTypeLoader.setClassLoader() to set the loader, " + + "e.g. with CTOleObject.class.getClassLoader()" + ; + throw new IllegalStateException(errStr); + } + _oleObject = (CTOleObject)xo; + } finally { + xc.dispose(); + } + } + + @Internal + public CTOleObject getCTOleObject(){ + return _oleObject; + } + + @Override + public XSLFObjectData getObjectData() { + String oleRel = getCTOleObject().getId(); + return getSheet().getRelationPartById(oleRel).getDocumentPart(); + } + + @Override + public String getProgId() { + return (_oleObject == null) ? null : _oleObject.getProgId(); + } + + @Override + public String getFullName() { + return (_oleObject == null) ? null : _oleObject.getName(); + } + + + /** + * Return the data on the (internal) picture. + * For an external linked picture, will return null + */ + @Override + public XSLFPictureData getPictureData() { + if(_data == null){ + String blipId = getBlipId(); + if (blipId == null) { + return null; + } + + PackagePart p = getSheet().getPackagePart(); + PackageRelationship rel = p.getRelationship(blipId); + if (rel != null) { + try { + PackagePart imgPart = p.getRelatedPart(rel); + _data = new XSLFPictureData(imgPart); + } + catch (Exception e) { + throw new POIXMLException(e); + } + } + } + return _data; + } + + protected CTBlip getBlip(){ + return getBlipFill().getBlip(); + } + + protected String getBlipId(){ + String id = getBlip().getEmbed(); + if (id.isEmpty()) { + return null; + } + return id; + } + + protected CTBlipFillProperties getBlipFill() { + String xquery = + "declare namespace p='http://schemas.openxmlformats.org/presentationml/2006/main' " + + ".//p:blipFill" + ; + XmlObject xo = selectProperty(XmlObject.class, xquery); + try { + xo = CTPicture.Factory.parse(xo.getDomNode()); + } catch (XmlException xe) { + return null; + } + return ((CTPicture)xo).getBlipFill(); + } + + + @Override + public OutputStream updateObjectData(final Application application, final ObjectMetaData metaData) throws IOException { + final ObjectMetaData md = (application != null) ? application.getMetaData() : metaData; + if (md == null || md.getClassID() == null) { + throw new IllegalArgumentException("either application and/or metaData needs to be set."); + } + + + final XSLFSheet sheet = getSheet(); + + final RelationPart rp; + if (_oleObject.isSetId()) { + // object data was already set + rp = sheet.getRelationPartById(_oleObject.getId()); + } else { + // object data needs to be initialized + try { + final XSLFRelation descriptor = XSLFRelation.OLE_OBJECT; + final OPCPackage pack = sheet.getPackagePart().getPackage(); + int nextIdx = pack.getUnusedPartIndex(descriptor.getDefaultFileName()); + rp = sheet.createRelationship(descriptor, XSLFFactory.getInstance(), nextIdx, false); + _oleObject.setId(rp.getRelationship().getId()); + } catch (InvalidFormatException e) { + throw new IOException("Unable to add new ole embedding", e); + } + + // setting spid only works with a vml drawing object + // oleObj.setSpid("_x0000_s"+(1025+objectIdx)); + } + + _oleObject.setProgId(md.getProgId()); + _oleObject.setName(md.getObjectName()); + + return new XSLFObjectOutputStream(rp.getDocumentPart().getPackagePart(),md); + } + + private static class XSLFObjectOutputStream extends ByteArrayOutputStream { + final PackagePart objectPart; + final ObjectMetaData metaData; + private XSLFObjectOutputStream(final PackagePart objectPart, final ObjectMetaData metaData) { + super(100000); + this.objectPart = objectPart; + this.metaData = metaData; + } + + public void close() throws IOException { + objectPart.clear(); + try (final OutputStream os = objectPart.getOutputStream()) { + final ByteArrayInputStream bis = new ByteArrayInputStream(this.buf, 0, size()); + final FileMagic fm = FileMagic.valueOf(this.buf); + + if (fm == FileMagic.OLE2) { + try (final POIFSFileSystem poifs = new POIFSFileSystem(bis)) { + poifs.getRoot().setStorageClsid(metaData.getClassID()); + poifs.writeFilesystem(os); + } + } else if (metaData.getOleEntry() == null) { + // OLE Name hasn't been specified, pass the input through + os.write(this.buf, 0, size()); + } else { + try (final POIFSFileSystem poifs = new POIFSFileSystem()) { + final ClassID clsId = metaData.getClassID(); + if (clsId != null) { + poifs.getRoot().setStorageClsid(clsId); + } + poifs.createDocument(bis, metaData.getOleEntry()); + + Ole10Native.createOleMarkerEntry(poifs); + + poifs.writeFilesystem(os); + } + } + } + } + } + + + /** + * + * + * @param shapeId 1-based shapeId + * @param picRel relationship to the picture data in the ooxml package + * @return + */ + static CTGraphicalObjectFrame prototype(int shapeId, String picRel){ + CTGraphicalObjectFrame frame = CTGraphicalObjectFrame.Factory.newInstance(); + CTGraphicalObjectFrameNonVisual nvGr = frame.addNewNvGraphicFramePr(); + + CTNonVisualDrawingProps cnv = nvGr.addNewCNvPr(); + // usually the shape name has its index based on the n-th embeding, but having + // the prototype separate from the actual updating of the object, we use the shape id + cnv.setName("Object " + (shapeId + 1)); + cnv.setId(shapeId + 1); + + // add empty property elements otherwise Powerpoint doesn't load the file ... + nvGr.addNewCNvGraphicFramePr(); + nvGr.addNewNvPr(); + + frame.addNewXfrm(); + CTGraphicalObjectData gr = frame.addNewGraphic().addNewGraphicData(); + gr.setUri(OLE_URI); + XmlCursor grCur = gr.newCursor(); + grCur.toEndToken(); + grCur.beginElement(new QName(PML_NS, "oleObj")); + grCur.insertElement(new QName(PML_NS, "embed")); + + + CTGroupShape grpShp = CTGroupShape.Factory.newInstance(); + CTPicture pic = grpShp.addNewPic(); + CTPictureNonVisual nvPicPr = pic.addNewNvPicPr(); + CTNonVisualDrawingProps cNvPr = nvPicPr.addNewCNvPr(); + cNvPr.setName(""); + cNvPr.setId(0); + nvPicPr.addNewCNvPicPr(); + nvPicPr.addNewNvPr(); + + + CTBlipFillProperties blip = pic.addNewBlipFill(); + blip.addNewBlip().setEmbed(picRel); + blip.addNewStretch().addNewFillRect(); + + CTShapeProperties spPr = pic.addNewSpPr(); + CTTransform2D xfrm = spPr.addNewXfrm(); + CTPoint2D off = xfrm.addNewOff(); + off.setX(1270000); + off.setY(1270000); + CTPositiveSize2D xext = xfrm.addNewExt(); + xext.setCx(1270000); + xext.setCy(1270000); + + spPr.addNewPrstGeom().setPrst(STShapeType.RECT); + + + XmlCursor picCur = grpShp.newCursor(); + picCur.toStartDoc(); + picCur.moveXmlContents(grCur); + picCur.dispose(); + + grCur.dispose(); + + + return frame; + } +} 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 3285b6738..54ede0913 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFRelation.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFRelation.java @@ -231,6 +231,14 @@ public class XSLFRelation extends POIXMLRelation { XSLFTableStyles.class ); + public static final XSLFRelation OLE_OBJECT = new XSLFRelation( + "application/vnd.openxmlformats-officedocument.oleObject", + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/oleObject", + "/ppt/embeddings/oleObject#.bin", + XSLFObjectData.class + ); + + private XSLFRelation(String type, String rel, String defaultName, Class cls) { super(type, rel, defaultName, cls); _table.put(rel, this); 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 6a8ea9ce7..89617009c 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSheet.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSheet.java @@ -18,6 +18,7 @@ package org.apache.poi.xslf.usermodel; import static org.apache.poi.POIXMLTypeLoader.DEFAULT_XML_OPTIONS; +import java.awt.Dimension; import java.awt.Graphics2D; import java.io.IOException; import java.io.InputStream; @@ -45,13 +46,23 @@ import org.apache.poi.sl.draw.Drawable; import org.apache.poi.sl.usermodel.PictureData; import org.apache.poi.sl.usermodel.Placeholder; import org.apache.poi.sl.usermodel.Sheet; -import org.apache.poi.util.*; +import org.apache.poi.util.Beta; +import org.apache.poi.util.IOUtils; +import org.apache.poi.util.POILogFactory; +import org.apache.poi.util.POILogger; +import org.apache.poi.util.Units; import org.apache.xmlbeans.XmlCursor; 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.presentationml.x2006.main.*; +import org.openxmlformats.schemas.presentationml.x2006.main.CTConnector; +import org.openxmlformats.schemas.presentationml.x2006.main.CTGraphicalObjectFrame; +import org.openxmlformats.schemas.presentationml.x2006.main.CTGroupShape; +import org.openxmlformats.schemas.presentationml.x2006.main.CTOleObject; +import org.openxmlformats.schemas.presentationml.x2006.main.CTPicture; +import org.openxmlformats.schemas.presentationml.x2006.main.CTPlaceholder; +import org.openxmlformats.schemas.presentationml.x2006.main.CTShape; @Beta public abstract class XSLFSheet extends POIXMLDocumentPart @@ -256,6 +267,28 @@ implements XSLFShapeContainer, Sheet { } + @Override + public XSLFObjectShape createOleShape(PictureData pictureData) { + if (!(pictureData instanceof XSLFPictureData)) { + throw new IllegalArgumentException("pictureData needs to be of type XSLFPictureData"); + } + XSLFPictureData xPictureData = (XSLFPictureData)pictureData; + PackagePart pic = xPictureData.getPackagePart(); + + RelationPart rp = addRelation(null, XSLFRelation.IMAGES, new XSLFPictureData(pic)); + + XSLFObjectShape sh = getDrawing().createOleShape(rp.getRelationship().getId()); + CTOleObject oleObj = sh.getCTOleObject(); + Dimension dim = pictureData.getImageDimension(); + oleObj.setImgW(Units.toEMU(dim.getWidth())); + oleObj.setImgH(Units.toEMU(dim.getHeight())); + + + getShapes().add(sh); + sh.setParent(this); + return sh; + } + /** * Returns an iterator over the shapes in this sheet * diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTable.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTable.java index f1269a799..86b81d682 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTable.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTable.java @@ -59,25 +59,28 @@ public class XSLFTable extends XSLFGraphicFrame implements Iterable(_table.sizeOfTrArray()); for(CTTableRow row : _table.getTrArray()) { _rows.add(new XSLFTableRow(row, this)); diff --git a/src/ooxml/testcases/org/apache/poi/sl/TestOleShape.java b/src/ooxml/testcases/org/apache/poi/sl/TestOleShape.java new file mode 100644 index 000000000..cd482f9a0 --- /dev/null +++ b/src/ooxml/testcases/org/apache/poi/sl/TestOleShape.java @@ -0,0 +1,220 @@ +/* ==================================================================== + 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.sl; + +import static org.apache.poi.sl.SLCommonUtils.xslfOnly; +import static org.apache.poi.sl.usermodel.ObjectMetaData.Application.EXCEL_V12; +import static org.apache.poi.sl.usermodel.ObjectMetaData.Application.EXCEL_V8; +import static org.apache.poi.sl.usermodel.ObjectMetaData.Application.PDF; +import static org.apache.poi.sl.usermodel.ObjectMetaData.Application.WORD_V12; +import static org.apache.poi.sl.usermodel.ObjectMetaData.Application.WORD_V8; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; +import static org.junit.Assume.assumeFalse; + +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.io.OutputStream; +import java.util.Arrays; +import java.util.Collection; + +import org.apache.poi.POIDataSamples; +import org.apache.poi.hslf.usermodel.HSLFSlideShow; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.hwpf.HWPFDocument; +import org.apache.poi.openxml4j.exceptions.InvalidFormatException; +import org.apache.poi.poifs.storage.RawDataUtil; +import org.apache.poi.sl.usermodel.ObjectMetaData; +import org.apache.poi.sl.usermodel.ObjectShape; +import org.apache.poi.sl.usermodel.PictureData; +import org.apache.poi.sl.usermodel.PictureData.PictureType; +import org.apache.poi.sl.usermodel.Slide; +import org.apache.poi.sl.usermodel.SlideShow; +import org.apache.poi.sl.usermodel.SlideShowFactory; +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.ss.usermodel.WorkbookFactory; +import org.apache.poi.util.IOUtils; +import org.apache.poi.xslf.usermodel.XMLSlideShow; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; +import org.apache.poi.xwpf.usermodel.XWPFDocument; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.Parameterized.Parameters; + +@RunWith(Parameterized.class) +public class TestOleShape { + private static final String PDF_SAMPLE = + "H4sIAAAAAAAAAJWUezRUWxzHe+o2FXncVtxLpxi3FPOeKYspjMdM5J1S4TTOaDIzxzpzJo9CUrnrSiUxIeT" + + "9jB7yqInihrhepTwqt1AT5VZCC7XcY0LWcv+5Z521zz6fvX+/vb/7t9cX78CyMiQZ0XD4W4OFEzgKQATgg4" + + "dxJiYAwRYS+aCHACqGnHAAABCs+AIUQrCvAEQhFsSFvSEck4kTowgECnEBl6E4OxGesfLkl2Bc0g5V/MCHt" + + "duqroTFC27YIKGAp+OL5Ou1SsmebA1XvciLk+Ucg84aLclQZhRZmh0amrG9Ina4t7Lh+ZCAHyezsg/NXsZg" + + "vuPw8NIedsrI2sZlz2vfhLkZfIgfMr4zFvTrmfbRgMmPw1UTvWk+r4MCZfLtj2WPPStVJ0P2PiKkxo+YnJ5" + + "Ua7v5UnefkB9ev0vSR37a8NrsC2lApaLp7086wS3Lzi2LqB3TMW2POrdRRUYMFYWs8vBo/kSQ6dYXpR6rxM" + + "UXM0vqu4arpe5dha7XS5MYS5P1arVG653sb8pXqReVw/TfjK8R3q4Z7X7Uk9dZ2Bcl8Wpmsl80Xf1QTOxe3" + + "Nutwus0kYge1LoHvgKLbc/f6WvdcsBfS9ctU3vSaneHNm0w/uhrm0Zett5O83s2xh2Gm8WZfWJ+/CNWruZ2" + + "cap8tR2/U9bAfRBbYt3PL9jvb3+0usqSF6vfrFuEq8Hf6jgrx/fERpZJEjKbHtJ11jCdUwI7Oc8QrmZf2pr" + + "L43WJn1mlT1ydV+QbrndcdN3qSEnicVhmoJyfWyprUEsIZlPyvi0tiEy7FzkOnqlE/qC6xFSpyg0E8tODa7" + + "qiKX61hMxRkZt73ITLWIHwtZtj71NbS4/BgKHnssOMcXOp1aacX4A//V+VFN4TWl6QryxdkcAp79BZcipmP" + + "OWOWkS6KhqUWlG+yCCxVMMtfW+9e56++gKHYEs9PNJztTI6KtyfOCDPOBppt3udRs3NpGrKfrs3i3Nivtrs" + + "VTl5LFerXZlTbf5XumbeYsOfwnve5ksEjKy6s1Z78rpbeJq7biTdzWwU3vhZ1GqkYb9D+t6mYvWLhXn6FWi" + + "c60VWpbBtVYHLQkmbh6Txwm6Ul3LbNW/Hs5FtLnlNX3fsAX2jPdlOI5W3HDIcm2MyNiR39rdyHlpwusjoOj" + + "I3IKfgPMILSzHZInmQaWyUFXEi0bHsCLX8pm9Gzl2vou7E3rrkPYdt1EwW3R5Qcg8rwzk88c1p13l8v+WkY" + + "75FHeS7XrvRsHgLy+wfr2EBRfNJ/4UVhUrihRqDktXciPGxWv1eXs396/0lqWG3YtU/A+D90hrT46cumSUN" + + "rBdG0G2Knn3T9Kw0X96vbhxMyr92uqUNOa4aEnGqP8us6GULm7mIyFKuxnOW2MZEEuKXmOpxnnqlwiMn+ju" + + "Xu+inC5mpG9oesxKkhcJq9bra5vR3H7l10hGbAxqu6t0LvHzaDnPIp/zeu0iXj1NNtVc+cMyUsH18u7TGXJ" + + "XiL4W3tqaL2mq6zkgXWB6kOTB3RxW8PHAOvzfaufDptdg7qmZlEcrUzbd5jKtVb85Sr9jaMT8a3y2Q30+3/" + + "FrsfGZDblh/mYnHhCg3ekm2q1JIYEVCd9rv42PNb9RFpuSsa4MNE0GfdSYDv6lsudikg4NE3tNugfWmfIY6" + + "7TeYvZCItG0zmDxrQwrjsQxArZ1RzHSA72CKgURgyqQszAASQOCCWItZETaAtdg7nYc2x85cAv0ggOAA+kC" + + "KnA4gAolQLGzG3ewgbz5oDgcA+zBEBEhUkhGDQiZvpc3tHlBMtYBFKBYsBiiz0dYILPGbs73vqynoDHLGKA" + + "KKxH5TK3MDZzAbQBEJNPNngc1iQUf4XMjJ2nxazxR3gsSwBOFCYoCsWPOHRtJ/ahQronbyvcWYnqljcJrdu" + + "2RK9pwE9DkJLLDioDACbOSCfAQGSEYkqhGJCGw8hKJ+xgSCgvogoN8hPldsBCM+mzZ9P0wE9pZwof8V92MD" + + "jHkKLEAUFMA+04XC1EzX6UdMAALxcERgK444+wB0Go1CA3jANCNRGdj1UoyIZhlpPsMobf48GmkeI1Pp8xi" + + "Nsm0eo9O3/mAoAvIFEKIQ58wPgrAtK+oJwyjAmL0+bBEPBugzGsUoiKAKhSQGmYjD4y3trXD/AmBc9IeqBwAA"; + + enum Api { HSLF, XSLF }; + + + @Parameter(value = 0) + public Api api; + @Parameter(value = 1) + public ObjectMetaData.Application app; + + + private static File pictureFile; + + @BeforeClass + public static void initPicture() { + pictureFile = POIDataSamples.getSlideShowInstance().getFile("wrench.emf"); + } + + + @Parameters(name="{0} {1}") + public static Collection data() { + return Arrays.asList(new Object[][] { + { Api.HSLF, EXCEL_V8 }, + { Api.HSLF, WORD_V8 }, + { Api.HSLF, PDF }, + { Api.XSLF, EXCEL_V12 }, + { Api.XSLF, WORD_V12 }, + { Api.XSLF, PDF }, + }); + } + + @Test + public void embedData() throws IOException, InvalidFormatException { + final ByteArrayInputStream pptBytes; + try (SlideShow ppt = createSlideShow()) { + final PictureData picData = ppt.addPicture(pictureFile, PictureType.EMF); + final Slide slide = ppt.createSlide(); + final ObjectShape oleShape = slide.createOleShape(picData); + oleShape.setAnchor(new Rectangle2D.Double(100,100,100,100)); + try (OutputStream os = oleShape.updateObjectData(app, null)) { + fillOleData(os); + } + final ByteArrayOutputStream bos = new ByteArrayOutputStream(50000); + ppt.write(bos); + pptBytes = new ByteArrayInputStream(bos.toByteArray()); + } + try (SlideShow ppt = SlideShowFactory.create(pptBytes)) { + final ObjectShape oleShape = (ObjectShape)ppt.getSlides().get(0).getShapes().get(0); + try (InputStream bis = oleShape.readObjectData()) { + validateOleData(bis); + } + } + } + + private SlideShow createSlideShow() { + if (api == Api.XSLF) { + return new XMLSlideShow(); + } else { + assumeFalse(xslfOnly()); + return new HSLFSlideShow(); + } + } + + + private void fillOleData(final OutputStream out) throws IOException { + switch (app) { + case EXCEL_V8: + case EXCEL_V12: + try (Workbook wb = (app == EXCEL_V12) ? new XSSFWorkbook() : new HSSFWorkbook()) { + wb.createSheet().createRow(0).createCell(0).setCellValue("test me"); + wb.write(out); + } + break; + case WORD_V8: + try (InputStream is = POIDataSamples.getDocumentInstance().openResourceAsStream("simple.doc")) { + IOUtils.copy(is, out); + } + break; + case WORD_V12: + try (XWPFDocument doc = new XWPFDocument()) { + doc.createParagraph().createRun().setText("Test me"); + doc.write(out); + } + break; + case PDF: + out.write(RawDataUtil.decompress(PDF_SAMPLE)); + break; + default: + case CUSTOM: + fail("not implemented"); + break; + } + } + + private void validateOleData(final InputStream in) throws IOException, InvalidFormatException { + switch (app) { + case EXCEL_V8: + case EXCEL_V12: + try (Workbook wb = WorkbookFactory.create(in)) { + assertEquals("test me", wb.getSheetAt(0).getRow(0).getCell(0).getStringCellValue()); + } + break; + case WORD_V8: + try (HWPFDocument doc = new HWPFDocument(in)) { + assertEquals("This is a simple file created with Word 97-SR2.\r", doc.getDocumentText()); + } + break; + case WORD_V12: + try (XWPFDocument doc = new XWPFDocument(in)) { + assertEquals("Test me", doc.getParagraphs().get(0).getText()); + } + break; + case PDF: + final byte[] expected = RawDataUtil.decompress(PDF_SAMPLE); + final byte[] actual = IOUtils.toByteArray(in); + assertArrayEquals(expected, actual); + break; + default: + case CUSTOM: + fail("not implemented"); + break; + } + + } +} diff --git a/src/scratchpad/src/org/apache/poi/hslf/extractor/PowerPointExtractor.java b/src/scratchpad/src/org/apache/poi/hslf/extractor/PowerPointExtractor.java index dcdf9c699..c34080874 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/extractor/PowerPointExtractor.java +++ b/src/scratchpad/src/org/apache/poi/hslf/extractor/PowerPointExtractor.java @@ -29,7 +29,6 @@ import org.apache.poi.POIOLE2TextExtractor; import org.apache.poi.hslf.model.Comment; import org.apache.poi.hslf.model.HSLFMetroShape; import org.apache.poi.hslf.model.HeadersFooters; -import org.apache.poi.hslf.model.OLEShape; import org.apache.poi.hslf.usermodel.HSLFMasterSheet; import org.apache.poi.hslf.usermodel.HSLFNotes; import org.apache.poi.hslf.usermodel.HSLFShape; @@ -41,6 +40,7 @@ import org.apache.poi.hslf.usermodel.HSLFTable; import org.apache.poi.hslf.usermodel.HSLFTableCell; import org.apache.poi.hslf.usermodel.HSLFTextParagraph; import org.apache.poi.hslf.usermodel.HSLFTextShape; +import org.apache.poi.hslf.usermodel.HSLFObjectShape; import org.apache.poi.poifs.filesystem.DirectoryNode; import org.apache.poi.poifs.filesystem.NPOIFSFileSystem; import org.apache.poi.poifs.filesystem.POIFSFileSystem; @@ -194,13 +194,13 @@ public final class PowerPointExtractor extends POIOLE2TextExtractor { return getText(false, true); } - public List getOLEShapes() { - List list = new ArrayList<>(); + public List getOLEShapes() { + List list = new ArrayList<>(); for (HSLFSlide slide : _slides) { for (HSLFShape shape : slide.getShapes()) { - if (shape instanceof OLEShape) { - list.add((OLEShape) shape); + if (shape instanceof HSLFObjectShape) { + list.add((HSLFObjectShape) shape); } } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/ExEmbed.java b/src/scratchpad/src/org/apache/poi/hslf/record/ExEmbed.java index 7903bb32d..2449adb6c 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/ExEmbed.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/ExEmbed.java @@ -17,8 +17,8 @@ package org.apache.poi.hslf.record; -import java.io.OutputStream; import java.io.IOException; +import java.io.OutputStream; import org.apache.poi.util.LittleEndian; import org.apache.poi.util.POILogger; @@ -31,7 +31,7 @@ public class ExEmbed extends RecordContainer { /** * Record header data. */ - private byte[] _header; + private final byte[] _header; // Links to our more interesting children private RecordAtom embedAtom; @@ -47,7 +47,7 @@ public class ExEmbed extends RecordContainer { * @param start the start offset into the byte array. * @param len the length of the slice in the byte array. */ - protected ExEmbed(byte[] source, int start, int len) { + protected ExEmbed(final byte[] source, final int start, final int len) { // Grab the header _header = new byte[8]; System.arraycopy(source,start,_header,0,8); @@ -62,7 +62,7 @@ public class ExEmbed extends RecordContainer { * * @param embedAtom the new embedAtom */ - protected ExEmbed(RecordAtom embedAtom) { + protected ExEmbed(final RecordAtom embedAtom) { this(); _children[0] = this.embedAtom = embedAtom; } @@ -81,11 +81,11 @@ public class ExEmbed extends RecordContainer { LittleEndian.putShort(_header, 2, (short)getRecordType()); // Setup our child records - CString cs1 = new CString(); + final CString cs1 = new CString(); cs1.setOptions(0x1 << 4); - CString cs2 = new CString(); + final CString cs2 = new CString(); cs2.setOptions(0x2 << 4); - CString cs3 = new CString(); + final CString cs3 = new CString(); cs3.setOptions(0x3 << 4); _children[0] = new ExEmbedAtom(); _children[1] = new ExOleObjAtom(); @@ -117,8 +117,8 @@ public class ExEmbed extends RecordContainer { for (int i = 2; i < _children.length; i++) { if (_children[i] instanceof CString){ - CString cs = (CString)_children[i]; - int opts = cs.getOptions() >> 4; + final CString cs = (CString)_children[i]; + final int opts = cs.getOptions() >> 4; switch(opts){ case 0x1: menuName = cs; break; case 0x2: progId = cs; break; @@ -134,8 +134,7 @@ public class ExEmbed extends RecordContainer { * * @return the {@link ExEmbedAtom}. */ - public ExEmbedAtom getExEmbedAtom() - { + public ExEmbedAtom getExEmbedAtom() { return (ExEmbedAtom)embedAtom; } @@ -144,8 +143,7 @@ public class ExEmbed extends RecordContainer { * * @return the {@link ExOleObjAtom}. */ - public ExOleObjAtom getExOleObjAtom() - { + public ExOleObjAtom getExOleObjAtom() { return oleObjAtom; } @@ -154,14 +152,13 @@ public class ExEmbed extends RecordContainer { * * @return the name used for menus and the Links dialog box. */ - public String getMenuName() - { + public String getMenuName() { return menuName == null ? null : menuName.getText(); } - public void setMenuName(String s) - { - if(menuName != null) menuName.setText(s); + public void setMenuName(final String menuName) { + this.menuName = safeCString(this.menuName, 0x1); + this.menuName.setText(menuName); } /** @@ -169,28 +166,29 @@ public class ExEmbed extends RecordContainer { * * @return the OLE Programmatic Identifier. */ - public String getProgId() - { + public String getProgId() { return progId == null ? null : progId.getText(); } - public void setProgId(String s) - { - if(progId != null) progId.setText(s); + public void setProgId(final String progId) { + this.progId = safeCString(this.progId, 0x2); + this.progId.setText(progId); } + + + /** * Gets the name that appears in the paste special dialog. * * @return the name that appears in the paste special dialog. */ - public String getClipboardName() - { + public String getClipboardName() { return clipboardName == null ? null : clipboardName.getText(); } - public void setClipboardName(String s) - { - if(clipboardName != null) clipboardName.setText(s); + public void setClipboardName(final String clipboardName) { + this.clipboardName = safeCString(this.clipboardName, 0x3); + this.clipboardName.setText(clipboardName); } /** @@ -199,6 +197,7 @@ public class ExEmbed extends RecordContainer { * * @return the record type. */ + @Override public long getRecordType() { return RecordTypes.ExEmbed.typeID; } @@ -210,7 +209,31 @@ public class ExEmbed extends RecordContainer { * @param out the output stream. * @throws IOException if there was an error writing to the stream. */ - public void writeOut(OutputStream out) throws IOException { + @Override + public void writeOut(final OutputStream out) throws IOException { writeOut(_header[0],_header[1],getRecordType(),_children,out); } + + private CString safeCString(CString oldStr, int optionsId) { + CString newStr = oldStr; + if (newStr == null) { + newStr = new CString(); + newStr.setOptions(optionsId << 4); + } + + boolean found = false; + for (final Record r : _children) { + // for simplicity just check for object identity + if (r == newStr) { + found = true; + break; + } + } + + if (!found) { + appendChildRecord(newStr); + } + + return newStr; + } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFGroupShape.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFGroupShape.java index 27f5cbaf5..b47373899 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFGroupShape.java +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFGroupShape.java @@ -39,8 +39,6 @@ import org.apache.poi.util.Units; /** * Represents a group of shapes. - * - * @author Yegor Kozlov */ public class HSLFGroupShape extends HSLFShape implements HSLFShapeContainer, GroupShape { @@ -362,4 +360,15 @@ implements HSLFShapeContainer, GroupShape { addShape(s); return s; } + + @Override + public HSLFObjectShape createOleShape(PictureData pictureData) { + if (!(pictureData instanceof HSLFPictureData)) { + throw new IllegalArgumentException("pictureData needs to be of type HSLFPictureData"); + } + HSLFObjectShape s = new HSLFObjectShape((HSLFPictureData)pictureData, this); + s.setAnchor(new Rectangle2D.Double(0, 0, 100, 100)); + addShape(s); + return s; + } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFObjectData.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFObjectData.java index f02ddbe78..b1cb5a432 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFObjectData.java +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFObjectData.java @@ -16,17 +16,25 @@ ==================================================================== */ package org.apache.poi.hslf.usermodel; -import java.io.InputStream; +import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import org.apache.poi.hslf.record.ExOleObjStg; +import org.apache.poi.poifs.filesystem.DirectoryEntry; +import org.apache.poi.poifs.filesystem.FileMagic; +import org.apache.poi.poifs.filesystem.POIFSFileSystem; +import org.apache.poi.sl.usermodel.ObjectData; +import org.apache.poi.util.POILogFactory; +import org.apache.poi.util.POILogger; /** * A class that represents object data embedded in a slide show. - * - * @author Daniel Noll */ -public class HSLFObjectData { +public class HSLFObjectData implements ObjectData { + private static final POILogger LOG = POILogFactory.getLogger(HSLFObjectData.class); + /** * The record that contains the object data. */ @@ -41,14 +49,19 @@ public class HSLFObjectData { this.storage = storage; } - /** - * Gets an input stream which returns the binary of the embedded data. - * - * @return the input stream which will contain the binary of the embedded data. - */ - public InputStream getData() { + @Override + public InputStream getInputStream() { return storage.getData(); } + + @Override + public OutputStream getOutputStream() throws IOException { + return new ByteArrayOutputStream(100000) { + public void close() throws IOException { + setData(getBytes()); + } + }; + } /** * Sets the embedded data. @@ -67,4 +80,15 @@ public class HSLFObjectData { public ExOleObjStg getExOleObjStg() { return storage; } + + + @Override + public String getOLE2ClassName() { + return null; + } + + @Override + public String getFileName() { + return null; + } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/OLEShape.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFObjectShape.java similarity index 60% rename from src/scratchpad/src/org/apache/poi/hslf/model/OLEShape.java rename to src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFObjectShape.java index 23c4daa20..ffe2c51b0 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/OLEShape.java +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFObjectShape.java @@ -15,7 +15,12 @@ limitations under the License. ==================================================================== */ -package org.apache.poi.hslf.model; +package org.apache.poi.hslf.usermodel; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; import org.apache.poi.ddf.EscherContainerRecord; import org.apache.poi.ddf.EscherProperties; @@ -26,22 +31,22 @@ import org.apache.poi.hslf.record.ExObjRefAtom; import org.apache.poi.hslf.record.HSLFEscherClientDataRecord; import org.apache.poi.hslf.record.Record; import org.apache.poi.hslf.record.RecordTypes; -import org.apache.poi.hslf.usermodel.HSLFObjectData; -import org.apache.poi.hslf.usermodel.HSLFPictureData; -import org.apache.poi.hslf.usermodel.HSLFPictureShape; -import org.apache.poi.hslf.usermodel.HSLFShape; -import org.apache.poi.hslf.usermodel.HSLFSlideShow; -import org.apache.poi.hslf.usermodel.HSLFTextParagraph; +import org.apache.poi.poifs.filesystem.FileMagic; +import org.apache.poi.poifs.filesystem.Ole10Native; +import org.apache.poi.poifs.filesystem.POIFSFileSystem; +import org.apache.poi.sl.usermodel.ObjectMetaData; +import org.apache.poi.sl.usermodel.ObjectMetaData.Application; +import org.apache.poi.sl.usermodel.ObjectShape; import org.apache.poi.sl.usermodel.ShapeContainer; import org.apache.poi.util.POILogFactory; import org.apache.poi.util.POILogger; /** - * A shape representing embedded OLE obejct. + * A shape representing embedded OLE object. */ -public final class OLEShape extends HSLFPictureShape { - private static final POILogger LOG = POILogFactory.getLogger(OLEShape.class); +public final class HSLFObjectShape extends HSLFPictureShape implements ObjectShape { + private static final POILogger LOG = POILogFactory.getLogger(HSLFObjectShape.class); private ExEmbed _exEmbed; @@ -50,7 +55,7 @@ public final class OLEShape extends HSLFPictureShape { * * @param data the picture data */ - public OLEShape(HSLFPictureData data){ + public HSLFObjectShape(HSLFPictureData data){ super(data); } @@ -60,7 +65,7 @@ public final class OLEShape extends HSLFPictureShape { * @param data the picture data * @param parent the parent shape */ - public OLEShape(HSLFPictureData data, ShapeContainer parent) { + public HSLFObjectShape(HSLFPictureData data, ShapeContainer parent) { super(data, parent); } @@ -71,7 +76,7 @@ public final class OLEShape extends HSLFPictureShape { * this picture in the Slide * @param parent the parent shape of this picture */ - public OLEShape(EscherContainerRecord escherRecord, ShapeContainer parent){ + public HSLFObjectShape(EscherContainerRecord escherRecord, ShapeContainer parent){ super(escherRecord, parent); } @@ -87,12 +92,12 @@ public final class OLEShape extends HSLFPictureShape { /** * Set the unique identifier for the OLE object and * register it in the necessary structures - * + * * @param objectId the unique identifier for the OLE object */ public void setObjectID(int objectId){ setEscherProperty(EscherProperties.BLIP__PICTUREID, objectId); - + EscherContainerRecord ecr = getSpContainer(); EscherSpRecord spRecord = ecr.getChildById(EscherSpRecord.RECORD_ID); spRecord.setFlags(spRecord.getFlags()|EscherSpRecord.FLAG_OLESHAPE); @@ -111,14 +116,13 @@ public final class OLEShape extends HSLFPictureShape { } uer.setExObjIdRef(objectId); } - - + + /** * Returns unique identifier for the OLE object. * * @return the unique identifier for the OLE object */ - @SuppressWarnings("resource") public HSLFObjectData getObjectData(){ HSLFSlideShow ppt = getSheet().getSlideShow(); HSLFObjectData[] ole = ppt.getEmbeddedObjects(); @@ -129,9 +133,10 @@ public final class OLEShape extends HSLFPictureShape { if(exEmbed != null) { int ref = exEmbed.getExOleObjAtom().getObjStgDataRef(); - for (int i = 0; i < ole.length; i++) { - if(ole[i].getExOleObjStg().getPersistId() == ref) { - data=ole[i]; + for (HSLFObjectData hod : ole) { + if(hod.getExOleObjStg().getPersistId() == ref) { + data=hod; + // keep searching to return the last persistent object with that refId } } } @@ -156,29 +161,40 @@ public final class OLEShape extends HSLFPictureShape { * 6. MetaFile( 4033), optional *

*/ - @SuppressWarnings("resource") public ExEmbed getExEmbed(){ - if(_exEmbed == null){ + return getExEmbed(false); + } + + private ExEmbed getExEmbed(boolean create) { + if (_exEmbed == null) { HSLFSlideShow ppt = getSheet().getSlideShow(); - ExObjList lst = ppt.getDocumentRecord().getExObjList(false); + ExObjList lst = ppt.getDocumentRecord().getExObjList(create); if(lst == null){ LOG.log(POILogger.WARN, "ExObjList not found"); return null; } int id = getObjectID(); - Record[] ch = lst.getChildRecords(); - for (int i = 0; i < ch.length; i++) { - if(ch[i] instanceof ExEmbed){ - ExEmbed embd = (ExEmbed)ch[i]; - if( embd.getExOleObjAtom().getObjID() == id) _exEmbed = embd; + for (Record ch : lst.getChildRecords()) { + if(ch instanceof ExEmbed){ + ExEmbed embd = (ExEmbed)ch; + if( embd.getExOleObjAtom().getObjID() == id) { + _exEmbed = embd; + } } } + + if (_exEmbed == null && create) { + _exEmbed = new ExEmbed(); + _exEmbed.getExOleObjAtom().setObjID(id); + lst.appendChildRecord(_exEmbed); + } } return _exEmbed; } - + + /** * Returns the instance name of the embedded object, e.g. "Document" or "Workbook". * @@ -189,26 +205,63 @@ public final class OLEShape extends HSLFPictureShape { return (ee == null) ? null : ee.getMenuName(); } - /** - * Returns the full name of the embedded object, - * e.g. "Microsoft Word Document" or "Microsoft Office Excel Worksheet". - * - * @return the full name of the embedded object - */ + @Override public String getFullName(){ ExEmbed ee = getExEmbed(); return (ee == null) ? null : ee.getClipboardName(); } - /** - * Returns the ProgID that stores the OLE Programmatic Identifier. - * A ProgID is a string that uniquely identifies a given object, for example, - * "Word.Document.8" or "Excel.Sheet.8". - * - * @return the ProgID - */ - public String getProgID(){ + public void setFullName(final String fullName) { + getExEmbed(true).setClipboardName(fullName); + } + + @Override + public String getProgId(){ ExEmbed ee = getExEmbed(); return (ee == null) ? null : ee.getProgId(); } + + public void setProgId(final String progId) { + getExEmbed(true).setProgId(progId); + } + + public OutputStream updateObjectData(final Application application, final ObjectMetaData metaData) throws IOException { + final ObjectMetaData md = (application != null) ? application.getMetaData() : metaData; + if (md == null) { + throw new RuntimeException("either application or metaData needs to be set"); + } + + return new ByteArrayOutputStream(100000) { + public void close() throws IOException { + final FileMagic fm = FileMagic.valueOf(this.buf); + final ByteArrayInputStream bis = new ByteArrayInputStream(this.buf, 0, this.count); + final HSLFSlideShow ppt = getSheet().getSlideShow(); + + try (POIFSFileSystem poifs = (fm == FileMagic.OLE2) ? new POIFSFileSystem(bis) : new POIFSFileSystem()) { + if (fm != FileMagic.OLE2) { + poifs.createDocument(bis, md.getOleEntry()); + } + + Ole10Native.createOleMarkerEntry(poifs); + + poifs.getRoot().setStorageClsid(md.getClassID()); + + + int oid = getObjectID(); + if (oid == 0) { + // assign new embedding + oid = ppt.addEmbed(poifs); + setObjectID(oid); + } else { + ByteArrayOutputStream bos = new ByteArrayOutputStream(this.size()+1000); + poifs.writeFilesystem(bos); + getObjectData().setData(bos.toByteArray()); + } + + setProgId(md.getProgId()); + setFullName(md.getObjectName()); + } + } + }; + } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFShapeContainer.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFShapeContainer.java index 8152befe2..47c895e0e 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFShapeContainer.java +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFShapeContainer.java @@ -45,5 +45,6 @@ public interface HSLFShapeContainer extends ShapeContainer shapes = ppe.getOLEShapes(); + List shapes = ppe.getOLEShapes(); assertEquals("Expected 6 ole shapes", 6, shapes.size()); int num_ppt = 0, num_doc = 0, num_xls = 0; - for (OLEShape ole : shapes) { + for (HSLFObjectShape ole : shapes) { String name = ole.getInstanceName(); - InputStream data = ole.getObjectData().getData(); + InputStream data = ole.getObjectData().getInputStream(); if ("Worksheet".equals(name)) { HSSFWorkbook wb = new HSSFWorkbook(data); num_xls++; @@ -239,8 +239,8 @@ public final class TestExtractor { @Test public void test52991() throws IOException { PowerPointExtractor ppe = openExtractor("badzip.ppt"); - for (OLEShape shape : ppe.getOLEShapes()) { - IOUtils.copy(shape.getObjectData().getData(), new ByteArrayOutputStream()); + for (HSLFObjectShape shape : ppe.getOLEShapes()) { + IOUtils.copy(shape.getObjectData().getInputStream(), new ByteArrayOutputStream()); } ppe.close(); } diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/model/TestOleEmbedding.java b/src/scratchpad/testcases/org/apache/poi/hslf/model/TestOleEmbedding.java index d8c331b7f..16f40a72f 100644 --- a/src/scratchpad/testcases/org/apache/poi/hslf/model/TestOleEmbedding.java +++ b/src/scratchpad/testcases/org/apache/poi/hslf/model/TestOleEmbedding.java @@ -35,6 +35,7 @@ import org.apache.poi.hslf.usermodel.HSLFShape; import org.apache.poi.hslf.usermodel.HSLFSlide; import org.apache.poi.hslf.usermodel.HSLFSlideShow; import org.apache.poi.hslf.usermodel.HSLFSlideShowImpl; +import org.apache.poi.hslf.usermodel.HSLFObjectShape; import org.apache.poi.hssf.usermodel.HSSFSheet; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.hwpf.HWPFDocument; @@ -70,7 +71,7 @@ public final class TestOleEmbedding { HSLFObjectData[] objects = slideShow.getEmbeddedObjects(); assertEquals("Should be two objects", 2, objects.length); for (HSLFObjectData od : objects) { - long checkEMF = IOUtils.calculateChecksum(od.getData()); + long checkEMF = IOUtils.calculateChecksum(od.getInputStream()); assertEquals(checkSums[checkId++], checkEMF); } @@ -86,13 +87,13 @@ public final class TestOleEmbedding { HSLFSlide slide = ppt.getSlides().get(0); int cnt = 0; for (HSLFShape sh : slide.getShapes()) { - if(sh instanceof OLEShape){ + if(sh instanceof HSLFObjectShape){ cnt++; - OLEShape ole = (OLEShape)sh; + HSLFObjectShape ole = (HSLFObjectShape)sh; HSLFObjectData data = ole.getObjectData(); if("Worksheet".equals(ole.getInstanceName())){ //Voila! we created a workbook from the embedded OLE data - HSSFWorkbook wb = new HSSFWorkbook(data.getData()); + HSSFWorkbook wb = new HSSFWorkbook(data.getInputStream()); HSSFSheet sheet = wb.getSheetAt(0); //verify we can access the xls data assertEquals(1, sheet.getRow(0).getCell(0).getNumericCellValue(), 0); @@ -103,7 +104,7 @@ public final class TestOleEmbedding { wb.close(); } else if ("Document".equals(ole.getInstanceName())){ //creating a HWPF document - HWPFDocument doc = new HWPFDocument(data.getData()); + HWPFDocument doc = new HWPFDocument(data.getInputStream()); String txt = doc.getRange().getParagraph(0).text(); assertEquals("OLE embedding is thoroughly unremarkable.\r", txt); doc.close(); @@ -129,14 +130,14 @@ public final class TestOleEmbedding { int oleObjectId1 = ppt.addEmbed(poiData1); HSLFSlide slide1 = ppt.createSlide(); - OLEShape oleShape1 = new OLEShape(pictData); + HSLFObjectShape oleShape1 = new HSLFObjectShape(pictData); oleShape1.setObjectID(oleObjectId1); slide1.addShape(oleShape1); oleShape1.setAnchor(new Rectangle2D.Double(100,100,100,100)); // add second slide with different order in object creation HSLFSlide slide2 = ppt.createSlide(); - OLEShape oleShape2 = new OLEShape(pictData); + HSLFObjectShape oleShape2 = new HSLFObjectShape(pictData); is = POIDataSamples.getSpreadSheetInstance().openResourceAsStream("SimpleWithImages.xls"); POIFSFileSystem poiData2 = new POIFSFileSystem(is); @@ -152,8 +153,8 @@ public final class TestOleEmbedding { ppt.write(bos); ppt = new HSLFSlideShow(new ByteArrayInputStream(bos.toByteArray())); - OLEShape comp = (OLEShape)ppt.getSlides().get(0).getShapes().get(0); - byte compData[] = IOUtils.toByteArray(comp.getObjectData().getData()); + HSLFObjectShape comp = (HSLFObjectShape)ppt.getSlides().get(0).getShapes().get(0); + byte compData[] = IOUtils.toByteArray(comp.getObjectData().getInputStream()); bos.reset(); poiData1.writeFilesystem(bos); diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestBugs.java b/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestBugs.java index 8bb83852e..df4421609 100644 --- a/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestBugs.java +++ b/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestBugs.java @@ -1010,7 +1010,7 @@ public final class TestBugs { long persistId = vbaAtom.getPersistIdRef(); for (HSLFObjectData objData : ppt.getEmbeddedObjects()) { if (objData.getExOleObjStg().getPersistId() == persistId) { - VBAMacroReader mr = new VBAMacroReader(objData.getData()); + VBAMacroReader mr = new VBAMacroReader(objData.getInputStream()); try { return mr.readMacros(); } finally {