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.

XSLFSimpleShape.java 36KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124
  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.Color;
  21. import java.awt.geom.Rectangle2D;
  22. import javax.xml.stream.XMLStreamException;
  23. import javax.xml.stream.XMLStreamReader;
  24. import org.apache.poi.openxml4j.opc.PackagePart;
  25. import org.apache.poi.sl.draw.DrawPaint;
  26. import org.apache.poi.sl.draw.geom.CustomGeometry;
  27. import org.apache.poi.sl.draw.geom.Guide;
  28. import org.apache.poi.sl.draw.geom.PresetGeometries;
  29. import org.apache.poi.sl.usermodel.FillStyle;
  30. import org.apache.poi.sl.usermodel.LineDecoration;
  31. import org.apache.poi.sl.usermodel.LineDecoration.DecorationShape;
  32. import org.apache.poi.sl.usermodel.LineDecoration.DecorationSize;
  33. import org.apache.poi.sl.usermodel.PaintStyle;
  34. import org.apache.poi.sl.usermodel.PaintStyle.SolidPaint;
  35. import org.apache.poi.sl.usermodel.ShapeType;
  36. import org.apache.poi.sl.usermodel.SimpleShape;
  37. import org.apache.poi.sl.usermodel.StrokeStyle;
  38. import org.apache.poi.sl.usermodel.StrokeStyle.LineCap;
  39. import org.apache.poi.sl.usermodel.StrokeStyle.LineCompound;
  40. import org.apache.poi.sl.usermodel.StrokeStyle.LineDash;
  41. import org.apache.poi.util.Beta;
  42. import org.apache.poi.util.POILogFactory;
  43. import org.apache.poi.util.POILogger;
  44. import org.apache.poi.util.Units;
  45. import org.apache.poi.xslf.model.PropertyFetcher;
  46. import org.apache.poi.xslf.usermodel.XSLFPropertiesDelegate.XSLFEffectProperties;
  47. import org.apache.poi.xslf.usermodel.XSLFPropertiesDelegate.XSLFFillProperties;
  48. import org.apache.poi.xslf.usermodel.XSLFPropertiesDelegate.XSLFGeometryProperties;
  49. import org.apache.xmlbeans.XmlObject;
  50. import org.openxmlformats.schemas.drawingml.x2006.main.*;
  51. /**
  52. * Represents a single (non-group) shape in a .pptx slide show
  53. */
  54. @Beta
  55. public abstract class XSLFSimpleShape extends XSLFShape
  56. implements SimpleShape<XSLFShape,XSLFTextParagraph> {
  57. private static final CTOuterShadowEffect NO_SHADOW = CTOuterShadowEffect.Factory.newInstance();
  58. private static final POILogger LOG = POILogFactory.getLogger(XSLFSimpleShape.class);
  59. /* package */XSLFSimpleShape(XmlObject shape, XSLFSheet sheet) {
  60. super(shape,sheet);
  61. }
  62. @Override
  63. public void setShapeType(ShapeType type) {
  64. XSLFGeometryProperties gp = XSLFPropertiesDelegate.getGeometryDelegate(getShapeProperties());
  65. if (gp == null) {
  66. return;
  67. }
  68. if (gp.isSetCustGeom()) {
  69. gp.unsetCustGeom();
  70. }
  71. CTPresetGeometry2D prst = (gp.isSetPrstGeom()) ? gp.getPrstGeom() : gp.addNewPrstGeom();
  72. prst.setPrst(STShapeType.Enum.forInt(type.ooxmlId));
  73. }
  74. @Override
  75. public ShapeType getShapeType(){
  76. XSLFGeometryProperties gp = XSLFPropertiesDelegate.getGeometryDelegate(getShapeProperties());
  77. if (gp != null && gp.isSetPrstGeom()) {
  78. STShapeType.Enum geom = gp.getPrstGeom().getPrst();
  79. if (geom != null) {
  80. return ShapeType.forId(geom.intValue(), true);
  81. }
  82. }
  83. return null;
  84. }
  85. protected CTTransform2D getXfrm(boolean create) {
  86. PropertyFetcher<CTTransform2D> fetcher = new PropertyFetcher<CTTransform2D>() {
  87. @Override
  88. public boolean fetch(XSLFShape shape) {
  89. XmlObject xo = shape.getShapeProperties();
  90. if (xo instanceof CTShapeProperties && ((CTShapeProperties)xo).isSetXfrm()) {
  91. setValue(((CTShapeProperties)xo).getXfrm());
  92. return true;
  93. }
  94. return false;
  95. }
  96. };
  97. fetchShapeProperty(fetcher);
  98. CTTransform2D xfrm = fetcher.getValue();
  99. if (!create || xfrm != null) {
  100. return xfrm;
  101. } else {
  102. XmlObject xo = getShapeProperties();
  103. if (xo instanceof CTShapeProperties) {
  104. return ((CTShapeProperties)xo).addNewXfrm();
  105. } else {
  106. // ... group shapes have their own getXfrm()
  107. LOG.log(POILogger.WARN, getClass() +" doesn't have xfrm element.");
  108. return null;
  109. }
  110. }
  111. }
  112. @Override
  113. public Rectangle2D getAnchor() {
  114. CTTransform2D xfrm = getXfrm(false);
  115. if (xfrm == null || !xfrm.isSetOff()) {
  116. return null;
  117. }
  118. CTPoint2D off = xfrm.getOff();
  119. double x = Units.toPoints(off.getX());
  120. double y = Units.toPoints(off.getY());
  121. CTPositiveSize2D ext = xfrm.getExt();
  122. double cx = Units.toPoints(ext.getCx());
  123. double cy = Units.toPoints(ext.getCy());
  124. return new Rectangle2D.Double(x, y, cx, cy);
  125. }
  126. @Override
  127. public void setAnchor(Rectangle2D anchor) {
  128. CTTransform2D xfrm = getXfrm(true);
  129. if (xfrm == null) {
  130. return;
  131. }
  132. CTPoint2D off = xfrm.isSetOff() ? xfrm.getOff() : xfrm.addNewOff();
  133. long x = Units.toEMU(anchor.getX());
  134. long y = Units.toEMU(anchor.getY());
  135. off.setX(x);
  136. off.setY(y);
  137. CTPositiveSize2D ext = xfrm.isSetExt() ? xfrm.getExt() : xfrm
  138. .addNewExt();
  139. long cx = Units.toEMU(anchor.getWidth());
  140. long cy = Units.toEMU(anchor.getHeight());
  141. ext.setCx(cx);
  142. ext.setCy(cy);
  143. }
  144. @Override
  145. public void setRotation(double theta) {
  146. CTTransform2D xfrm = getXfrm(true);
  147. if (xfrm != null) {
  148. xfrm.setRot((int) (theta * 60000));
  149. }
  150. }
  151. @Override
  152. public double getRotation() {
  153. CTTransform2D xfrm = getXfrm(false);
  154. return (xfrm == null || !xfrm.isSetRot()) ? 0 : (xfrm.getRot() / 60000.d);
  155. }
  156. @Override
  157. public void setFlipHorizontal(boolean flip) {
  158. CTTransform2D xfrm = getXfrm(true);
  159. if (xfrm != null) {
  160. xfrm.setFlipH(flip);
  161. }
  162. }
  163. @Override
  164. public void setFlipVertical(boolean flip) {
  165. CTTransform2D xfrm = getXfrm(true);
  166. if (xfrm != null) {
  167. xfrm.setFlipV(flip);
  168. }
  169. }
  170. @Override
  171. public boolean getFlipHorizontal() {
  172. CTTransform2D xfrm = getXfrm(false);
  173. return (xfrm != null && xfrm.isSetFlipH()) && xfrm.getFlipH();
  174. }
  175. @Override
  176. public boolean getFlipVertical() {
  177. CTTransform2D xfrm = getXfrm(false);
  178. return (xfrm != null && xfrm.isSetFlipV()) && xfrm.getFlipV();
  179. }
  180. /**
  181. * Get default line properties defined in the theme (if any).
  182. * Used internally to resolve shape properties.
  183. *
  184. * @return line properties from the theme of null
  185. */
  186. private CTLineProperties getDefaultLineProperties() {
  187. CTShapeStyle style = getSpStyle();
  188. if (style == null) {
  189. return null;
  190. }
  191. CTStyleMatrixReference lnRef = style.getLnRef();
  192. if (lnRef == null) {
  193. return null;
  194. }
  195. // 1-based index of a line style within the style matrix
  196. int idx = Math.toIntExact(lnRef.getIdx());
  197. XSLFTheme theme = getSheet().getTheme();
  198. if (theme == null) {
  199. return null;
  200. }
  201. CTBaseStyles styles = theme.getXmlObject().getThemeElements();
  202. if (styles == null) {
  203. return null;
  204. }
  205. CTStyleMatrix styleMatrix = styles.getFmtScheme();
  206. if (styleMatrix == null) {
  207. return null;
  208. }
  209. CTLineStyleList lineStyles = styleMatrix.getLnStyleLst();
  210. if (lineStyles == null || lineStyles.sizeOfLnArray() < idx) {
  211. return null;
  212. }
  213. return lineStyles.getLnArray(idx - 1);
  214. }
  215. /**
  216. * @param color the color to paint the shape outline.
  217. * A <code>null</code> value turns off the shape outline.
  218. */
  219. public void setLineColor(Color color) {
  220. CTLineProperties ln = getLn(this, true);
  221. if (ln == null) {
  222. return;
  223. }
  224. if (ln.isSetSolidFill()) {
  225. ln.unsetSolidFill();
  226. }
  227. if (ln.isSetGradFill()) {
  228. ln.unsetGradFill();
  229. }
  230. if (ln.isSetPattFill()) {
  231. ln.unsetPattFill();
  232. }
  233. if (ln.isSetNoFill()) {
  234. ln.unsetNoFill();
  235. }
  236. if (color == null) {
  237. ln.addNewNoFill();
  238. } else {
  239. CTSolidColorFillProperties fill = ln.addNewSolidFill();
  240. XSLFColor col = new XSLFColor(fill, getSheet().getTheme(), fill.getSchemeClr(), getSheet());
  241. col.setColor(color);
  242. }
  243. }
  244. /**
  245. *
  246. * @return the color of the shape outline or <code>null</code>
  247. * if outline is turned off
  248. */
  249. @SuppressWarnings("WeakerAccess")
  250. public Color getLineColor() {
  251. PaintStyle ps = getLinePaint();
  252. if (ps instanceof SolidPaint) {
  253. return ((SolidPaint)ps).getSolidColor().getColor();
  254. }
  255. return null;
  256. }
  257. @SuppressWarnings("WeakerAccess")
  258. protected PaintStyle getLinePaint() {
  259. XSLFSheet sheet = getSheet();
  260. final XSLFTheme theme = sheet.getTheme();
  261. final boolean hasPlaceholder = getPlaceholder() != null;
  262. PropertyFetcher<PaintStyle> fetcher = new PropertyFetcher<PaintStyle>() {
  263. @Override
  264. public boolean fetch(XSLFShape shape) {
  265. CTLineProperties spPr = getLn(shape, false);
  266. XSLFFillProperties fp = XSLFPropertiesDelegate.getFillDelegate(spPr);
  267. if (fp != null && fp.isSetNoFill()) {
  268. setValue(null);
  269. return true;
  270. }
  271. PackagePart pp = shape.getSheet().getPackagePart();
  272. PaintStyle paint = selectPaint(fp, null, pp, theme, hasPlaceholder);
  273. if (paint != null) {
  274. setValue(paint);
  275. return true;
  276. }
  277. CTShapeStyle style = shape.getSpStyle();
  278. if (style != null) {
  279. fp = XSLFPropertiesDelegate.getFillDelegate(style.getLnRef());
  280. paint = selectPaint(fp, null, pp, theme, hasPlaceholder);
  281. // line color was not found, check if it is defined in the theme
  282. if (paint == null) {
  283. paint = getThemePaint(style, pp);
  284. }
  285. }
  286. if (paint != null) {
  287. setValue(paint);
  288. return true;
  289. }
  290. return false;
  291. }
  292. PaintStyle getThemePaint(CTShapeStyle style, PackagePart pp) {
  293. // get a reference to a line style within the style matrix.
  294. CTStyleMatrixReference lnRef = style.getLnRef();
  295. if (lnRef == null) {
  296. return null;
  297. }
  298. int idx = Math.toIntExact(lnRef.getIdx());
  299. CTSchemeColor phClr = lnRef.getSchemeClr();
  300. if(idx <= 0){
  301. return null;
  302. }
  303. CTLineProperties props = theme.getXmlObject().getThemeElements().getFmtScheme().getLnStyleLst().getLnArray(idx - 1);
  304. XSLFFillProperties fp = XSLFPropertiesDelegate.getFillDelegate(props);
  305. return selectPaint(fp, phClr, pp, theme, hasPlaceholder);
  306. }
  307. };
  308. fetchShapeProperty(fetcher);
  309. return fetcher.getValue();
  310. }
  311. /**
  312. *
  313. * @param width line width in points. <code>0</code> means no line
  314. */
  315. @SuppressWarnings("WeakerAccess")
  316. public void setLineWidth(double width) {
  317. CTLineProperties lnPr = getLn(this, true);
  318. if (lnPr == null) {
  319. return;
  320. }
  321. if (width == 0.) {
  322. if (lnPr.isSetW()) {
  323. lnPr.unsetW();
  324. }
  325. if (!lnPr.isSetNoFill()) {
  326. lnPr.addNewNoFill();
  327. }
  328. if (lnPr.isSetSolidFill()) {
  329. lnPr.unsetSolidFill();
  330. }
  331. if (lnPr.isSetGradFill()) {
  332. lnPr.unsetGradFill();
  333. }
  334. if (lnPr.isSetPattFill()) {
  335. lnPr.unsetPattFill();
  336. }
  337. } else {
  338. if (lnPr.isSetNoFill()) {
  339. lnPr.unsetNoFill();
  340. }
  341. lnPr.setW(Units.toEMU(width));
  342. }
  343. }
  344. /**
  345. * @return line width in points. <code>0</code> means no line.
  346. */
  347. @SuppressWarnings("WeakerAccess")
  348. public double getLineWidth() {
  349. PropertyFetcher<Double> fetcher = new PropertyFetcher<Double>() {
  350. @Override
  351. public boolean fetch(XSLFShape shape) {
  352. CTLineProperties ln = getLn(shape, false);
  353. if (ln != null) {
  354. if (ln.isSetNoFill()) {
  355. setValue(0.);
  356. return true;
  357. }
  358. if (ln.isSetW()) {
  359. setValue(Units.toPoints(ln.getW()));
  360. return true;
  361. }
  362. }
  363. return false;
  364. }
  365. };
  366. fetchShapeProperty(fetcher);
  367. double lineWidth = 0;
  368. if (fetcher.getValue() == null) {
  369. CTLineProperties defaultLn = getDefaultLineProperties();
  370. if (defaultLn != null) {
  371. if (defaultLn.isSetW()) {
  372. lineWidth = Units.toPoints(defaultLn.getW());
  373. }
  374. }
  375. } else {
  376. lineWidth = fetcher.getValue();
  377. }
  378. return lineWidth;
  379. }
  380. /**
  381. * @param compound set the line compound style
  382. */
  383. @SuppressWarnings("WeakerAccess")
  384. public void setLineCompound(LineCompound compound) {
  385. CTLineProperties ln = getLn(this, true);
  386. if (ln == null) {
  387. return;
  388. }
  389. if (compound == null) {
  390. if (ln.isSetCmpd()) {
  391. ln.unsetCmpd();
  392. }
  393. } else {
  394. STCompoundLine.Enum xCmpd;
  395. switch (compound) {
  396. default:
  397. case SINGLE:
  398. xCmpd = STCompoundLine.SNG;
  399. break;
  400. case DOUBLE:
  401. xCmpd = STCompoundLine.DBL;
  402. break;
  403. case THICK_THIN:
  404. xCmpd = STCompoundLine.THICK_THIN;
  405. break;
  406. case THIN_THICK:
  407. xCmpd = STCompoundLine.THIN_THICK;
  408. break;
  409. case TRIPLE:
  410. xCmpd = STCompoundLine.TRI;
  411. break;
  412. }
  413. ln.setCmpd(xCmpd);
  414. }
  415. }
  416. /**
  417. * @return the line compound
  418. */
  419. @SuppressWarnings("WeakerAccess")
  420. public LineCompound getLineCompound() {
  421. PropertyFetcher<Integer> fetcher = new PropertyFetcher<Integer>() {
  422. @Override
  423. public boolean fetch(XSLFShape shape) {
  424. CTLineProperties ln = getLn(shape, false);
  425. if (ln != null) {
  426. STCompoundLine.Enum stCmpd = ln.getCmpd();
  427. if (stCmpd != null) {
  428. setValue(stCmpd.intValue());
  429. return true;
  430. }
  431. }
  432. return false;
  433. }
  434. };
  435. fetchShapeProperty(fetcher);
  436. Integer cmpd = fetcher.getValue();
  437. if (cmpd == null) {
  438. CTLineProperties defaultLn = getDefaultLineProperties();
  439. if (defaultLn != null && defaultLn.isSetCmpd()) {
  440. switch (defaultLn.getCmpd().intValue()) {
  441. default:
  442. case STCompoundLine.INT_SNG:
  443. return LineCompound.SINGLE;
  444. case STCompoundLine.INT_DBL:
  445. return LineCompound.DOUBLE;
  446. case STCompoundLine.INT_THICK_THIN:
  447. return LineCompound.THICK_THIN;
  448. case STCompoundLine.INT_THIN_THICK:
  449. return LineCompound.THIN_THICK;
  450. case STCompoundLine.INT_TRI:
  451. return LineCompound.TRIPLE;
  452. }
  453. }
  454. }
  455. return null;
  456. }
  457. /**
  458. *
  459. * @param dash a preset line dashing scheme to stroke thr shape outline
  460. */
  461. @SuppressWarnings("WeakerAccess")
  462. public void setLineDash(LineDash dash) {
  463. CTLineProperties ln = getLn(this, true);
  464. if (ln == null) {
  465. return;
  466. }
  467. if (dash == null) {
  468. if (ln.isSetPrstDash()) {
  469. ln.unsetPrstDash();
  470. }
  471. } else {
  472. CTPresetLineDashProperties ldp = ln.isSetPrstDash() ? ln.getPrstDash() : ln.addNewPrstDash();
  473. ldp.setVal(STPresetLineDashVal.Enum.forInt(dash.ooxmlId));
  474. }
  475. }
  476. /**
  477. * @return a preset line dashing scheme to stroke the shape outline
  478. */
  479. @SuppressWarnings("WeakerAccess")
  480. public LineDash getLineDash() {
  481. PropertyFetcher<LineDash> fetcher = new PropertyFetcher<LineDash>() {
  482. @Override
  483. public boolean fetch(XSLFShape shape) {
  484. CTLineProperties ln = getLn(shape, false);
  485. if (ln == null || !ln.isSetPrstDash()) {
  486. return false;
  487. }
  488. setValue(LineDash.fromOoxmlId(ln.getPrstDash().getVal().intValue()));
  489. return true;
  490. }
  491. };
  492. fetchShapeProperty(fetcher);
  493. LineDash dash = fetcher.getValue();
  494. if (dash == null) {
  495. CTLineProperties defaultLn = getDefaultLineProperties();
  496. if (defaultLn != null && defaultLn.isSetPrstDash()) {
  497. dash = LineDash.fromOoxmlId(defaultLn.getPrstDash().getVal().intValue());
  498. }
  499. }
  500. return dash;
  501. }
  502. /**
  503. *
  504. * @param cap the line end cap style
  505. */
  506. @SuppressWarnings("WeakerAccess")
  507. public void setLineCap(LineCap cap) {
  508. CTLineProperties ln = getLn(this, true);
  509. if (ln == null) {
  510. return;
  511. }
  512. if (cap == null) {
  513. if (ln.isSetCap()) {
  514. ln.unsetCap();
  515. }
  516. } else {
  517. ln.setCap(STLineCap.Enum.forInt(cap.ooxmlId));
  518. }
  519. }
  520. /**
  521. *
  522. * @return the line end cap style
  523. */
  524. @SuppressWarnings("WeakerAccess")
  525. public LineCap getLineCap() {
  526. PropertyFetcher<LineCap> fetcher = new PropertyFetcher<LineCap>() {
  527. @Override
  528. public boolean fetch(XSLFShape shape) {
  529. CTLineProperties ln = getLn(shape, false);
  530. if (ln != null && ln.isSetCap()) {
  531. setValue(LineCap.fromOoxmlId(ln.getCap().intValue()));
  532. return true;
  533. }
  534. return false;
  535. }
  536. };
  537. fetchShapeProperty(fetcher);
  538. LineCap cap = fetcher.getValue();
  539. if (cap == null) {
  540. CTLineProperties defaultLn = getDefaultLineProperties();
  541. if (defaultLn != null && defaultLn.isSetCap()) {
  542. cap = LineCap.fromOoxmlId(defaultLn.getCap().intValue());
  543. }
  544. }
  545. return cap;
  546. }
  547. @Override
  548. public void setFillColor(Color color) {
  549. XSLFFillProperties fp = XSLFPropertiesDelegate.getFillDelegate(getShapeProperties());
  550. if (fp == null) {
  551. return;
  552. }
  553. if (color == null) {
  554. if (fp.isSetSolidFill()) {
  555. fp.unsetSolidFill();
  556. }
  557. if (fp.isSetGradFill()) {
  558. fp.unsetGradFill();
  559. }
  560. if (fp.isSetPattFill()) {
  561. fp.unsetGradFill();
  562. }
  563. if (fp.isSetBlipFill()) {
  564. fp.unsetBlipFill();
  565. }
  566. if (!fp.isSetNoFill()) {
  567. fp.addNewNoFill();
  568. }
  569. } else {
  570. if (fp.isSetNoFill()) {
  571. fp.unsetNoFill();
  572. }
  573. CTSolidColorFillProperties fill = fp.isSetSolidFill() ? fp.getSolidFill() : fp.addNewSolidFill();
  574. XSLFColor col = new XSLFColor(fill, getSheet().getTheme(), fill.getSchemeClr(), getSheet());
  575. col.setColor(color);
  576. }
  577. }
  578. @Override
  579. public Color getFillColor() {
  580. PaintStyle ps = getFillPaint();
  581. if (ps instanceof SolidPaint) {
  582. return DrawPaint.applyColorTransform(((SolidPaint)ps).getSolidColor());
  583. }
  584. return null;
  585. }
  586. /**
  587. * @return shadow of this shape or null if shadow is disabled
  588. */
  589. @Override
  590. public XSLFShadow getShadow() {
  591. PropertyFetcher<CTOuterShadowEffect> fetcher = new PropertyFetcher<CTOuterShadowEffect>() {
  592. @Override
  593. public boolean fetch(XSLFShape shape) {
  594. XSLFEffectProperties ep = XSLFPropertiesDelegate.getEffectDelegate(shape.getShapeProperties());
  595. if (ep != null && ep.isSetEffectLst()) {
  596. CTOuterShadowEffect obj = ep.getEffectLst().getOuterShdw();
  597. setValue(obj == null ? NO_SHADOW : obj);
  598. return true;
  599. }
  600. return false;
  601. }
  602. };
  603. fetchShapeProperty(fetcher);
  604. CTOuterShadowEffect obj = fetcher.getValue();
  605. if (obj == null) {
  606. // fill color was not found, check if it is defined in the theme
  607. CTShapeStyle style = getSpStyle();
  608. if (style != null && style.getEffectRef() != null) {
  609. // 1-based index of a shadow style within the style matrix
  610. int idx = (int) style.getEffectRef().getIdx();
  611. if(idx != 0) {
  612. CTStyleMatrix styleMatrix = getSheet().getTheme().getXmlObject().getThemeElements().getFmtScheme();
  613. CTEffectStyleItem ef = styleMatrix.getEffectStyleLst().getEffectStyleArray(idx - 1);
  614. obj = ef.getEffectLst().getOuterShdw();
  615. }
  616. }
  617. }
  618. return (obj == null || obj == NO_SHADOW) ? null : new XSLFShadow(obj, this);
  619. }
  620. /**
  621. * @return definition of the shape geometry
  622. */
  623. @Override
  624. public CustomGeometry getGeometry() {
  625. XSLFGeometryProperties gp = XSLFPropertiesDelegate.getGeometryDelegate(getShapeProperties());
  626. if (gp == null) {
  627. return null;
  628. }
  629. CustomGeometry geom;
  630. PresetGeometries dict = PresetGeometries.getInstance();
  631. if(gp.isSetPrstGeom()){
  632. String name = gp.getPrstGeom().getPrst().toString();
  633. geom = dict.get(name);
  634. if(geom == null) {
  635. throw new IllegalStateException("Unknown shape geometry: " + name + ", available geometries are: " + dict.keySet());
  636. }
  637. } else if (gp.isSetCustGeom()){
  638. XMLStreamReader staxReader = gp.getCustGeom().newXMLStreamReader();
  639. geom = PresetGeometries.convertCustomGeometry(staxReader);
  640. try {
  641. staxReader.close();
  642. }
  643. catch (XMLStreamException e) {
  644. LOG.log(POILogger.WARN,
  645. "An error occurred while closing a Custom Geometry XML Stream Reader: " + e.getMessage());
  646. }
  647. } else {
  648. geom = dict.get("rect");
  649. }
  650. return geom;
  651. }
  652. @Override
  653. void copy(XSLFShape sh){
  654. super.copy(sh);
  655. XSLFSimpleShape s = (XSLFSimpleShape)sh;
  656. Color srsSolidFill = s.getFillColor();
  657. Color tgtSoliFill = getFillColor();
  658. if(srsSolidFill != null && !srsSolidFill.equals(tgtSoliFill)){
  659. setFillColor(srsSolidFill);
  660. }
  661. XSLFFillProperties fp = XSLFPropertiesDelegate.getFillDelegate(getShapeProperties());
  662. if(fp != null && fp.isSetBlipFill()){
  663. CTBlip blip = fp.getBlipFill().getBlip();
  664. String blipId = blip.getEmbed();
  665. String relId = getSheet().importBlip(blipId, s.getSheet());
  666. blip.setEmbed(relId);
  667. }
  668. Color srcLineColor = s.getLineColor();
  669. Color tgtLineColor = getLineColor();
  670. if(srcLineColor != null && !srcLineColor.equals(tgtLineColor)) {
  671. setLineColor(srcLineColor);
  672. }
  673. double srcLineWidth = s.getLineWidth();
  674. double tgtLineWidth = getLineWidth();
  675. if(srcLineWidth != tgtLineWidth) {
  676. setLineWidth(srcLineWidth);
  677. }
  678. LineDash srcLineDash = s.getLineDash();
  679. LineDash tgtLineDash = getLineDash();
  680. if(srcLineDash != null && srcLineDash != tgtLineDash) {
  681. setLineDash(srcLineDash);
  682. }
  683. LineCap srcLineCap = s.getLineCap();
  684. LineCap tgtLineCap = getLineCap();
  685. if(srcLineCap != null && srcLineCap != tgtLineCap) {
  686. setLineCap(srcLineCap);
  687. }
  688. }
  689. /**
  690. * Specifies the line end decoration, such as a triangle or arrowhead.
  691. *
  692. * @param style the line end docoration style
  693. */
  694. @SuppressWarnings("WeakerAccess")
  695. public void setLineHeadDecoration(DecorationShape style) {
  696. CTLineProperties ln = getLn(this, true);
  697. if (ln == null) {
  698. return;
  699. }
  700. CTLineEndProperties lnEnd = ln.isSetHeadEnd() ? ln.getHeadEnd() : ln.addNewHeadEnd();
  701. if (style == null) {
  702. if (lnEnd.isSetType()) {
  703. lnEnd.unsetType();
  704. }
  705. } else {
  706. lnEnd.setType(STLineEndType.Enum.forInt(style.ooxmlId));
  707. }
  708. }
  709. /**
  710. * @return the line end decoration shape
  711. */
  712. @SuppressWarnings("WeakerAccess")
  713. public DecorationShape getLineHeadDecoration() {
  714. CTLineProperties ln = getLn(this, false);
  715. DecorationShape ds = DecorationShape.NONE;
  716. if (ln != null && ln.isSetHeadEnd() && ln.getHeadEnd().isSetType()) {
  717. ds = DecorationShape.fromOoxmlId(ln.getHeadEnd().getType().intValue());
  718. }
  719. return ds;
  720. }
  721. /**
  722. * specifies decoration width of the head of a line.
  723. *
  724. * @param style the decoration width
  725. */
  726. @SuppressWarnings("WeakerAccess")
  727. public void setLineHeadWidth(DecorationSize style) {
  728. CTLineProperties ln = getLn(this, true);
  729. if (ln == null) {
  730. return;
  731. }
  732. CTLineEndProperties lnEnd = ln.isSetHeadEnd() ? ln.getHeadEnd() : ln.addNewHeadEnd();
  733. if (style == null) {
  734. if (lnEnd.isSetW()) {
  735. lnEnd.unsetW();
  736. }
  737. } else {
  738. lnEnd.setW(STLineEndWidth.Enum.forInt(style.ooxmlId));
  739. }
  740. }
  741. /**
  742. * @return the line end decoration width
  743. */
  744. @SuppressWarnings("WeakerAccess")
  745. public DecorationSize getLineHeadWidth() {
  746. CTLineProperties ln = getLn(this, false);
  747. DecorationSize ds = DecorationSize.MEDIUM;
  748. if (ln != null && ln.isSetHeadEnd() && ln.getHeadEnd().isSetW()) {
  749. ds = DecorationSize.fromOoxmlId(ln.getHeadEnd().getW().intValue());
  750. }
  751. return ds;
  752. }
  753. /**
  754. * Specifies the line end width in relation to the line width.
  755. */
  756. @SuppressWarnings("WeakerAccess")
  757. public void setLineHeadLength(DecorationSize style) {
  758. CTLineProperties ln = getLn(this, true);
  759. if (ln == null) {
  760. return;
  761. }
  762. CTLineEndProperties lnEnd = ln.isSetHeadEnd() ? ln.getHeadEnd() : ln.addNewHeadEnd();
  763. if (style == null) {
  764. if (lnEnd.isSetLen()) {
  765. lnEnd.unsetLen();
  766. }
  767. } else {
  768. lnEnd.setLen(STLineEndLength.Enum.forInt(style.ooxmlId));
  769. }
  770. }
  771. /**
  772. * @return the line end decoration length
  773. */
  774. @SuppressWarnings("WeakerAccess")
  775. public DecorationSize getLineHeadLength() {
  776. CTLineProperties ln = getLn(this, false);
  777. DecorationSize ds = DecorationSize.MEDIUM;
  778. if (ln != null && ln.isSetHeadEnd() && ln.getHeadEnd().isSetLen()) {
  779. ds = DecorationSize.fromOoxmlId(ln.getHeadEnd().getLen().intValue());
  780. }
  781. return ds;
  782. }
  783. /**
  784. * Specifies the line end decoration, such as a triangle or arrowhead.
  785. */
  786. @SuppressWarnings("WeakerAccess")
  787. public void setLineTailDecoration(DecorationShape style) {
  788. CTLineProperties ln = getLn(this, true);
  789. if (ln == null) {
  790. return;
  791. }
  792. CTLineEndProperties lnEnd = ln.isSetTailEnd() ? ln.getTailEnd() : ln.addNewTailEnd();
  793. if (style == null) {
  794. if (lnEnd.isSetType()) {
  795. lnEnd.unsetType();
  796. }
  797. } else {
  798. lnEnd.setType(STLineEndType.Enum.forInt(style.ooxmlId));
  799. }
  800. }
  801. /**
  802. * @return the line end decoration shape
  803. */
  804. @SuppressWarnings("WeakerAccess")
  805. public DecorationShape getLineTailDecoration() {
  806. CTLineProperties ln = getLn(this, false);
  807. DecorationShape ds = DecorationShape.NONE;
  808. if (ln != null && ln.isSetTailEnd() && ln.getTailEnd().isSetType()) {
  809. ds = DecorationShape.fromOoxmlId(ln.getTailEnd().getType().intValue());
  810. }
  811. return ds;
  812. }
  813. /**
  814. * specifies decorations which can be added to the tail of a line.
  815. */
  816. @SuppressWarnings("WeakerAccess")
  817. public void setLineTailWidth(DecorationSize style) {
  818. CTLineProperties ln = getLn(this, true);
  819. if (ln == null) {
  820. return;
  821. }
  822. CTLineEndProperties lnEnd = ln.isSetTailEnd() ? ln.getTailEnd() : ln.addNewTailEnd();
  823. if (style == null) {
  824. if (lnEnd.isSetW()) {
  825. lnEnd.unsetW();
  826. }
  827. } else {
  828. lnEnd.setW(STLineEndWidth.Enum.forInt(style.ooxmlId));
  829. }
  830. }
  831. /**
  832. * @return the line end decoration width
  833. */
  834. @SuppressWarnings("WeakerAccess")
  835. public DecorationSize getLineTailWidth() {
  836. CTLineProperties ln = getLn(this, false);
  837. DecorationSize ds = DecorationSize.MEDIUM;
  838. if (ln != null && ln.isSetTailEnd() && ln.getTailEnd().isSetW()) {
  839. ds = DecorationSize.fromOoxmlId(ln.getTailEnd().getW().intValue());
  840. }
  841. return ds;
  842. }
  843. /**
  844. * Specifies the line end width in relation to the line width.
  845. */
  846. @SuppressWarnings("WeakerAccess")
  847. public void setLineTailLength(DecorationSize style) {
  848. CTLineProperties ln = getLn(this, true);
  849. if (ln == null) {
  850. return;
  851. }
  852. CTLineEndProperties lnEnd = ln.isSetTailEnd() ? ln.getTailEnd() : ln.addNewTailEnd();
  853. if (style == null) {
  854. if (lnEnd.isSetLen()) {
  855. lnEnd.unsetLen();
  856. }
  857. } else {
  858. lnEnd.setLen(STLineEndLength.Enum.forInt(style.ooxmlId));
  859. }
  860. }
  861. /**
  862. * @return the line end decoration length
  863. */
  864. @SuppressWarnings("WeakerAccess")
  865. public DecorationSize getLineTailLength() {
  866. CTLineProperties ln = getLn(this, false);
  867. DecorationSize ds = DecorationSize.MEDIUM;
  868. if (ln != null && ln.isSetTailEnd() && ln.getTailEnd().isSetLen()) {
  869. ds = DecorationSize.fromOoxmlId(ln.getTailEnd().getLen().intValue());
  870. }
  871. return ds;
  872. }
  873. @Override
  874. public Guide getAdjustValue(String name) {
  875. XSLFGeometryProperties gp = XSLFPropertiesDelegate.getGeometryDelegate(getShapeProperties());
  876. if (gp != null && gp.isSetPrstGeom() && gp.getPrstGeom().isSetAvLst()) {
  877. //noinspection deprecation
  878. for (CTGeomGuide g : gp.getPrstGeom().getAvLst().getGdArray()) {
  879. if (g.getName().equals(name)) {
  880. Guide gd = new Guide();
  881. gd.setName(g.getName());
  882. gd.setFmla(g.getFmla());
  883. return gd;
  884. }
  885. }
  886. }
  887. return null;
  888. }
  889. @Override
  890. public LineDecoration getLineDecoration() {
  891. return new LineDecoration() {
  892. @Override
  893. public DecorationShape getHeadShape() {
  894. return getLineHeadDecoration();
  895. }
  896. @Override
  897. public DecorationSize getHeadWidth() {
  898. return getLineHeadWidth();
  899. }
  900. @Override
  901. public DecorationSize getHeadLength() {
  902. return getLineHeadLength();
  903. }
  904. @Override
  905. public DecorationShape getTailShape() {
  906. return getLineTailDecoration();
  907. }
  908. @Override
  909. public DecorationSize getTailWidth() {
  910. return getLineTailWidth();
  911. }
  912. @Override
  913. public DecorationSize getTailLength() {
  914. return getLineTailLength();
  915. }
  916. };
  917. }
  918. /**
  919. * fetch shape fill as a java.awt.Paint
  920. *
  921. * @return either Color or GradientPaint or TexturePaint or null
  922. */
  923. @Override
  924. public FillStyle getFillStyle() {
  925. return XSLFSimpleShape.this::getFillPaint;
  926. }
  927. @Override
  928. public StrokeStyle getStrokeStyle() {
  929. return new StrokeStyle() {
  930. @Override
  931. public PaintStyle getPaint() {
  932. return XSLFSimpleShape.this.getLinePaint();
  933. }
  934. @Override
  935. public LineCap getLineCap() {
  936. return XSLFSimpleShape.this.getLineCap();
  937. }
  938. @Override
  939. public LineDash getLineDash() {
  940. return XSLFSimpleShape.this.getLineDash();
  941. }
  942. @Override
  943. public double getLineWidth() {
  944. return XSLFSimpleShape.this.getLineWidth();
  945. }
  946. @Override
  947. public LineCompound getLineCompound() {
  948. return XSLFSimpleShape.this.getLineCompound();
  949. }
  950. };
  951. }
  952. @Override
  953. public void setStrokeStyle(Object... styles) {
  954. if (styles.length == 0) {
  955. // remove stroke
  956. setLineColor(null);
  957. return;
  958. }
  959. // TODO: handle PaintStyle
  960. for (Object st : styles) {
  961. if (st instanceof Number) {
  962. setLineWidth(((Number)st).doubleValue());
  963. } else if (st instanceof LineCap) {
  964. setLineCap((LineCap)st);
  965. } else if (st instanceof LineDash) {
  966. setLineDash((LineDash)st);
  967. } else if (st instanceof LineCompound) {
  968. setLineCompound((LineCompound)st);
  969. } else if (st instanceof Color) {
  970. setLineColor((Color)st);
  971. }
  972. }
  973. }
  974. @Override
  975. public XSLFHyperlink getHyperlink() {
  976. CTNonVisualDrawingProps cNvPr = getCNvPr();
  977. if (!cNvPr.isSetHlinkClick()) {
  978. return null;
  979. }
  980. return new XSLFHyperlink(cNvPr.getHlinkClick(), getSheet());
  981. }
  982. @Override
  983. public XSLFHyperlink createHyperlink() {
  984. XSLFHyperlink hl = getHyperlink();
  985. if (hl == null) {
  986. CTNonVisualDrawingProps cNvPr = getCNvPr();
  987. hl = new XSLFHyperlink(cNvPr.addNewHlinkClick(), getSheet());
  988. }
  989. return hl;
  990. }
  991. private static CTLineProperties getLn(XSLFShape shape, boolean create) {
  992. XmlObject pr = shape.getShapeProperties();
  993. if (!(pr instanceof CTShapeProperties)) {
  994. LOG.log(POILogger.WARN, shape.getClass() +" doesn't have line properties");
  995. return null;
  996. }
  997. CTShapeProperties spr = (CTShapeProperties)pr;
  998. return (spr.isSetLn() || !create) ? spr.getLn() : spr.addNewLn();
  999. }
  1000. }