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.

XSLFShape.java 18KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488
  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.awt.Graphics2D;
  21. import java.awt.geom.Rectangle2D;
  22. import javax.xml.namespace.QName;
  23. import javax.xml.stream.XMLStreamReader;
  24. import org.apache.poi.ooxml.util.XPathHelper;
  25. import org.apache.poi.openxml4j.opc.PackagePart;
  26. import org.apache.poi.sl.draw.DrawFactory;
  27. import org.apache.poi.sl.draw.DrawPaint;
  28. import org.apache.poi.sl.usermodel.MasterSheet;
  29. import org.apache.poi.sl.usermodel.PaintStyle;
  30. import org.apache.poi.sl.usermodel.PlaceableShape;
  31. import org.apache.poi.sl.usermodel.Placeholder;
  32. import org.apache.poi.sl.usermodel.PlaceholderDetails;
  33. import org.apache.poi.sl.usermodel.Shape;
  34. import org.apache.poi.sl.usermodel.SimpleShape;
  35. import org.apache.poi.util.Beta;
  36. import org.apache.poi.util.Internal;
  37. import org.apache.poi.xslf.model.PropertyFetcher;
  38. import org.apache.poi.xslf.usermodel.XSLFPropertiesDelegate.XSLFFillProperties;
  39. import org.apache.xmlbeans.XmlCursor;
  40. import org.apache.xmlbeans.XmlException;
  41. import org.apache.xmlbeans.XmlObject;
  42. import org.openxmlformats.schemas.drawingml.x2006.main.CTBlipFillProperties;
  43. import org.openxmlformats.schemas.drawingml.x2006.main.CTGradientFillProperties;
  44. import org.openxmlformats.schemas.drawingml.x2006.main.CTGroupShapeProperties;
  45. import org.openxmlformats.schemas.drawingml.x2006.main.CTNonVisualDrawingProps;
  46. import org.openxmlformats.schemas.drawingml.x2006.main.CTSchemeColor;
  47. import org.openxmlformats.schemas.drawingml.x2006.main.CTShapeProperties;
  48. import org.openxmlformats.schemas.drawingml.x2006.main.CTShapeStyle;
  49. import org.openxmlformats.schemas.drawingml.x2006.main.CTSolidColorFillProperties;
  50. import org.openxmlformats.schemas.drawingml.x2006.main.CTStyleMatrix;
  51. import org.openxmlformats.schemas.drawingml.x2006.main.CTStyleMatrixReference;
  52. import org.openxmlformats.schemas.drawingml.x2006.main.STSchemeColorVal;
  53. import org.openxmlformats.schemas.presentationml.x2006.main.CTBackgroundProperties;
  54. import org.openxmlformats.schemas.presentationml.x2006.main.CTPicture;
  55. import org.openxmlformats.schemas.presentationml.x2006.main.CTPlaceholder;
  56. import org.openxmlformats.schemas.presentationml.x2006.main.CTShape;
  57. import org.openxmlformats.schemas.presentationml.x2006.main.STPlaceholderType;
  58. /**
  59. * Base super-class class for all shapes in PresentationML
  60. */
  61. @Beta
  62. public abstract class XSLFShape implements Shape<XSLFShape,XSLFTextParagraph> {
  63. @Internal
  64. public interface ReparseFactory<T extends XmlObject> {
  65. T parse(XMLStreamReader reader) throws XmlException;
  66. }
  67. static final String DML_NS = "http://schemas.openxmlformats.org/drawingml/2006/main";
  68. static final String PML_NS = "http://schemas.openxmlformats.org/presentationml/2006/main";
  69. private static final QName[] NV_CONTAINER = {
  70. new QName(PML_NS, "nvSpPr"),
  71. new QName(PML_NS, "nvCxnSpPr"),
  72. new QName(PML_NS, "nvGrpSpPr"),
  73. new QName(PML_NS, "nvPicPr"),
  74. new QName(PML_NS, "nvGraphicFramePr")
  75. };
  76. private static final QName[] CNV_PROPS = {
  77. new QName(PML_NS, "cNvPr")
  78. };
  79. private final XmlObject _shape;
  80. private final XSLFSheet _sheet;
  81. private XSLFShapeContainer _parent;
  82. private CTShapeStyle _spStyle;
  83. private CTNonVisualDrawingProps _nvPr;
  84. protected XSLFShape(XmlObject shape, XSLFSheet sheet) {
  85. _shape = shape;
  86. _sheet = sheet;
  87. }
  88. /**
  89. * @return the xml bean holding this shape's data
  90. */
  91. public final XmlObject getXmlObject() {
  92. // it's final because the xslf inheritance hierarchy is not necessary the same as
  93. // the (not existing) xmlbeans hierarchy and subclasses shouldn't narrow it's return value
  94. return _shape;
  95. }
  96. @Override
  97. public XSLFSheet getSheet() {
  98. return _sheet;
  99. }
  100. @Override
  101. public String getShapeName() {
  102. CTNonVisualDrawingProps nonVisualDrawingProps = getCNvPr();
  103. return nonVisualDrawingProps == null ? null : nonVisualDrawingProps.getName();
  104. }
  105. @Override
  106. public int getShapeId() {
  107. CTNonVisualDrawingProps nonVisualDrawingProps = getCNvPr();
  108. if (nonVisualDrawingProps == null) {
  109. throw new IllegalStateException("no underlying shape exists");
  110. }
  111. return Math.toIntExact(nonVisualDrawingProps.getId());
  112. }
  113. /**
  114. * Set the contents of this shape to be a copy of the source shape.
  115. * This method is called recursively for each shape when merging slides
  116. *
  117. * @param sh the source shape
  118. * @see org.apache.poi.xslf.usermodel.XSLFSlide#importContent(XSLFSheet)
  119. */
  120. @Internal
  121. void copy(XSLFShape sh) {
  122. if (!getClass().isInstance(sh)) {
  123. throw new IllegalArgumentException(
  124. "Can't copy " + sh.getClass().getSimpleName() + " into " + getClass().getSimpleName());
  125. }
  126. if (this instanceof PlaceableShape) {
  127. PlaceableShape<?,?> ps = (PlaceableShape<?,?>)this;
  128. ps.setAnchor(sh.getAnchor());
  129. }
  130. }
  131. public void setParent(XSLFShapeContainer parent) {
  132. this._parent = parent;
  133. }
  134. @Override
  135. public XSLFShapeContainer getParent() {
  136. return this._parent;
  137. }
  138. protected PaintStyle getFillPaint() {
  139. final XSLFTheme theme = getSheet().getTheme();
  140. final boolean hasPlaceholder = getPlaceholder() != null;
  141. PropertyFetcher<PaintStyle> fetcher = new PropertyFetcher<PaintStyle>() {
  142. @Override
  143. public boolean fetch(XSLFShape shape) {
  144. PackagePart pp = shape.getSheet().getPackagePart();
  145. if (shape instanceof XSLFPictureShape) {
  146. CTPicture pic = (CTPicture)shape.getXmlObject();
  147. if (pic.getBlipFill() != null) {
  148. setValue(selectPaint(pic.getBlipFill(), pp, null, theme));
  149. return true;
  150. }
  151. }
  152. XSLFFillProperties fp = XSLFPropertiesDelegate.getFillDelegate(shape.getShapeProperties());
  153. if (fp == null) {
  154. return false;
  155. }
  156. if (fp.isSetNoFill()) {
  157. setValue(null);
  158. return true;
  159. }
  160. PaintStyle paint = selectPaint(fp, null, pp, theme, hasPlaceholder);
  161. if (paint != null) {
  162. setValue(paint);
  163. return true;
  164. }
  165. CTShapeStyle style = shape.getSpStyle();
  166. if (style != null) {
  167. fp = XSLFPropertiesDelegate.getFillDelegate(style.getFillRef());
  168. paint = selectPaint(fp, null, pp, theme, hasPlaceholder);
  169. }
  170. if (paint != null) {
  171. setValue(paint);
  172. return true;
  173. }
  174. return false;
  175. }
  176. };
  177. fetchShapeProperty(fetcher);
  178. return fetcher.getValue();
  179. }
  180. @SuppressWarnings("unused")
  181. protected CTBackgroundProperties getBgPr() {
  182. return getChild(CTBackgroundProperties.class, PML_NS, "bgPr");
  183. }
  184. @SuppressWarnings("unused")
  185. protected CTStyleMatrixReference getBgRef() {
  186. return getChild(CTStyleMatrixReference.class, PML_NS, "bgRef");
  187. }
  188. protected CTGroupShapeProperties getGrpSpPr() {
  189. return getChild(CTGroupShapeProperties.class, PML_NS, "grpSpPr");
  190. }
  191. protected CTNonVisualDrawingProps getCNvPr() {
  192. try {
  193. if (_nvPr == null) {
  194. _nvPr = XPathHelper.selectProperty(getXmlObject(), CTNonVisualDrawingProps.class, null, NV_CONTAINER, CNV_PROPS);
  195. }
  196. return _nvPr;
  197. } catch (XmlException e) {
  198. return null;
  199. }
  200. }
  201. @SuppressWarnings("WeakerAccess")
  202. protected CTShapeStyle getSpStyle() {
  203. if (_spStyle == null) {
  204. _spStyle = getChild(CTShapeStyle.class, PML_NS, "style");
  205. }
  206. return _spStyle;
  207. }
  208. /**
  209. * Return direct child objects of this shape
  210. *
  211. * @param childClass the class to cast the properties to
  212. * @param namespace the namespace - usually it is {@code "http://schemas.openxmlformats.org/presentationml/2006/main"}
  213. * @param nodename the node name, without prefix
  214. * @return the properties object or null if it can't be found
  215. */
  216. @SuppressWarnings({"unchecked", "WeakerAccess", "unused", "SameParameterValue"})
  217. protected <T extends XmlObject> T getChild(Class<T> childClass, String namespace, String nodename) {
  218. XmlCursor cur = getXmlObject().newCursor();
  219. T child = null;
  220. if (cur.toChild(namespace, nodename)) {
  221. child = (T)cur.getObject();
  222. }
  223. if (cur.toChild(XSLFRelation.NS_DRAWINGML, nodename)) {
  224. child = (T)cur.getObject();
  225. }
  226. cur.dispose();
  227. return child;
  228. }
  229. public boolean isPlaceholder() {
  230. return getPlaceholderDetails().getCTPlaceholder(false) != null;
  231. }
  232. /**
  233. * @see PlaceholderDetails#getPlaceholder()
  234. */
  235. public Placeholder getPlaceholder() {
  236. return getPlaceholderDetails().getPlaceholder();
  237. }
  238. /**
  239. * @see PlaceholderDetails#setPlaceholder(Placeholder)
  240. */
  241. public void setPlaceholder(final Placeholder placeholder) {
  242. getPlaceholderDetails().setPlaceholder(placeholder);
  243. }
  244. /**
  245. * @see SimpleShape#getPlaceholderDetails()
  246. */
  247. @SuppressWarnings("WeakerAccess")
  248. public XSLFPlaceholderDetails getPlaceholderDetails() {
  249. return new XSLFPlaceholderDetails(this);
  250. }
  251. /**
  252. * As there's no xmlbeans hierarchy, but XSLF works with subclassing, not all
  253. * child classes work with a {@link CTShape} object, but often contain the same
  254. * properties. This method is the generalized form of selecting and casting those
  255. * properties.
  256. *
  257. * @param resultClass the requested result class
  258. * @param xquery the simple (xmlbean) xpath expression to the property
  259. * @return the xml object at the xpath location, or null if not found
  260. */
  261. @SuppressWarnings({"unchecked", "WeakerAccess"})
  262. protected <T extends XmlObject> T selectProperty(Class<T> resultClass, String xquery) {
  263. XmlObject[] rs = getXmlObject().selectPath(xquery);
  264. if (rs.length == 0) {
  265. return null;
  266. }
  267. return (resultClass.isInstance(rs[0])) ? (T)rs[0] : null;
  268. }
  269. /**
  270. * Walk up the inheritance tree and fetch shape properties.<p>
  271. *
  272. * The following order of inheritance is assumed:<p>
  273. * <ol>
  274. * <li>slide
  275. * <li>slideLayout
  276. * <li>slideMaster
  277. * </ol>
  278. *
  279. * Currently themes and their defaults aren't correctly handled
  280. *
  281. * @param visitor the object that collects the desired property
  282. * @return true if the property was fetched
  283. */
  284. @SuppressWarnings("WeakerAccess")
  285. @Internal
  286. public boolean fetchShapeProperty(PropertyFetcher<?> visitor) {
  287. // try shape properties in slide
  288. if (visitor.fetch(this)) {
  289. return true;
  290. }
  291. final CTPlaceholder ph = getPlaceholderDetails().getCTPlaceholder(false);
  292. if (ph == null) {
  293. return false;
  294. }
  295. MasterSheet<XSLFShape,XSLFTextParagraph> sm = getSheet().getMasterSheet();
  296. // try slide layout
  297. if (sm instanceof XSLFSlideLayout) {
  298. XSLFSlideLayout slideLayout = (XSLFSlideLayout)sm;
  299. XSLFSimpleShape placeholderShape = slideLayout.getPlaceholder(ph);
  300. if (placeholderShape != null && visitor.fetch(placeholderShape)) {
  301. return true;
  302. }
  303. sm = slideLayout.getMasterSheet();
  304. }
  305. // try slide master
  306. if (sm instanceof XSLFSlideMaster) {
  307. XSLFSlideMaster master = (XSLFSlideMaster)sm;
  308. int textType = getPlaceholderType(ph);
  309. XSLFSimpleShape masterShape = master.getPlaceholderByType(textType);
  310. return masterShape != null && visitor.fetch(masterShape);
  311. }
  312. return false;
  313. }
  314. private static int getPlaceholderType(CTPlaceholder ph) {
  315. if ( !ph.isSetType()) {
  316. return STPlaceholderType.INT_BODY;
  317. }
  318. switch (ph.getType().intValue()) {
  319. case STPlaceholderType.INT_TITLE:
  320. case STPlaceholderType.INT_CTR_TITLE:
  321. return STPlaceholderType.INT_TITLE;
  322. case STPlaceholderType.INT_FTR:
  323. case STPlaceholderType.INT_SLD_NUM:
  324. case STPlaceholderType.INT_DT:
  325. return ph.getType().intValue();
  326. default:
  327. return STPlaceholderType.INT_BODY;
  328. }
  329. }
  330. /**
  331. * Convert shape fill into java.awt.Paint. The result is either Color or
  332. * TexturePaint or GradientPaint or null
  333. *
  334. * @param fp a properties handler specific to the underlying shape properties
  335. * @param phClr context color
  336. * @param parentPart the parent package part. Any external references (images, etc.) are resolved relative to it.
  337. * @param theme the theme for the shape/sheet
  338. *
  339. * @return the applied Paint or null if none was applied
  340. */
  341. @SuppressWarnings("WeakerAccess")
  342. protected PaintStyle selectPaint(XSLFFillProperties fp, final CTSchemeColor phClr, final PackagePart parentPart, final XSLFTheme theme, boolean hasPlaceholder) {
  343. if (fp == null || fp.isSetNoFill()) {
  344. return null;
  345. } else if (fp.isSetSolidFill()) {
  346. return selectPaint(fp.getSolidFill(), phClr, theme);
  347. } else if (fp.isSetBlipFill()) {
  348. return selectPaint(fp.getBlipFill(), parentPart, phClr, theme);
  349. } else if (fp.isSetGradFill()) {
  350. return selectPaint(fp.getGradFill(), phClr, theme);
  351. } else if (fp.isSetMatrixStyle()) {
  352. return selectPaint(fp.getMatrixStyle(), theme, fp.isLineStyle(), hasPlaceholder);
  353. } else if (phClr != null) {
  354. return selectPaint(phClr, theme);
  355. } else {
  356. return null;
  357. }
  358. }
  359. protected PaintStyle selectPaint(CTSchemeColor phClr, final XSLFTheme theme) {
  360. final XSLFColor c = new XSLFColor(null, theme, phClr, _sheet);
  361. return DrawPaint.createSolidPaint(c.getColorStyle());
  362. }
  363. @SuppressWarnings("WeakerAccess")
  364. protected PaintStyle selectPaint(CTSolidColorFillProperties solidFill, CTSchemeColor phClr, final XSLFTheme theme) {
  365. CTSchemeColor nestedPhClr = solidFill.getSchemeClr();
  366. boolean useNested = nestedPhClr != null && nestedPhClr.getVal() != null && !STSchemeColorVal.PH_CLR.equals(nestedPhClr.getVal());
  367. final XSLFColor c = new XSLFColor(solidFill, theme, useNested ? nestedPhClr : phClr, _sheet);
  368. return DrawPaint.createSolidPaint(c.getColorStyle());
  369. }
  370. @SuppressWarnings("WeakerAccess")
  371. protected PaintStyle selectPaint(final CTBlipFillProperties blipFill, final PackagePart parentPart, CTSchemeColor phClr, final XSLFTheme theme) {
  372. return new XSLFTexturePaint(blipFill, parentPart, phClr, theme, _sheet);
  373. }
  374. @SuppressWarnings("WeakerAccess")
  375. protected PaintStyle selectPaint(final CTGradientFillProperties gradFill, CTSchemeColor phClr, final XSLFTheme theme) {
  376. return new XSLFGradientPaint(gradFill, phClr, theme, _sheet);
  377. }
  378. @SuppressWarnings("WeakerAccess")
  379. protected PaintStyle selectPaint(CTStyleMatrixReference fillRef, final XSLFTheme theme, boolean isLineStyle, boolean hasPlaceholder) {
  380. if (fillRef == null) {
  381. return null;
  382. }
  383. // The idx attribute refers to the index of a fill style or
  384. // background fill style within the presentation's style matrix, defined by the fmtScheme element.
  385. // value of 0 or 1000 indicates no background,
  386. // values 1-999 refer to the index of a fill style within the fillStyleLst element
  387. // values 1001 and above refer to the index of a background fill style within the bgFillStyleLst element.
  388. long idx = fillRef.getIdx();
  389. CTStyleMatrix matrix = theme.getXmlObject().getThemeElements().getFmtScheme();
  390. final XmlObject styleLst;
  391. long childIdx;
  392. if (idx >= 1 && idx <= 999) {
  393. childIdx = idx-1;
  394. styleLst = (isLineStyle) ? matrix.getLnStyleLst() : matrix.getFillStyleLst();
  395. } else if (idx >= 1001 ){
  396. childIdx = idx - 1001;
  397. styleLst = matrix.getBgFillStyleLst();
  398. } else {
  399. return null;
  400. }
  401. XmlCursor cur = styleLst.newCursor();
  402. XSLFFillProperties fp = null;
  403. if (cur.toChild(Math.toIntExact(childIdx))) {
  404. fp = XSLFPropertiesDelegate.getFillDelegate(cur.getObject());
  405. }
  406. cur.dispose();
  407. CTSchemeColor phClr = fillRef.getSchemeClr();
  408. PaintStyle res = selectPaint(fp, phClr, theme.getPackagePart(), theme, hasPlaceholder);
  409. // check for empty placeholder value
  410. // see http://officeopenxml.com/prSlide-color.php - "Color Placeholders within Themes"
  411. if (res != null || hasPlaceholder) {
  412. return res;
  413. }
  414. XSLFColor col = new XSLFColor(fillRef, theme, phClr, _sheet);
  415. return DrawPaint.createSolidPaint(col.getColorStyle());
  416. }
  417. @Override
  418. public void draw(Graphics2D graphics, Rectangle2D bounds) {
  419. DrawFactory.getInstance(graphics).drawShape(graphics, this, bounds);
  420. }
  421. /**
  422. * Return the shape specific (visual) properties
  423. *
  424. * @return the shape specific properties
  425. */
  426. protected XmlObject getShapeProperties() {
  427. return getChild(CTShapeProperties.class, PML_NS, "spPr");
  428. }
  429. }