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.

XDDFTextParagraph.java 29KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868
  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.xddf.usermodel.text;
  16. import java.util.ArrayList;
  17. import java.util.Collections;
  18. import java.util.Iterator;
  19. import java.util.List;
  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.commons.collections4.iterators.IteratorIterable;
  25. import org.apache.commons.collections4.iterators.ReverseListIterator;
  26. import org.apache.poi.ooxml.util.POIXMLUnits;
  27. import org.apache.poi.util.Beta;
  28. import org.apache.poi.util.Internal;
  29. import org.apache.poi.util.LocaleUtil;
  30. import org.apache.poi.util.Units;
  31. import org.apache.poi.xddf.usermodel.XDDFColor;
  32. import org.apache.xmlbeans.QNameSet;
  33. import org.apache.xmlbeans.XmlObject;
  34. import org.openxmlformats.schemas.drawingml.x2006.main.CTRegularTextRun;
  35. import org.openxmlformats.schemas.drawingml.x2006.main.CTTextCharacterProperties;
  36. import org.openxmlformats.schemas.drawingml.x2006.main.CTTextField;
  37. import org.openxmlformats.schemas.drawingml.x2006.main.CTTextLineBreak;
  38. import org.openxmlformats.schemas.drawingml.x2006.main.CTTextParagraph;
  39. import org.openxmlformats.schemas.drawingml.x2006.main.CTTextParagraphProperties;
  40. import org.openxmlformats.schemas.drawingml.x2006.main.CTTextSpacing;
  41. /**
  42. * Represents a paragraph of text within the containing text body. The paragraph
  43. * is the highest level text separation mechanism.
  44. */
  45. @Beta
  46. public class XDDFTextParagraph implements Iterable<XDDFTextRun> {
  47. private XDDFTextBody _parent;
  48. private XDDFParagraphProperties _properties;
  49. private final CTTextParagraph _p;
  50. private final ArrayList<XDDFTextRun> _runs;
  51. @Internal
  52. protected XDDFTextParagraph(CTTextParagraph paragraph, XDDFTextBody parent) {
  53. this._p = paragraph;
  54. this._parent = parent;
  55. final int count = paragraph.sizeOfBrArray() + paragraph.sizeOfFldArray() + paragraph.sizeOfRArray();
  56. this._runs = new ArrayList<>(count);
  57. for (XmlObject xo : paragraph.selectChildren(QNameSet.ALL)) {
  58. if (xo instanceof CTTextLineBreak) {
  59. _runs.add(new XDDFTextRun((CTTextLineBreak) xo, this));
  60. } else if (xo instanceof CTTextField) {
  61. _runs.add(new XDDFTextRun((CTTextField) xo, this));
  62. } else if (xo instanceof CTRegularTextRun) {
  63. _runs.add(new XDDFTextRun((CTRegularTextRun) xo, this));
  64. }
  65. }
  66. addDefaultRunProperties();
  67. addAfterLastRunProperties();
  68. }
  69. public void setText(String text) {
  70. // keep the properties of current last run
  71. XmlObject existing = null;
  72. if (_runs.size() > 0) {
  73. existing = _runs.get(_runs.size() - 1).getProperties().copy();
  74. }
  75. // remove all runs
  76. for (int i = _p.sizeOfBrArray() - 1; i >= 0; i--) {
  77. _p.removeBr(i);
  78. }
  79. for (int i = _p.sizeOfFldArray() - 1; i >= 0; i--) {
  80. _p.removeFld(i);
  81. }
  82. for (int i = _p.sizeOfRArray() - 1; i >= 0; i--) {
  83. _p.removeR(i);
  84. }
  85. _runs.clear();
  86. XDDFTextRun run = appendRegularRun(text);
  87. if (existing != null) {
  88. run.getProperties().set(existing);
  89. }
  90. }
  91. public String getText() {
  92. StringBuilder out = new StringBuilder();
  93. for (XDDFTextRun r : _runs) {
  94. out.append(r.getText());
  95. }
  96. return out.toString();
  97. }
  98. public XDDFTextBody getParentBody() {
  99. return _parent;
  100. }
  101. public List<XDDFTextRun> getTextRuns() {
  102. return _runs;
  103. }
  104. @Override
  105. public Iterator<XDDFTextRun> iterator() {
  106. return _runs.iterator();
  107. }
  108. /**
  109. * @since POI 5.2.0
  110. */
  111. @Override
  112. public Spliterator<XDDFTextRun> spliterator() {
  113. return _runs.spliterator();
  114. }
  115. /**
  116. * Append a line break.
  117. *
  118. * @return text run representing this line break ('\n').
  119. */
  120. public XDDFTextRun appendLineBreak() {
  121. CTTextLineBreak br = _p.addNewBr();
  122. // by default, line break has the font properties of the last text run
  123. for (XDDFTextRun tr : new IteratorIterable<>(new ReverseListIterator<>(_runs))) {
  124. CTTextCharacterProperties prevProps = tr.getProperties();
  125. // let's find one which is not undefined
  126. if (prevProps != null) {
  127. br.setRPr((CTTextCharacterProperties) prevProps.copy());
  128. break;
  129. }
  130. }
  131. XDDFTextRun run = new XDDFTextRun(br, this);
  132. _runs.add(run);
  133. return run;
  134. }
  135. /**
  136. * Append a new text field.
  137. *
  138. * @return the new text field.
  139. */
  140. public XDDFTextRun appendField(String id, String type, String text) {
  141. CTTextField f = _p.addNewFld();
  142. f.setId(id);
  143. f.setType(type);
  144. f.setT(text);
  145. CTTextCharacterProperties rPr = f.addNewRPr();
  146. rPr.setLang(LocaleUtil.getUserLocale().toLanguageTag());
  147. XDDFTextRun run = new XDDFTextRun(f, this);
  148. _runs.add(run);
  149. return run;
  150. }
  151. /**
  152. * Append a new run of text.
  153. *
  154. * @return the new run of text.
  155. */
  156. public XDDFTextRun appendRegularRun(String text) {
  157. CTRegularTextRun r = _p.addNewR();
  158. r.setT(text);
  159. CTTextCharacterProperties rPr = r.addNewRPr();
  160. rPr.setLang(LocaleUtil.getUserLocale().toLanguageTag());
  161. XDDFTextRun run = new XDDFTextRun(r, this);
  162. _runs.add(run);
  163. return run;
  164. }
  165. /**
  166. * Returns the paragraph indentation level.
  167. *
  168. * @return indentation level of the paragraph.
  169. */
  170. public int getIndentationLevel() {
  171. if (_p.isSetPPr()) {
  172. return getProperties().getLevel();
  173. } else {
  174. return 0;
  175. }
  176. }
  177. /**
  178. * Specifies the paragraph indentation level, between 1 and 9.
  179. *
  180. * @param level
  181. * new indentation level for the paragraph.
  182. * Use <code>null</code> to unset the indentation level.
  183. */
  184. public void setIndentationLevel(Integer level) {
  185. if (_p.isSetPPr()) {
  186. getProperties().setLevel(level);
  187. }
  188. }
  189. /**
  190. * Returns the alignment that is applied to the paragraph.
  191. *
  192. * If this attribute is omitted, then a value of left is implied.
  193. *
  194. * @return alignment that is applied to the paragraph
  195. */
  196. public TextAlignment getTextAlignment() {
  197. return findDefinedParagraphProperty(
  198. CTTextParagraphProperties::isSetAlgn,
  199. CTTextParagraphProperties::getAlgn)
  200. .map(TextAlignment::valueOf).orElse(null);
  201. }
  202. /**
  203. * Specifies the alignment that is to be applied to the paragraph. Possible
  204. * values for this include left, right, centered, justified and distributed,
  205. *
  206. * @param align
  207. * text alignment
  208. */
  209. public void setTextAlignment(TextAlignment align) {
  210. if (align != null || _p.isSetPPr()) {
  211. getOrCreateProperties().setTextAlignment(align);
  212. }
  213. }
  214. /**
  215. * Returns where vertically on a line of text the actual words are
  216. * positioned. This deals with vertical placement of the characters with
  217. * respect to the baselines.
  218. *
  219. * If this attribute is omitted, then a value of baseline is implied.
  220. *
  221. * @return alignment that is applied to the paragraph
  222. */
  223. public FontAlignment getFontAlignment() {
  224. return findDefinedParagraphProperty(
  225. CTTextParagraphProperties::isSetFontAlgn,
  226. CTTextParagraphProperties::getFontAlgn)
  227. .map(FontAlignment::valueOf).orElse(null);
  228. }
  229. /**
  230. * Determines where vertically on a line of text the actual words are
  231. * positioned. This deals with vertical placement of the characters with
  232. * respect to the baselines. For instance having text anchored to the top
  233. * baseline, anchored to the bottom baseline, centered in between, etc.
  234. *
  235. * @param align
  236. * text font alignment
  237. */
  238. public void setFontAlignment(FontAlignment align) {
  239. if (align != null || _p.isSetPPr()) {
  240. getOrCreateProperties().setFontAlignment(align);
  241. }
  242. }
  243. /**
  244. *
  245. * @return the indentation, in points, applied to the first line of text in
  246. * the paragraph.
  247. */
  248. public Double getIndentation() {
  249. return findDefinedParagraphProperty(
  250. CTTextParagraphProperties::isSetIndent,
  251. CTTextParagraphProperties::getIndent)
  252. .map(Units::toPoints).orElse(null);
  253. }
  254. /**
  255. * Specifies the indentation size that will be applied to the first line of
  256. * text in the paragraph.
  257. *
  258. * @param points
  259. * the indentation in points. The value <code>null</code> unsets
  260. * the indentation for this paragraph.
  261. * <dl>
  262. * <dt>Minimum inclusive =</dt>
  263. * <dd>-4032</dd>
  264. * <dt>Maximum inclusive =</dt>
  265. * <dd>4032</dd>
  266. * </dl>
  267. */
  268. public void setIndentation(Double points) {
  269. if (points != null || _p.isSetPPr()) {
  270. getOrCreateProperties().setIndentation(points);
  271. }
  272. }
  273. /**
  274. *
  275. * @return the left margin, in points, of the paragraph.
  276. */
  277. public Double getMarginLeft() {
  278. return findDefinedParagraphProperty(
  279. CTTextParagraphProperties::isSetMarL,
  280. CTTextParagraphProperties::getMarL)
  281. .map(Units::toPoints).orElse(null);
  282. }
  283. /**
  284. * Specifies the left margin of the paragraph. This is specified in addition
  285. * to the text body inset and applies only to this text paragraph. That is
  286. * the text body inset and the LeftMargin attributes are additive with
  287. * respect to the text position.
  288. *
  289. * @param points
  290. * the margin in points. The value <code>null</code> unsets the
  291. * left margin for this paragraph.
  292. * <dl>
  293. * <dt>Minimum inclusive =</dt>
  294. * <dd>0</dd>
  295. * <dt>Maximum inclusive =</dt>
  296. * <dd>4032</dd>
  297. * </dl>
  298. */
  299. public void setMarginLeft(Double points) {
  300. if (points != null || _p.isSetPPr()) {
  301. getOrCreateProperties().setMarginLeft(points);
  302. }
  303. }
  304. /**
  305. *
  306. * @return the right margin, in points, of the paragraph.
  307. */
  308. public Double getMarginRight() {
  309. return findDefinedParagraphProperty(
  310. CTTextParagraphProperties::isSetMarR,
  311. CTTextParagraphProperties::getMarR)
  312. .map(Units::toPoints).orElse(null);
  313. }
  314. /**
  315. * Specifies the right margin of the paragraph. This is specified in
  316. * addition to the text body inset and applies only to this text paragraph.
  317. * That is the text body inset and the RightMargin attributes are additive
  318. * with respect to the text position.
  319. *
  320. * @param points
  321. * the margin in points. The value <code>null</code> unsets the
  322. * right margin for this paragraph.
  323. * <dl>
  324. * <dt>Minimum inclusive =</dt>
  325. * <dd>0</dd>
  326. * <dt>Maximum inclusive =</dt>
  327. * <dd>4032</dd>
  328. * </dl>
  329. */
  330. public void setMarginRight(Double points) {
  331. if (points != null || _p.isSetPPr()) {
  332. getOrCreateProperties().setMarginRight(points);
  333. }
  334. }
  335. /**
  336. *
  337. * @return the default size for a tab character within this paragraph in
  338. * points.
  339. */
  340. public Double getDefaultTabSize() {
  341. return findDefinedParagraphProperty(
  342. CTTextParagraphProperties::isSetDefTabSz,
  343. CTTextParagraphProperties::xgetDefTabSz)
  344. .map(POIXMLUnits::parseLength).map(Units::toPoints).orElse(null);
  345. }
  346. /**
  347. * Specifies the default size for a tab character within this paragraph.
  348. *
  349. * @param points
  350. * the default tab size in points. The value <code>null</code>
  351. * unsets the default tab size for this paragraph.
  352. */
  353. public void setDefaultTabSize(Double points) {
  354. if (points != null || _p.isSetPPr()) {
  355. getOrCreateProperties().setDefaultTabSize(points);
  356. }
  357. }
  358. /**
  359. * Returns the vertical line spacing that is to be used within a paragraph.
  360. * This may be specified in two different ways, percentage spacing or font
  361. * points spacing:
  362. * <p>
  363. * If line spacing is a percentage of normal line height, result is instance
  364. * of XDDFSpacingPercent. If line spacing is expressed in points, result is
  365. * instance of XDDFSpacingPoints.
  366. * </p>
  367. *
  368. * @return the vertical line spacing.
  369. */
  370. public XDDFSpacing getLineSpacing() {
  371. return findDefinedParagraphProperty(
  372. CTTextParagraphProperties::isSetLnSpc,
  373. CTTextParagraphProperties::getLnSpc)
  374. .map(this::extractSpacing).orElse(null);
  375. }
  376. /**
  377. * This element specifies the vertical line spacing that is to be used
  378. * within a paragraph. This may be specified in two different ways,
  379. * percentage spacing or font points spacing:
  380. * <p>
  381. * If spacing is instance of XDDFSpacingPercent, then line spacing is a
  382. * percentage of normal line height. If spacing is instance of
  383. * XDDFSpacingPoints, then line spacing is expressed in points.
  384. * </p>
  385. * Examples:
  386. *
  387. * <pre>
  388. * <code>
  389. * // spacing will be 120% of the size of the largest text on each line
  390. * paragraph.setLineSpacing(new XDDFSpacingPercent(120));
  391. *
  392. * // spacing will be 200% of the size of the largest text on each line
  393. * paragraph.setLineSpacing(new XDDFSpacingPercent(200));
  394. *
  395. * // spacing will be 48 points
  396. * paragraph.setLineSpacing(new XDDFSpacingPoints(48.0));
  397. * </code>
  398. * </pre>
  399. *
  400. * @param linespacing
  401. * the vertical line spacing
  402. */
  403. public void setLineSpacing(XDDFSpacing linespacing) {
  404. if (linespacing != null || _p.isSetPPr()) {
  405. getOrCreateProperties().setLineSpacing(linespacing);
  406. }
  407. }
  408. /**
  409. * The amount of vertical white space before the paragraph. This may be
  410. * specified in two different ways, percentage spacing or font points
  411. * spacing:
  412. * <p>
  413. * If spacing is a percentage of normal line height, result is instance of
  414. * XDDFSpacingPercent. If spacing is expressed in points, result is instance
  415. * of XDDFSpacingPoints.
  416. * </p>
  417. *
  418. * @return the vertical white space before the paragraph.
  419. */
  420. public XDDFSpacing getSpaceBefore() {
  421. return findDefinedParagraphProperty(
  422. CTTextParagraphProperties::isSetSpcBef,
  423. CTTextParagraphProperties::getSpcBef)
  424. .map(this::extractSpacing).orElse(null);
  425. }
  426. /**
  427. * Set the amount of vertical white space that will be present before the
  428. * paragraph. This may be specified in two different ways, percentage
  429. * spacing or font points spacing:
  430. * <p>
  431. * If spacing is instance of XDDFSpacingPercent, then spacing is a
  432. * percentage of normal line height. If spacing is instance of
  433. * XDDFSpacingPoints, then spacing is expressed in points.
  434. * </p>
  435. * Examples:
  436. *
  437. * <pre>
  438. * <code>
  439. * // The paragraph will be formatted to have a spacing before the paragraph text.
  440. * // The spacing will be 200% of the size of the largest text on each line
  441. * paragraph.setSpaceBefore(new XDDFSpacingPercent(200));
  442. *
  443. * // The spacing will be a size of 48 points
  444. * paragraph.setSpaceBefore(new XDDFSpacingPoints(48.0));
  445. * </code>
  446. * </pre>
  447. *
  448. * @param spaceBefore
  449. * the vertical white space before the paragraph.
  450. */
  451. public void setSpaceBefore(XDDFSpacing spaceBefore) {
  452. if (spaceBefore != null || _p.isSetPPr()) {
  453. getOrCreateProperties().setSpaceBefore(spaceBefore);
  454. }
  455. }
  456. /**
  457. * The amount of vertical white space after the paragraph. This may be
  458. * specified in two different ways, percentage spacing or font points
  459. * spacing:
  460. * <p>
  461. * If spacing is a percentage of normal line height, result is instance of
  462. * XDDFSpacingPercent. If spacing is expressed in points, result is instance
  463. * of XDDFSpacingPoints.
  464. * </p>
  465. *
  466. * @return the vertical white space after the paragraph.
  467. */
  468. public XDDFSpacing getSpaceAfter() {
  469. return findDefinedParagraphProperty(
  470. CTTextParagraphProperties::isSetSpcAft,
  471. CTTextParagraphProperties::getSpcAft)
  472. .map(this::extractSpacing).orElse(null);
  473. }
  474. /**
  475. * Set the amount of vertical white space that will be present after the
  476. * paragraph. This may be specified in two different ways, percentage
  477. * spacing or font points spacing:
  478. * <p>
  479. * If spacing is instance of XDDFSpacingPercent, then spacing is a
  480. * percentage of normal line height. If spacing is instance of
  481. * XDDFSpacingPoints, then spacing is expressed in points.
  482. * </p>
  483. * Examples:
  484. *
  485. * <pre>
  486. * <code>
  487. * // The paragraph will be formatted to have a spacing after the paragraph text.
  488. * // The spacing will be 200% of the size of the largest text on each line
  489. * paragraph.setSpaceAfter(new XDDFSpacingPercent(200));
  490. *
  491. * // The spacing will be a size of 48 points
  492. * paragraph.setSpaceAfter(new XDDFSpacingPoints(48.0));
  493. * </code>
  494. * </pre>
  495. *
  496. * @param spaceAfter
  497. * the vertical white space after the paragraph.
  498. */
  499. public void setSpaceAfter(XDDFSpacing spaceAfter) {
  500. if (spaceAfter != null || _p.isSetPPr()) {
  501. getOrCreateProperties().setSpaceAfter(spaceAfter);
  502. }
  503. }
  504. /**
  505. *
  506. * @return the color of bullet characters within a given paragraph. A
  507. * <code>null</code> value means to use the text font color.
  508. */
  509. public XDDFColor getBulletColor() {
  510. return findDefinedParagraphProperty(props -> props.isSetBuClr() || props.isSetBuClrTx(),
  511. props -> new XDDFParagraphBulletProperties(props).getBulletColor()).orElse(null);
  512. }
  513. /**
  514. * Set the color to be used on bullet characters within a given paragraph.
  515. *
  516. * @param color
  517. * the bullet color
  518. */
  519. public void setBulletColor(XDDFColor color) {
  520. if (color != null || _p.isSetPPr()) {
  521. getOrCreateBulletProperties().setBulletColor(color);
  522. }
  523. }
  524. /**
  525. * Specifies the color to be used on bullet characters has to follow text
  526. * color within a given paragraph.
  527. */
  528. public void setBulletColorFollowText() {
  529. getOrCreateBulletProperties().setBulletColorFollowText();
  530. }
  531. /**
  532. *
  533. * @return the font of bullet characters within a given paragraph. A
  534. * <code>null</code> value means to use the text font font.
  535. */
  536. public XDDFFont getBulletFont() {
  537. return findDefinedParagraphProperty(props -> props.isSetBuFont() || props.isSetBuFontTx(),
  538. props -> new XDDFParagraphBulletProperties(props).getBulletFont()).orElse(null);
  539. }
  540. /**
  541. * Set the font to be used on bullet characters within a given paragraph.
  542. *
  543. * @param font
  544. * the bullet font
  545. */
  546. public void setBulletFont(XDDFFont font) {
  547. if (font != null || _p.isSetPPr()) {
  548. getOrCreateBulletProperties().setBulletFont(font);
  549. }
  550. }
  551. /**
  552. * Specifies the font to be used on bullet characters has to follow text
  553. * font within a given paragraph.
  554. */
  555. public void setBulletFontFollowText() {
  556. getOrCreateBulletProperties().setBulletFontFollowText();
  557. }
  558. /**
  559. * Returns the bullet size that is to be used within a paragraph. This may
  560. * be specified in three different ways, follows text size, percentage size
  561. * and font points size:
  562. * <p>
  563. * If returned value is instance of XDDFBulletSizeFollowText, then bullet
  564. * size is text size; If returned value is instance of
  565. * XDDFBulletSizePercent, then bullet size is a percentage of the font size;
  566. * If returned value is instance of XDDFBulletSizePoints, then bullet size
  567. * is specified in points.
  568. * </p>
  569. *
  570. * @return the bullet size
  571. */
  572. public XDDFBulletSize getBulletSize() {
  573. return findDefinedParagraphProperty(
  574. props -> props.isSetBuSzPct() || props.isSetBuSzPts() || props.isSetBuSzTx(),
  575. props -> new XDDFParagraphBulletProperties(props).getBulletSize()).orElse(null);
  576. }
  577. /**
  578. * Sets the bullet size that is to be used within a paragraph. This may be
  579. * specified in three different ways, follows text size, percentage size and
  580. * font points size:
  581. * <p>
  582. * If given value is instance of XDDFBulletSizeFollowText, then bullet size
  583. * is text size; If given value is instance of XDDFBulletSizePercent, then
  584. * bullet size is a percentage of the font size; If given value is instance
  585. * of XDDFBulletSizePoints, then bullet size is specified in points.
  586. * </p>
  587. *
  588. * @param size
  589. * the bullet size specification
  590. */
  591. public void setBulletSize(XDDFBulletSize size) {
  592. if (size != null || _p.isSetPPr()) {
  593. getOrCreateBulletProperties().setBulletSize(size);
  594. }
  595. }
  596. public XDDFBulletStyle getBulletStyle() {
  597. return findDefinedParagraphProperty(
  598. props -> props.isSetBuAutoNum() || props.isSetBuBlip() || props.isSetBuChar() || props.isSetBuNone(),
  599. props -> new XDDFParagraphBulletProperties(props).getBulletStyle()).orElse(null);
  600. }
  601. public void setBulletStyle(XDDFBulletStyle style) {
  602. if (style != null || _p.isSetPPr()) {
  603. getOrCreateBulletProperties().setBulletStyle(style);
  604. }
  605. }
  606. public boolean hasEastAsianLineBreak() {
  607. return findDefinedParagraphProperty(
  608. CTTextParagraphProperties::isSetEaLnBrk,
  609. CTTextParagraphProperties::getEaLnBrk)
  610. .orElse(false);
  611. }
  612. public void setEastAsianLineBreak(Boolean value) {
  613. if (value != null || _p.isSetPPr()) {
  614. getOrCreateProperties().setEastAsianLineBreak(value);
  615. }
  616. }
  617. public boolean hasLatinLineBreak() {
  618. return findDefinedParagraphProperty(
  619. CTTextParagraphProperties::isSetLatinLnBrk,
  620. CTTextParagraphProperties::getLatinLnBrk)
  621. .orElse(false);
  622. }
  623. public void setLatinLineBreak(Boolean value) {
  624. if (value != null || _p.isSetPPr()) {
  625. getOrCreateProperties().setLatinLineBreak(value);
  626. }
  627. }
  628. public boolean hasHangingPunctuation() {
  629. return findDefinedParagraphProperty(
  630. CTTextParagraphProperties::isSetHangingPunct,
  631. CTTextParagraphProperties::getHangingPunct)
  632. .orElse(false);
  633. }
  634. public void setHangingPunctuation(Boolean value) {
  635. if (value != null || _p.isSetPPr()) {
  636. getOrCreateProperties().setHangingPunctuation(value);
  637. }
  638. }
  639. public boolean isRightToLeft() {
  640. return findDefinedParagraphProperty(
  641. CTTextParagraphProperties::isSetRtl,
  642. CTTextParagraphProperties::getRtl)
  643. .orElse(false);
  644. }
  645. public void setRightToLeft(Boolean value) {
  646. if (value != null || _p.isSetPPr()) {
  647. getOrCreateProperties().setRightToLeft(value);
  648. }
  649. }
  650. public XDDFTabStop addTabStop() {
  651. return getOrCreateProperties().addTabStop();
  652. }
  653. public XDDFTabStop insertTabStop(int index) {
  654. return getOrCreateProperties().insertTabStop(index);
  655. }
  656. public void removeTabStop(int index) {
  657. if (_p.isSetPPr()) {
  658. getProperties().removeTabStop(index);
  659. }
  660. }
  661. public XDDFTabStop getTabStop(int index) {
  662. if (_p.isSetPPr()) {
  663. return getProperties().getTabStop(index);
  664. } else {
  665. return null;
  666. }
  667. }
  668. public List<XDDFTabStop> getTabStops() {
  669. if (_p.isSetPPr()) {
  670. return getProperties().getTabStops();
  671. } else {
  672. return Collections.emptyList();
  673. }
  674. }
  675. public int countTabStops() {
  676. if (_p.isSetPPr()) {
  677. return getProperties().countTabStops();
  678. } else {
  679. return 0;
  680. }
  681. }
  682. public XDDFParagraphBulletProperties getOrCreateBulletProperties() {
  683. return getOrCreateProperties().getBulletProperties();
  684. }
  685. public XDDFParagraphBulletProperties getBulletProperties() {
  686. if (_p.isSetPPr()) {
  687. return getProperties().getBulletProperties();
  688. } else {
  689. return null;
  690. }
  691. }
  692. /**
  693. * @since 4.0.1
  694. */
  695. public XDDFRunProperties addDefaultRunProperties() {
  696. return getOrCreateProperties().addDefaultRunProperties();
  697. }
  698. public XDDFRunProperties getDefaultRunProperties() {
  699. if (_p.isSetPPr()) {
  700. return getProperties().getDefaultRunProperties();
  701. } else {
  702. return null;
  703. }
  704. }
  705. public void setDefaultRunProperties(XDDFRunProperties properties) {
  706. if (properties != null || _p.isSetPPr()) {
  707. getOrCreateProperties().setDefaultRunProperties(properties);
  708. }
  709. }
  710. public XDDFRunProperties addAfterLastRunProperties() {
  711. if (!_p.isSetEndParaRPr()) {
  712. _p.addNewEndParaRPr();
  713. }
  714. return getAfterLastRunProperties();
  715. }
  716. public XDDFRunProperties getAfterLastRunProperties() {
  717. if (_p.isSetEndParaRPr()) {
  718. return new XDDFRunProperties(_p.getEndParaRPr());
  719. } else {
  720. return null;
  721. }
  722. }
  723. public void setAfterLastRunProperties(XDDFRunProperties properties) {
  724. if (properties == null) {
  725. if (_p.isSetEndParaRPr()) {
  726. _p.unsetEndParaRPr();
  727. }
  728. } else {
  729. _p.setEndParaRPr(properties.getXmlObject());
  730. }
  731. }
  732. private XDDFSpacing extractSpacing(CTTextSpacing spacing) {
  733. if (spacing.isSetSpcPct()) {
  734. double scale = 1 - _parent.getBodyProperties().getAutoFit().getLineSpaceReduction() / 100_000.0;
  735. return new XDDFSpacingPercent(spacing, spacing.getSpcPct(), scale);
  736. } else if (spacing.isSetSpcPts()) {
  737. return new XDDFSpacingPoints(spacing, spacing.getSpcPts());
  738. }
  739. return null;
  740. }
  741. private XDDFParagraphProperties getProperties() {
  742. if (_properties == null) {
  743. _properties = new XDDFParagraphProperties(_p.getPPr());
  744. }
  745. return _properties;
  746. }
  747. private XDDFParagraphProperties getOrCreateProperties() {
  748. if (!_p.isSetPPr()) {
  749. _properties = new XDDFParagraphProperties(_p.addNewPPr());
  750. }
  751. return getProperties();
  752. }
  753. protected <R> Optional<R> findDefinedParagraphProperty(Predicate<CTTextParagraphProperties> isSet,
  754. Function<CTTextParagraphProperties, R> getter) {
  755. if (_p.isSetPPr()) {
  756. int level = _p.getPPr().isSetLvl() ? 1 + _p.getPPr().getLvl() : 0;
  757. return findDefinedParagraphProperty(isSet, getter, level);
  758. } else {
  759. return _parent.findDefinedParagraphProperty(isSet, getter, 0);
  760. }
  761. }
  762. private <R> Optional<R> findDefinedParagraphProperty(Predicate<CTTextParagraphProperties> isSet,
  763. Function<CTTextParagraphProperties, R> getter, int level) {
  764. final CTTextParagraphProperties props = _p.getPPr();
  765. if (props != null && isSet.test(props)) {
  766. return Optional.ofNullable(getter.apply(props));
  767. } else {
  768. return _parent.findDefinedParagraphProperty(isSet, getter, level);
  769. }
  770. }
  771. protected <R> Optional<R> findDefinedRunProperty(Predicate<CTTextCharacterProperties> isSet,
  772. Function<CTTextCharacterProperties, R> getter) {
  773. if (_p.isSetPPr()) {
  774. int level = _p.getPPr().isSetLvl() ? 1 + _p.getPPr().getLvl() : 0;
  775. return findDefinedRunProperty(isSet, getter, level);
  776. } else {
  777. return _parent.findDefinedRunProperty(isSet, getter, 0);
  778. }
  779. }
  780. private <R> Optional<R> findDefinedRunProperty(Predicate<CTTextCharacterProperties> isSet,
  781. Function<CTTextCharacterProperties, R> getter, int level) {
  782. final CTTextCharacterProperties props = _p.getPPr().isSetDefRPr() ? _p.getPPr().getDefRPr() : null;
  783. if (props != null && isSet.test(props)) {
  784. return Optional.ofNullable(getter.apply(props));
  785. } else {
  786. return _parent.findDefinedRunProperty(isSet, getter, level);
  787. }
  788. }
  789. }