blob: 8ee5017d1f9b4cf7277e19680085ab33f690b1e3 [file] [log] [blame]
/*
* ====================================================================
* 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.OutputStream;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLStreamReader;
import org.apache.poi.hpsf.ClassID;
import org.apache.poi.ooxml.POIXMLDocumentPart.RelationPart;
import org.apache.poi.ooxml.POIXMLException;
import org.apache.poi.ooxml.util.XPathHelper;
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.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.Internal;
import org.apache.xmlbeans.XmlCursor;
import org.apache.xmlbeans.XmlException;
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 static final QName[] GRAPHIC = { new QName(DML_NS, "graphic") };
private static final QName[] GRAPHIC_DATA = { new QName(DML_NS, "graphicData") };
private static final QName[] OLE_OBJ = { new QName(PML_NS, "oleObj") };
private static final QName[] CT_PICTURE = { new QName(PML_NS, "pic") };
private CTOleObject _oleObject;
private XSLFPictureData _data;
/*package*/ XSLFObjectShape(CTGraphicalObjectFrame shape, XSLFSheet sheet){
super(shape, sheet);
// select oleObj potentially under AlternateContent
// usually the mc:Choice element will be selected first
try {
_oleObject = XPathHelper.selectProperty(getXmlObject(), CTOleObject.class, null, GRAPHIC, GRAPHIC_DATA, OLE_OBJ);
} catch (XmlException e) {
// ole objects should be also inside AlternateContent tags, even with ECMA 376 edition 1
throw new IllegalStateException(e);
}
}
@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() {
try {
CTPicture pic = XPathHelper.selectProperty
(getXmlObject(), CTPicture.class, XSLFObjectShape::parse, GRAPHIC, GRAPHIC_DATA, OLE_OBJ, CT_PICTURE);
return (pic != null) ? pic.getBlipFill() : null;
} catch (XmlException e) {
return null;
}
}
private static CTPicture parse(XMLStreamReader reader) throws XmlException {
CTGroupShape gs = CTGroupShape.Factory.parse(reader);
return (gs.sizeOfPicArray() > 0) ? gs.getPicArray(0) : null;
}
@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);
cnv.setId(shapeId);
// 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;
}
}