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.

XSSFSimpleShape.java 35KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030
  1. /* ====================================================================
  2. Licensed to the Apache Software Foundation (ASF) under one or more
  3. contributor license agreements. See the NOTICE file distributed with
  4. this work for additional information regarding copyright ownership.
  5. The ASF licenses this file to You under the Apache License, Version 2.0
  6. (the "License"); you may not use this file except in compliance with
  7. the License. You may obtain a copy of the License at
  8. http://www.apache.org/licenses/LICENSE-2.0
  9. Unless required by applicable law or agreed to in writing, software
  10. distributed under the License is distributed on an "AS IS" BASIS,
  11. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. See the License for the specific language governing permissions and
  13. limitations under the License.
  14. ==================================================================== */
  15. package org.apache.poi.xssf.usermodel;
  16. import java.util.ArrayList;
  17. import java.util.Iterator;
  18. import java.util.List;
  19. import java.util.Locale;
  20. import java.util.Optional;
  21. import java.util.Spliterator;
  22. import java.util.function.Function;
  23. import java.util.function.Predicate;
  24. import org.apache.poi.hssf.util.HSSFColor;
  25. import org.apache.poi.ss.usermodel.SimpleShape;
  26. import org.apache.poi.ss.usermodel.VerticalAlignment;
  27. import org.apache.poi.util.Beta;
  28. import org.apache.poi.util.Internal;
  29. import org.apache.poi.xddf.usermodel.XDDFColor;
  30. import org.apache.poi.xddf.usermodel.XDDFColorRgbBinary;
  31. import org.apache.poi.xddf.usermodel.XDDFFillProperties;
  32. import org.apache.poi.xddf.usermodel.XDDFSolidFillProperties;
  33. import org.apache.poi.xddf.usermodel.text.TextContainer;
  34. import org.apache.poi.xddf.usermodel.text.XDDFRunProperties;
  35. import org.apache.poi.xddf.usermodel.text.XDDFTextBody;
  36. import org.apache.poi.xddf.usermodel.text.XDDFTextParagraph;
  37. import org.openxmlformats.schemas.drawingml.x2006.main.CTNonVisualDrawingProps;
  38. import org.openxmlformats.schemas.drawingml.x2006.main.CTPoint2D;
  39. import org.openxmlformats.schemas.drawingml.x2006.main.CTPositiveSize2D;
  40. import org.openxmlformats.schemas.drawingml.x2006.main.CTPresetGeometry2D;
  41. import org.openxmlformats.schemas.drawingml.x2006.main.CTRegularTextRun;
  42. import org.openxmlformats.schemas.drawingml.x2006.main.CTSRgbColor;
  43. import org.openxmlformats.schemas.drawingml.x2006.main.CTShapeProperties;
  44. import org.openxmlformats.schemas.drawingml.x2006.main.CTSolidColorFillProperties;
  45. import org.openxmlformats.schemas.drawingml.x2006.main.CTTextBody;
  46. import org.openxmlformats.schemas.drawingml.x2006.main.CTTextBodyProperties;
  47. import org.openxmlformats.schemas.drawingml.x2006.main.CTTextCharacterProperties;
  48. import org.openxmlformats.schemas.drawingml.x2006.main.CTTextFont;
  49. import org.openxmlformats.schemas.drawingml.x2006.main.CTTextParagraph;
  50. import org.openxmlformats.schemas.drawingml.x2006.main.CTTextParagraphProperties;
  51. import org.openxmlformats.schemas.drawingml.x2006.main.CTTransform2D;
  52. import org.openxmlformats.schemas.drawingml.x2006.main.STShapeType;
  53. import org.openxmlformats.schemas.drawingml.x2006.main.STTextAnchoringType;
  54. import org.openxmlformats.schemas.drawingml.x2006.main.STTextHorzOverflowType;
  55. import org.openxmlformats.schemas.drawingml.x2006.main.STTextUnderlineType;
  56. import org.openxmlformats.schemas.drawingml.x2006.main.STTextVertOverflowType;
  57. import org.openxmlformats.schemas.drawingml.x2006.main.STTextVerticalType;
  58. import org.openxmlformats.schemas.drawingml.x2006.main.STTextWrappingType;
  59. import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTShape;
  60. import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTShapeNonVisual;
  61. import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTRElt;
  62. import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTRPrElt;
  63. import org.openxmlformats.schemas.spreadsheetml.x2006.main.STUnderlineValues;
  64. /**
  65. * Represents a shape with a predefined geometry in a SpreadsheetML drawing.
  66. * Possible shape types are defined in
  67. * {@link org.apache.poi.ss.usermodel.ShapeTypes}
  68. */
  69. public class XSSFSimpleShape extends XSSFShape implements Iterable<XSSFTextParagraph>, SimpleShape, TextContainer {
  70. /**
  71. * The text body containing the paragraphs for this shape.
  72. */
  73. private final XDDFTextBody _textBody;
  74. /**
  75. * List of the paragraphs that make up the text in this shape
  76. */
  77. private final List<XSSFTextParagraph> _paragraphs;
  78. /**
  79. * A default instance of CTShape used for creating new shapes.
  80. */
  81. private static CTShape prototype;
  82. /**
  83. * Xml bean that stores properties of this shape
  84. */
  85. private CTShape ctShape;
  86. protected XSSFSimpleShape(XSSFDrawing drawing, CTShape ctShape) {
  87. this.drawing = drawing;
  88. this.ctShape = ctShape;
  89. _paragraphs = new ArrayList<>();
  90. // initialize any existing paragraphs - this will be the default body
  91. // paragraph in a new shape,
  92. // or existing paragraphs that have been loaded from the file
  93. CTTextBody body = ctShape.getTxBody();
  94. if (body == null) {
  95. _textBody = null;
  96. } else {
  97. _textBody = new XDDFTextBody(this, body);
  98. for (int i = 0; i < body.sizeOfPArray(); i++) {
  99. _paragraphs.add(new XSSFTextParagraph(body.getPArray(i), ctShape));
  100. }
  101. }
  102. }
  103. /**
  104. * Prototype with the default structure of a new auto-shape.
  105. */
  106. protected static CTShape prototype() {
  107. if (prototype == null) {
  108. CTShape shape = CTShape.Factory.newInstance();
  109. CTShapeNonVisual nv = shape.addNewNvSpPr();
  110. CTNonVisualDrawingProps nvp = nv.addNewCNvPr();
  111. nvp.setId(1);
  112. nvp.setName("Shape 1");
  113. nv.addNewCNvSpPr();
  114. CTShapeProperties sp = shape.addNewSpPr();
  115. CTTransform2D t2d = sp.addNewXfrm();
  116. CTPositiveSize2D p1 = t2d.addNewExt();
  117. p1.setCx(0);
  118. p1.setCy(0);
  119. CTPoint2D p2 = t2d.addNewOff();
  120. p2.setX(0);
  121. p2.setY(0);
  122. CTPresetGeometry2D geom = sp.addNewPrstGeom();
  123. geom.setPrst(STShapeType.RECT);
  124. geom.addNewAvLst();
  125. XDDFTextBody body = new XDDFTextBody(null, shape.addNewTxBody());
  126. XDDFTextParagraph p = body.initialize();
  127. XDDFRunProperties rp = p.getAfterLastRunProperties();
  128. XDDFColor black = new XDDFColorRgbBinary(new byte[] { 0, 0, 0 });
  129. XDDFFillProperties fp = new XDDFSolidFillProperties(black);
  130. rp.setFillProperties(fp);
  131. prototype = shape;
  132. }
  133. return prototype;
  134. }
  135. @Internal
  136. public CTShape getCTShape() {
  137. return ctShape;
  138. }
  139. @Beta
  140. public XDDFTextBody getTextBody() {
  141. return _textBody;
  142. }
  143. protected void setXfrm(CTTransform2D t2d) {
  144. ctShape.getSpPr().setXfrm(t2d);
  145. }
  146. @Override
  147. public Iterator<XSSFTextParagraph> iterator() {
  148. return _paragraphs.iterator();
  149. }
  150. /**
  151. * @since POI 5.2.0
  152. */
  153. @Override
  154. public Spliterator<XSSFTextParagraph> spliterator() {
  155. return _paragraphs.spliterator();
  156. }
  157. /**
  158. * Returns the text from all paragraphs in the shape. Paragraphs are
  159. * separated by new lines.
  160. *
  161. * @return text contained within this shape or empty string
  162. */
  163. public String getText() {
  164. final int MAX_LEVELS = 9;
  165. StringBuilder out = new StringBuilder();
  166. List<Integer> levelCount = new ArrayList<>(MAX_LEVELS); // maximum 9
  167. // levels
  168. // initialise the levelCount array - this maintains a record of the
  169. // numbering to be used at each level
  170. for (int k = 0; k < MAX_LEVELS; k++) {
  171. levelCount.add(0);
  172. }
  173. for (int i = 0; i < _paragraphs.size(); i++) {
  174. if (out.length() > 0) {
  175. out.append('\n');
  176. }
  177. XSSFTextParagraph p = _paragraphs.get(i);
  178. if (p.isBullet() && p.getText().length() > 0) {
  179. int level = Math.min(p.getLevel(), MAX_LEVELS - 1);
  180. if (p.isBulletAutoNumber()) {
  181. i = processAutoNumGroup(i, level, levelCount, out);
  182. } else {
  183. // indent appropriately for the level
  184. for (int j = 0; j < level; j++) {
  185. out.append('\t');
  186. }
  187. String character = p.getBulletCharacter();
  188. out.append(character.length() > 0 ? character + " " : "- ");
  189. out.append(p.getText());
  190. }
  191. } else {
  192. out.append(p.getText());
  193. // this paragraph is not a bullet, so reset the count array
  194. for (int k = 0; k < MAX_LEVELS; k++) {
  195. levelCount.set(k, 0);
  196. }
  197. }
  198. }
  199. return out.toString();
  200. }
  201. /**
  202. *
  203. */
  204. private int processAutoNumGroup(int index, int level, List<Integer> levelCount, StringBuilder out) {
  205. XSSFTextParagraph p = _paragraphs.get(index);
  206. // The rules for generating the auto numbers are as follows. If the
  207. // following paragraph is also
  208. // an auto-number, has the same type/scheme (and startAt if defined on
  209. // this paragraph) then they are
  210. // considered part of the same group. An empty bullet paragraph is
  211. // counted as part of the same
  212. // group but does not increment the count for the group. A change of
  213. // type, startAt or the paragraph
  214. // not being a bullet resets the count for that level to 1.
  215. // first auto-number paragraph so initialise to 1 or the bullets startAt
  216. // if present
  217. int startAt = p.getBulletAutoNumberStart();
  218. ListAutoNumber scheme = p.getBulletAutoNumberScheme();
  219. if (levelCount.get(level) == 0) {
  220. levelCount.set(level, startAt == 0 ? 1 : startAt);
  221. }
  222. // indent appropriately for the level
  223. for (int j = 0; j < level; j++) {
  224. out.append('\t');
  225. }
  226. if (p.getText().length() > 0) {
  227. out.append(getBulletPrefix(scheme, levelCount.get(level)));
  228. out.append(p.getText());
  229. }
  230. while (true) {
  231. XSSFTextParagraph nextp = (index + 1) == _paragraphs.size() ? null : _paragraphs.get(index + 1);
  232. if (nextp == null) {
  233. break; // out of paragraphs
  234. }
  235. if (!(nextp.isBullet() && p.isBulletAutoNumber())) {
  236. break; // not an auto-number bullet
  237. }
  238. if (nextp.getLevel() > level) {
  239. // recurse into the new level group
  240. if (out.length() > 0) {
  241. out.append('\n');
  242. }
  243. index = processAutoNumGroup(index + 1, nextp.getLevel(), levelCount, out);
  244. continue; // restart the loop given the new index
  245. } else if (nextp.getLevel() < level) {
  246. break; // changed level
  247. }
  248. ListAutoNumber nextScheme = nextp.getBulletAutoNumberScheme();
  249. int nextStartAt = nextp.getBulletAutoNumberStart();
  250. if (nextScheme == scheme && nextStartAt == startAt) {
  251. // bullet is valid, so increment i
  252. ++index;
  253. if (out.length() > 0) {
  254. out.append('\n');
  255. }
  256. // indent for the level
  257. for (int j = 0; j < level; j++) {
  258. out.append('\t');
  259. }
  260. // check for empty text - only output a bullet if there is text,
  261. // but it is still part of the group
  262. if (nextp.getText().length() > 0) {
  263. // increment the count for this level
  264. levelCount.set(level, levelCount.get(level) + 1);
  265. out.append(getBulletPrefix(nextScheme, levelCount.get(level)));
  266. out.append(nextp.getText());
  267. }
  268. } else {
  269. // something doesn't match so stop
  270. break;
  271. }
  272. }
  273. // end of the group so reset the count for this level
  274. levelCount.set(level, 0);
  275. return index;
  276. }
  277. /**
  278. * Returns a string containing an appropriate prefix for an auto-numbering
  279. * bullet
  280. *
  281. * @param scheme
  282. * the auto-numbering scheme used by the bullet
  283. * @param value
  284. * the value of the bullet
  285. * @return appropriate prefix for an auto-numbering bullet
  286. */
  287. private String getBulletPrefix(ListAutoNumber scheme, int value) {
  288. StringBuilder out = new StringBuilder();
  289. switch (scheme) {
  290. case ALPHA_LC_PARENT_BOTH:
  291. case ALPHA_LC_PARENT_R:
  292. if (scheme == ListAutoNumber.ALPHA_LC_PARENT_BOTH) {
  293. out.append('(');
  294. }
  295. out.append(valueToAlpha(value).toLowerCase(Locale.ROOT));
  296. out.append(')');
  297. break;
  298. case ALPHA_UC_PARENT_BOTH:
  299. case ALPHA_UC_PARENT_R:
  300. if (scheme == ListAutoNumber.ALPHA_UC_PARENT_BOTH) {
  301. out.append('(');
  302. }
  303. out.append(valueToAlpha(value));
  304. out.append(')');
  305. break;
  306. case ALPHA_LC_PERIOD:
  307. out.append(valueToAlpha(value).toLowerCase(Locale.ROOT));
  308. out.append('.');
  309. break;
  310. case ALPHA_UC_PERIOD:
  311. out.append(valueToAlpha(value));
  312. out.append('.');
  313. break;
  314. case ARABIC_PARENT_BOTH:
  315. case ARABIC_PARENT_R:
  316. if (scheme == ListAutoNumber.ARABIC_PARENT_BOTH) {
  317. out.append('(');
  318. }
  319. out.append(value);
  320. out.append(')');
  321. break;
  322. case ARABIC_PERIOD:
  323. out.append(value);
  324. out.append('.');
  325. break;
  326. case ARABIC_PLAIN:
  327. out.append(value);
  328. break;
  329. case ROMAN_LC_PARENT_BOTH:
  330. case ROMAN_LC_PARENT_R:
  331. if (scheme == ListAutoNumber.ROMAN_LC_PARENT_BOTH) {
  332. out.append('(');
  333. }
  334. out.append(valueToRoman(value).toLowerCase(Locale.ROOT));
  335. out.append(')');
  336. break;
  337. case ROMAN_UC_PARENT_BOTH:
  338. case ROMAN_UC_PARENT_R:
  339. if (scheme == ListAutoNumber.ROMAN_UC_PARENT_BOTH) {
  340. out.append('(');
  341. }
  342. out.append(valueToRoman(value));
  343. out.append(')');
  344. break;
  345. case ROMAN_LC_PERIOD:
  346. out.append(valueToRoman(value).toLowerCase(Locale.ROOT));
  347. out.append('.');
  348. break;
  349. case ROMAN_UC_PERIOD:
  350. out.append(valueToRoman(value));
  351. out.append('.');
  352. break;
  353. default:
  354. out.append('\u2022'); // can't set the font to wingdings so use the
  355. // default bullet character
  356. break;
  357. }
  358. out.append(" ");
  359. return out.toString();
  360. }
  361. /**
  362. * Convert an integer to its alpha equivalent e.g. 1 = A, 2 = B, 27 = AA etc
  363. */
  364. private String valueToAlpha(int value) {
  365. String alpha = "";
  366. int modulo;
  367. while (value > 0) {
  368. modulo = (value - 1) % 26;
  369. alpha = (char) (65 + modulo) + alpha;
  370. value = (value - modulo) / 26;
  371. }
  372. return alpha;
  373. }
  374. private static String[] _romanChars = new String[] { "M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V",
  375. "IV", "I" };
  376. private static int[] _romanAlphaValues = new int[] { 1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1 };
  377. /**
  378. * Convert an integer to its roman equivalent e.g. 1 = I, 9 = IX etc
  379. */
  380. private String valueToRoman(int value) {
  381. StringBuilder out = new StringBuilder();
  382. for (int i = 0; value > 0 && i < _romanChars.length; i++) {
  383. while (_romanAlphaValues[i] <= value) {
  384. out.append(_romanChars[i]);
  385. value -= _romanAlphaValues[i];
  386. }
  387. }
  388. return out.toString();
  389. }
  390. /**
  391. * Clear all text from this shape
  392. */
  393. public void clearText() {
  394. _paragraphs.clear();
  395. CTTextBody txBody = ctShape.getTxBody();
  396. txBody.setPArray(null); // remove any existing paragraphs
  397. }
  398. /**
  399. * Set a single paragraph of text on the shape. Note this will replace all
  400. * existing paragraphs created on the shape.
  401. *
  402. * @param text
  403. * string representing the paragraph text
  404. */
  405. public void setText(String text) {
  406. clearText();
  407. addNewTextParagraph().addNewTextRun().setText(text);
  408. }
  409. /**
  410. * Set a single paragraph of text on the shape. Note this will replace all
  411. * existing paragraphs created on the shape.
  412. *
  413. * @param str
  414. * rich text string representing the paragraph text
  415. */
  416. public void setText(XSSFRichTextString str) {
  417. XSSFWorkbook wb = (XSSFWorkbook) getDrawing().getParent().getParent();
  418. str.setStylesTableReference(wb.getStylesSource());
  419. CTTextParagraph p = CTTextParagraph.Factory.newInstance();
  420. if (str.numFormattingRuns() == 0) {
  421. CTRegularTextRun r = p.addNewR();
  422. CTTextCharacterProperties rPr = r.addNewRPr();
  423. rPr.setLang("en-US");
  424. rPr.setSz(1100);
  425. r.setT(str.getString());
  426. } else {
  427. for (int i = 0; i < str.getCTRst().sizeOfRArray(); i++) {
  428. CTRElt lt = str.getCTRst().getRArray(i);
  429. CTRPrElt ltPr = lt.getRPr();
  430. if (ltPr == null) {
  431. ltPr = lt.addNewRPr();
  432. }
  433. CTRegularTextRun r = p.addNewR();
  434. CTTextCharacterProperties rPr = r.addNewRPr();
  435. rPr.setLang("en-US");
  436. applyAttributes(ltPr, rPr);
  437. r.setT(lt.getT());
  438. }
  439. }
  440. clearText();
  441. ctShape.getTxBody().setPArray(new CTTextParagraph[] { p });
  442. _paragraphs.add(new XSSFTextParagraph(ctShape.getTxBody().getPArray(0), ctShape));
  443. }
  444. /**
  445. * Returns a collection of the XSSFTextParagraphs that are attached to this
  446. * shape
  447. *
  448. * @return text paragraphs in this shape
  449. */
  450. public List<XSSFTextParagraph> getTextParagraphs() {
  451. return _paragraphs;
  452. }
  453. /**
  454. * Add a new paragraph run to this shape
  455. *
  456. * @return created paragraph run
  457. */
  458. public XSSFTextParagraph addNewTextParagraph() {
  459. CTTextBody txBody = ctShape.getTxBody();
  460. CTTextParagraph p = txBody.addNewP();
  461. XSSFTextParagraph paragraph = new XSSFTextParagraph(p, ctShape);
  462. _paragraphs.add(paragraph);
  463. return paragraph;
  464. }
  465. /**
  466. * Add a new paragraph run to this shape, set to the provided string
  467. *
  468. * @return created paragraph run
  469. */
  470. public XSSFTextParagraph addNewTextParagraph(String text) {
  471. XSSFTextParagraph paragraph = addNewTextParagraph();
  472. paragraph.addNewTextRun().setText(text);
  473. return paragraph;
  474. }
  475. /**
  476. * Add a new paragraph run to this shape, set to the provided rich text
  477. * string
  478. *
  479. * @return created paragraph run
  480. */
  481. public XSSFTextParagraph addNewTextParagraph(XSSFRichTextString str) {
  482. CTTextBody txBody = ctShape.getTxBody();
  483. CTTextParagraph p = txBody.addNewP();
  484. if (str.numFormattingRuns() == 0) {
  485. CTRegularTextRun r = p.addNewR();
  486. CTTextCharacterProperties rPr = r.addNewRPr();
  487. rPr.setLang("en-US");
  488. rPr.setSz(1100);
  489. r.setT(str.getString());
  490. } else {
  491. for (int i = 0; i < str.getCTRst().sizeOfRArray(); i++) {
  492. CTRElt lt = str.getCTRst().getRArray(i);
  493. CTRPrElt ltPr = lt.getRPr();
  494. if (ltPr == null) {
  495. ltPr = lt.addNewRPr();
  496. }
  497. CTRegularTextRun r = p.addNewR();
  498. CTTextCharacterProperties rPr = r.addNewRPr();
  499. rPr.setLang("en-US");
  500. applyAttributes(ltPr, rPr);
  501. r.setT(lt.getT());
  502. }
  503. }
  504. // Note: the XSSFTextParagraph constructor will create its required
  505. // XSSFTextRuns from the provided CTTextParagraph
  506. XSSFTextParagraph paragraph = new XSSFTextParagraph(p, ctShape);
  507. _paragraphs.add(paragraph);
  508. return paragraph;
  509. }
  510. /**
  511. * Sets the type of horizontal overflow for the text.
  512. *
  513. * @param overflow
  514. * - the type of horizontal overflow. A <code>null</code> values
  515. * unsets this property.
  516. */
  517. public void setTextHorizontalOverflow(TextHorizontalOverflow overflow) {
  518. CTTextBodyProperties bodyPr = ctShape.getTxBody().getBodyPr();
  519. if (bodyPr != null) {
  520. if (overflow == null) {
  521. if (bodyPr.isSetHorzOverflow()) {
  522. bodyPr.unsetHorzOverflow();
  523. }
  524. } else {
  525. bodyPr.setHorzOverflow(STTextHorzOverflowType.Enum.forInt(overflow.ordinal() + 1));
  526. }
  527. }
  528. }
  529. /**
  530. * Returns the type of horizontal overflow for the text.
  531. *
  532. * @return the type of horizontal overflow
  533. */
  534. public TextHorizontalOverflow getTextHorizontalOverflow() {
  535. CTTextBodyProperties bodyPr = ctShape.getTxBody().getBodyPr();
  536. if (bodyPr != null) {
  537. if (bodyPr.isSetHorzOverflow()) {
  538. return TextHorizontalOverflow.values()[bodyPr.getHorzOverflow().intValue() - 1];
  539. }
  540. }
  541. return TextHorizontalOverflow.OVERFLOW;
  542. }
  543. /**
  544. * Sets the type of vertical overflow for the text.
  545. *
  546. * @param overflow
  547. * - the type of vertical overflow. A <code>null</code> values
  548. * unsets this property.
  549. */
  550. public void setTextVerticalOverflow(TextVerticalOverflow overflow) {
  551. CTTextBodyProperties bodyPr = ctShape.getTxBody().getBodyPr();
  552. if (bodyPr != null) {
  553. if (overflow == null) {
  554. if (bodyPr.isSetVertOverflow()) {
  555. bodyPr.unsetVertOverflow();
  556. }
  557. } else {
  558. bodyPr.setVertOverflow(STTextVertOverflowType.Enum.forInt(overflow.ordinal() + 1));
  559. }
  560. }
  561. }
  562. /**
  563. * Returns the type of vertical overflow for the text.
  564. *
  565. * @return the type of vertical overflow
  566. */
  567. public TextVerticalOverflow getTextVerticalOverflow() {
  568. CTTextBodyProperties bodyPr = ctShape.getTxBody().getBodyPr();
  569. if (bodyPr != null) {
  570. if (bodyPr.isSetVertOverflow()) {
  571. return TextVerticalOverflow.values()[bodyPr.getVertOverflow().intValue() - 1];
  572. }
  573. }
  574. return TextVerticalOverflow.OVERFLOW;
  575. }
  576. /**
  577. * Sets the type of vertical alignment for the text within the shape.
  578. *
  579. * @param anchor
  580. * - the type of alignment. A <code>null</code> values unsets
  581. * this property.
  582. */
  583. public void setVerticalAlignment(VerticalAlignment anchor) {
  584. CTTextBodyProperties bodyPr = ctShape.getTxBody().getBodyPr();
  585. if (bodyPr != null) {
  586. if (anchor == null) {
  587. if (bodyPr.isSetAnchor()) {
  588. bodyPr.unsetAnchor();
  589. }
  590. } else {
  591. bodyPr.setAnchor(STTextAnchoringType.Enum.forInt(anchor.ordinal() + 1));
  592. }
  593. }
  594. }
  595. /**
  596. * Returns the type of vertical alignment for the text within the shape.
  597. *
  598. * @return the type of vertical alignment
  599. */
  600. public VerticalAlignment getVerticalAlignment() {
  601. CTTextBodyProperties bodyPr = ctShape.getTxBody().getBodyPr();
  602. if (bodyPr != null) {
  603. if (bodyPr.isSetAnchor()) {
  604. return VerticalAlignment.values()[bodyPr.getAnchor().intValue() - 1];
  605. }
  606. }
  607. return VerticalAlignment.TOP;
  608. }
  609. /**
  610. * Sets the vertical orientation of the text
  611. *
  612. * @param orientation
  613. * vertical orientation of the text A <code>null</code> values
  614. * unsets this property.
  615. */
  616. public void setTextDirection(TextDirection orientation) {
  617. CTTextBodyProperties bodyPr = ctShape.getTxBody().getBodyPr();
  618. if (bodyPr != null) {
  619. if (orientation == null) {
  620. if (bodyPr.isSetVert()) {
  621. bodyPr.unsetVert();
  622. }
  623. } else {
  624. bodyPr.setVert(STTextVerticalType.Enum.forInt(orientation.ordinal() + 1));
  625. }
  626. }
  627. }
  628. /**
  629. * Gets the vertical orientation of the text
  630. *
  631. * @return vertical orientation of the text
  632. */
  633. public TextDirection getTextDirection() {
  634. CTTextBodyProperties bodyPr = ctShape.getTxBody().getBodyPr();
  635. if (bodyPr != null) {
  636. STTextVerticalType.Enum val = bodyPr.getVert();
  637. if (val != null) {
  638. return TextDirection.values()[val.intValue() - 1];
  639. }
  640. }
  641. return TextDirection.HORIZONTAL;
  642. }
  643. /**
  644. * Returns the distance (in points) between the bottom of the text frame and
  645. * the bottom of the inscribed rectangle of the shape that contains the
  646. * text.
  647. *
  648. * @return the bottom inset in points
  649. */
  650. public double getBottomInset() {
  651. Double inset = _textBody.getBodyProperties().getBottomInset();
  652. if (inset == null) {
  653. // If this attribute is omitted, then a value of 0.05 inches is
  654. // implied
  655. return 3.6;
  656. } else {
  657. return inset;
  658. }
  659. }
  660. /**
  661. * Returns the distance (in points) between the left edge of the text frame
  662. * and the left edge of the inscribed rectangle of the shape that contains
  663. * the text.
  664. *
  665. * @return the left inset in points
  666. */
  667. public double getLeftInset() {
  668. Double inset = _textBody.getBodyProperties().getLeftInset();
  669. if (inset == null) {
  670. // If this attribute is omitted, then a value of 0.05 inches is
  671. // implied
  672. return 3.6;
  673. } else {
  674. return inset;
  675. }
  676. }
  677. /**
  678. * Returns the distance (in points) between the right edge of the text frame
  679. * and the right edge of the inscribed rectangle of the shape that contains
  680. * the text.
  681. *
  682. * @return the right inset in points
  683. */
  684. public double getRightInset() {
  685. Double inset = _textBody.getBodyProperties().getRightInset();
  686. if (inset == null) {
  687. // If this attribute is omitted, then a value of 0.05 inches is
  688. // implied
  689. return 3.6;
  690. } else {
  691. return inset;
  692. }
  693. }
  694. /**
  695. * Returns the distance (in points) between the top of the text frame and
  696. * the top of the inscribed rectangle of the shape that contains the text.
  697. *
  698. * @return the top inset in points
  699. */
  700. public double getTopInset() {
  701. Double inset = _textBody.getBodyProperties().getTopInset();
  702. if (inset == null) {
  703. // If this attribute is omitted, then a value of 0.05 inches is
  704. // implied
  705. return 3.6;
  706. } else {
  707. return inset;
  708. }
  709. }
  710. /**
  711. * Sets the bottom inset.
  712. *
  713. * @see #getBottomInset()
  714. *
  715. * @param margin
  716. * the bottom margin
  717. */
  718. public void setBottomInset(double margin) {
  719. if (margin == -1) {
  720. _textBody.getBodyProperties().setBottomInset(null);
  721. } else {
  722. _textBody.getBodyProperties().setBottomInset(margin);
  723. }
  724. }
  725. /**
  726. * Sets the left inset.
  727. *
  728. * @see #getLeftInset()
  729. *
  730. * @param margin
  731. * the left margin
  732. */
  733. public void setLeftInset(double margin) {
  734. if (margin == -1) {
  735. _textBody.getBodyProperties().setLeftInset(null);
  736. } else {
  737. _textBody.getBodyProperties().setLeftInset(margin);
  738. }
  739. }
  740. /**
  741. * Sets the right inset.
  742. *
  743. * @see #getRightInset()
  744. *
  745. * @param margin
  746. * the right margin
  747. */
  748. public void setRightInset(double margin) {
  749. if (margin == -1) {
  750. _textBody.getBodyProperties().setRightInset(null);
  751. } else {
  752. _textBody.getBodyProperties().setRightInset(margin);
  753. }
  754. }
  755. /**
  756. * Sets the top inset.
  757. *
  758. * @see #getTopInset()
  759. *
  760. * @param margin
  761. * the top margin
  762. */
  763. public void setTopInset(double margin) {
  764. if (margin == -1) {
  765. _textBody.getBodyProperties().setTopInset(null);
  766. } else {
  767. _textBody.getBodyProperties().setTopInset(margin);
  768. }
  769. }
  770. /**
  771. * @return whether to wrap words within the bounding rectangle
  772. */
  773. public boolean getWordWrap() {
  774. CTTextBodyProperties bodyPr = ctShape.getTxBody().getBodyPr();
  775. if (bodyPr != null) {
  776. if (bodyPr.isSetWrap()) {
  777. return bodyPr.getWrap() == STTextWrappingType.SQUARE;
  778. }
  779. }
  780. return true;
  781. }
  782. /**
  783. *
  784. * @param wrap
  785. * whether to wrap words within the bounding rectangle
  786. */
  787. public void setWordWrap(boolean wrap) {
  788. CTTextBodyProperties bodyPr = ctShape.getTxBody().getBodyPr();
  789. if (bodyPr != null) {
  790. bodyPr.setWrap(wrap ? STTextWrappingType.SQUARE : STTextWrappingType.NONE);
  791. }
  792. }
  793. /**
  794. *
  795. * Specifies that a shape should be auto-fit to fully contain the text
  796. * described within it. Auto-fitting is when text within a shape is scaled
  797. * in order to contain all the text inside
  798. *
  799. * @param value
  800. * type of autofit
  801. */
  802. public void setTextAutofit(TextAutofit value) {
  803. CTTextBodyProperties bodyPr = ctShape.getTxBody().getBodyPr();
  804. if (bodyPr != null) {
  805. if (bodyPr.isSetSpAutoFit()) {
  806. bodyPr.unsetSpAutoFit();
  807. }
  808. if (bodyPr.isSetNoAutofit()) {
  809. bodyPr.unsetNoAutofit();
  810. }
  811. if (bodyPr.isSetNormAutofit()) {
  812. bodyPr.unsetNormAutofit();
  813. }
  814. switch (value) {
  815. case NONE:
  816. bodyPr.addNewNoAutofit();
  817. break;
  818. case NORMAL:
  819. bodyPr.addNewNormAutofit();
  820. break;
  821. case SHAPE:
  822. bodyPr.addNewSpAutoFit();
  823. break;
  824. }
  825. }
  826. }
  827. /**
  828. *
  829. * @return type of autofit
  830. */
  831. public TextAutofit getTextAutofit() {
  832. CTTextBodyProperties bodyPr = ctShape.getTxBody().getBodyPr();
  833. if (bodyPr != null) {
  834. if (bodyPr.isSetNoAutofit()) {
  835. return TextAutofit.NONE;
  836. } else if (bodyPr.isSetNormAutofit()) {
  837. return TextAutofit.NORMAL;
  838. } else if (bodyPr.isSetSpAutoFit()) {
  839. return TextAutofit.SHAPE;
  840. }
  841. }
  842. return TextAutofit.NORMAL;
  843. }
  844. /**
  845. * Gets the shape type, one of the constants defined in
  846. * {@link org.apache.poi.ss.usermodel.ShapeTypes}.
  847. *
  848. * @return the shape type
  849. * @see org.apache.poi.ss.usermodel.ShapeTypes
  850. */
  851. public int getShapeType() {
  852. return ctShape.getSpPr().getPrstGeom().getPrst().intValue();
  853. }
  854. /**
  855. * Sets the shape types.
  856. *
  857. * @param type
  858. * the shape type, one of the constants defined in
  859. * {@link org.apache.poi.ss.usermodel.ShapeTypes}.
  860. * @see org.apache.poi.ss.usermodel.ShapeTypes
  861. */
  862. public void setShapeType(int type) {
  863. ctShape.getSpPr().getPrstGeom().setPrst(STShapeType.Enum.forInt(type));
  864. }
  865. @Override
  866. protected CTShapeProperties getShapeProperties() {
  867. return ctShape.getSpPr();
  868. }
  869. /**
  870. * org.openxmlformats.schemas.spreadsheetml.x2006.main.CTRPrElt to
  871. * org.openxmlformats.schemas.drawingml.x2006.main.CTFont adapter
  872. */
  873. private static void applyAttributes(CTRPrElt pr, CTTextCharacterProperties rPr) {
  874. if (pr.sizeOfBArray() > 0) {
  875. rPr.setB(pr.getBArray(0).getVal());
  876. }
  877. if (pr.sizeOfUArray() > 0) {
  878. STUnderlineValues.Enum u1 = pr.getUArray(0).getVal();
  879. if (u1 == STUnderlineValues.SINGLE) {
  880. rPr.setU(STTextUnderlineType.SNG);
  881. } else if (u1 == STUnderlineValues.DOUBLE) {
  882. rPr.setU(STTextUnderlineType.DBL);
  883. } else if (u1 == STUnderlineValues.NONE) {
  884. rPr.setU(STTextUnderlineType.NONE);
  885. }
  886. }
  887. if (pr.sizeOfIArray() > 0) {
  888. rPr.setI(pr.getIArray(0).getVal());
  889. }
  890. if (pr.sizeOfRFontArray() > 0) {
  891. CTTextFont rFont = rPr.isSetLatin() ? rPr.getLatin() : rPr.addNewLatin();
  892. rFont.setTypeface(pr.getRFontArray(0).getVal());
  893. }
  894. if (pr.sizeOfSzArray() > 0) {
  895. int sz = (int) (pr.getSzArray(0).getVal() * 100);
  896. rPr.setSz(sz);
  897. }
  898. if (pr.sizeOfColorArray() > 0) {
  899. CTSolidColorFillProperties fill = rPr.isSetSolidFill() ? rPr.getSolidFill() : rPr.addNewSolidFill();
  900. org.openxmlformats.schemas.spreadsheetml.x2006.main.CTColor xlsColor = pr.getColorArray(0);
  901. if (xlsColor.isSetRgb()) {
  902. CTSRgbColor clr = fill.isSetSrgbClr() ? fill.getSrgbClr() : fill.addNewSrgbClr();
  903. clr.setVal(xlsColor.getRgb());
  904. } else if (xlsColor.isSetIndexed()) {
  905. HSSFColor indexed = HSSFColor.getIndexHash().get((int) xlsColor.getIndexed());
  906. if (indexed != null) {
  907. byte[] rgb = new byte[3];
  908. rgb[0] = (byte) indexed.getTriplet()[0];
  909. rgb[1] = (byte) indexed.getTriplet()[1];
  910. rgb[2] = (byte) indexed.getTriplet()[2];
  911. CTSRgbColor clr = fill.isSetSrgbClr() ? fill.getSrgbClr() : fill.addNewSrgbClr();
  912. clr.setVal(rgb);
  913. }
  914. }
  915. }
  916. }
  917. @Override
  918. public String getShapeName() {
  919. return ctShape.getNvSpPr().getCNvPr().getName();
  920. }
  921. @Override
  922. public int getShapeId() {
  923. return (int) ctShape.getNvSpPr().getCNvPr().getId();
  924. }
  925. @Override
  926. public <R> Optional<R> findDefinedParagraphProperty(Predicate<CTTextParagraphProperties> isSet,
  927. Function<CTTextParagraphProperties, R> getter) {
  928. // TODO Auto-generated method stub
  929. return Optional.empty();
  930. }
  931. @Override
  932. public <R> Optional<R> findDefinedRunProperty(Predicate<CTTextCharacterProperties> isSet,
  933. Function<CTTextCharacterProperties, R> getter) {
  934. // TODO Auto-generated method stub
  935. return Optional.empty();
  936. }
  937. }