You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

XSLFObjectShape.java 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. /*
  2. * ====================================================================
  3. * Licensed to the Apache Software Foundation (ASF) under one or more
  4. * contributor license agreements. See the NOTICE file distributed with
  5. * this work for additional information regarding copyright ownership.
  6. * The ASF licenses this file to You under the Apache License, Version 2.0
  7. * (the "License"); you may not use this file except in compliance with
  8. * the License. You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing, software
  13. * distributed under the License is distributed on an "AS IS" BASIS,
  14. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. * See the License for the specific language governing permissions and
  16. * limitations under the License.
  17. * ====================================================================
  18. */
  19. package org.apache.poi.xslf.usermodel;
  20. import java.io.ByteArrayInputStream;
  21. import java.io.ByteArrayOutputStream;
  22. import java.io.IOException;
  23. import java.io.OutputStream;
  24. import javax.xml.namespace.QName;
  25. import javax.xml.stream.XMLStreamReader;
  26. import org.apache.poi.hpsf.ClassID;
  27. import org.apache.poi.ooxml.POIXMLDocumentPart.RelationPart;
  28. import org.apache.poi.ooxml.POIXMLException;
  29. import org.apache.poi.ooxml.util.XPathHelper;
  30. import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
  31. import org.apache.poi.openxml4j.opc.OPCPackage;
  32. import org.apache.poi.openxml4j.opc.PackagePart;
  33. import org.apache.poi.openxml4j.opc.PackageRelationship;
  34. import org.apache.poi.poifs.filesystem.FileMagic;
  35. import org.apache.poi.poifs.filesystem.Ole10Native;
  36. import org.apache.poi.poifs.filesystem.POIFSFileSystem;
  37. import org.apache.poi.sl.usermodel.ObjectMetaData;
  38. import org.apache.poi.sl.usermodel.ObjectMetaData.Application;
  39. import org.apache.poi.sl.usermodel.ObjectShape;
  40. import org.apache.poi.util.Internal;
  41. import org.apache.xmlbeans.XmlCursor;
  42. import org.apache.xmlbeans.XmlException;
  43. import org.openxmlformats.schemas.drawingml.x2006.main.CTBlip;
  44. import org.openxmlformats.schemas.drawingml.x2006.main.CTBlipFillProperties;
  45. import org.openxmlformats.schemas.drawingml.x2006.main.CTGraphicalObjectData;
  46. import org.openxmlformats.schemas.drawingml.x2006.main.CTNonVisualDrawingProps;
  47. import org.openxmlformats.schemas.drawingml.x2006.main.CTPoint2D;
  48. import org.openxmlformats.schemas.drawingml.x2006.main.CTPositiveSize2D;
  49. import org.openxmlformats.schemas.drawingml.x2006.main.CTShapeProperties;
  50. import org.openxmlformats.schemas.drawingml.x2006.main.CTTransform2D;
  51. import org.openxmlformats.schemas.drawingml.x2006.main.STShapeType;
  52. import org.openxmlformats.schemas.presentationml.x2006.main.CTGraphicalObjectFrame;
  53. import org.openxmlformats.schemas.presentationml.x2006.main.CTGraphicalObjectFrameNonVisual;
  54. import org.openxmlformats.schemas.presentationml.x2006.main.CTGroupShape;
  55. import org.openxmlformats.schemas.presentationml.x2006.main.CTOleObject;
  56. import org.openxmlformats.schemas.presentationml.x2006.main.CTPicture;
  57. import org.openxmlformats.schemas.presentationml.x2006.main.CTPictureNonVisual;
  58. public class XSLFObjectShape extends XSLFGraphicFrame implements ObjectShape<XSLFShape,XSLFTextParagraph> {
  59. /* package */ static final String OLE_URI = "http://schemas.openxmlformats.org/presentationml/2006/ole";
  60. private static final QName[] GRAPHIC = { new QName(DML_NS, "graphic") };
  61. private static final QName[] GRAPHIC_DATA = { new QName(DML_NS, "graphicData") };
  62. private static final QName[] OLE_OBJ = { new QName(PML_NS, "oleObj") };
  63. private static final QName[] CT_PICTURE = { new QName(PML_NS, "pic") };
  64. private CTOleObject _oleObject;
  65. private XSLFPictureData _data;
  66. /*package*/ XSLFObjectShape(CTGraphicalObjectFrame shape, XSLFSheet sheet){
  67. super(shape, sheet);
  68. // select oleObj potentially under AlternateContent
  69. // usually the mc:Choice element will be selected first
  70. try {
  71. _oleObject = XPathHelper.selectProperty(getXmlObject(), CTOleObject.class, null, GRAPHIC, GRAPHIC_DATA, OLE_OBJ);
  72. } catch (XmlException e) {
  73. // ole objects should be also inside AlternateContent tags, even with ECMA 376 edition 1
  74. throw new IllegalStateException(e);
  75. }
  76. }
  77. @Internal
  78. public CTOleObject getCTOleObject(){
  79. return _oleObject;
  80. }
  81. @Override
  82. public XSLFObjectData getObjectData() {
  83. String oleRel = getCTOleObject().getId();
  84. return getSheet().getRelationPartById(oleRel).getDocumentPart();
  85. }
  86. @Override
  87. public String getProgId() {
  88. return (_oleObject == null) ? null : _oleObject.getProgId();
  89. }
  90. @Override
  91. public String getFullName() {
  92. return (_oleObject == null) ? null : _oleObject.getName();
  93. }
  94. /**
  95. * Return the data on the (internal) picture.
  96. * For an external linked picture, will return null
  97. */
  98. @Override
  99. public XSLFPictureData getPictureData() {
  100. if(_data == null){
  101. String blipId = getBlipId();
  102. if (blipId == null) {
  103. return null;
  104. }
  105. PackagePart p = getSheet().getPackagePart();
  106. PackageRelationship rel = p.getRelationship(blipId);
  107. if (rel != null) {
  108. try {
  109. PackagePart imgPart = p.getRelatedPart(rel);
  110. _data = new XSLFPictureData(imgPart);
  111. }
  112. catch (Exception e) {
  113. throw new POIXMLException(e);
  114. }
  115. }
  116. }
  117. return _data;
  118. }
  119. protected CTBlip getBlip(){
  120. return getBlipFill().getBlip();
  121. }
  122. protected String getBlipId(){
  123. String id = getBlip().getEmbed();
  124. if (id.isEmpty()) {
  125. return null;
  126. }
  127. return id;
  128. }
  129. protected CTBlipFillProperties getBlipFill() {
  130. try {
  131. CTPicture pic = XPathHelper.selectProperty
  132. (getXmlObject(), CTPicture.class, XSLFObjectShape::parse, GRAPHIC, GRAPHIC_DATA, OLE_OBJ, CT_PICTURE);
  133. return (pic != null) ? pic.getBlipFill() : null;
  134. } catch (XmlException e) {
  135. return null;
  136. }
  137. }
  138. private static CTPicture parse(XMLStreamReader reader) throws XmlException {
  139. CTGroupShape gs = CTGroupShape.Factory.parse(reader);
  140. return (gs.sizeOfPicArray() > 0) ? gs.getPicArray(0) : null;
  141. }
  142. @Override
  143. public OutputStream updateObjectData(final Application application, final ObjectMetaData metaData) throws IOException {
  144. final ObjectMetaData md = (application != null) ? application.getMetaData() : metaData;
  145. if (md == null || md.getClassID() == null) {
  146. throw new IllegalArgumentException("either application and/or metaData needs to be set.");
  147. }
  148. final XSLFSheet sheet = getSheet();
  149. final RelationPart rp;
  150. if (_oleObject.isSetId()) {
  151. // object data was already set
  152. rp = sheet.getRelationPartById(_oleObject.getId());
  153. } else {
  154. // object data needs to be initialized
  155. try {
  156. final XSLFRelation descriptor = XSLFRelation.OLE_OBJECT;
  157. final OPCPackage pack = sheet.getPackagePart().getPackage();
  158. int nextIdx = pack.getUnusedPartIndex(descriptor.getDefaultFileName());
  159. rp = sheet.createRelationship(descriptor, XSLFFactory.getInstance(), nextIdx, false);
  160. _oleObject.setId(rp.getRelationship().getId());
  161. } catch (InvalidFormatException e) {
  162. throw new IOException("Unable to add new ole embedding", e);
  163. }
  164. // setting spid only works with a vml drawing object
  165. // oleObj.setSpid("_x0000_s"+(1025+objectIdx));
  166. }
  167. _oleObject.setProgId(md.getProgId());
  168. _oleObject.setName(md.getObjectName());
  169. return new XSLFObjectOutputStream(rp.getDocumentPart().getPackagePart(),md);
  170. }
  171. private static class XSLFObjectOutputStream extends ByteArrayOutputStream {
  172. final PackagePart objectPart;
  173. final ObjectMetaData metaData;
  174. private XSLFObjectOutputStream(final PackagePart objectPart, final ObjectMetaData metaData) {
  175. super(100000);
  176. this.objectPart = objectPart;
  177. this.metaData = metaData;
  178. }
  179. public void close() throws IOException {
  180. objectPart.clear();
  181. try (final OutputStream os = objectPart.getOutputStream()) {
  182. final ByteArrayInputStream bis = new ByteArrayInputStream(this.buf, 0, size());
  183. final FileMagic fm = FileMagic.valueOf(this.buf);
  184. if (fm == FileMagic.OLE2) {
  185. try (final POIFSFileSystem poifs = new POIFSFileSystem(bis)) {
  186. poifs.getRoot().setStorageClsid(metaData.getClassID());
  187. poifs.writeFilesystem(os);
  188. }
  189. } else if (metaData.getOleEntry() == null) {
  190. // OLE Name hasn't been specified, pass the input through
  191. os.write(this.buf, 0, size());
  192. } else {
  193. try (final POIFSFileSystem poifs = new POIFSFileSystem()) {
  194. final ClassID clsId = metaData.getClassID();
  195. if (clsId != null) {
  196. poifs.getRoot().setStorageClsid(clsId);
  197. }
  198. poifs.createDocument(bis, metaData.getOleEntry());
  199. Ole10Native.createOleMarkerEntry(poifs);
  200. poifs.writeFilesystem(os);
  201. }
  202. }
  203. }
  204. }
  205. }
  206. /**
  207. *
  208. *
  209. * @param shapeId 1-based shapeId
  210. * @param picRel relationship to the picture data in the ooxml package
  211. * @return
  212. */
  213. static CTGraphicalObjectFrame prototype(int shapeId, String picRel){
  214. CTGraphicalObjectFrame frame = CTGraphicalObjectFrame.Factory.newInstance();
  215. CTGraphicalObjectFrameNonVisual nvGr = frame.addNewNvGraphicFramePr();
  216. CTNonVisualDrawingProps cnv = nvGr.addNewCNvPr();
  217. // usually the shape name has its index based on the n-th embeding, but having
  218. // the prototype separate from the actual updating of the object, we use the shape id
  219. cnv.setName("Object " + shapeId);
  220. cnv.setId(shapeId);
  221. // add empty property elements otherwise Powerpoint doesn't load the file ...
  222. nvGr.addNewCNvGraphicFramePr();
  223. nvGr.addNewNvPr();
  224. frame.addNewXfrm();
  225. CTGraphicalObjectData gr = frame.addNewGraphic().addNewGraphicData();
  226. gr.setUri(OLE_URI);
  227. XmlCursor grCur = gr.newCursor();
  228. grCur.toEndToken();
  229. grCur.beginElement(new QName(PML_NS, "oleObj"));
  230. grCur.insertElement(new QName(PML_NS, "embed"));
  231. CTGroupShape grpShp = CTGroupShape.Factory.newInstance();
  232. CTPicture pic = grpShp.addNewPic();
  233. CTPictureNonVisual nvPicPr = pic.addNewNvPicPr();
  234. CTNonVisualDrawingProps cNvPr = nvPicPr.addNewCNvPr();
  235. cNvPr.setName("");
  236. cNvPr.setId(0);
  237. nvPicPr.addNewCNvPicPr();
  238. nvPicPr.addNewNvPr();
  239. CTBlipFillProperties blip = pic.addNewBlipFill();
  240. blip.addNewBlip().setEmbed(picRel);
  241. blip.addNewStretch().addNewFillRect();
  242. CTShapeProperties spPr = pic.addNewSpPr();
  243. CTTransform2D xfrm = spPr.addNewXfrm();
  244. CTPoint2D off = xfrm.addNewOff();
  245. off.setX(1270000);
  246. off.setY(1270000);
  247. CTPositiveSize2D xext = xfrm.addNewExt();
  248. xext.setCx(1270000);
  249. xext.setCy(1270000);
  250. spPr.addNewPrstGeom().setPrst(STShapeType.RECT);
  251. XmlCursor picCur = grpShp.newCursor();
  252. picCur.toStartDoc();
  253. picCur.moveXmlContents(grCur);
  254. picCur.dispose();
  255. grCur.dispose();
  256. return frame;
  257. }
  258. }