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.

XSLFTextParagraph.java 27KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725
  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.xslf.usermodel;
  16. import org.apache.poi.util.Beta;
  17. import org.apache.poi.util.Internal;
  18. import org.apache.poi.util.Units;
  19. import org.apache.poi.xslf.model.PropertyFetcher;
  20. import org.apache.poi.xslf.model.ParagraphPropertyFetcher;
  21. import org.apache.xmlbeans.XmlObject;
  22. import org.openxmlformats.schemas.drawingml.x2006.main.CTRegularTextRun;
  23. import org.openxmlformats.schemas.drawingml.x2006.main.CTTextParagraph;
  24. import org.openxmlformats.schemas.drawingml.x2006.main.CTTextParagraphProperties;
  25. import org.openxmlformats.schemas.drawingml.x2006.main.CTTextSpacing;
  26. import org.openxmlformats.schemas.drawingml.x2006.main.STTextAlignType;
  27. import org.openxmlformats.schemas.drawingml.x2006.main.CTTextField;
  28. import org.openxmlformats.schemas.presentationml.x2006.main.CTPlaceholder;
  29. import org.openxmlformats.schemas.presentationml.x2006.main.STPlaceholderType;
  30. import java.util.ArrayList;
  31. import java.util.Iterator;
  32. import java.util.List;
  33. import java.awt.*;
  34. import java.awt.geom.Rectangle2D;
  35. import java.awt.font.TextLayout;
  36. import java.awt.font.TextAttribute;
  37. import java.awt.font.LineBreakMeasurer;
  38. import java.text.AttributedString;
  39. import java.text.AttributedCharacterIterator;
  40. /**
  41. * Represents a paragraph of text within the containing text body.
  42. * The paragraph is the highest level text separation mechanism.
  43. *
  44. * @author Yegor Kozlov
  45. * @since POI-3.8
  46. */
  47. @Beta
  48. public class XSLFTextParagraph implements Iterable<XSLFTextRun>{
  49. private final CTTextParagraph _p;
  50. private final List<XSLFTextRun> _runs;
  51. private final XSLFTextShape _shape;
  52. private List<TextFragment> _lines;
  53. private TextFragment _bullet;
  54. XSLFTextParagraph(CTTextParagraph p, XSLFTextShape shape){
  55. _p = p;
  56. _runs = new ArrayList<XSLFTextRun>();
  57. _shape = shape;
  58. for (CTRegularTextRun r : _p.getRList()) {
  59. _runs.add(new XSLFTextRun(r, this));
  60. }
  61. for (CTTextField f : _p.getFldList()) {
  62. CTRegularTextRun r = CTRegularTextRun.Factory.newInstance();
  63. r.setT(f.getT());
  64. _runs.add(new XSLFTextRun(r, this));
  65. }
  66. }
  67. public String getText(){
  68. StringBuilder out = new StringBuilder();
  69. for (XSLFTextRun r : _runs) {
  70. out.append(r.getText());
  71. }
  72. return out.toString();
  73. }
  74. @Internal
  75. public CTTextParagraph getXmlObject(){
  76. return _p;
  77. }
  78. XSLFTextShape getParentShape() {
  79. return _shape;
  80. }
  81. public List<XSLFTextRun> getTextRuns(){
  82. return _runs;
  83. }
  84. public Iterator<XSLFTextRun> iterator(){
  85. return _runs.iterator();
  86. }
  87. public XSLFTextRun addNewTextRun(){
  88. CTRegularTextRun r = _p.addNewR();
  89. r.addNewRPr();
  90. XSLFTextRun run = new XSLFTextRun(r, this);
  91. _runs.add(run);
  92. return run;
  93. }
  94. public void addLineBreak(){
  95. _p.addNewBr();
  96. }
  97. /**
  98. * Returns the alignment that is applied to the paragraph.
  99. *
  100. * If this attribute is omitted, then a value of left is implied.
  101. * @return ??? alignment that is applied to the paragraph
  102. */
  103. public TextAlign getTextAlign(){
  104. ParagraphPropertyFetcher<TextAlign> fetcher = new ParagraphPropertyFetcher<TextAlign>(getLevel()){
  105. public boolean fetch(CTTextParagraphProperties props){
  106. if(props.isSetAlgn()){
  107. TextAlign val = TextAlign.values()[props.getAlgn().intValue() - 1];
  108. setValue(val);
  109. return true;
  110. }
  111. return false;
  112. }
  113. };
  114. fetchParagraphProperty(fetcher);
  115. return fetcher.getValue() == null ? TextAlign.LEFT : fetcher.getValue();
  116. }
  117. /**
  118. * Specifies the alignment that is to be applied to the paragraph.
  119. * Possible values for this include left, right, centered, justified and distributed,
  120. * see {@link org.apache.poi.xslf.usermodel.TextAlign}.
  121. *
  122. * @param align text align
  123. */
  124. public void setTextAlign(TextAlign align){
  125. CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr();
  126. if(align == null) {
  127. if(pr.isSetAlgn()) pr.unsetAlgn();
  128. } else {
  129. pr.setAlgn(STTextAlignType.Enum.forInt(align.ordinal() + 1));
  130. }
  131. }
  132. /**
  133. * @return the font to be used on bullet characters within a given paragraph
  134. */
  135. public String getBulletFont(){
  136. ParagraphPropertyFetcher<String> fetcher = new ParagraphPropertyFetcher<String>(getLevel()){
  137. public boolean fetch(CTTextParagraphProperties props){
  138. if(props.isSetBuFont()){
  139. setValue(props.getBuFont().getTypeface());
  140. return true;
  141. }
  142. return false;
  143. }
  144. };
  145. fetchParagraphProperty(fetcher);
  146. return fetcher.getValue();
  147. }
  148. /**
  149. * @return the character to be used in place of the standard bullet point
  150. */
  151. public String getBulletCharacter(){
  152. ParagraphPropertyFetcher<String> fetcher = new ParagraphPropertyFetcher<String>(getLevel()){
  153. public boolean fetch(CTTextParagraphProperties props){
  154. if(props.isSetBuChar()){
  155. setValue(props.getBuChar().getChar());
  156. return true;
  157. }
  158. return false;
  159. }
  160. };
  161. fetchParagraphProperty(fetcher);
  162. return fetcher.getValue();
  163. }
  164. public Color getBulletFontColor(){
  165. final XSLFTheme theme = getParentShape().getSheet().getTheme();
  166. ParagraphPropertyFetcher<Color> fetcher = new ParagraphPropertyFetcher<Color>(getLevel()){
  167. public boolean fetch(CTTextParagraphProperties props){
  168. if(props.isSetBuClr()){
  169. setValue(theme.getColor(props.getBuClr()));
  170. return true;
  171. }
  172. return false;
  173. }
  174. };
  175. fetchParagraphProperty(fetcher);
  176. return fetcher.getValue();
  177. }
  178. public double getBulletFontSize(){
  179. ParagraphPropertyFetcher<Double> fetcher = new ParagraphPropertyFetcher<Double>(getLevel()){
  180. public boolean fetch(CTTextParagraphProperties props){
  181. if(props.isSetBuSzPct()){
  182. setValue(props.getBuSzPct().getVal() * 0.001);
  183. return true;
  184. }
  185. if(props.isSetBuSzPts()){
  186. setValue( - props.getBuSzPts().getVal() * 0.01);
  187. return true;
  188. }
  189. return false;
  190. }
  191. };
  192. fetchParagraphProperty(fetcher);
  193. return fetcher.getValue() == null ? 100 : fetcher.getValue();
  194. }
  195. /**
  196. * Specifies the indent size that will be applied to the first line of text in the paragraph.
  197. *
  198. * @param value the indent in points. The value of -1 unsets the indent attribute
  199. * from the underlying xml bean.
  200. */
  201. public void setIndent(double value){
  202. CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr();
  203. if(value == -1) {
  204. if(pr.isSetIndent()) pr.unsetIndent();
  205. } else {
  206. pr.setIndent(Units.toEMU(value));
  207. }
  208. }
  209. /**
  210. *
  211. * @return the indent applied to the first line of text in the paragraph.
  212. */
  213. public double getIndent(){
  214. ParagraphPropertyFetcher<Double> fetcher = new ParagraphPropertyFetcher<Double>(getLevel()){
  215. public boolean fetch(CTTextParagraphProperties props){
  216. if(props.isSetIndent()){
  217. setValue(Units.toPoints(props.getIndent()));
  218. return true;
  219. }
  220. return false;
  221. }
  222. };
  223. fetchParagraphProperty(fetcher);
  224. return fetcher.getValue() == null ? 0 : fetcher.getValue();
  225. }
  226. /**
  227. * Specifies the left margin of the paragraph. This is specified in addition to the text body
  228. * inset and applies only to this text paragraph. That is the text body Inset and the LeftMargin
  229. * attributes are additive with respect to the text position.
  230. *
  231. * @param value the left margin of the paragraph
  232. */
  233. public void setLeftMargin(double value){
  234. CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr();
  235. pr.setMarL(Units.toEMU(value));
  236. }
  237. /**
  238. *
  239. * @return the left margin of the paragraph
  240. */
  241. public double getLeftMargin(){
  242. ParagraphPropertyFetcher<Double> fetcher = new ParagraphPropertyFetcher<Double>(getLevel()){
  243. public boolean fetch(CTTextParagraphProperties props){
  244. if(props.isSetMarL()){
  245. double val = Units.toPoints(props.getMarL());
  246. setValue(val);
  247. return true;
  248. }
  249. return false;
  250. }
  251. };
  252. fetchParagraphProperty(fetcher);
  253. return fetcher.getValue() == null ? 0 : fetcher.getValue();
  254. }
  255. /**
  256. * This element specifies the vertical line spacing that is to be used within a paragraph.
  257. * This may be specified in two different ways, percentage spacing and font point spacing:
  258. * <p>
  259. * If linespacing >= 0, then linespacing is a percentage of normal line height
  260. * If linespacing < 0, the absolute value of linespacing is the spacing in points
  261. * </p>
  262. * Examples:
  263. * <pre><code>
  264. * // spacing will be 120% of the size of the largest text on each line
  265. * paragraph.setLineSpacing(120);
  266. *
  267. * // spacing will be 200% of the size of the largest text on each line
  268. * paragraph.setLineSpacing(200);
  269. *
  270. * // spacing will be 48 points
  271. * paragraph.setLineSpacing(-48.0);
  272. * </code></pre>
  273. *
  274. * @param linespacing the vertical line spacing
  275. */
  276. public void setLineSpacing(double linespacing){
  277. CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr();
  278. CTTextSpacing spc = CTTextSpacing.Factory.newInstance();
  279. if(linespacing >= 0) spc.addNewSpcPct().setVal((int)(linespacing*1000));
  280. else spc.addNewSpcPts().setVal((int)(-linespacing*100));
  281. pr.setLnSpc(spc);
  282. }
  283. /**
  284. * Returns the vertical line spacing that is to be used within a paragraph.
  285. * This may be specified in two different ways, percentage spacing and font point spacing:
  286. * <p>
  287. * If linespacing >= 0, then linespacing is a percentage of normal line height.
  288. * If linespacing < 0, the absolute value of linespacing is the spacing in points
  289. * </p>
  290. *
  291. * @return the vertical line spacing.
  292. */
  293. public double getLineSpacing(){
  294. ParagraphPropertyFetcher<Double> fetcher = new ParagraphPropertyFetcher<Double>(getLevel()){
  295. public boolean fetch(CTTextParagraphProperties props){
  296. if(props.isSetLnSpc()){
  297. CTTextSpacing spc = props.getLnSpc();
  298. if(spc.isSetSpcPct()) setValue( spc.getSpcPct().getVal()*0.001 );
  299. else if (spc.isSetSpcPts()) setValue( -spc.getSpcPts().getVal()*0.01 );
  300. return true;
  301. }
  302. return false;
  303. }
  304. };
  305. fetchParagraphProperty(fetcher);
  306. return fetcher.getValue() == null ? 100 : fetcher.getValue();
  307. }
  308. /**
  309. * Set the amount of vertical white space that will be present before the paragraph.
  310. * This space is specified in either percentage or points:
  311. * <p>
  312. * If spaceBefore >= 0, then space is a percentage of normal line height.
  313. * If spaceBefore < 0, the absolute value of linespacing is the spacing in points
  314. * </p>
  315. * Examples:
  316. * <pre><code>
  317. * // The paragraph will be formatted to have a spacing before the paragraph text.
  318. * // The spacing will be 200% of the size of the largest text on each line
  319. * paragraph.setSpaceBefore(200);
  320. *
  321. * // The spacing will be a size of 48 points
  322. * paragraph.setSpaceBefore(-48.0);
  323. * </code></pre>
  324. *
  325. * @param spaceBefore the vertical white space before the paragraph.
  326. */
  327. public void setSpaceBefore(double spaceBefore){
  328. CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr();
  329. CTTextSpacing spc = CTTextSpacing.Factory.newInstance();
  330. if(spaceBefore >= 0) spc.addNewSpcPct().setVal((int)(spaceBefore*1000));
  331. else spc.addNewSpcPts().setVal((int)(-spaceBefore*100));
  332. pr.setSpcBef(spc);
  333. }
  334. /**
  335. * The amount of vertical white space before the paragraph
  336. * This may be specified in two different ways, percentage spacing and font point spacing:
  337. * <p>
  338. * If spaceBefore >= 0, then space is a percentage of normal line height.
  339. * If spaceBefore < 0, the absolute value of linespacing is the spacing in points
  340. * </p>
  341. *
  342. * @return the vertical white space before the paragraph
  343. */
  344. public double getSpaceBefore(){
  345. ParagraphPropertyFetcher<Double> fetcher = new ParagraphPropertyFetcher<Double>(getLevel()){
  346. public boolean fetch(CTTextParagraphProperties props){
  347. if(props.isSetSpcBef()){
  348. CTTextSpacing spc = props.getSpcBef();
  349. if(spc.isSetSpcPct()) setValue( spc.getSpcPct().getVal()*0.001 );
  350. else if (spc.isSetSpcPts()) setValue( -spc.getSpcPts().getVal()*0.01 );
  351. return true;
  352. }
  353. return false;
  354. }
  355. };
  356. fetchParagraphProperty(fetcher);
  357. return fetcher.getValue() == null ? 0 : fetcher.getValue();
  358. }
  359. /**
  360. * Set the amount of vertical white space that will be present after the paragraph.
  361. * This space is specified in either percentage or points:
  362. * <p>
  363. * If spaceAfter >= 0, then space is a percentage of normal line height.
  364. * If spaceAfter < 0, the absolute value of linespacing is the spacing in points
  365. * </p>
  366. * Examples:
  367. * <pre><code>
  368. * // The paragraph will be formatted to have a spacing after the paragraph text.
  369. * // The spacing will be 200% of the size of the largest text on each line
  370. * paragraph.setSpaceAfter(200);
  371. *
  372. * // The spacing will be a size of 48 points
  373. * paragraph.setSpaceAfter(-48.0);
  374. * </code></pre>
  375. *
  376. * @param spaceAfter the vertical white space after the paragraph.
  377. */
  378. public void setSpaceAfter(double spaceAfter){
  379. CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr();
  380. CTTextSpacing spc = CTTextSpacing.Factory.newInstance();
  381. if(spaceAfter >= 0) spc.addNewSpcPct().setVal((int)(spaceAfter*1000));
  382. else spc.addNewSpcPts().setVal((int)(-spaceAfter*100));
  383. pr.setSpcAft(spc);
  384. }
  385. /**
  386. * The amount of vertical white space after the paragraph
  387. * This may be specified in two different ways, percentage spacing and font point spacing:
  388. * <p>
  389. * If spaceBefore >= 0, then space is a percentage of normal line height.
  390. * If spaceBefore < 0, the absolute value of linespacing is the spacing in points
  391. * </p>
  392. *
  393. * @return the vertical white space after the paragraph
  394. */
  395. public double getSpaceAfter(){
  396. ParagraphPropertyFetcher<Double> fetcher = new ParagraphPropertyFetcher<Double>(getLevel()){
  397. public boolean fetch(CTTextParagraphProperties props){
  398. if(props.isSetSpcAft()){
  399. CTTextSpacing spc = props.getSpcAft();
  400. if(spc.isSetSpcPct()) setValue( spc.getSpcPct().getVal()*0.001 );
  401. else if (spc.isSetSpcPts()) setValue( -spc.getSpcPts().getVal()*0.01 );
  402. return true;
  403. }
  404. return false;
  405. }
  406. };
  407. fetchParagraphProperty(fetcher);
  408. return fetcher.getValue() == null ? 0 : fetcher.getValue();
  409. }
  410. /**
  411. * Specifies the particular level text properties that this paragraph will follow.
  412. * The value for this attribute formats the text according to the corresponding level
  413. * paragraph properties defined in the SlideMaster.
  414. *
  415. * @param level the level (0 ... 4)
  416. */
  417. public void setLevel(int level){
  418. CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr();
  419. pr.setLvl(level);
  420. }
  421. /**
  422. *
  423. * @return the text level of this paragraph. Default is 0.
  424. */
  425. public int getLevel(){
  426. CTTextParagraphProperties pr = _p.getPPr();
  427. if(pr == null) return 0;
  428. return pr.getLvl();
  429. }
  430. /**
  431. * Returns whether this paragraph has bullets
  432. */
  433. public boolean isBullet() {
  434. ParagraphPropertyFetcher<Boolean> fetcher = new ParagraphPropertyFetcher<Boolean>(getLevel()){
  435. public boolean fetch(CTTextParagraphProperties props){
  436. if(props.isSetBuNone()) {
  437. setValue(false);
  438. return true;
  439. }
  440. if(props.isSetBuFont()){
  441. setValue(true);
  442. return true;
  443. }
  444. return false;
  445. }
  446. };
  447. fetchParagraphProperty(fetcher);
  448. return fetcher.getValue() == null ? false : fetcher.getValue();
  449. }
  450. @Override
  451. public String toString(){
  452. return "[" + getClass() + "]" + getText();
  453. }
  454. public List<TextFragment> getTextLines(){
  455. return _lines;
  456. }
  457. public double draw(Graphics2D graphics, double x, double y){
  458. double marginLeft = _shape.getMarginLeft();
  459. double marginRight = _shape.getMarginRight();
  460. Rectangle2D anchor = _shape.getAnchor();
  461. double penY = y;
  462. double textOffset = getLeftMargin();
  463. for(TextFragment line : _lines){
  464. double penX = x;
  465. switch (getTextAlign()) {
  466. case CENTER:
  467. penX += textOffset + (anchor.getWidth() - textOffset - line.getWidth() - marginLeft - marginRight) / 2;
  468. break;
  469. case RIGHT:
  470. penX += (anchor.getWidth() - line.getWidth() - marginLeft - marginRight);
  471. break;
  472. default:
  473. penX = x + textOffset;
  474. break;
  475. }
  476. if(_bullet != null){
  477. _bullet.draw(graphics, penX + getIndent(), penY);
  478. }
  479. line.draw(graphics, penX, penY);
  480. //The vertical line spacing
  481. double spacing = getLineSpacing();
  482. if(spacing > 0) {
  483. // If linespacing >= 0, then linespacing is a percentage of normal line height.
  484. penY += spacing*0.01*line.getHeight();
  485. } else {
  486. // positive value means absolute spacing in points
  487. penY += -spacing;
  488. }
  489. }
  490. return penY - y;
  491. }
  492. static class TextFragment {
  493. private TextLayout _layout;
  494. private AttributedString _str;
  495. TextFragment(TextLayout layout, AttributedString str){
  496. _layout = layout;
  497. _str = str;
  498. }
  499. void draw(Graphics2D graphics, double x, double y){
  500. double yBaseline = y + _layout.getAscent();
  501. graphics.drawString(_str.getIterator(), (float)x, (float)yBaseline );
  502. }
  503. public float getHeight(){
  504. return _layout.getAscent() + _layout.getDescent() + _layout.getLeading();
  505. }
  506. public float getWidth(){
  507. return _layout.getAdvance();
  508. }
  509. }
  510. public AttributedString getAttributedString(){
  511. String text = getText();
  512. AttributedString string = new AttributedString(text);
  513. int startIndex = 0;
  514. for (XSLFTextRun run : _runs){
  515. int length = run.getText().length();
  516. if(length == 0) {
  517. // skip empty runs
  518. continue;
  519. }
  520. int endIndex = startIndex + length;
  521. string.addAttribute(TextAttribute.FOREGROUND, run.getFontColor(), startIndex, endIndex);
  522. string.addAttribute(TextAttribute.FAMILY, run.getFontFamily(), startIndex, endIndex);
  523. string.addAttribute(TextAttribute.SIZE, run.getFontSize(), startIndex, endIndex);
  524. if(run.isBold()) {
  525. string.addAttribute(TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD, startIndex, endIndex);
  526. }
  527. if(run.isItalic()) {
  528. string.addAttribute(TextAttribute.POSTURE, TextAttribute.POSTURE_OBLIQUE, startIndex, endIndex);
  529. }
  530. if(run.isUnderline()) {
  531. string.addAttribute(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON, startIndex, endIndex);
  532. }
  533. if(run.isStrikethrough()) {
  534. string.addAttribute(TextAttribute.STRIKETHROUGH, TextAttribute.STRIKETHROUGH_ON, startIndex, endIndex);
  535. }
  536. startIndex = endIndex;
  537. }
  538. return string;
  539. }
  540. void breakText(Graphics2D graphics){
  541. _lines = new ArrayList<TextFragment>();
  542. AttributedString at = getAttributedString();
  543. AttributedCharacterIterator it = at.getIterator();
  544. if(it.getBeginIndex() == it.getEndIndex()) {
  545. return;
  546. }
  547. LineBreakMeasurer measurer = new LineBreakMeasurer(it, graphics.getFontRenderContext());
  548. for (;;) {
  549. int startIndex = measurer.getPosition();
  550. double wrappingWidth = getWrappingWidth() + 1; // add a pixel to compensate rounding errors
  551. TextLayout layout = measurer.nextLayout((float)wrappingWidth, it.getEndIndex(), true);
  552. if (layout == null) {
  553. // layout can be null if the entire word at the current position
  554. // does not fit within the wrapping width. Try with requireNextWord=false.
  555. layout = measurer.nextLayout((float)wrappingWidth, it.getEndIndex(), false);
  556. }
  557. int endIndex = measurer.getPosition();
  558. if(getTextAlign() == TextAlign.JUSTIFY) {
  559. layout = layout.getJustifiedLayout((float)wrappingWidth);
  560. }
  561. AttributedString str = new AttributedString(it, startIndex, endIndex);
  562. TextFragment line = new TextFragment(layout, str);
  563. _lines.add(line);
  564. if(endIndex == it.getEndIndex()) break;
  565. }
  566. if(isBullet()) {
  567. String buCharacter = getBulletCharacter();
  568. String buFont = getBulletFont();
  569. if(buCharacter != null && buFont != null && _lines.size() > 0) {
  570. AttributedString str = new AttributedString(buCharacter);
  571. TextFragment firstLine = _lines.get(0);
  572. AttributedCharacterIterator bit = firstLine._str.getIterator();
  573. Color buColor = getBulletFontColor();
  574. str.addAttribute(TextAttribute.FOREGROUND, buColor == null ?
  575. bit.getAttribute(TextAttribute.FOREGROUND) : buColor);
  576. str.addAttribute(TextAttribute.FAMILY, buFont);
  577. double fontSize = (Double)bit.getAttribute(TextAttribute.SIZE);
  578. double buSz = getBulletFontSize();
  579. if(buSz > 0) fontSize *= buSz* 0.01;
  580. else fontSize = -buSz;
  581. str.addAttribute(TextAttribute.SIZE, fontSize);
  582. TextLayout layout = new TextLayout(str.getIterator(), graphics.getFontRenderContext());
  583. _bullet = new TextFragment(layout, str);
  584. }
  585. }
  586. }
  587. double getWrappingWidth(){
  588. double width;
  589. if(!_shape.getWordWrap()) {
  590. width = _shape.getSheet().getSlideShow().getPageSize().getWidth();
  591. } else {
  592. width = _shape.getAnchor().getWidth() -
  593. _shape.getMarginLeft() - _shape.getMarginRight() - getLeftMargin();
  594. }
  595. return width;
  596. }
  597. CTTextParagraphProperties getDefaultStyle(){
  598. CTPlaceholder ph = _shape.getCTPlaceholder();
  599. String defaultStyleSelector;
  600. if(ph == null) defaultStyleSelector = "otherStyle";
  601. else {
  602. switch(ph.getType().intValue()){
  603. case STPlaceholderType.INT_TITLE:
  604. case STPlaceholderType.INT_CTR_TITLE:
  605. defaultStyleSelector = "titleStyle";
  606. break;
  607. case STPlaceholderType.INT_FTR:
  608. case STPlaceholderType.INT_SLD_NUM:
  609. case STPlaceholderType.INT_DT:
  610. defaultStyleSelector = "otherStyle";
  611. break;
  612. default:
  613. defaultStyleSelector = "bodyStyle";
  614. break;
  615. }
  616. }
  617. int level = getLevel();
  618. XmlObject[] o = _shape.getSheet().getSlideMaster().getXmlObject().selectPath(
  619. "declare namespace p='http://schemas.openxmlformats.org/presentationml/2006/main' " +
  620. "declare namespace a='http://schemas.openxmlformats.org/drawingml/2006/main' " +
  621. ".//p:txStyles/p:" + defaultStyleSelector +"/a:lvl" +(level+1)+ "pPr");
  622. if(o.length == 1){
  623. return (CTTextParagraphProperties)o[0];
  624. }
  625. throw new IllegalArgumentException("Failed to fetch default style for " +
  626. defaultStyleSelector + " and level=" + level);
  627. }
  628. private boolean fetchParagraphProperty(ParagraphPropertyFetcher visitor){
  629. boolean ok = false;
  630. if(_p.isSetPPr()) ok = visitor.fetch(_p.getPPr());
  631. if(!ok) {
  632. XSLFTextShape shape = getParentShape();
  633. ok = shape.fetchShapeProperty(visitor);
  634. if(!ok) {
  635. CTTextParagraphProperties defaultProps = getDefaultStyle();
  636. if(defaultProps != null) ok = visitor.fetch(defaultProps);
  637. }
  638. }
  639. return ok;
  640. }
  641. }