#61797 - Embed Excel / Ole objects into powerpoint

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1819710 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Andreas Beeker 2017-12-31 01:14:08 +00:00
parent cdab1a4511
commit 704b41ab9a
25 changed files with 1355 additions and 142 deletions

View File

@ -21,8 +21,8 @@ import java.io.FileInputStream;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.InputStream; import java.io.InputStream;
import org.apache.poi.hslf.model.OLEShape;
import org.apache.poi.hslf.usermodel.HSLFObjectData; 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.HSLFPictureData;
import org.apache.poi.hslf.usermodel.HSLFPictureShape; import org.apache.poi.hslf.usermodel.HSLFPictureShape;
import org.apache.poi.hslf.usermodel.HSLFShape; import org.apache.poi.hslf.usermodel.HSLFShape;
@ -67,19 +67,19 @@ public final class DataExtraction {
for (HSLFSlide slide : ppt.getSlides()) { for (HSLFSlide slide : ppt.getSlides()) {
//extract embedded OLE documents //extract embedded OLE documents
for (HSLFShape shape : slide.getShapes()) { for (HSLFShape shape : slide.getShapes()) {
if (shape instanceof OLEShape) { if (shape instanceof HSLFObjectShape) {
oleIdx++; oleIdx++;
OLEShape ole = (OLEShape) shape; HSLFObjectShape ole = (HSLFObjectShape) shape;
HSLFObjectData data = ole.getObjectData(); HSLFObjectData data = ole.getObjectData();
String name = ole.getInstanceName(); String name = ole.getInstanceName();
if ("Worksheet".equals(name)) { if ("Worksheet".equals(name)) {
//read xls //read xls
@SuppressWarnings({ "unused", "resource" }) @SuppressWarnings({ "unused", "resource" })
HSSFWorkbook wb = new HSSFWorkbook(data.getData()); HSSFWorkbook wb = new HSSFWorkbook(data.getInputStream());
} else if ("Document".equals(name)) { } else if ("Document".equals(name)) {
HWPFDocument doc = new HWPFDocument(data.getData()); HWPFDocument doc = new HWPFDocument(data.getInputStream());
//read the word document //read the word document
Range r = doc.getRange(); Range r = doc.getRange();
for(int k = 0; k < r.numParagraphs(); k++) { for(int k = 0; k < r.numParagraphs(); k++) {
@ -93,8 +93,8 @@ public final class DataExtraction {
out.close(); out.close();
doc.close(); doc.close();
} else { } else {
FileOutputStream out = new FileOutputStream(ole.getProgID() + "-"+(oleIdx+1)+".dat"); FileOutputStream out = new FileOutputStream(ole.getProgId() + "-"+(oleIdx+1)+".dat");
InputStream dis = data.getData(); InputStream dis = data.getInputStream();
byte[] chunk = new byte[2048]; byte[] chunk = new byte[2048];
int count; int count;
while ((count = dis.read(chunk)) >= 0) { while ((count = dis.read(chunk)) >= 0) {

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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<S,P>,
P extends TextParagraph<S,P,? extends TextRun>
> extends Shape<S,P>, PlaceableShape<S,P> {
/**
* 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.<p>
*
* 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();
}

View File

@ -84,4 +84,11 @@ public interface ShapeContainer<
* @param numCols the number of columns * @param numCols the number of columns
*/ */
TableShape<S,P> createTable(int numRows, int numCols); TableShape<S,P> 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);
} }

View File

@ -18,20 +18,30 @@ package org.apache.poi.xslf.usermodel;
import java.awt.Color; import java.awt.Color;
import java.awt.geom.Rectangle2D; 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.poi.util.Beta;
import org.apache.xmlbeans.XmlObject; import org.apache.xmlbeans.XmlObject;
import org.openxmlformats.schemas.drawingml.x2006.main.CTNonVisualDrawingProps; import org.openxmlformats.schemas.drawingml.x2006.main.CTNonVisualDrawingProps;
import org.openxmlformats.schemas.presentationml.x2006.main.CTConnector; import org.openxmlformats.schemas.presentationml.x2006.main.CTConnector;
import org.openxmlformats.schemas.presentationml.x2006.main.CTGraphicalObjectFrame; import org.openxmlformats.schemas.presentationml.x2006.main.CTGraphicalObjectFrame;
import org.openxmlformats.schemas.presentationml.x2006.main.CTGroupShape; 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.CTPicture;
import org.openxmlformats.schemas.presentationml.x2006.main.CTShape; import org.openxmlformats.schemas.presentationml.x2006.main.CTShape;
/**
* @author Yegor Kozlov
*/
@Beta @Beta
public class XSLFDrawing { public class XSLFDrawing {
private XSLFSheet _sheet; private XSLFSheet _sheet;
@ -110,4 +120,12 @@ public class XSLFDrawing {
shape.setAnchor(new Rectangle2D.Double()); shape.setAnchor(new Rectangle2D.Double());
return shape; 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;
}
} }

View File

@ -37,6 +37,7 @@ import org.apache.poi.util.Units;
import org.apache.xmlbeans.XmlCursor; import org.apache.xmlbeans.XmlCursor;
import org.apache.xmlbeans.XmlException; import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlObject; 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.CTGraphicalObjectData;
import org.openxmlformats.schemas.drawingml.x2006.main.CTPoint2D; import org.openxmlformats.schemas.drawingml.x2006.main.CTPoint2D;
import org.openxmlformats.schemas.drawingml.x2006.main.CTPositiveSize2D; import org.openxmlformats.schemas.drawingml.x2006.main.CTPositiveSize2D;
@ -86,13 +87,24 @@ public class XSLFGraphicFrame extends XSLFShape implements GraphicalFrame<XSLFSh
static XSLFGraphicFrame create(CTGraphicalObjectFrame shape, XSLFSheet sheet){ static XSLFGraphicFrame create(CTGraphicalObjectFrame shape, XSLFSheet sheet){
String uri = shape.getGraphic().getGraphicData().getUri(); switch (getUri(shape)) {
if(XSLFTable.TABLE_URI.equals(uri)){ case XSLFTable.TABLE_URI:
return new XSLFTable(shape, sheet); return new XSLFTable(shape, sheet);
} else { case XSLFObjectShape.OLE_URI:
return new XSLFObjectShape(shape, sheet);
default:
return new XSLFGraphicFrame(shape, sheet); return new XSLFGraphicFrame(shape, sheet);
} }
} }
private static String getUri(CTGraphicalObjectFrame shape) {
final CTGraphicalObject g = shape.getGraphic();
if (g == null) {
return null;
}
CTGraphicalObjectData gd = g.getGraphicData();
return (gd == null) ? null : gd.getUri();
}
/** /**
* Rotate this shape. * Rotate this shape.

View File

@ -19,6 +19,7 @@
package org.apache.poi.xslf.usermodel; package org.apache.poi.xslf.usermodel;
import java.awt.Dimension;
import java.awt.geom.Rectangle2D; import java.awt.geom.Rectangle2D;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Iterator; import java.util.Iterator;
@ -44,6 +45,7 @@ import org.openxmlformats.schemas.presentationml.x2006.main.CTConnector;
import org.openxmlformats.schemas.presentationml.x2006.main.CTGraphicalObjectFrame; import org.openxmlformats.schemas.presentationml.x2006.main.CTGraphicalObjectFrame;
import org.openxmlformats.schemas.presentationml.x2006.main.CTGroupShape; import org.openxmlformats.schemas.presentationml.x2006.main.CTGroupShape;
import org.openxmlformats.schemas.presentationml.x2006.main.CTGroupShapeNonVisual; import org.openxmlformats.schemas.presentationml.x2006.main.CTGroupShapeNonVisual;
import org.openxmlformats.schemas.presentationml.x2006.main.CTOleObject;
import org.openxmlformats.schemas.presentationml.x2006.main.CTPicture; import org.openxmlformats.schemas.presentationml.x2006.main.CTPicture;
import org.openxmlformats.schemas.presentationml.x2006.main.CTShape; import org.openxmlformats.schemas.presentationml.x2006.main.CTShape;
@ -275,6 +277,29 @@ implements XSLFShapeContainer, GroupShape<XSLFShape,XSLFTextParagraph> {
return sh; 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(){ public XSLFTable createTable(){
XSLFTable sh = getDrawing().createTable(); XSLFTable sh = getDrawing().createTable();
_shapes.add(sh); _shapes.add(sh);

View File

@ -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;
}
}

View File

@ -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<XSLFShape,XSLFTextParagraph> {
/* 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;
}
}

View File

@ -231,6 +231,14 @@ public class XSLFRelation extends POIXMLRelation {
XSLFTableStyles.class 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<? extends POIXMLDocumentPart> cls) { private XSLFRelation(String type, String rel, String defaultName, Class<? extends POIXMLDocumentPart> cls) {
super(type, rel, defaultName, cls); super(type, rel, defaultName, cls);
_table.put(rel, this); _table.put(rel, this);

View File

@ -18,6 +18,7 @@ package org.apache.poi.xslf.usermodel;
import static org.apache.poi.POIXMLTypeLoader.DEFAULT_XML_OPTIONS; import static org.apache.poi.POIXMLTypeLoader.DEFAULT_XML_OPTIONS;
import java.awt.Dimension;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; 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.PictureData;
import org.apache.poi.sl.usermodel.Placeholder; import org.apache.poi.sl.usermodel.Placeholder;
import org.apache.poi.sl.usermodel.Sheet; 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.XmlCursor;
import org.apache.xmlbeans.XmlException; import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlObject; import org.apache.xmlbeans.XmlObject;
import org.apache.xmlbeans.XmlOptions; import org.apache.xmlbeans.XmlOptions;
import org.apache.xmlbeans.impl.values.XmlAnyTypeImpl; 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 @Beta
public abstract class XSLFSheet extends POIXMLDocumentPart public abstract class XSLFSheet extends POIXMLDocumentPart
@ -256,6 +267,28 @@ implements XSLFShapeContainer, Sheet<XSLFShape,XSLFTextParagraph> {
} }
@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 * Returns an iterator over the shapes in this sheet
* *

View File

@ -59,25 +59,28 @@ public class XSLFTable extends XSLFGraphicFrame implements Iterable<XSLFTableRow
CTGraphicalObjectData god = shape.getGraphic().getGraphicData(); CTGraphicalObjectData god = shape.getGraphic().getGraphicData();
XmlCursor xc = god.newCursor(); XmlCursor xc = god.newCursor();
if (!xc.toChild(DRAWINGML_URI, "tbl")) { try {
throw new IllegalStateException("a:tbl element was not found in\n " + god); if (!xc.toChild(DRAWINGML_URI, "tbl")) {
throw new IllegalStateException("a:tbl 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 CTTable 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 CTTable.class.getClassLoader()"
;
throw new IllegalStateException(errStr);
}
_table = (CTTable)xo;
} finally {
xc.dispose();
} }
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 CTTable 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 CTTable.class.getClassLoader()"
;
throw new IllegalStateException(errStr);
}
_table = (CTTable)xo;
xc.dispose();
_rows = new ArrayList<>(_table.sizeOfTrArray()); _rows = new ArrayList<>(_table.sizeOfTrArray());
for(CTTableRow row : _table.getTrArray()) { for(CTTableRow row : _table.getTrArray()) {
_rows.add(new XSLFTableRow(row, this)); _rows.add(new XSLFTableRow(row, this));

View File

@ -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<Object[]> 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;
}
}
}

View File

@ -29,7 +29,6 @@ import org.apache.poi.POIOLE2TextExtractor;
import org.apache.poi.hslf.model.Comment; import org.apache.poi.hslf.model.Comment;
import org.apache.poi.hslf.model.HSLFMetroShape; import org.apache.poi.hslf.model.HSLFMetroShape;
import org.apache.poi.hslf.model.HeadersFooters; 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.HSLFMasterSheet;
import org.apache.poi.hslf.usermodel.HSLFNotes; import org.apache.poi.hslf.usermodel.HSLFNotes;
import org.apache.poi.hslf.usermodel.HSLFShape; 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.HSLFTableCell;
import org.apache.poi.hslf.usermodel.HSLFTextParagraph; import org.apache.poi.hslf.usermodel.HSLFTextParagraph;
import org.apache.poi.hslf.usermodel.HSLFTextShape; 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.DirectoryNode;
import org.apache.poi.poifs.filesystem.NPOIFSFileSystem; import org.apache.poi.poifs.filesystem.NPOIFSFileSystem;
import org.apache.poi.poifs.filesystem.POIFSFileSystem; import org.apache.poi.poifs.filesystem.POIFSFileSystem;
@ -194,13 +194,13 @@ public final class PowerPointExtractor extends POIOLE2TextExtractor {
return getText(false, true); return getText(false, true);
} }
public List<OLEShape> getOLEShapes() { public List<HSLFObjectShape> getOLEShapes() {
List<OLEShape> list = new ArrayList<>(); List<HSLFObjectShape> list = new ArrayList<>();
for (HSLFSlide slide : _slides) { for (HSLFSlide slide : _slides) {
for (HSLFShape shape : slide.getShapes()) { for (HSLFShape shape : slide.getShapes()) {
if (shape instanceof OLEShape) { if (shape instanceof HSLFObjectShape) {
list.add((OLEShape) shape); list.add((HSLFObjectShape) shape);
} }
} }
} }

View File

@ -17,8 +17,8 @@
package org.apache.poi.hslf.record; package org.apache.poi.hslf.record;
import java.io.OutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream;
import org.apache.poi.util.LittleEndian; import org.apache.poi.util.LittleEndian;
import org.apache.poi.util.POILogger; import org.apache.poi.util.POILogger;
@ -31,7 +31,7 @@ public class ExEmbed extends RecordContainer {
/** /**
* Record header data. * Record header data.
*/ */
private byte[] _header; private final byte[] _header;
// Links to our more interesting children // Links to our more interesting children
private RecordAtom embedAtom; private RecordAtom embedAtom;
@ -47,7 +47,7 @@ public class ExEmbed extends RecordContainer {
* @param start the start offset into the byte array. * @param start the start offset into the byte array.
* @param len the length of the slice in 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 // Grab the header
_header = new byte[8]; _header = new byte[8];
System.arraycopy(source,start,_header,0,8); System.arraycopy(source,start,_header,0,8);
@ -62,7 +62,7 @@ public class ExEmbed extends RecordContainer {
* *
* @param embedAtom the new embedAtom * @param embedAtom the new embedAtom
*/ */
protected ExEmbed(RecordAtom embedAtom) { protected ExEmbed(final RecordAtom embedAtom) {
this(); this();
_children[0] = this.embedAtom = embedAtom; _children[0] = this.embedAtom = embedAtom;
} }
@ -81,11 +81,11 @@ public class ExEmbed extends RecordContainer {
LittleEndian.putShort(_header, 2, (short)getRecordType()); LittleEndian.putShort(_header, 2, (short)getRecordType());
// Setup our child records // Setup our child records
CString cs1 = new CString(); final CString cs1 = new CString();
cs1.setOptions(0x1 << 4); cs1.setOptions(0x1 << 4);
CString cs2 = new CString(); final CString cs2 = new CString();
cs2.setOptions(0x2 << 4); cs2.setOptions(0x2 << 4);
CString cs3 = new CString(); final CString cs3 = new CString();
cs3.setOptions(0x3 << 4); cs3.setOptions(0x3 << 4);
_children[0] = new ExEmbedAtom(); _children[0] = new ExEmbedAtom();
_children[1] = new ExOleObjAtom(); _children[1] = new ExOleObjAtom();
@ -117,8 +117,8 @@ public class ExEmbed extends RecordContainer {
for (int i = 2; i < _children.length; i++) { for (int i = 2; i < _children.length; i++) {
if (_children[i] instanceof CString){ if (_children[i] instanceof CString){
CString cs = (CString)_children[i]; final CString cs = (CString)_children[i];
int opts = cs.getOptions() >> 4; final int opts = cs.getOptions() >> 4;
switch(opts){ switch(opts){
case 0x1: menuName = cs; break; case 0x1: menuName = cs; break;
case 0x2: progId = cs; break; case 0x2: progId = cs; break;
@ -134,8 +134,7 @@ public class ExEmbed extends RecordContainer {
* *
* @return the {@link ExEmbedAtom}. * @return the {@link ExEmbedAtom}.
*/ */
public ExEmbedAtom getExEmbedAtom() public ExEmbedAtom getExEmbedAtom() {
{
return (ExEmbedAtom)embedAtom; return (ExEmbedAtom)embedAtom;
} }
@ -144,8 +143,7 @@ public class ExEmbed extends RecordContainer {
* *
* @return the {@link ExOleObjAtom}. * @return the {@link ExOleObjAtom}.
*/ */
public ExOleObjAtom getExOleObjAtom() public ExOleObjAtom getExOleObjAtom() {
{
return oleObjAtom; return oleObjAtom;
} }
@ -154,14 +152,13 @@ public class ExEmbed extends RecordContainer {
* *
* @return the name used for menus and the Links dialog box. * @return the name used for menus and the Links dialog box.
*/ */
public String getMenuName() public String getMenuName() {
{
return menuName == null ? null : menuName.getText(); return menuName == null ? null : menuName.getText();
} }
public void setMenuName(String s) public void setMenuName(final String menuName) {
{ this.menuName = safeCString(this.menuName, 0x1);
if(menuName != null) menuName.setText(s); this.menuName.setText(menuName);
} }
/** /**
@ -169,28 +166,29 @@ public class ExEmbed extends RecordContainer {
* *
* @return the OLE Programmatic Identifier. * @return the OLE Programmatic Identifier.
*/ */
public String getProgId() public String getProgId() {
{
return progId == null ? null : progId.getText(); return progId == null ? null : progId.getText();
} }
public void setProgId(String s) public void setProgId(final String progId) {
{ this.progId = safeCString(this.progId, 0x2);
if(progId != null) progId.setText(s); this.progId.setText(progId);
} }
/** /**
* Gets the name that appears in the paste special dialog. * Gets the name that appears in the paste special dialog.
* *
* @return 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(); return clipboardName == null ? null : clipboardName.getText();
} }
public void setClipboardName(String s) public void setClipboardName(final String clipboardName) {
{ this.clipboardName = safeCString(this.clipboardName, 0x3);
if(clipboardName != null) clipboardName.setText(s); this.clipboardName.setText(clipboardName);
} }
/** /**
@ -199,6 +197,7 @@ public class ExEmbed extends RecordContainer {
* *
* @return the record type. * @return the record type.
*/ */
@Override
public long getRecordType() { public long getRecordType() {
return RecordTypes.ExEmbed.typeID; return RecordTypes.ExEmbed.typeID;
} }
@ -210,7 +209,31 @@ public class ExEmbed extends RecordContainer {
* @param out the output stream. * @param out the output stream.
* @throws IOException if there was an error writing to the 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); 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;
}
} }

View File

@ -39,8 +39,6 @@ import org.apache.poi.util.Units;
/** /**
* Represents a group of shapes. * Represents a group of shapes.
*
* @author Yegor Kozlov
*/ */
public class HSLFGroupShape extends HSLFShape public class HSLFGroupShape extends HSLFShape
implements HSLFShapeContainer, GroupShape<HSLFShape,HSLFTextParagraph> { implements HSLFShapeContainer, GroupShape<HSLFShape,HSLFTextParagraph> {
@ -362,4 +360,15 @@ implements HSLFShapeContainer, GroupShape<HSLFShape,HSLFTextParagraph> {
addShape(s); addShape(s);
return 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;
}
} }

View File

@ -16,17 +16,25 @@
==================================================================== */ ==================================================================== */
package org.apache.poi.hslf.usermodel; package org.apache.poi.hslf.usermodel;
import java.io.InputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import org.apache.poi.hslf.record.ExOleObjStg; 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. * 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. * The record that contains the object data.
*/ */
@ -41,14 +49,19 @@ public class HSLFObjectData {
this.storage = storage; this.storage = storage;
} }
/** @Override
* Gets an input stream which returns the binary of the embedded data. public InputStream getInputStream() {
*
* @return the input stream which will contain the binary of the embedded data.
*/
public InputStream getData() {
return storage.getData(); return storage.getData();
} }
@Override
public OutputStream getOutputStream() throws IOException {
return new ByteArrayOutputStream(100000) {
public void close() throws IOException {
setData(getBytes());
}
};
}
/** /**
* Sets the embedded data. * Sets the embedded data.
@ -67,4 +80,15 @@ public class HSLFObjectData {
public ExOleObjStg getExOleObjStg() { public ExOleObjStg getExOleObjStg() {
return storage; return storage;
} }
@Override
public String getOLE2ClassName() {
return null;
}
@Override
public String getFileName() {
return null;
}
} }

View File

@ -15,7 +15,12 @@
limitations under the License. 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.EscherContainerRecord;
import org.apache.poi.ddf.EscherProperties; 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.HSLFEscherClientDataRecord;
import org.apache.poi.hslf.record.Record; import org.apache.poi.hslf.record.Record;
import org.apache.poi.hslf.record.RecordTypes; import org.apache.poi.hslf.record.RecordTypes;
import org.apache.poi.hslf.usermodel.HSLFObjectData; import org.apache.poi.poifs.filesystem.FileMagic;
import org.apache.poi.hslf.usermodel.HSLFPictureData; import org.apache.poi.poifs.filesystem.Ole10Native;
import org.apache.poi.hslf.usermodel.HSLFPictureShape; import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.hslf.usermodel.HSLFShape; import org.apache.poi.sl.usermodel.ObjectMetaData;
import org.apache.poi.hslf.usermodel.HSLFSlideShow; import org.apache.poi.sl.usermodel.ObjectMetaData.Application;
import org.apache.poi.hslf.usermodel.HSLFTextParagraph; import org.apache.poi.sl.usermodel.ObjectShape;
import org.apache.poi.sl.usermodel.ShapeContainer; import org.apache.poi.sl.usermodel.ShapeContainer;
import org.apache.poi.util.POILogFactory; import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger; import org.apache.poi.util.POILogger;
/** /**
* A shape representing embedded OLE obejct. * A shape representing embedded OLE object.
*/ */
public final class OLEShape extends HSLFPictureShape { public final class HSLFObjectShape extends HSLFPictureShape implements ObjectShape<HSLFShape,HSLFTextParagraph> {
private static final POILogger LOG = POILogFactory.getLogger(OLEShape.class); private static final POILogger LOG = POILogFactory.getLogger(HSLFObjectShape.class);
private ExEmbed _exEmbed; private ExEmbed _exEmbed;
@ -50,7 +55,7 @@ public final class OLEShape extends HSLFPictureShape {
* *
* @param data the picture data * @param data the picture data
*/ */
public OLEShape(HSLFPictureData data){ public HSLFObjectShape(HSLFPictureData data){
super(data); super(data);
} }
@ -60,7 +65,7 @@ public final class OLEShape extends HSLFPictureShape {
* @param data the picture data * @param data the picture data
* @param parent the parent shape * @param parent the parent shape
*/ */
public OLEShape(HSLFPictureData data, ShapeContainer<HSLFShape,HSLFTextParagraph> parent) { public HSLFObjectShape(HSLFPictureData data, ShapeContainer<HSLFShape,HSLFTextParagraph> parent) {
super(data, parent); super(data, parent);
} }
@ -71,7 +76,7 @@ public final class OLEShape extends HSLFPictureShape {
* this picture in the <code>Slide</code> * this picture in the <code>Slide</code>
* @param parent the parent shape of this picture * @param parent the parent shape of this picture
*/ */
public OLEShape(EscherContainerRecord escherRecord, ShapeContainer<HSLFShape,HSLFTextParagraph> parent){ public HSLFObjectShape(EscherContainerRecord escherRecord, ShapeContainer<HSLFShape,HSLFTextParagraph> parent){
super(escherRecord, parent); super(escherRecord, parent);
} }
@ -87,12 +92,12 @@ public final class OLEShape extends HSLFPictureShape {
/** /**
* Set the unique identifier for the OLE object and * Set the unique identifier for the OLE object and
* register it in the necessary structures * register it in the necessary structures
* *
* @param objectId the unique identifier for the OLE object * @param objectId the unique identifier for the OLE object
*/ */
public void setObjectID(int objectId){ public void setObjectID(int objectId){
setEscherProperty(EscherProperties.BLIP__PICTUREID, objectId); setEscherProperty(EscherProperties.BLIP__PICTUREID, objectId);
EscherContainerRecord ecr = getSpContainer(); EscherContainerRecord ecr = getSpContainer();
EscherSpRecord spRecord = ecr.getChildById(EscherSpRecord.RECORD_ID); EscherSpRecord spRecord = ecr.getChildById(EscherSpRecord.RECORD_ID);
spRecord.setFlags(spRecord.getFlags()|EscherSpRecord.FLAG_OLESHAPE); spRecord.setFlags(spRecord.getFlags()|EscherSpRecord.FLAG_OLESHAPE);
@ -111,14 +116,13 @@ public final class OLEShape extends HSLFPictureShape {
} }
uer.setExObjIdRef(objectId); uer.setExObjIdRef(objectId);
} }
/** /**
* Returns unique identifier for the OLE object. * Returns unique identifier for the OLE object.
* *
* @return the unique identifier for the OLE object * @return the unique identifier for the OLE object
*/ */
@SuppressWarnings("resource")
public HSLFObjectData getObjectData(){ public HSLFObjectData getObjectData(){
HSLFSlideShow ppt = getSheet().getSlideShow(); HSLFSlideShow ppt = getSheet().getSlideShow();
HSLFObjectData[] ole = ppt.getEmbeddedObjects(); HSLFObjectData[] ole = ppt.getEmbeddedObjects();
@ -129,9 +133,10 @@ public final class OLEShape extends HSLFPictureShape {
if(exEmbed != null) { if(exEmbed != null) {
int ref = exEmbed.getExOleObjAtom().getObjStgDataRef(); int ref = exEmbed.getExOleObjAtom().getObjStgDataRef();
for (int i = 0; i < ole.length; i++) { for (HSLFObjectData hod : ole) {
if(ole[i].getExOleObjStg().getPersistId() == ref) { if(hod.getExOleObjStg().getPersistId() == ref) {
data=ole[i]; 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 * 6. MetaFile( 4033), optional
* </p> * </p>
*/ */
@SuppressWarnings("resource")
public ExEmbed getExEmbed(){ public ExEmbed getExEmbed(){
if(_exEmbed == null){ return getExEmbed(false);
}
private ExEmbed getExEmbed(boolean create) {
if (_exEmbed == null) {
HSLFSlideShow ppt = getSheet().getSlideShow(); HSLFSlideShow ppt = getSheet().getSlideShow();
ExObjList lst = ppt.getDocumentRecord().getExObjList(false); ExObjList lst = ppt.getDocumentRecord().getExObjList(create);
if(lst == null){ if(lst == null){
LOG.log(POILogger.WARN, "ExObjList not found"); LOG.log(POILogger.WARN, "ExObjList not found");
return null; return null;
} }
int id = getObjectID(); int id = getObjectID();
Record[] ch = lst.getChildRecords(); for (Record ch : lst.getChildRecords()) {
for (int i = 0; i < ch.length; i++) { if(ch instanceof ExEmbed){
if(ch[i] instanceof ExEmbed){ ExEmbed embd = (ExEmbed)ch;
ExEmbed embd = (ExEmbed)ch[i]; if( embd.getExOleObjAtom().getObjID() == id) {
if( embd.getExOleObjAtom().getObjID() == id) _exEmbed = embd; _exEmbed = embd;
}
} }
} }
if (_exEmbed == null && create) {
_exEmbed = new ExEmbed();
_exEmbed.getExOleObjAtom().setObjID(id);
lst.appendChildRecord(_exEmbed);
}
} }
return _exEmbed; return _exEmbed;
} }
/** /**
* Returns the instance name of the embedded object, e.g. "Document" or "Workbook". * 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(); return (ee == null) ? null : ee.getMenuName();
} }
/** @Override
* 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
*/
public String getFullName(){ public String getFullName(){
ExEmbed ee = getExEmbed(); ExEmbed ee = getExEmbed();
return (ee == null) ? null : ee.getClipboardName(); return (ee == null) ? null : ee.getClipboardName();
} }
/** public void setFullName(final String fullName) {
* Returns the ProgID that stores the OLE Programmatic Identifier. getExEmbed(true).setClipboardName(fullName);
* A ProgID is a string that uniquely identifies a given object, for example, }
* "Word.Document.8" or "Excel.Sheet.8".
* @Override
* @return the ProgID public String getProgId(){
*/
public String getProgID(){
ExEmbed ee = getExEmbed(); ExEmbed ee = getExEmbed();
return (ee == null) ? null : ee.getProgId(); 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());
}
}
};
}
} }

View File

@ -45,5 +45,6 @@ public interface HSLFShapeContainer extends ShapeContainer<HSLFShape,HSLFTextPar
@Override @Override
HSLFPictureShape createPicture(PictureData pictureData); HSLFPictureShape createPicture(PictureData pictureData);
@Override
HSLFObjectShape createOleShape(PictureData pictureData);
} }

View File

@ -31,7 +31,6 @@ import org.apache.poi.ddf.EscherSimpleProperty;
import org.apache.poi.ddf.EscherSpRecord; import org.apache.poi.ddf.EscherSpRecord;
import org.apache.poi.ddf.EscherTextboxRecord; import org.apache.poi.ddf.EscherTextboxRecord;
import org.apache.poi.hslf.model.MovieShape; import org.apache.poi.hslf.model.MovieShape;
import org.apache.poi.hslf.model.OLEShape;
import org.apache.poi.hslf.record.ExObjRefAtom; import org.apache.poi.hslf.record.ExObjRefAtom;
import org.apache.poi.hslf.record.HSLFEscherClientDataRecord; import org.apache.poi.hslf.record.HSLFEscherClientDataRecord;
import org.apache.poi.hslf.record.InteractiveInfo; import org.apache.poi.hslf.record.InteractiveInfo;
@ -134,7 +133,7 @@ public final class HSLFShapeFactory {
if(info != null && info.getInteractiveInfoAtom() != null){ if(info != null && info.getInteractiveInfoAtom() != null){
switch(info.getInteractiveInfoAtom().getAction()){ switch(info.getInteractiveInfoAtom().getAction()){
case InteractiveInfoAtom.ACTION_OLE: case InteractiveInfoAtom.ACTION_OLE:
return new OLEShape(spContainer, parent); return new HSLFObjectShape(spContainer, parent);
case InteractiveInfoAtom.ACTION_MEDIA: case InteractiveInfoAtom.ACTION_MEDIA:
return new MovieShape(spContainer, parent); return new MovieShape(spContainer, parent);
default: default:
@ -144,7 +143,7 @@ public final class HSLFShapeFactory {
ExObjRefAtom oes = getClientDataRecord(spContainer, RecordTypes.ExObjRefAtom.typeID); ExObjRefAtom oes = getClientDataRecord(spContainer, RecordTypes.ExObjRefAtom.typeID);
return (oes != null) return (oes != null)
? new OLEShape(spContainer, parent) ? new HSLFObjectShape(spContainer, parent)
: new HSLFPictureShape(spContainer, parent); : new HSLFPictureShape(spContainer, parent);
} }

View File

@ -442,4 +442,15 @@ public abstract class HSLFSheet implements HSLFShapeContainer, Sheet<HSLFShape,H
addShape(s); addShape(s);
return 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);
s.setAnchor(new Rectangle2D.Double(0, 0, 100, 100));
addShape(s);
return s;
}
} }

View File

@ -33,9 +33,9 @@ import java.io.InputStream;
import java.util.List; import java.util.List;
import org.apache.poi.POIDataSamples; import org.apache.poi.POIDataSamples;
import org.apache.poi.hslf.model.OLEShape;
import org.apache.poi.hslf.usermodel.HSLFSlideShow; import org.apache.poi.hslf.usermodel.HSLFSlideShow;
import org.apache.poi.hslf.usermodel.HSLFSlideShowImpl; import org.apache.poi.hslf.usermodel.HSLFSlideShowImpl;
import org.apache.poi.hslf.usermodel.HSLFObjectShape;
import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hwpf.HWPFDocument; import org.apache.poi.hwpf.HWPFDocument;
import org.apache.poi.poifs.filesystem.DirectoryNode; import org.apache.poi.poifs.filesystem.DirectoryNode;
@ -206,12 +206,12 @@ public final class TestExtractor {
@Test @Test
public void testExtractFromOwnEmbeded() throws IOException { public void testExtractFromOwnEmbeded() throws IOException {
PowerPointExtractor ppe = openExtractor("ppt_with_embeded.ppt"); PowerPointExtractor ppe = openExtractor("ppt_with_embeded.ppt");
List<OLEShape> shapes = ppe.getOLEShapes(); List<HSLFObjectShape> shapes = ppe.getOLEShapes();
assertEquals("Expected 6 ole shapes", 6, shapes.size()); assertEquals("Expected 6 ole shapes", 6, shapes.size());
int num_ppt = 0, num_doc = 0, num_xls = 0; int num_ppt = 0, num_doc = 0, num_xls = 0;
for (OLEShape ole : shapes) { for (HSLFObjectShape ole : shapes) {
String name = ole.getInstanceName(); String name = ole.getInstanceName();
InputStream data = ole.getObjectData().getData(); InputStream data = ole.getObjectData().getInputStream();
if ("Worksheet".equals(name)) { if ("Worksheet".equals(name)) {
HSSFWorkbook wb = new HSSFWorkbook(data); HSSFWorkbook wb = new HSSFWorkbook(data);
num_xls++; num_xls++;
@ -239,8 +239,8 @@ public final class TestExtractor {
@Test @Test
public void test52991() throws IOException { public void test52991() throws IOException {
PowerPointExtractor ppe = openExtractor("badzip.ppt"); PowerPointExtractor ppe = openExtractor("badzip.ppt");
for (OLEShape shape : ppe.getOLEShapes()) { for (HSLFObjectShape shape : ppe.getOLEShapes()) {
IOUtils.copy(shape.getObjectData().getData(), new ByteArrayOutputStream()); IOUtils.copy(shape.getObjectData().getInputStream(), new ByteArrayOutputStream());
} }
ppe.close(); ppe.close();
} }

View File

@ -35,6 +35,7 @@ import org.apache.poi.hslf.usermodel.HSLFShape;
import org.apache.poi.hslf.usermodel.HSLFSlide; import org.apache.poi.hslf.usermodel.HSLFSlide;
import org.apache.poi.hslf.usermodel.HSLFSlideShow; import org.apache.poi.hslf.usermodel.HSLFSlideShow;
import org.apache.poi.hslf.usermodel.HSLFSlideShowImpl; 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.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hwpf.HWPFDocument; import org.apache.poi.hwpf.HWPFDocument;
@ -70,7 +71,7 @@ public final class TestOleEmbedding {
HSLFObjectData[] objects = slideShow.getEmbeddedObjects(); HSLFObjectData[] objects = slideShow.getEmbeddedObjects();
assertEquals("Should be two objects", 2, objects.length); assertEquals("Should be two objects", 2, objects.length);
for (HSLFObjectData od : objects) { for (HSLFObjectData od : objects) {
long checkEMF = IOUtils.calculateChecksum(od.getData()); long checkEMF = IOUtils.calculateChecksum(od.getInputStream());
assertEquals(checkSums[checkId++], checkEMF); assertEquals(checkSums[checkId++], checkEMF);
} }
@ -86,13 +87,13 @@ public final class TestOleEmbedding {
HSLFSlide slide = ppt.getSlides().get(0); HSLFSlide slide = ppt.getSlides().get(0);
int cnt = 0; int cnt = 0;
for (HSLFShape sh : slide.getShapes()) { for (HSLFShape sh : slide.getShapes()) {
if(sh instanceof OLEShape){ if(sh instanceof HSLFObjectShape){
cnt++; cnt++;
OLEShape ole = (OLEShape)sh; HSLFObjectShape ole = (HSLFObjectShape)sh;
HSLFObjectData data = ole.getObjectData(); HSLFObjectData data = ole.getObjectData();
if("Worksheet".equals(ole.getInstanceName())){ if("Worksheet".equals(ole.getInstanceName())){
//Voila! we created a workbook from the embedded OLE data //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); HSSFSheet sheet = wb.getSheetAt(0);
//verify we can access the xls data //verify we can access the xls data
assertEquals(1, sheet.getRow(0).getCell(0).getNumericCellValue(), 0); assertEquals(1, sheet.getRow(0).getCell(0).getNumericCellValue(), 0);
@ -103,7 +104,7 @@ public final class TestOleEmbedding {
wb.close(); wb.close();
} else if ("Document".equals(ole.getInstanceName())){ } else if ("Document".equals(ole.getInstanceName())){
//creating a HWPF document //creating a HWPF document
HWPFDocument doc = new HWPFDocument(data.getData()); HWPFDocument doc = new HWPFDocument(data.getInputStream());
String txt = doc.getRange().getParagraph(0).text(); String txt = doc.getRange().getParagraph(0).text();
assertEquals("OLE embedding is thoroughly unremarkable.\r", txt); assertEquals("OLE embedding is thoroughly unremarkable.\r", txt);
doc.close(); doc.close();
@ -129,14 +130,14 @@ public final class TestOleEmbedding {
int oleObjectId1 = ppt.addEmbed(poiData1); int oleObjectId1 = ppt.addEmbed(poiData1);
HSLFSlide slide1 = ppt.createSlide(); HSLFSlide slide1 = ppt.createSlide();
OLEShape oleShape1 = new OLEShape(pictData); HSLFObjectShape oleShape1 = new HSLFObjectShape(pictData);
oleShape1.setObjectID(oleObjectId1); oleShape1.setObjectID(oleObjectId1);
slide1.addShape(oleShape1); slide1.addShape(oleShape1);
oleShape1.setAnchor(new Rectangle2D.Double(100,100,100,100)); oleShape1.setAnchor(new Rectangle2D.Double(100,100,100,100));
// add second slide with different order in object creation // add second slide with different order in object creation
HSLFSlide slide2 = ppt.createSlide(); HSLFSlide slide2 = ppt.createSlide();
OLEShape oleShape2 = new OLEShape(pictData); HSLFObjectShape oleShape2 = new HSLFObjectShape(pictData);
is = POIDataSamples.getSpreadSheetInstance().openResourceAsStream("SimpleWithImages.xls"); is = POIDataSamples.getSpreadSheetInstance().openResourceAsStream("SimpleWithImages.xls");
POIFSFileSystem poiData2 = new POIFSFileSystem(is); POIFSFileSystem poiData2 = new POIFSFileSystem(is);
@ -152,8 +153,8 @@ public final class TestOleEmbedding {
ppt.write(bos); ppt.write(bos);
ppt = new HSLFSlideShow(new ByteArrayInputStream(bos.toByteArray())); ppt = new HSLFSlideShow(new ByteArrayInputStream(bos.toByteArray()));
OLEShape comp = (OLEShape)ppt.getSlides().get(0).getShapes().get(0); HSLFObjectShape comp = (HSLFObjectShape)ppt.getSlides().get(0).getShapes().get(0);
byte compData[] = IOUtils.toByteArray(comp.getObjectData().getData()); byte compData[] = IOUtils.toByteArray(comp.getObjectData().getInputStream());
bos.reset(); bos.reset();
poiData1.writeFilesystem(bos); poiData1.writeFilesystem(bos);

View File

@ -1010,7 +1010,7 @@ public final class TestBugs {
long persistId = vbaAtom.getPersistIdRef(); long persistId = vbaAtom.getPersistIdRef();
for (HSLFObjectData objData : ppt.getEmbeddedObjects()) { for (HSLFObjectData objData : ppt.getEmbeddedObjects()) {
if (objData.getExOleObjStg().getPersistId() == persistId) { if (objData.getExOleObjStg().getPersistId() == persistId) {
VBAMacroReader mr = new VBAMacroReader(objData.getData()); VBAMacroReader mr = new VBAMacroReader(objData.getInputStream());
try { try {
return mr.readMacros(); return mr.readMacros();
} finally { } finally {