#60586 - Support embedding OLE1.0 package in XSSF / SS Common

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1778869 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Andreas Beeker 2017-01-15 02:04:57 +00:00
parent 86eeda4cbd
commit 5899f8884d
10 changed files with 409 additions and 37 deletions

View File

@ -48,6 +48,7 @@ import org.apache.poi.poifs.filesystem.DirectoryNode;
import org.apache.poi.ss.usermodel.Chart;
import org.apache.poi.ss.usermodel.ClientAnchor;
import org.apache.poi.ss.usermodel.Drawing;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.util.HexDump;
import org.apache.poi.util.Internal;
import org.apache.poi.util.NotImplemented;
@ -137,6 +138,7 @@ public final class HSSFPatriarch implements HSSFShapeContainer, Drawing<HSSFShap
* @param shape to be removed
* @return true of shape is removed
*/
@Override
public boolean removeShape(HSSFShape shape) {
boolean isRemoved = _mainSpgrContainer.removeChildRecord(shape.getEscherContainer());
if (isRemoved){
@ -214,22 +216,13 @@ public final class HSSFPatriarch implements HSSFShapeContainer, Drawing<HSSFShap
*
* @return newly created shape
*/
@Override
public HSSFPicture createPicture(ClientAnchor anchor, int pictureIndex) {
return createPicture((HSSFClientAnchor) anchor, pictureIndex);
}
/**
* Adds a new OLE Package Shape
*
* @param anchor the client anchor describes how this picture is
* attached to the sheet.
* @param storageId the storageId returned by {@link HSSFWorkbook#addOlePackage(POIFSFileSystem,String,String,String)}
* @param pictureIndex the index of the picture (used as preview image) in the
* workbook collection of pictures.
*
* @return newly created shape
*/
public HSSFObjectData createObjectData(HSSFClientAnchor anchor, int storageId, int pictureIndex) {
@Override
public HSSFObjectData createObjectData(ClientAnchor anchor, int storageId, int pictureIndex) {
ObjRecord obj = new ObjRecord();
CommonObjectDataSubRecord ftCmo = new CommonObjectDataSubRecord();
@ -248,15 +241,15 @@ public final class HSSFPatriarch implements HSSFShapeContainer, Drawing<HSSFShap
FtCfSubRecord ftCf = new FtCfSubRecord();
HSSFPictureData pictData = getSheet().getWorkbook().getAllPictures().get(pictureIndex-1);
switch (pictData.getFormat()) {
case HSSFWorkbook.PICTURE_TYPE_WMF:
case HSSFWorkbook.PICTURE_TYPE_EMF:
case Workbook.PICTURE_TYPE_WMF:
case Workbook.PICTURE_TYPE_EMF:
// this needs patch #49658 to be applied to actually work
ftCf.setFlags(FtCfSubRecord.METAFILE_BIT);
break;
case HSSFWorkbook.PICTURE_TYPE_DIB:
case HSSFWorkbook.PICTURE_TYPE_PNG:
case HSSFWorkbook.PICTURE_TYPE_JPEG:
case HSSFWorkbook.PICTURE_TYPE_PICT:
case Workbook.PICTURE_TYPE_DIB:
case Workbook.PICTURE_TYPE_PNG:
case Workbook.PICTURE_TYPE_JPEG:
case Workbook.PICTURE_TYPE_PICT:
ftCf.setFlags(FtCfSubRecord.BITMAP_BIT);
break;
default:
@ -280,14 +273,16 @@ public final class HSSFPatriarch implements HSSFShapeContainer, Drawing<HSSFShap
DirectoryEntry oleRoot;
try {
DirectoryNode dn = _sheet.getWorkbook().getDirectory();
if (dn == null) throw new FileNotFoundException();
if (dn == null) {
throw new FileNotFoundException();
}
oleRoot = (DirectoryEntry)dn.getEntry(entryName);
} catch (FileNotFoundException e) {
throw new IllegalStateException("trying to add ole shape without actually adding data first - use HSSFWorkbook.addOlePackage first", e);
}
// create picture shape, which need to be minimal modified for oleshapes
HSSFPicture shape = new HSSFPicture(null, anchor);
HSSFPicture shape = new HSSFPicture(null, (HSSFClientAnchor)anchor);
shape.setPictureIndex(pictureIndex);
EscherContainerRecord spContainer = shape.getEscherContainer();
EscherSpRecord spRecord = spContainer.getChildById(EscherSpRecord.RECORD_ID);
@ -355,6 +350,7 @@ public final class HSSFPatriarch implements HSSFShapeContainer, Drawing<HSSFShap
return shape;
}
@Override
public HSSFComment createCellComment(ClientAnchor anchor) {
return createComment((HSSFAnchor) anchor);
}
@ -362,6 +358,7 @@ public final class HSSFPatriarch implements HSSFShapeContainer, Drawing<HSSFShap
/**
* Returns a unmodifiable list of all shapes contained by the patriarch.
*/
@Override
public List<HSSFShape> getChildren() {
return Collections.unmodifiableList(_shapes);
}
@ -369,6 +366,7 @@ public final class HSSFPatriarch implements HSSFShapeContainer, Drawing<HSSFShap
/**
* add a shape to this drawing
*/
@Override
@Internal
public void addShape(HSSFShape shape) {
shape.setPatriarch(this);
@ -405,6 +403,7 @@ public final class HSSFPatriarch implements HSSFShapeContainer, Drawing<HSSFShap
* Sets the coordinate space of this group. All children are constrained
* to these coordinates.
*/
@Override
public void setCoordinates(int x1, int y1, int x2, int y2) {
_spgrRecord.setRectY1(y1);
_spgrRecord.setRectY2(y2);
@ -415,6 +414,7 @@ public final class HSSFPatriarch implements HSSFShapeContainer, Drawing<HSSFShap
/**
* remove all shapes inside patriarch
*/
@Override
public void clear() {
ArrayList <HSSFShape> copy = new ArrayList<HSSFShape>(_shapes);
for (HSSFShape shape: copy){
@ -469,6 +469,7 @@ public final class HSSFPatriarch implements HSSFShapeContainer, Drawing<HSSFShap
/**
* @return x coordinate of the left up corner
*/
@Override
public int getX1() {
return _spgrRecord.getRectX1();
}
@ -476,6 +477,7 @@ public final class HSSFPatriarch implements HSSFShapeContainer, Drawing<HSSFShap
/**
* @return y coordinate of the left up corner
*/
@Override
public int getY1() {
return _spgrRecord.getRectY1();
}
@ -483,6 +485,7 @@ public final class HSSFPatriarch implements HSSFShapeContainer, Drawing<HSSFShap
/**
* @return x coordinate of the right down corner
*/
@Override
public int getX2() {
return _spgrRecord.getRectX2();
}
@ -490,6 +493,7 @@ public final class HSSFPatriarch implements HSSFShapeContainer, Drawing<HSSFShap
/**
* @return y coordinate of the right down corner
*/
@Override
public int getY2() {
return _spgrRecord.getRectY2();
}
@ -517,10 +521,12 @@ public final class HSSFPatriarch implements HSSFShapeContainer, Drawing<HSSFShap
* @param row2 the row (0 based) of the second cell.
* @return the newly created client anchor
*/
@Override
public HSSFClientAnchor createAnchor(int dx1, int dy1, int dx2, int dy2, int col1, int row1, int col2, int row2) {
return new HSSFClientAnchor(dx1, dy1, dx2, dy2, (short) col1, row1, (short) col2, row2);
}
@Override
@NotImplemented
public Chart createChart(ClientAnchor anchor) {
throw new RuntimeException("NotImplemented");

View File

@ -2048,6 +2048,16 @@ public final class HSSFWorkbook extends POIDocument implements org.apache.poi.ss
return olemap;
}
/**
* Adds an OLE package manager object with the given POIFS to the sheet
*
* @param poiData an POIFS containing the embedded document, to be added
* @param label the label of the payload
* @param fileName the original filename
* @param command the command to open the payload
* @return the index of the added ole object
* @throws IOException if the object can't be embedded
*/
public int addOlePackage(POIFSFileSystem poiData, String label, String fileName, String command)
throws IOException {
DirectoryNode root = poiData.getRoot();
@ -2064,6 +2074,7 @@ public final class HSSFWorkbook extends POIDocument implements org.apache.poi.ss
return addOlePackage(bos.toByteArray(), label, fileName, command);
}
@Override
public int addOlePackage(byte[] oleData, String label, String fileName, String command)
throws IOException {
// check if we were created by POIFS otherwise create a new dummy POIFS for storing the package data

View File

@ -62,4 +62,17 @@ public interface Drawing<T extends Shape> extends ShapeContainer<T> {
* @return the newly created client anchor
*/
ClientAnchor createAnchor(int dx1, int dy1, int dx2, int dy2, int col1, int row1, int col2, int row2);
/**
* Adds a new OLE Package Shape
*
* @param anchor the client anchor describes how this picture is
* attached to the sheet.
* @param storageId the storageId returned by {@link Workbook#addOlePackage(byte[], String, String, String)}
* @param pictureIndex the index of the picture (used as preview image) in the
* workbook collection of pictures.
*
* @return newly created shape
*/
ObjectData createObjectData(ClientAnchor anchor, int storageId, int pictureIndex);
}

View File

@ -286,6 +286,7 @@ public interface Workbook extends Closeable, Iterable<Sheet> {
* @return the font with the matched attributes or <code>null</code>
* @deprecated POI 3.15 beta 2. Use {@link #findFont(boolean, short, short, String, boolean, boolean, short, byte)} instead.
*/
@Deprecated
Font findFont(short boldWeight, short color, short fontHeight, String name, boolean italic, boolean strikeout, short typeOffset, byte underline);
/**
@ -635,4 +636,18 @@ public interface Workbook extends Closeable, Iterable<Sheet> {
* @since 3.14 beta 2
*/
SpreadsheetVersion getSpreadsheetVersion();
/**
* Adds an OLE package manager object with the given content to the sheet
*
* @param oleData the payload
* @param label the label of the payload
* @param fileName the original filename
* @param command the command to open the payload
*
* @return the index of the added ole object, i.e. the storage id
*
* @throws IOException if the object can't be embedded
*/
int addOlePackage(byte[] oleData, String label, String fileName, String command) throws IOException;
}

View File

@ -23,6 +23,7 @@ import org.apache.poi.ss.usermodel.Chart;
import org.apache.poi.ss.usermodel.ClientAnchor;
import org.apache.poi.ss.usermodel.Comment;
import org.apache.poi.ss.usermodel.Drawing;
import org.apache.poi.ss.usermodel.ObjectData;
import org.apache.poi.xssf.usermodel.XSSFDrawing;
import org.apache.poi.xssf.usermodel.XSSFPicture;
import org.apache.poi.xssf.usermodel.XSSFShape;
@ -62,9 +63,15 @@ public class SXSSFDrawing implements Drawing<XSSFShape> {
return _drawing.createAnchor(dx1, dy1, dx2, dy2, col1, row1, col2, row2);
}
@Override
public ObjectData createObjectData(ClientAnchor anchor, int storageId, int pictureIndex) {
return _drawing.createObjectData(anchor, storageId, pictureIndex);
}
@Override
public Iterator<XSSFShape> iterator() {
return _drawing.getShapes().iterator();
}
}

View File

@ -1391,5 +1391,10 @@ public class SXSSFWorkbook implements Workbook {
return SpreadsheetVersion.EXCEL2007;
}
@Override
public int addOlePackage(byte[] oleData, String label, String fileName, String command) throws IOException {
return _wb.addOlePackage(oleData, label, fileName, command);
}
//end of interface implementation
}

View File

@ -28,9 +28,16 @@ import java.util.List;
import javax.xml.namespace.QName;
import org.apache.poi.POIXMLDocument;
import org.apache.poi.POIXMLDocumentPart;
import org.apache.poi.POIXMLException;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.openxml4j.opc.PackagePart;
import org.apache.poi.openxml4j.opc.PackagePartName;
import org.apache.poi.openxml4j.opc.PackageRelationship;
import org.apache.poi.openxml4j.opc.PackageRelationshipTypes;
import org.apache.poi.openxml4j.opc.PackagingURIHelper;
import org.apache.poi.openxml4j.opc.TargetMode;
import org.apache.poi.ss.usermodel.ClientAnchor;
import org.apache.poi.ss.usermodel.Drawing;
import org.apache.poi.ss.util.CellAddress;
@ -45,7 +52,9 @@ import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlObject;
import org.apache.xmlbeans.XmlOptions;
import org.apache.xmlbeans.impl.values.XmlAnyTypeImpl;
import org.openxmlformats.schemas.drawingml.x2006.main.CTBlipFillProperties;
import org.openxmlformats.schemas.drawingml.x2006.main.CTGroupTransform2D;
import org.openxmlformats.schemas.drawingml.x2006.main.CTNonVisualDrawingProps;
import org.openxmlformats.schemas.drawingml.x2006.main.CTPoint2D;
import org.openxmlformats.schemas.drawingml.x2006.main.CTPositiveSize2D;
import org.openxmlformats.schemas.drawingml.x2006.main.CTTransform2D;
@ -59,19 +68,22 @@ import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTPicture;
import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTShape;
import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTTwoCellAnchor;
import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.STEditAs;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTOleObject;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTOleObjects;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTWorksheet;
/**
* Represents a SpreadsheetML drawing
*/
public final class XSSFDrawing extends POIXMLDocumentPart implements Drawing<XSSFShape> {
private static final POILogger LOG = POILogFactory.getLogger(XSSFDrawing.class);
/**
* Root element of the SpreadsheetML Drawing part
*/
private CTDrawing drawing;
private long numOfGraphicFrames = 0L;
protected static final String NAMESPACE_A = XSSFRelation.NS_DRAWINGML;
protected static final String NAMESPACE_C = XSSFRelation.NS_CHART;
@ -90,7 +102,7 @@ public final class XSSFDrawing extends POIXMLDocumentPart implements Drawing<XSS
*
* @param part the package part holding the drawing data,
* the content type must be <code>application/vnd.openxmlformats-officedocument.drawing+xml</code>
*
*
* @since POI 3.14-Beta1
*/
public XSSFDrawing(PackagePart part) throws IOException, XmlException {
@ -105,7 +117,7 @@ public final class XSSFDrawing extends POIXMLDocumentPart implements Drawing<XSS
is.close();
}
}
/**
* Construct a new CTDrawing bean. By default, it's just an empty placeholder for drawing objects
*
@ -194,7 +206,7 @@ public final class XSSFDrawing extends POIXMLDocumentPart implements Drawing<XSS
shape.anchor = anchor;
shape.setPictureReference(rel);
ctShape.getSpPr().setXfrm(createXfrm(anchor));
return shape;
}
@ -319,7 +331,7 @@ public final class XSSFDrawing extends POIXMLDocumentPart implements Drawing<XSS
@Override
public XSSFComment createCellComment(ClientAnchor anchor) {
XSSFClientAnchor ca = (XSSFClientAnchor)anchor;
XSSFSheet sheet = (XSSFSheet)getParent();
XSSFSheet sheet = getSheet();
//create comments and vmlDrawing parts if they don't exist
CommentsTable comments = sheet.getCommentsTable(true);
@ -344,7 +356,7 @@ public final class XSSFDrawing extends POIXMLDocumentPart implements Drawing<XSS
if(comments.findCellComment(ref) != null) {
throw new IllegalArgumentException("Multiple cell comments in one cell are not allowed, cell: " + ref);
}
return new XSSFComment(comments, comments.newComment(ref), vmlShape);
}
@ -368,7 +380,85 @@ public final class XSSFDrawing extends POIXMLDocumentPart implements Drawing<XSS
graphicFrame.setName("Diagramm" + frameId);
return graphicFrame;
}
@Override
public XSSFObjectData createObjectData(ClientAnchor anchor, int storageId, int pictureIndex) {
XSSFSheet sh = getSheet();
PackagePart sheetPart = sh.getPackagePart();
long shapeId = newShapeId();
// add reference to OLE part
PackagePartName olePN;
try {
olePN = PackagingURIHelper.createPartName( "/xl/embeddings/oleObject"+storageId+".bin" );
} catch (InvalidFormatException e) {
throw new POIXMLException(e);
}
PackageRelationship olePR = sheetPart.addRelationship( olePN, TargetMode.INTERNAL, POIXMLDocument.OLE_OBJECT_REL_TYPE );
// add reference to image part
XSSFPictureData imgPD = sh.getWorkbook().getAllPictures().get(pictureIndex);
PackagePartName imgPN = imgPD.getPackagePart().getPartName();
PackageRelationship imgSheetPR = sheetPart.addRelationship( imgPN, TargetMode.INTERNAL, PackageRelationshipTypes.IMAGE_PART );
PackageRelationship imgDrawPR = getPackagePart().addRelationship( imgPN, TargetMode.INTERNAL, PackageRelationshipTypes.IMAGE_PART );
// add OLE part metadata to sheet
CTWorksheet cwb = sh.getCTWorksheet();
CTOleObjects oo = cwb.isSetOleObjects() ? cwb.getOleObjects() : cwb.addNewOleObjects();
CTOleObject ole1 = oo.addNewOleObject();
ole1.setProgId("Package");
ole1.setShapeId(shapeId);
ole1.setId(olePR.getId());
XmlCursor cur1 = ole1.newCursor();
cur1.toEndToken();
cur1.beginElement("objectPr", XSSFRelation.NS_SPREADSHEETML);
cur1.insertAttributeWithValue("id", PackageRelationshipTypes.CORE_PROPERTIES_ECMA376_NS, imgSheetPR.getId());
cur1.insertAttributeWithValue("defaultSize", "0");
cur1.beginElement("anchor", XSSFRelation.NS_SPREADSHEETML);
cur1.insertAttributeWithValue("moveWithCells", "1");
CTTwoCellAnchor ctAnchor = createTwoCellAnchor((XSSFClientAnchor)anchor);
XmlCursor cur2 = ctAnchor.newCursor();
cur2.copyXmlContents(cur1);
cur2.dispose();
cur1.toParent();
cur1.toFirstChild();
cur1.setName(new QName(XSSFRelation.NS_SPREADSHEETML, "from"));
cur1.toNextSibling();
cur1.setName(new QName(XSSFRelation.NS_SPREADSHEETML, "to"));
cur1.dispose();
// add a new shape and link OLE & image part
CTShape ctShape = ctAnchor.addNewSp();
ctShape.set(XSSFObjectData.prototype());
ctShape.getSpPr().setXfrm(createXfrm((XSSFClientAnchor)anchor));
// workaround for not having the vmlDrawing filled
CTBlipFillProperties blipFill = ctShape.getSpPr().addNewBlipFill();
blipFill.addNewBlip().setEmbed(imgDrawPR.getId());
blipFill.addNewStretch().addNewFillRect();
CTNonVisualDrawingProps cNvPr = ctShape.getNvSpPr().getCNvPr();
cNvPr.setId(shapeId);
XmlCursor extCur = cNvPr.getExtLst().getExtArray(0).newCursor();
extCur.toFirstChild();
extCur.setAttributeText(new QName("spid"), "_x0000_s"+shapeId);
extCur.dispose();
XSSFObjectData shape = new XSSFObjectData(this, ctShape);
shape.anchor = (XSSFClientAnchor)anchor;
return shape;
}
/**
* Returns all charts in this drawing.
*/
@ -410,7 +500,7 @@ public final class XSSFDrawing extends POIXMLDocumentPart implements Drawing<XSS
CTPoint2D off = xfrm.addNewOff();
off.setX(anchor.getDx1());
off.setY(anchor.getDy1());
XSSFSheet sheet = (XSSFSheet)getParent();
XSSFSheet sheet = getSheet();
double widthPx = 0;
for (int col=anchor.getCol1(); col<anchor.getCol2(); col++) {
widthPx += sheet.getColumnWidthInPixels(col);
@ -424,11 +514,11 @@ public final class XSSFDrawing extends POIXMLDocumentPart implements Drawing<XSS
CTPositiveSize2D ext = xfrm.addNewExt();
ext.setCx(width - anchor.getDx1() + anchor.getDx2());
ext.setCy(height - anchor.getDy1() + anchor.getDy2());
// TODO: handle vflip/hflip
return xfrm;
}
private long newShapeId(){
return drawing.sizeOfTwoCellAnchorArray() + 1;
}
@ -462,7 +552,7 @@ public final class XSSFDrawing extends POIXMLDocumentPart implements Drawing<XSS
}
return lst;
}
private void addShapes(XmlCursor cur, List<XSSFShape> lst) {
try {
do {
@ -470,7 +560,7 @@ public final class XSSFDrawing extends POIXMLDocumentPart implements Drawing<XSS
if (cur.toFirstChild()) {
do {
XmlObject obj = cur.getObject();
XSSFShape shape;
if (obj instanceof CTMarker) {
// ignore anchor elements
@ -480,7 +570,7 @@ public final class XSSFDrawing extends POIXMLDocumentPart implements Drawing<XSS
} else if(obj instanceof CTConnector) {
shape = new XSSFConnector(this, (CTConnector)obj) ;
} else if(obj instanceof CTShape) {
shape = hasOleLink(obj)
shape = hasOleLink(obj)
? new XSSFObjectData(this, (CTShape)obj)
: new XSSFSimpleShape(this, (CTShape)obj) ;
} else if(obj instanceof CTGraphicalObjectFrame) {
@ -492,7 +582,7 @@ public final class XSSFDrawing extends POIXMLDocumentPart implements Drawing<XSS
+ "this unlinks the returned Shapes from the underlying xml content, "
+ "so those shapes can't be used to modify the drawing, "
+ "i.e. modifications will be ignored!");
// XmlAnyTypeImpl is returned for AlternateContent parts, which might contain a CTDrawing
cur.push();
cur.toFirstChild();
@ -522,7 +612,7 @@ public final class XSSFDrawing extends POIXMLDocumentPart implements Drawing<XSS
assert(shape != null);
shape.anchor = getAnchorFromParent(obj);
lst.add(shape);
} while (cur.toNextSibling());
}
cur.pop();
@ -575,4 +665,12 @@ public final class XSSFDrawing extends POIXMLDocumentPart implements Drawing<XSS
public Iterator<XSSFShape> iterator() {
return getShapes().iterator();
}
/**
* @return the sheet associated with the drawing
*/
public XSSFSheet getSheet() {
return (XSSFSheet)getParent();
}
}

View File

@ -36,7 +36,17 @@ import org.apache.poi.util.IOUtils;
import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger;
import org.apache.xmlbeans.XmlCursor;
import org.openxmlformats.schemas.drawingml.x2006.main.CTNonVisualDrawingProps;
import org.openxmlformats.schemas.drawingml.x2006.main.CTOfficeArtExtension;
import org.openxmlformats.schemas.drawingml.x2006.main.CTOfficeArtExtensionList;
import org.openxmlformats.schemas.drawingml.x2006.main.CTPoint2D;
import org.openxmlformats.schemas.drawingml.x2006.main.CTPositiveSize2D;
import org.openxmlformats.schemas.drawingml.x2006.main.CTPresetGeometry2D;
import org.openxmlformats.schemas.drawingml.x2006.main.CTShapeProperties;
import org.openxmlformats.schemas.drawingml.x2006.main.CTTransform2D;
import org.openxmlformats.schemas.drawingml.x2006.main.STShapeType;
import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTShape;
import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTShapeNonVisual;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTOleObject;
/**
@ -56,16 +66,57 @@ public class XSSFObjectData extends XSSFSimpleShape implements ObjectData {
super(drawing, ctShape);
}
/**
* Prototype with the default structure of a new auto-shape.
*/
/**
* Prototype with the default structure of a new auto-shape.
*/
protected static CTShape prototype() {
final String drawNS = "http://schemas.microsoft.com/office/drawing/2010/main";
if(prototype == null) {
prototype = XSSFSimpleShape.prototype();
CTShape shape = CTShape.Factory.newInstance();
CTShapeNonVisual nv = shape.addNewNvSpPr();
CTNonVisualDrawingProps nvp = nv.addNewCNvPr();
nvp.setId(1);
nvp.setName("Shape 1");
// nvp.setHidden(true);
CTOfficeArtExtensionList extLst = nvp.addNewExtLst();
// https://msdn.microsoft.com/en-us/library/dd911027(v=office.12).aspx
CTOfficeArtExtension ext = extLst.addNewExt();
ext.setUri("{63B3BB69-23CF-44E3-9099-C40C66FF867C}");
XmlCursor cur = ext.newCursor();
cur.toEndToken();
cur.beginElement(new QName(drawNS, "compatExt", "a14"));
cur.insertNamespace("a14", drawNS);
cur.insertAttributeWithValue("spid", "_x0000_s1");
cur.dispose();
nv.addNewCNvSpPr();
CTShapeProperties sp = shape.addNewSpPr();
CTTransform2D t2d = sp.addNewXfrm();
CTPositiveSize2D p1 = t2d.addNewExt();
p1.setCx(0);
p1.setCy(0);
CTPoint2D p2 = t2d.addNewOff();
p2.setX(0);
p2.setY(0);
CTPresetGeometry2D geom = sp.addNewPrstGeom();
geom.setPrst(STShapeType.RECT);
geom.addNewAvLst();
prototype = shape;
}
return prototype;
}
@Override
public String getOLE2ClassName() {
return getOleObject().getProgId();

View File

@ -47,6 +47,7 @@ import org.apache.poi.POIXMLDocument;
import org.apache.poi.POIXMLDocumentPart;
import org.apache.poi.POIXMLException;
import org.apache.poi.POIXMLProperties;
import org.apache.poi.hpsf.ClassID;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
import org.apache.poi.openxml4j.opc.OPCPackage;
@ -58,6 +59,9 @@ import org.apache.poi.openxml4j.opc.PackageRelationshipTypes;
import org.apache.poi.openxml4j.opc.PackagingURIHelper;
import org.apache.poi.openxml4j.opc.TargetMode;
import org.apache.poi.poifs.crypt.HashAlgorithm;
import org.apache.poi.poifs.filesystem.DirectoryNode;
import org.apache.poi.poifs.filesystem.Ole10Native;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.ss.SpreadsheetVersion;
import org.apache.poi.ss.formula.SheetNameFormatter;
import org.apache.poi.ss.formula.udf.AggregatingUDFFinder;
@ -2437,4 +2441,41 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook {
}
return null;
}
@Override
public int addOlePackage(byte[] oleData, String label, String fileName, String command)
throws IOException {
// find an unused part name
OPCPackage opc = getPackage();
PackagePartName pnOLE;
int oleId=0;
do {
try {
pnOLE = PackagingURIHelper.createPartName( "/xl/embeddings/oleObject"+(++oleId)+".bin" );
} catch (InvalidFormatException e) {
throw new IOException("ole object name not recognized", e);
}
} while (opc.containPart(pnOLE));
PackagePart pp = opc.createPart( pnOLE, "application/vnd.openxmlformats-officedocument.oleObject" );
Ole10Native ole10 = new Ole10Native(label, fileName, command, oleData);
ByteArrayOutputStream bos = new ByteArrayOutputStream(oleData.length+500);
ole10.writeOut(bos);
POIFSFileSystem poifs = new POIFSFileSystem();
DirectoryNode root = poifs.getRoot();
root.createDocument(Ole10Native.OLE10_NATIVE, new ByteArrayInputStream(bos.toByteArray()));
root.setStorageClsid(ClassID.OLE10_PACKAGE);
// TODO: generate CombObj stream
OutputStream os = pp.getOutputStream();
poifs.writeFilesystem(os);
os.close();
poifs.close();
return oleId;
}
}

View File

@ -0,0 +1,125 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.ss.usermodel;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.URL;
import javax.imageio.ImageIO;
import org.apache.poi.hssf.HSSFTestDataSamples;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.sl.usermodel.AutoShape;
import org.apache.poi.sl.usermodel.ShapeType;
import org.apache.poi.sl.usermodel.Slide;
import org.apache.poi.sl.usermodel.SlideShow;
import org.apache.poi.ss.extractor.EmbeddedData;
import org.apache.poi.ss.extractor.EmbeddedExtractor;
import org.apache.poi.xslf.usermodel.XMLSlideShow;
import org.apache.poi.xssf.XSSFTestDataSamples;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.junit.Test;
public class TestEmbedOLEPackage {
@Test
public void embedXSSF() throws IOException {
Workbook wb1 = new XSSFWorkbook();
Sheet sh = wb1.createSheet();
int picIdx = wb1.addPicture(getSamplePng(), Workbook.PICTURE_TYPE_PNG);
byte samplePPTX[] = getSamplePPT(true);
int oleIdx = wb1.addOlePackage(samplePPTX, "dummy.pptx", "dummy.pptx", "dummy.pptx");
Drawing<?> pat = sh.createDrawingPatriarch();
ClientAnchor anchor = pat.createAnchor(0, 0, 0, 0, 1, 1, 3, 6);
pat.createObjectData(anchor, oleIdx, picIdx);
Workbook wb2 = XSSFTestDataSamples.writeOutAndReadBack(wb1);
pat = wb2.getSheetAt(0).getDrawingPatriarch();
assertTrue(pat.iterator().next() instanceof ObjectData);
EmbeddedExtractor ee = new EmbeddedExtractor();
EmbeddedData ed = ee.extractAll(wb2.getSheetAt(0)).get(0);
assertArrayEquals(samplePPTX, ed.getEmbeddedData());
wb2.close();
wb1.close();
}
@Test
public void embedHSSF() throws IOException {
try {
Class.forName("org.apache.poi.hslf.usermodel.HSLFSlideShow");
} catch (Exception e) {
assumeTrue(false);
}
Workbook wb1 = new HSSFWorkbook();
Sheet sh = wb1.createSheet();
int picIdx = wb1.addPicture(getSamplePng(), Workbook.PICTURE_TYPE_PNG);
byte samplePPT[] = getSamplePPT(false);
int oleIdx = wb1.addOlePackage(samplePPT, "dummy.ppt", "dummy.ppt", "dummy.ppt");
Drawing<?> pat = sh.createDrawingPatriarch();
ClientAnchor anchor = pat.createAnchor(0, 0, 0, 0, 1, 1, 3, 6);
pat.createObjectData(anchor, oleIdx, picIdx);
Workbook wb2 = HSSFTestDataSamples.writeOutAndReadBack((HSSFWorkbook)wb1);
pat = wb2.getSheetAt(0).getDrawingPatriarch();
assertTrue(pat.iterator().next() instanceof ObjectData);
EmbeddedExtractor ee = new EmbeddedExtractor();
EmbeddedData ed = ee.extractAll(wb2.getSheetAt(0)).get(0);
assertArrayEquals(samplePPT, ed.getEmbeddedData());
wb2.close();
wb1.close();
}
static byte[] getSamplePng() throws IOException {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
URL imgUrl = cl.getResource("javax/swing/plaf/metal/icons/ocean/directory.gif");
BufferedImage img = ImageIO.read(imgUrl);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ImageIO.write(img, "PNG", bos);
return bos.toByteArray();
}
static byte[] getSamplePPT(boolean ooxml) throws IOException {
SlideShow<?,?> ppt = (ooxml) ? new XMLSlideShow() : new org.apache.poi.hslf.usermodel.HSLFSlideShow();
Slide<?,?> slide = ppt.createSlide();
AutoShape<?,?> sh1 = slide.createAutoShape();
sh1.setShapeType(ShapeType.STAR_32);
sh1.setAnchor(new java.awt.Rectangle(50, 50, 100, 200));
sh1.setFillColor(java.awt.Color.red);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ppt.write(bos);
ppt.close();
return bos.toByteArray();
}
}