PageRenderTime 171ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/pptx/src/org/apache/poi/xslf/usermodel/XSLFTextParagraph.java

https://github.com/minstrelsy/POI-Android
Java | 1184 lines | 644 code | 114 blank | 426 comment | 153 complexity | dc9cd103f74ef072c0c52e3cc628a7cb MD5 | raw file
  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 java.util.ArrayList;
  17. import java.util.Iterator;
  18. import java.util.List;
  19. import net.pbdavey.awt.Graphics2D;
  20. import org.apache.poi.util.Beta;
  21. import org.apache.poi.util.Internal;
  22. import org.apache.poi.util.Units;
  23. import org.apache.poi.xslf.model.ParagraphPropertyFetcher;
  24. import org.apache.xmlbeans.XmlObject;
  25. import org.openxmlformats.schemas.drawingml.x2006.main.CTColor;
  26. import org.openxmlformats.schemas.drawingml.x2006.main.CTRegularTextRun;
  27. import org.openxmlformats.schemas.drawingml.x2006.main.CTSRgbColor;
  28. import org.openxmlformats.schemas.drawingml.x2006.main.CTTextBulletSizePercent;
  29. import org.openxmlformats.schemas.drawingml.x2006.main.CTTextAutonumberBullet;
  30. import org.openxmlformats.schemas.drawingml.x2006.main.CTTextBulletSizePoint;
  31. //import org.openxmlformats.schemas.drawingml.x2006.main.CTTextBulletSizePoint;
  32. import org.openxmlformats.schemas.drawingml.x2006.main.CTTextCharBullet;
  33. import org.openxmlformats.schemas.drawingml.x2006.main.CTTextCharacterProperties;
  34. import org.openxmlformats.schemas.drawingml.x2006.main.CTTextField;
  35. import org.openxmlformats.schemas.drawingml.x2006.main.CTTextFont;
  36. import org.openxmlformats.schemas.drawingml.x2006.main.CTTextLineBreak;
  37. import org.openxmlformats.schemas.drawingml.x2006.main.CTTextNormalAutofit;
  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. import org.openxmlformats.schemas.drawingml.x2006.main.CTTextTabStop;
  42. import org.openxmlformats.schemas.drawingml.x2006.main.CTTextTabStopList;
  43. import org.openxmlformats.schemas.drawingml.x2006.main.STTextAlignType;
  44. import org.openxmlformats.schemas.drawingml.x2006.main.STTextAutonumberScheme;
  45. import org.openxmlformats.schemas.presentationml.x2006.main.CTPlaceholder;
  46. import org.openxmlformats.schemas.presentationml.x2006.main.STPlaceholderType;
  47. import and.awt.Color;
  48. import and.awt.geom.Rectangle2D;
  49. import android.content.res.ColorStateList;
  50. import android.graphics.Paint;
  51. import android.graphics.Typeface;
  52. import android.text.Layout.Alignment;
  53. import android.text.Spannable;
  54. import android.text.SpannableStringBuilder;
  55. import android.text.StaticLayout;
  56. import android.text.TextPaint;
  57. import android.text.style.StrikethroughSpan;
  58. import android.text.style.SubscriptSpan;
  59. import android.text.style.SuperscriptSpan;
  60. import android.text.style.TextAppearanceSpan;
  61. import android.text.style.UnderlineSpan;
  62. import android.util.Log;
  63. /**
  64. * Represents a paragraph of text within the containing text body.
  65. * The paragraph is the highest level text separation mechanism.
  66. *
  67. * @author Yegor Kozlov
  68. * @since POI-3.8
  69. */
  70. @Beta
  71. public class XSLFTextParagraph implements Iterable<XSLFTextRun>{
  72. private final CTTextParagraph _p;
  73. private final List<XSLFTextRun> _runs;
  74. private final XSLFTextShape _shape;
  75. // private List<TextFragment> _lines;
  76. // private TextFragment _bullet;
  77. StaticLayout sl;
  78. /**
  79. * the highest line in this paragraph. Used for line spacing.
  80. */
  81. private double _maxLineHeight;
  82. XSLFTextParagraph(CTTextParagraph p, XSLFTextShape shape){
  83. _p = p;
  84. _runs = new ArrayList<XSLFTextRun>();
  85. _shape = shape;
  86. for(XmlObject ch : _p.selectPath("*")){
  87. if(ch instanceof CTRegularTextRun){
  88. CTRegularTextRun r = (CTRegularTextRun)ch;
  89. _runs.add(new XSLFTextRun(r, this));
  90. } else if (ch instanceof CTTextLineBreak){
  91. CTTextLineBreak br = (CTTextLineBreak)ch;
  92. CTRegularTextRun r = CTRegularTextRun.Factory.newInstance();
  93. r.setRPr(br.getRPr());
  94. r.setT("\n");
  95. _runs.add(new XSLFTextRun(r, this));
  96. } else if (ch instanceof CTTextField){
  97. CTTextField f = (CTTextField)ch;
  98. CTRegularTextRun r = CTRegularTextRun.Factory.newInstance();
  99. r.setRPr(f.getRPr());
  100. r.setT(f.getT());
  101. _runs.add(new XSLFTextRun(r, this));
  102. }
  103. }
  104. }
  105. public String getText(){
  106. StringBuilder out = new StringBuilder();
  107. for (XSLFTextRun r : _runs) {
  108. out.append(r.getText());
  109. }
  110. return out.toString();
  111. }
  112. String getRenderableText(){
  113. StringBuilder out = new StringBuilder();
  114. for (XSLFTextRun r : _runs) {
  115. out.append(r.getRenderableText());
  116. }
  117. return out.toString();
  118. }
  119. @Internal
  120. public CTTextParagraph getXmlObject(){
  121. return _p;
  122. }
  123. XSLFTextShape getParentShape() {
  124. return _shape;
  125. }
  126. public List<XSLFTextRun> getTextRuns(){
  127. return _runs;
  128. }
  129. public Iterator<XSLFTextRun> iterator(){
  130. return _runs.iterator();
  131. }
  132. /**
  133. * Add a new run of text
  134. *
  135. * @return a new run of text
  136. */
  137. public XSLFTextRun addNewTextRun(){
  138. CTRegularTextRun r = _p.addNewR();
  139. CTTextCharacterProperties rPr = r.addNewRPr();
  140. rPr.setLang("en-US");
  141. XSLFTextRun run = new XSLFTextRun(r, this);
  142. _runs.add(run);
  143. return run;
  144. }
  145. /**
  146. * Insert a line break
  147. *
  148. * @return text run representing this line break ('\n')
  149. */
  150. public XSLFTextRun addLineBreak(){
  151. CTTextLineBreak br = _p.addNewBr();
  152. CTTextCharacterProperties brProps = br.addNewRPr();
  153. if(_runs.size() > 0){
  154. // by default line break has the font size of the last text run
  155. CTTextCharacterProperties prevRun = _runs.get(_runs.size() - 1).getRPr();
  156. brProps.set(prevRun);
  157. }
  158. CTRegularTextRun r = CTRegularTextRun.Factory.newInstance();
  159. r.setRPr(brProps);
  160. r.setT("\n");
  161. XSLFTextRun run = new XSLFLineBreak(r, this, brProps);
  162. _runs.add(run);
  163. return run;
  164. }
  165. /**
  166. * Returns the alignment that is applied to the paragraph.
  167. *
  168. * If this attribute is omitted, then a value of left is implied.
  169. * @return ??? alignment that is applied to the paragraph
  170. */
  171. public TextAlign getTextAlign(){
  172. ParagraphPropertyFetcher<TextAlign> fetcher = new ParagraphPropertyFetcher<TextAlign>(getLevel()){
  173. public boolean fetch(CTTextParagraphProperties props){
  174. if(props.isSetAlgn()){
  175. TextAlign val = TextAlign.values()[props.getAlgn().intValue() - 1];
  176. setValue(val);
  177. return true;
  178. }
  179. return false;
  180. }
  181. };
  182. fetchParagraphProperty(fetcher);
  183. return fetcher.getValue() == null ? TextAlign.LEFT : fetcher.getValue();
  184. }
  185. /**
  186. * Specifies the alignment that is to be applied to the paragraph.
  187. * Possible values for this include left, right, centered, justified and distributed,
  188. * see {@link org.apache.poi.xslf.usermodel.TextAlign}.
  189. *
  190. * @param align text align
  191. */
  192. public void setTextAlign(TextAlign align){
  193. CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr();
  194. if(align == null) {
  195. if(pr.isSetAlgn()) pr.unsetAlgn();
  196. } else {
  197. pr.setAlgn(STTextAlignType.Enum.forInt(align.ordinal() + 1));
  198. }
  199. }
  200. /**
  201. * @return the font to be used on bullet characters within a given paragraph
  202. */
  203. public String getBulletFont(){
  204. ParagraphPropertyFetcher<String> fetcher = new ParagraphPropertyFetcher<String>(getLevel()){
  205. public boolean fetch(CTTextParagraphProperties props){
  206. if(props.isSetBuFont()){
  207. setValue(props.getBuFont().getTypeface());
  208. return true;
  209. }
  210. return false;
  211. }
  212. };
  213. fetchParagraphProperty(fetcher);
  214. return fetcher.getValue();
  215. }
  216. public void setBulletFont(String typeface){
  217. CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr();
  218. CTTextFont font = pr.isSetBuFont() ? pr.getBuFont() : pr.addNewBuFont();
  219. font.setTypeface(typeface);
  220. }
  221. /**
  222. * @return the character to be used in place of the standard bullet point
  223. */
  224. public String getBulletCharacter(){
  225. ParagraphPropertyFetcher<String> fetcher = new ParagraphPropertyFetcher<String>(getLevel()){
  226. public boolean fetch(CTTextParagraphProperties props){
  227. if(props.isSetBuChar()){
  228. setValue(props.getBuChar().getChar());
  229. return true;
  230. }
  231. return false;
  232. }
  233. };
  234. fetchParagraphProperty(fetcher);
  235. return fetcher.getValue();
  236. }
  237. public void setBulletCharacter(String str){
  238. CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr();
  239. CTTextCharBullet c = pr.isSetBuChar() ? pr.getBuChar() : pr.addNewBuChar();
  240. c.setChar(str);
  241. }
  242. /**
  243. *
  244. * @return the color of bullet characters within a given paragraph.
  245. * A <code>null</code> value means to use the text font color.
  246. */
  247. public Color getBulletFontColor(){
  248. final XSLFTheme theme = getParentShape().getSheet().getTheme();
  249. ParagraphPropertyFetcher<Color> fetcher = new ParagraphPropertyFetcher<Color>(getLevel()){
  250. public boolean fetch(CTTextParagraphProperties props){
  251. if(props.isSetBuClr()){
  252. XSLFColor c = new XSLFColor(props.getBuClr(), theme, null);
  253. setValue(c.getColor());
  254. return true;
  255. }
  256. return false;
  257. }
  258. };
  259. fetchParagraphProperty(fetcher);
  260. return fetcher.getValue();
  261. }
  262. /**
  263. * Set the color to be used on bullet characters within a given paragraph.
  264. *
  265. * @param color the bullet color
  266. */
  267. public void setBulletFontColor(Color color){
  268. CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr();
  269. CTColor c = pr.isSetBuClr() ? pr.getBuClr() : pr.addNewBuClr();
  270. CTSRgbColor clr = c.isSetSrgbClr() ? c.getSrgbClr() : c.addNewSrgbClr();
  271. clr.setVal(new byte[]{(byte) color.getRed(), (byte) color.getGreen(), (byte) color.getBlue()});
  272. }
  273. /**
  274. * Returns the bullet size that is to be used within a paragraph.
  275. * This may be specified in two different ways, percentage spacing and font point spacing:
  276. * <p>
  277. * If bulletSize >= 0, then bulletSize is a percentage of the font size.
  278. * If bulletSize < 0, then it specifies the size in points
  279. * </p>
  280. *
  281. * @return the bullet size
  282. */
  283. public double getBulletFontSize(){
  284. ParagraphPropertyFetcher<Double> fetcher = new ParagraphPropertyFetcher<Double>(getLevel()){
  285. public boolean fetch(CTTextParagraphProperties props){
  286. if(props.isSetBuSzPct()){
  287. setValue(props.getBuSzPct().getVal() * 0.001);
  288. return true;
  289. }
  290. if(props.isSetBuSzPts()){
  291. setValue( - props.getBuSzPts().getVal() * 0.01);
  292. return true;
  293. }
  294. return false;
  295. }
  296. };
  297. fetchParagraphProperty(fetcher);
  298. return fetcher.getValue() == null ? 100 : fetcher.getValue();
  299. }
  300. /**
  301. * Sets the bullet size that is to be used within a paragraph.
  302. * This may be specified in two different ways, percentage spacing and font point spacing:
  303. * <p>
  304. * If bulletSize >= 0, then bulletSize is a percentage of the font size.
  305. * If bulletSize < 0, then it specifies the size in points
  306. * </p>
  307. *
  308. * @return the bullet size
  309. */
  310. public void setBulletFontSize(double bulletSize){
  311. CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr();
  312. if(bulletSize >= 0) {
  313. CTTextBulletSizePercent pt = pr.isSetBuSzPct() ? pr.getBuSzPct() : pr.addNewBuSzPct();
  314. pt.setVal((int)(bulletSize*1000));
  315. if(pr.isSetBuSzPts()) pr.unsetBuSzPts();
  316. } else {
  317. CTTextBulletSizePoint pt = pr.isSetBuSzPts() ? pr.getBuSzPts() : pr.addNewBuSzPts();
  318. pt.setVal((int)(-bulletSize*100));
  319. if(pr.isSetBuSzPct()) pr.unsetBuSzPct();
  320. }
  321. }
  322. /**
  323. * Specifies the indent size that will be applied to the first line of text in the paragraph.
  324. *
  325. * @param value the indent in points.
  326. */
  327. public void setIndent(double value){
  328. CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr();
  329. if(value == -1) {
  330. if(pr.isSetIndent()) pr.unsetIndent();
  331. } else {
  332. pr.setIndent(Units.toEMU(value));
  333. }
  334. }
  335. /**
  336. *
  337. * @return the indent applied to the first line of text in the paragraph.
  338. */
  339. public double getIndent(){
  340. ParagraphPropertyFetcher<Double> fetcher = new ParagraphPropertyFetcher<Double>(getLevel()){
  341. public boolean fetch(CTTextParagraphProperties props){
  342. if(props.isSetIndent()){
  343. setValue(Units.toPoints(props.getIndent()));
  344. return true;
  345. }
  346. return false;
  347. }
  348. };
  349. fetchParagraphProperty(fetcher);
  350. return fetcher.getValue() == null ? 0 : fetcher.getValue();
  351. }
  352. /**
  353. * Specifies the left margin of the paragraph. This is specified in addition to the text body
  354. * inset and applies only to this text paragraph. That is the text body Inset and the LeftMargin
  355. * attributes are additive with respect to the text position.
  356. *
  357. * @param value the left margin of the paragraph
  358. */
  359. public void setLeftMargin(double value){
  360. CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr();
  361. if(value == -1) {
  362. if(pr.isSetMarL()) pr.unsetMarL();
  363. } else {
  364. pr.setMarL(Units.toEMU(value));
  365. }
  366. }
  367. /**
  368. *
  369. * @return the left margin of the paragraph
  370. */
  371. public double getLeftMargin(){
  372. ParagraphPropertyFetcher<Double> fetcher = new ParagraphPropertyFetcher<Double>(getLevel()){
  373. public boolean fetch(CTTextParagraphProperties props){
  374. if(props.isSetMarL()){
  375. double val = Units.toPoints(props.getMarL());
  376. setValue(val);
  377. return true;
  378. }
  379. return false;
  380. }
  381. };
  382. fetchParagraphProperty(fetcher);
  383. // if the marL attribute is omitted, then a value of 347663 is implied
  384. return fetcher.getValue() == null ? 0 : fetcher.getValue();
  385. }
  386. /**
  387. *
  388. * @return the default size for a tab character within this paragraph in points
  389. */
  390. public double getDefaultTabSize(){
  391. ParagraphPropertyFetcher<Double> fetcher = new ParagraphPropertyFetcher<Double>(getLevel()){
  392. public boolean fetch(CTTextParagraphProperties props){
  393. if(props.isSetDefTabSz()){
  394. double val = Units.toPoints(props.getDefTabSz());
  395. setValue(val);
  396. return true;
  397. }
  398. return false;
  399. }
  400. };
  401. fetchParagraphProperty(fetcher);
  402. return fetcher.getValue() == null ? 0 : fetcher.getValue();
  403. }
  404. public double getTabStop(final int idx){
  405. ParagraphPropertyFetcher<Double> fetcher = new ParagraphPropertyFetcher<Double>(getLevel()){
  406. public boolean fetch(CTTextParagraphProperties props){
  407. if(props.isSetTabLst()){
  408. CTTextTabStopList tabStops = props.getTabLst();
  409. if(idx < tabStops.sizeOfTabArray() ) {
  410. CTTextTabStop ts = tabStops.getTabArray(idx);
  411. double val = Units.toPoints(ts.getPos());
  412. setValue(val);
  413. return true;
  414. }
  415. }
  416. return false;
  417. }
  418. };
  419. fetchParagraphProperty(fetcher);
  420. return fetcher.getValue() == null ? getDefaultTabSize() : fetcher.getValue();
  421. }
  422. /**
  423. * This element specifies the vertical line spacing that is to be used within a paragraph.
  424. * This may be specified in two different ways, percentage spacing and font point spacing:
  425. * <p>
  426. * If linespacing >= 0, then linespacing is a percentage of normal line height
  427. * If linespacing < 0, the absolute value of linespacing is the spacing in points
  428. * </p>
  429. * Examples:
  430. * <pre><code>
  431. * // spacing will be 120% of the size of the largest text on each line
  432. * paragraph.setLineSpacing(120);
  433. *
  434. * // spacing will be 200% of the size of the largest text on each line
  435. * paragraph.setLineSpacing(200);
  436. *
  437. * // spacing will be 48 points
  438. * paragraph.setLineSpacing(-48.0);
  439. * </code></pre>
  440. *
  441. * @param linespacing the vertical line spacing
  442. */
  443. public void setLineSpacing(double linespacing){
  444. CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr();
  445. CTTextSpacing spc = CTTextSpacing.Factory.newInstance();
  446. if(linespacing >= 0) spc.addNewSpcPct().setVal((int)(linespacing*1000));
  447. else spc.addNewSpcPts().setVal((int)(-linespacing*100));
  448. pr.setLnSpc(spc);
  449. }
  450. /**
  451. * Returns the vertical line spacing that is to be used within a paragraph.
  452. * This may be specified in two different ways, percentage spacing and font point spacing:
  453. * <p>
  454. * If linespacing >= 0, then linespacing is a percentage of normal line height.
  455. * If linespacing < 0, the absolute value of linespacing is the spacing in points
  456. * </p>
  457. *
  458. * @return the vertical line spacing.
  459. */
  460. public double getLineSpacing(){
  461. ParagraphPropertyFetcher<Double> fetcher = new ParagraphPropertyFetcher<Double>(getLevel()){
  462. public boolean fetch(CTTextParagraphProperties props){
  463. if(props.isSetLnSpc()){
  464. CTTextSpacing spc = props.getLnSpc();
  465. if(spc.isSetSpcPct()) setValue( spc.getSpcPct().getVal()*0.001 );
  466. else if (spc.isSetSpcPts()) setValue( -spc.getSpcPts().getVal()*0.01 );
  467. return true;
  468. }
  469. return false;
  470. }
  471. };
  472. fetchParagraphProperty(fetcher);
  473. double lnSpc = fetcher.getValue() == null ? 100 : fetcher.getValue();
  474. if(lnSpc > 0) {
  475. // check if the percentage value is scaled
  476. CTTextNormalAutofit normAutofit = getParentShape().getTextBodyPr().getNormAutofit();
  477. if(normAutofit != null) {
  478. double scale = 1 - (double)normAutofit.getLnSpcReduction() / 100000;
  479. lnSpc *= scale;
  480. }
  481. }
  482. return lnSpc;
  483. }
  484. /**
  485. * Set the amount of vertical white space that will be present before the paragraph.
  486. * This space is specified in either percentage or points:
  487. * <p>
  488. * If spaceBefore >= 0, then space is a percentage of normal line height.
  489. * If spaceBefore < 0, the absolute value of linespacing is the spacing in points
  490. * </p>
  491. * Examples:
  492. * <pre><code>
  493. * // The paragraph will be formatted to have a spacing before the paragraph text.
  494. * // The spacing will be 200% of the size of the largest text on each line
  495. * paragraph.setSpaceBefore(200);
  496. *
  497. * // The spacing will be a size of 48 points
  498. * paragraph.setSpaceBefore(-48.0);
  499. * </code></pre>
  500. *
  501. * @param spaceBefore the vertical white space before the paragraph.
  502. */
  503. public void setSpaceBefore(double spaceBefore){
  504. CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr();
  505. CTTextSpacing spc = CTTextSpacing.Factory.newInstance();
  506. if(spaceBefore >= 0) spc.addNewSpcPct().setVal((int)(spaceBefore*1000));
  507. else spc.addNewSpcPts().setVal((int)(-spaceBefore*100));
  508. pr.setSpcBef(spc);
  509. }
  510. /**
  511. * The amount of vertical white space before the paragraph
  512. * This may be specified in two different ways, percentage spacing and font point spacing:
  513. * <p>
  514. * If spaceBefore >= 0, then space is a percentage of normal line height.
  515. * If spaceBefore < 0, the absolute value of linespacing is the spacing in points
  516. * </p>
  517. *
  518. * @return the vertical white space before the paragraph
  519. */
  520. public double getSpaceBefore(){
  521. ParagraphPropertyFetcher<Double> fetcher = new ParagraphPropertyFetcher<Double>(getLevel()){
  522. public boolean fetch(CTTextParagraphProperties props){
  523. if(props.isSetSpcBef()){
  524. CTTextSpacing spc = props.getSpcBef();
  525. if(spc.isSetSpcPct()) setValue( spc.getSpcPct().getVal()*0.001 );
  526. else if (spc.isSetSpcPts()) setValue( -spc.getSpcPts().getVal()*0.01 );
  527. return true;
  528. }
  529. return false;
  530. }
  531. };
  532. fetchParagraphProperty(fetcher);
  533. double spcBef = fetcher.getValue() == null ? 0 : fetcher.getValue();
  534. return spcBef;
  535. }
  536. /**
  537. * Set the amount of vertical white space that will be present after the paragraph.
  538. * This space is specified in either percentage or points:
  539. * <p>
  540. * If spaceAfter >= 0, then space is a percentage of normal line height.
  541. * If spaceAfter < 0, the absolute value of linespacing is the spacing in points
  542. * </p>
  543. * Examples:
  544. * <pre><code>
  545. * // The paragraph will be formatted to have a spacing after the paragraph text.
  546. * // The spacing will be 200% of the size of the largest text on each line
  547. * paragraph.setSpaceAfter(200);
  548. *
  549. * // The spacing will be a size of 48 points
  550. * paragraph.setSpaceAfter(-48.0);
  551. * </code></pre>
  552. *
  553. * @param spaceAfter the vertical white space after the paragraph.
  554. */
  555. public void setSpaceAfter(double spaceAfter){
  556. CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr();
  557. CTTextSpacing spc = CTTextSpacing.Factory.newInstance();
  558. if(spaceAfter >= 0) spc.addNewSpcPct().setVal((int)(spaceAfter*1000));
  559. else spc.addNewSpcPts().setVal((int)(-spaceAfter*100));
  560. pr.setSpcAft(spc);
  561. }
  562. /**
  563. * The amount of vertical white space after the paragraph
  564. * This may be specified in two different ways, percentage spacing and font point spacing:
  565. * <p>
  566. * If spaceBefore >= 0, then space is a percentage of normal line height.
  567. * If spaceBefore < 0, the absolute value of linespacing is the spacing in points
  568. * </p>
  569. *
  570. * @return the vertical white space after the paragraph
  571. */
  572. public double getSpaceAfter(){
  573. ParagraphPropertyFetcher<Double> fetcher = new ParagraphPropertyFetcher<Double>(getLevel()){
  574. public boolean fetch(CTTextParagraphProperties props){
  575. if(props.isSetSpcAft()){
  576. CTTextSpacing spc = props.getSpcAft();
  577. if(spc.isSetSpcPct()) setValue( spc.getSpcPct().getVal()*0.001 );
  578. else if (spc.isSetSpcPts()) setValue( -spc.getSpcPts().getVal()*0.01 );
  579. return true;
  580. }
  581. return false;
  582. }
  583. };
  584. fetchParagraphProperty(fetcher);
  585. return fetcher.getValue() == null ? 0 : fetcher.getValue();
  586. }
  587. /**
  588. * Specifies the particular level text properties that this paragraph will follow.
  589. * The value for this attribute formats the text according to the corresponding level
  590. * paragraph properties defined in the SlideMaster.
  591. *
  592. * @param level the level (0 ... 4)
  593. */
  594. public void setLevel(int level){
  595. CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr();
  596. pr.setLvl(level);
  597. }
  598. /**
  599. *
  600. * @return the text level of this paragraph (0-based). Default is 0.
  601. */
  602. public int getLevel(){
  603. CTTextParagraphProperties pr = _p.getPPr();
  604. if(pr == null) return 0;
  605. return pr.getLvl();
  606. }
  607. /**
  608. * Returns whether this paragraph has bullets
  609. */
  610. public boolean isBullet() {
  611. ParagraphPropertyFetcher<Boolean> fetcher = new ParagraphPropertyFetcher<Boolean>(getLevel()){
  612. public boolean fetch(CTTextParagraphProperties props){
  613. if(props.isSetBuNone()) {
  614. setValue(false);
  615. return true;
  616. }
  617. if(props.isSetBuFont() || props.isSetBuChar()){
  618. setValue(true);
  619. return true;
  620. }
  621. return false;
  622. }
  623. };
  624. fetchParagraphProperty(fetcher);
  625. return fetcher.getValue() == null ? false : fetcher.getValue();
  626. }
  627. /**
  628. *
  629. * @param flag whether text in this paragraph has bullets
  630. */
  631. public void setBullet(boolean flag) {
  632. if(isBullet() == flag) return;
  633. CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr();
  634. if(!flag) {
  635. pr.addNewBuNone();
  636. } else {
  637. pr.addNewBuFont().setTypeface("Arial");
  638. pr.addNewBuChar().setChar("\u2022");
  639. }
  640. }
  641. /**
  642. * Specifies that automatic numbered bullet points should be applied to this paragraph
  643. *
  644. * @param scheme type of auto-numbering
  645. * @param startAt the number that will start number for a given sequence of automatically
  646. numbered bullets (1-based).
  647. */
  648. public void setBulletAutoNumber(ListAutoNumber scheme, int startAt) {
  649. if(startAt < 1) throw new IllegalArgumentException("Start Number must be greater or equal that 1") ;
  650. CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr();
  651. CTTextAutonumberBullet lst = pr.isSetBuAutoNum() ? pr.getBuAutoNum() : pr.addNewBuAutoNum();
  652. lst.setType(STTextAutonumberScheme.Enum.forInt(scheme.ordinal() + 1));
  653. lst.setStartAt(startAt);
  654. }
  655. @Override
  656. public String toString(){
  657. return "[" + getClass() + "]" + getText();
  658. }
  659. // List<TextFragment> getTextLines(){
  660. // return _lines;
  661. // }
  662. /**
  663. * Returns wrapping width to break lines in this paragraph
  664. *
  665. * @param firstLine whether the first line is breaking
  666. *
  667. * @return wrapping width in points
  668. */
  669. double getWrappingWidth(boolean firstLine, Graphics2D graphics){
  670. // internal margins for the text box
  671. double leftInset = _shape.getLeftInset();
  672. double rightInset = _shape.getRightInset();
  673. RenderableShape rShape = new RenderableShape(_shape);
  674. Rectangle2D anchor = rShape.getAnchor(graphics);
  675. double leftMargin = getLeftMargin();
  676. double indent = getIndent();
  677. double width;
  678. if(!_shape.getWordWrap()) {
  679. // if wordWrap == false then we return the advance to the right border of the sheet
  680. width = _shape.getSheet().getSlideShow().getPageSize().getWidth() - anchor.getX();
  681. } else {
  682. width = anchor.getWidth() - leftInset - rightInset - leftMargin;
  683. if(firstLine) {
  684. if(isBullet()){
  685. if(indent > 0) width -= indent;
  686. } else {
  687. if(indent > 0) width -= indent; // first line indentation
  688. else if (indent < 0) { // hanging indentation: the first line start at the left margin
  689. width += leftMargin;
  690. }
  691. }
  692. }
  693. }
  694. return width;
  695. }
  696. public double draw(Graphics2D graphics, double x, double y){
  697. double leftInset = _shape.getLeftInset();
  698. double rightInset = _shape.getRightInset();
  699. RenderableShape rShape = new RenderableShape(_shape);
  700. Rectangle2D anchor = rShape.getAnchor(graphics);
  701. float penY = (float) y;
  702. float leftMargin = (float) getLeftMargin();
  703. float indent = (float) getIndent();
  704. float penX = (float) (x + leftMargin + indent);
  705. graphics.canvas.save();
  706. graphics.canvas.translate(penX, penY);
  707. if (sl != null) {
  708. sl.draw(graphics.canvas);
  709. }
  710. graphics.canvas.restore();
  711. return sl.getHeight();
  712. // double leftMargin = getLeftMargin();
  713. // boolean firstLine = true;
  714. // double indent = getIndent();
  715. //
  716. // //The vertical line spacing
  717. // double spacing = getLineSpacing();
  718. // for(TextFragment line : _lines){
  719. // double penX = x + leftMargin;
  720. //
  721. // if(firstLine) {
  722. //// if(_bullet != null){
  723. //// if(indent < 0) {
  724. //// // a negative value means "Hanging" indentation and
  725. //// // indicates the position of the actual bullet character.
  726. //// // (the bullet is shifted to right relative to the text)
  727. //// _bullet.draw(graphics, penX + indent, penY);
  728. //// } else if(indent > 0){
  729. //// // a positive value means the "First Line" indentation:
  730. //// // the first line is indented and other lines start at the bullet ofset
  731. //// _bullet.draw(graphics, penX, penY);
  732. //// penX += indent;
  733. //// } else {
  734. //// // a zero indent means that the bullet and text have the same offset
  735. //// _bullet.draw(graphics, penX, penY);
  736. ////
  737. //// // don't let text overlay the bullet and advance by the bullet width
  738. //// penX += _bullet._layout.getAdvance() + 1;
  739. //// }
  740. //// } else {
  741. //// penX += indent;
  742. //// }
  743. // penX += indent;
  744. // // XXX: DD
  745. // }
  746. //
  747. //
  748. // switch (getTextAlign()) {
  749. // case CENTER:
  750. // penX += (anchor.getWidth() - leftMargin - line.getWidth() - leftInset - rightInset) / 2;
  751. // break;
  752. // case RIGHT:
  753. // penX += (anchor.getWidth() - line.getWidth() - leftInset - rightInset);
  754. // break;
  755. // default:
  756. // break;
  757. // }
  758. //
  759. // line.draw(graphics, penX, penY);
  760. //
  761. // if(spacing > 0) {
  762. // // If linespacing >= 0, then linespacing is a percentage of normal line height.
  763. // penY += spacing*0.01* line.getHeight();
  764. // } else {
  765. // // positive value means absolute spacing in points
  766. // penY += -spacing;
  767. // }
  768. //
  769. // firstLine = false;
  770. // }
  771. //
  772. // return penY - y;
  773. }
  774. SpannableStringBuilder getAttributedString(Graphics2D graphics){
  775. String text = getRenderableText();
  776. // AttributedString string = new AttributedString(text);
  777. // XSLFFontManager fontHandler = (XSLFFontManager)graphics.getRenderingHint(XSLFRenderingHint.FONT_HANDLER);
  778. // System.out.println("text: " + text);
  779. Log.d("textInset", "text: " + text);
  780. SpannableStringBuilder at = new SpannableStringBuilder(text);
  781. int startIndex = 0;
  782. for (XSLFTextRun run : _runs){
  783. int length = run.getRenderableText().length();
  784. if(length == 0) {
  785. // skip empty runs
  786. continue;
  787. }
  788. int endIndex = startIndex + length;
  789. // string.addAttribute(TextAttribute.FOREGROUND, run.getFontColor(), startIndex, endIndex);
  790. int style = Typeface.NORMAL;
  791. if (run.isBold() && run.isItalic()) {
  792. style = Typeface.BOLD_ITALIC;
  793. } else if (run.isItalic()) {
  794. style = Typeface.ITALIC;
  795. } else if (run.isBold()) {
  796. style = Typeface.BOLD;
  797. }
  798. ColorStateList color = ColorStateList.valueOf(run.getFontColor().getRGB());
  799. at.setSpan(new TextAppearanceSpan(run.getFontFamily(), style, (int) run.getFontSize(),
  800. color, color), startIndex, endIndex, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
  801. // user can pass an custom object to convert fonts
  802. // String fontFamily = run.getFontFamily();
  803. //// if(fontHandler != null) {
  804. //// fontFamily = fontHandler.getRendererableFont(fontFamily, run.getPitchAndFamily());
  805. //// }
  806. // string.addAttribute(TextAttribute.FAMILY, fontFamily, startIndex, endIndex);
  807. // float fontSz = (float)run.getFontSize();
  808. // string.addAttribute(TextAttribute.SIZE, fontSz , startIndex, endIndex);
  809. // if(run.isBold()) {
  810. // string.addAttribute(TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD, startIndex, endIndex);
  811. // }
  812. // if(run.isItalic()) {
  813. // string.addAttribute(TextAttribute.POSTURE, TextAttribute.POSTURE_OBLIQUE, startIndex, endIndex);
  814. // }
  815. if(run.isUnderline()) {
  816. at.setSpan(new UnderlineSpan(), startIndex, endIndex, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
  817. }
  818. if(run.isStrikethrough()) {
  819. at.setSpan(new StrikethroughSpan(), startIndex, endIndex, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
  820. }
  821. // if(run.isUnderline()) {
  822. // string.addAttribute(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON, startIndex, endIndex);
  823. // string.addAttribute(TextAttribute.INPUT_METHOD_UNDERLINE, TextAttribute.UNDERLINE_LOW_TWO_PIXEL, startIndex, endIndex);
  824. // }
  825. // if(run.isStrikethrough()) {
  826. // string.addAttribute(TextAttribute.STRIKETHROUGH, TextAttribute.STRIKETHROUGH_ON, startIndex, endIndex);
  827. // }
  828. if (run.isSuperscript()) {
  829. at.setSpan(new SuperscriptSpan(), startIndex, endIndex, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
  830. }
  831. if (run.isSubscript()) {
  832. at.setSpan(new SubscriptSpan(), startIndex, endIndex, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
  833. }
  834. // if(run.isSubscript()) {
  835. // string.addAttribute(TextAttribute.SUPERSCRIPT, TextAttribute.SUPERSCRIPT_SUB, startIndex, endIndex);
  836. // }
  837. // if(run.isSuperscript()) {
  838. // string.addAttribute(TextAttribute.SUPERSCRIPT, TextAttribute.SUPERSCRIPT_SUPER, startIndex, endIndex);
  839. // }
  840. startIndex = endIndex;
  841. }
  842. return at;
  843. }
  844. /**
  845. * ensure that the paragraph contains at least one character.
  846. * We need this trick to correctly measure text
  847. */
  848. private void ensureNotEmpty(){
  849. XSLFTextRun r = addNewTextRun();
  850. r.setText(" ");
  851. CTTextCharacterProperties endPr = _p.getEndParaRPr();
  852. if(endPr != null) {
  853. if(endPr.isSetSz()) r.setFontSize(endPr.getSz() / 100);
  854. }
  855. }
  856. /**
  857. * break text into lines
  858. *
  859. * @param graphics
  860. * @return array of text fragments,
  861. * each representing a line of text that fits in the wrapping width
  862. */
  863. void breakText(Graphics2D graphics){
  864. // _lines = new ArrayList<TextFragment>();
  865. // does this paragraph contain text?
  866. boolean emptyParagraph = _runs.size() == 0;
  867. // ensure that the paragraph contains at least one character
  868. if(_runs.size() == 0) ensureNotEmpty();
  869. String text = getRenderableText();
  870. if(text.length() == 0) return;
  871. SpannableStringBuilder at = getAttributedString(graphics);
  872. RenderableShape rShape = new RenderableShape(_shape);
  873. Rectangle2D anchor = rShape.getAnchor(graphics);
  874. Paint p = new TextPaint();
  875. p.setAntiAlias(true);
  876. p.setColor(android.graphics.Color.BLACK);
  877. sl = new StaticLayout(at, new TextPaint(p), (int) Math.ceil(anchor.getWidth()), Alignment.ALIGN_NORMAL, 1f, 0f, false);
  878. // AttributedString at = getAttributedString(graphics);
  879. // AttributedCharacterIterator it = at.getIterator();
  880. // LineBreakMeasurer measurer = new LineBreakMeasurer(it, graphics.getFontRenderContext()) ;
  881. // for (;;) {
  882. // int startIndex = measurer.getPosition();
  883. //
  884. // double wrappingWidth = getWrappingWidth(_lines.size() == 0, graphics) + 1; // add a pixel to compensate rounding errors
  885. // // shape width can be smaller that the sum of insets (this was proved by a test file)
  886. // if(wrappingWidth < 0) wrappingWidth = 1;
  887. //
  888. // int nextBreak = text.indexOf('\n', startIndex + 1);
  889. // if(nextBreak == -1) nextBreak = it.getEndIndex();
  890. //
  891. // TextLayout layout = measurer.nextLayout((float)wrappingWidth, nextBreak, true);
  892. // if (layout == null) {
  893. // // layout can be null if the entire word at the current position
  894. // // does not fit within the wrapping width. Try with requireNextWord=false.
  895. // layout = measurer.nextLayout((float)wrappingWidth, nextBreak, false);
  896. // }
  897. //
  898. // if(layout == null) {
  899. // // exit if can't break any more
  900. // break;
  901. // }
  902. //
  903. // int endIndex = measurer.getPosition();
  904. // // skip over new line breaks (we paint 'clear' text runs not starting or ending with \n)
  905. // if(endIndex < it.getEndIndex() && text.charAt(endIndex) == '\n'){
  906. // measurer.setPosition(endIndex + 1);
  907. // }
  908. //
  909. // TextAlign hAlign = getTextAlign();
  910. // if(hAlign == TextAlign.JUSTIFY || hAlign == TextAlign.JUSTIFY_LOW) {
  911. // layout = layout.getJustifiedLayout((float)wrappingWidth);
  912. // }
  913. //
  914. // AttributedString str = new AttributedString(it, startIndex, endIndex);
  915. // TextFragment line = new TextFragment(
  916. // layout, // we will not paint empty paragraphs
  917. // emptyParagraph ? null : str);
  918. // _lines.add(line);
  919. //
  920. // _maxLineHeight = Math.max(_maxLineHeight, line.getHeight());
  921. //
  922. // if(endIndex == it.getEndIndex()) break;
  923. //
  924. // }
  925. //
  926. // if(isBullet() && !emptyParagraph) {
  927. // String buCharacter = getBulletCharacter();
  928. // String buFont = getBulletFont();
  929. // if(buFont == null) buFont = getTextRuns().get(0).getFontFamily();
  930. // if(buCharacter != null && buFont != null && _lines.size() > 0) {
  931. // AttributedString str = new AttributedString(buCharacter);
  932. //
  933. // TextFragment firstLine = _lines.get(0);
  934. // AttributedCharacterIterator bit = firstLine._str.getIterator();
  935. //
  936. // Color buColor = getBulletFontColor();
  937. // str.addAttribute(TextAttribute.FOREGROUND, buColor == null ?
  938. // bit.getAttribute(TextAttribute.FOREGROUND) : buColor);
  939. // str.addAttribute(TextAttribute.FAMILY, buFont);
  940. //
  941. // float fontSize = (Float)bit.getAttribute(TextAttribute.SIZE);
  942. // float buSz = (float)getBulletFontSize();
  943. // if(buSz > 0) fontSize *= buSz* 0.01;
  944. // else fontSize = -buSz;
  945. //
  946. // str.addAttribute(TextAttribute.SIZE, fontSize);
  947. //
  948. // TextLayout layout = new TextLayout(str.getIterator(), graphics.getFontRenderContext());
  949. // _bullet = new TextFragment(layout, str);
  950. // }
  951. // }
  952. // return _lines;
  953. }
  954. CTTextParagraphProperties getDefaultMasterStyle(){
  955. CTPlaceholder ph = _shape.getCTPlaceholder();
  956. String defaultStyleSelector;
  957. if(ph == null) defaultStyleSelector = "otherStyle"; // no placeholder means plain text box
  958. else {
  959. switch(ph.getType().intValue()){
  960. case STPlaceholderType.INT_TITLE:
  961. case STPlaceholderType.INT_CTR_TITLE:
  962. defaultStyleSelector = "titleStyle";
  963. break;
  964. case STPlaceholderType.INT_FTR:
  965. case STPlaceholderType.INT_SLD_NUM:
  966. case STPlaceholderType.INT_DT:
  967. defaultStyleSelector = "otherStyle";
  968. break;
  969. default:
  970. defaultStyleSelector = "bodyStyle";
  971. break;
  972. }
  973. }
  974. int level = getLevel();
  975. // wind up and find the root master sheet which must be slide master
  976. XSLFSheet masterSheet = _shape.getSheet();
  977. while (masterSheet.getMasterSheet() != null){
  978. masterSheet = masterSheet.getMasterSheet();
  979. }
  980. XmlObject[] o = masterSheet.getXmlObject().selectPath(
  981. "declare namespace p='http://schemas.openxmlformats.org/presentationml/2006/main' " +
  982. "declare namespace a='http://schemas.openxmlformats.org/drawingml/2006/main' " +
  983. ".//p:txStyles/p:" + defaultStyleSelector +"/a:lvl" +(level+1)+ "pPr");
  984. if(o.length == 1){
  985. return (CTTextParagraphProperties)o[0];
  986. }
  987. throw new IllegalArgumentException("Failed to fetch default style for " +
  988. defaultStyleSelector + " and level=" + level);
  989. }
  990. private boolean fetchParagraphProperty(ParagraphPropertyFetcher visitor){
  991. boolean ok = false;
  992. if(_p.isSetPPr()) ok = visitor.fetch(_p.getPPr());
  993. if(!ok) {
  994. XSLFTextShape shape = getParentShape();
  995. ok = shape.fetchShapeProperty(visitor);
  996. if(!ok){
  997. CTPlaceholder ph = shape.getCTPlaceholder();
  998. if(ph == null){
  999. // if it is a plain text box then take defaults from presentation.xml
  1000. XMLSlideShow ppt = getParentShape().getSheet().getSlideShow();
  1001. CTTextParagraphProperties themeProps = ppt.getDefaultParagraphStyle(getLevel());
  1002. if(themeProps != null) ok = visitor.fetch(themeProps);
  1003. }
  1004. if(!ok){
  1005. // defaults for placeholders are defined in the slide master
  1006. CTTextParagraphProperties defaultProps = getDefaultMasterStyle();
  1007. if(defaultProps != null) ok = visitor.fetch(defaultProps);
  1008. }
  1009. }
  1010. }
  1011. return ok;
  1012. }
  1013. void copy(XSLFTextParagraph p){
  1014. TextAlign srcAlign = p.getTextAlign();
  1015. if(srcAlign != getTextAlign()){
  1016. setTextAlign(srcAlign);
  1017. }
  1018. boolean isBullet = p.isBullet();
  1019. if(isBullet != isBullet()){
  1020. setBullet(isBullet);
  1021. if(isBullet) {
  1022. String buFont = p.getBulletFont();
  1023. if(buFont != null && !buFont.equals(getBulletFont())){
  1024. setBulletFont(buFont);
  1025. }
  1026. String buChar = p.getBulletCharacter();
  1027. if(buChar != null && !buChar.equals(getBulletCharacter())){
  1028. setBulletCharacter(buChar);
  1029. }
  1030. Color buColor = p.getBulletFontColor();
  1031. if(buColor != null && !buColor.equals(getBulletFontColor())){
  1032. setBulletFontColor(buColor);
  1033. }
  1034. double buSize = p.getBulletFontSize();
  1035. if(buSize != getBulletFontSize()){
  1036. setBulletFontSize(buSize);
  1037. }
  1038. }
  1039. }
  1040. double leftMargin = p.getLeftMargin();
  1041. if(leftMargin != getLeftMargin()){
  1042. setLeftMargin(leftMargin);
  1043. }
  1044. double indent = p.getIndent();
  1045. if(indent != getIndent()){
  1046. setIndent(indent);
  1047. }
  1048. double spaceAfter = p.getSpaceAfter();
  1049. if(spaceAfter != getSpaceAfter()){
  1050. setSpaceAfter(spaceAfter);
  1051. }
  1052. double spaceBefore = p.getSpaceBefore();
  1053. if(spaceBefore != getSpaceBefore()){
  1054. setSpaceBefore(spaceBefore);
  1055. }
  1056. double lineSpacing = p.getLineSpacing();
  1057. if(lineSpacing != getLineSpacing()){
  1058. setLineSpacing(lineSpacing);
  1059. }
  1060. List<XSLFTextRun> srcR = p.getTextRuns();
  1061. List<XSLFTextRun> tgtR = getTextRuns();
  1062. for(int i = 0; i < srcR.size(); i++){
  1063. XSLFTextRun r1 = srcR.get(i);
  1064. XSLFTextRun r2 = tgtR.get(i);
  1065. r2.copy(r1);
  1066. }
  1067. }
  1068. }