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

View File

@ -2048,6 +2048,16 @@ public final class HSSFWorkbook extends POIDocument implements org.apache.poi.ss
return olemap; 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) public int addOlePackage(POIFSFileSystem poiData, String label, String fileName, String command)
throws IOException { throws IOException {
DirectoryNode root = poiData.getRoot(); 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); return addOlePackage(bos.toByteArray(), label, fileName, command);
} }
@Override
public int addOlePackage(byte[] oleData, String label, String fileName, String command) public int addOlePackage(byte[] oleData, String label, String fileName, String command)
throws IOException { throws IOException {
// check if we were created by POIFS otherwise create a new dummy POIFS for storing the package data // 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 * @return the newly created client anchor
*/ */
ClientAnchor createAnchor(int dx1, int dy1, int dx2, int dy2, int col1, int row1, int col2, int row2); 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> * @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 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); 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 * @since 3.14 beta 2
*/ */
SpreadsheetVersion getSpreadsheetVersion(); 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.ClientAnchor;
import org.apache.poi.ss.usermodel.Comment; import org.apache.poi.ss.usermodel.Comment;
import org.apache.poi.ss.usermodel.Drawing; 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.XSSFDrawing;
import org.apache.poi.xssf.usermodel.XSSFPicture; import org.apache.poi.xssf.usermodel.XSSFPicture;
import org.apache.poi.xssf.usermodel.XSSFShape; 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); 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 @Override
public Iterator<XSSFShape> iterator() { public Iterator<XSSFShape> iterator() {
return _drawing.getShapes().iterator(); return _drawing.getShapes().iterator();
} }
} }

View File

@ -1391,5 +1391,10 @@ public class SXSSFWorkbook implements Workbook {
return SpreadsheetVersion.EXCEL2007; 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 //end of interface implementation
} }

View File

@ -28,9 +28,16 @@ import java.util.List;
import javax.xml.namespace.QName; import javax.xml.namespace.QName;
import org.apache.poi.POIXMLDocument;
import org.apache.poi.POIXMLDocumentPart; 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.PackagePart;
import org.apache.poi.openxml4j.opc.PackagePartName;
import org.apache.poi.openxml4j.opc.PackageRelationship; 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.ClientAnchor;
import org.apache.poi.ss.usermodel.Drawing; import org.apache.poi.ss.usermodel.Drawing;
import org.apache.poi.ss.util.CellAddress; 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.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.drawingml.x2006.main.CTBlipFillProperties;
import org.openxmlformats.schemas.drawingml.x2006.main.CTGroupTransform2D; 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.CTPoint2D;
import org.openxmlformats.schemas.drawingml.x2006.main.CTPositiveSize2D; import org.openxmlformats.schemas.drawingml.x2006.main.CTPositiveSize2D;
import org.openxmlformats.schemas.drawingml.x2006.main.CTTransform2D; import org.openxmlformats.schemas.drawingml.x2006.main.CTTransform2D;
@ -59,6 +68,9 @@ import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTPicture;
import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTShape; import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTShape;
import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTTwoCellAnchor; import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTTwoCellAnchor;
import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.STEditAs; 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 * Represents a SpreadsheetML drawing
@ -319,7 +331,7 @@ public final class XSSFDrawing extends POIXMLDocumentPart implements Drawing<XSS
@Override @Override
public XSSFComment createCellComment(ClientAnchor anchor) { public XSSFComment createCellComment(ClientAnchor anchor) {
XSSFClientAnchor ca = (XSSFClientAnchor)anchor; XSSFClientAnchor ca = (XSSFClientAnchor)anchor;
XSSFSheet sheet = (XSSFSheet)getParent(); XSSFSheet sheet = getSheet();
//create comments and vmlDrawing parts if they don't exist //create comments and vmlDrawing parts if they don't exist
CommentsTable comments = sheet.getCommentsTable(true); CommentsTable comments = sheet.getCommentsTable(true);
@ -369,6 +381,84 @@ public final class XSSFDrawing extends POIXMLDocumentPart implements Drawing<XSS
return graphicFrame; 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. * Returns all charts in this drawing.
*/ */
@ -410,7 +500,7 @@ public final class XSSFDrawing extends POIXMLDocumentPart implements Drawing<XSS
CTPoint2D off = xfrm.addNewOff(); CTPoint2D off = xfrm.addNewOff();
off.setX(anchor.getDx1()); off.setX(anchor.getDx1());
off.setY(anchor.getDy1()); off.setY(anchor.getDy1());
XSSFSheet sheet = (XSSFSheet)getParent(); XSSFSheet sheet = getSheet();
double widthPx = 0; double widthPx = 0;
for (int col=anchor.getCol1(); col<anchor.getCol2(); col++) { for (int col=anchor.getCol1(); col<anchor.getCol2(); col++) {
widthPx += sheet.getColumnWidthInPixels(col); widthPx += sheet.getColumnWidthInPixels(col);
@ -575,4 +665,12 @@ public final class XSSFDrawing extends POIXMLDocumentPart implements Drawing<XSS
public Iterator<XSSFShape> iterator() { public Iterator<XSSFShape> iterator() {
return getShapes().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.POILogFactory;
import org.apache.poi.util.POILogger; import org.apache.poi.util.POILogger;
import org.apache.xmlbeans.XmlCursor; 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.CTShape;
import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTShapeNonVisual;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTOleObject; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTOleObject;
/** /**
@ -56,16 +66,57 @@ public class XSSFObjectData extends XSSFSimpleShape implements ObjectData {
super(drawing, ctShape); super(drawing, ctShape);
} }
/**
* Prototype with the default structure of a new auto-shape.
*/
/** /**
* Prototype with the default structure of a new auto-shape. * Prototype with the default structure of a new auto-shape.
*/ */
protected static CTShape prototype() { protected static CTShape prototype() {
final String drawNS = "http://schemas.microsoft.com/office/drawing/2010/main";
if(prototype == null) { 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; return prototype;
} }
@Override @Override
public String getOLE2ClassName() { public String getOLE2ClassName() {
return getOleObject().getProgId(); return getOleObject().getProgId();

View File

@ -47,6 +47,7 @@ import org.apache.poi.POIXMLDocument;
import org.apache.poi.POIXMLDocumentPart; import org.apache.poi.POIXMLDocumentPart;
import org.apache.poi.POIXMLException; import org.apache.poi.POIXMLException;
import org.apache.poi.POIXMLProperties; import org.apache.poi.POIXMLProperties;
import org.apache.poi.hpsf.ClassID;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException; import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.openxml4j.exceptions.OpenXML4JException; import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
import org.apache.poi.openxml4j.opc.OPCPackage; 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.PackagingURIHelper;
import org.apache.poi.openxml4j.opc.TargetMode; import org.apache.poi.openxml4j.opc.TargetMode;
import org.apache.poi.poifs.crypt.HashAlgorithm; 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.SpreadsheetVersion;
import org.apache.poi.ss.formula.SheetNameFormatter; import org.apache.poi.ss.formula.SheetNameFormatter;
import org.apache.poi.ss.formula.udf.AggregatingUDFFinder; import org.apache.poi.ss.formula.udf.AggregatingUDFFinder;
@ -2437,4 +2441,41 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook {
} }
return null; 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();
}
}