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

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