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 20KB

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