| /* |
| * ==================================================================== |
| * 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; |
| } |
| } |