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 39KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065
  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.awt.Color;
  17. import java.util.ArrayList;
  18. import java.util.Iterator;
  19. import java.util.List;
  20. import org.apache.poi.sl.draw.DrawPaint;
  21. import org.apache.poi.sl.usermodel.AutoNumberingScheme;
  22. import org.apache.poi.sl.usermodel.PaintStyle;
  23. import org.apache.poi.sl.usermodel.PaintStyle.SolidPaint;
  24. import org.apache.poi.sl.usermodel.TextParagraph;
  25. import org.apache.poi.util.Beta;
  26. import org.apache.poi.util.Internal;
  27. import org.apache.poi.util.Units;
  28. import org.apache.poi.xslf.model.ParagraphPropertyFetcher;
  29. import org.apache.xmlbeans.XmlCursor;
  30. import org.apache.xmlbeans.XmlObject;
  31. import org.openxmlformats.schemas.drawingml.x2006.main.CTColor;
  32. import org.openxmlformats.schemas.drawingml.x2006.main.CTRegularTextRun;
  33. import org.openxmlformats.schemas.drawingml.x2006.main.CTSRgbColor;
  34. import org.openxmlformats.schemas.drawingml.x2006.main.CTTextAutonumberBullet;
  35. import org.openxmlformats.schemas.drawingml.x2006.main.CTTextBulletSizePercent;
  36. import org.openxmlformats.schemas.drawingml.x2006.main.CTTextBulletSizePoint;
  37. import org.openxmlformats.schemas.drawingml.x2006.main.CTTextCharBullet;
  38. import org.openxmlformats.schemas.drawingml.x2006.main.CTTextCharacterProperties;
  39. import org.openxmlformats.schemas.drawingml.x2006.main.CTTextField;
  40. import org.openxmlformats.schemas.drawingml.x2006.main.CTTextFont;
  41. import org.openxmlformats.schemas.drawingml.x2006.main.CTTextLineBreak;
  42. import org.openxmlformats.schemas.drawingml.x2006.main.CTTextNormalAutofit;
  43. import org.openxmlformats.schemas.drawingml.x2006.main.CTTextParagraph;
  44. import org.openxmlformats.schemas.drawingml.x2006.main.CTTextParagraphProperties;
  45. import org.openxmlformats.schemas.drawingml.x2006.main.CTTextSpacing;
  46. import org.openxmlformats.schemas.drawingml.x2006.main.CTTextTabStop;
  47. import org.openxmlformats.schemas.drawingml.x2006.main.CTTextTabStopList;
  48. import org.openxmlformats.schemas.drawingml.x2006.main.STTextAlignType;
  49. import org.openxmlformats.schemas.drawingml.x2006.main.STTextAutonumberScheme;
  50. import org.openxmlformats.schemas.drawingml.x2006.main.STTextFontAlignType;
  51. import org.openxmlformats.schemas.presentationml.x2006.main.CTPlaceholder;
  52. import org.openxmlformats.schemas.presentationml.x2006.main.STPlaceholderType;
  53. /**
  54. * Represents a paragraph of text within the containing text body.
  55. * The paragraph is the highest level text separation mechanism.
  56. *
  57. * @author Yegor Kozlov
  58. * @since POI-3.8
  59. */
  60. @Beta
  61. public class XSLFTextParagraph implements TextParagraph<XSLFShape,XSLFTextParagraph,XSLFTextRun> {
  62. private final CTTextParagraph _p;
  63. private final List<XSLFTextRun> _runs;
  64. private final XSLFTextShape _shape;
  65. XSLFTextParagraph(CTTextParagraph p, XSLFTextShape shape){
  66. _p = p;
  67. _runs = new ArrayList<XSLFTextRun>();
  68. _shape = shape;
  69. for(XmlObject ch : _p.selectPath("*")){
  70. if(ch instanceof CTRegularTextRun){
  71. CTRegularTextRun r = (CTRegularTextRun)ch;
  72. _runs.add(new XSLFTextRun(r, this));
  73. } else if (ch instanceof CTTextLineBreak){
  74. CTTextLineBreak br = (CTTextLineBreak)ch;
  75. CTRegularTextRun r = CTRegularTextRun.Factory.newInstance();
  76. r.setRPr(br.getRPr());
  77. r.setT("\n");
  78. _runs.add(new XSLFTextRun(r, this));
  79. } else if (ch instanceof CTTextField){
  80. CTTextField f = (CTTextField)ch;
  81. CTRegularTextRun r = CTRegularTextRun.Factory.newInstance();
  82. r.setRPr(f.getRPr());
  83. r.setT(f.getT());
  84. _runs.add(new XSLFTextRun(r, this));
  85. }
  86. }
  87. }
  88. public String getText(){
  89. StringBuilder out = new StringBuilder();
  90. for (XSLFTextRun r : _runs) {
  91. out.append(r.getRawText());
  92. }
  93. return out.toString();
  94. }
  95. String getRenderableText(){
  96. StringBuilder out = new StringBuilder();
  97. for (XSLFTextRun r : _runs) {
  98. out.append(r.getRenderableText());
  99. }
  100. return out.toString();
  101. }
  102. @Internal
  103. public CTTextParagraph getXmlObject(){
  104. return _p;
  105. }
  106. public XSLFTextShape getParentShape() {
  107. return _shape;
  108. }
  109. @Override
  110. public List<XSLFTextRun> getTextRuns(){
  111. return _runs;
  112. }
  113. public Iterator<XSLFTextRun> iterator(){
  114. return _runs.iterator();
  115. }
  116. /**
  117. * Add a new run of text
  118. *
  119. * @return a new run of text
  120. */
  121. public XSLFTextRun addNewTextRun(){
  122. CTRegularTextRun r = _p.addNewR();
  123. CTTextCharacterProperties rPr = r.addNewRPr();
  124. rPr.setLang("en-US");
  125. XSLFTextRun run = new XSLFTextRun(r, this);
  126. _runs.add(run);
  127. return run;
  128. }
  129. /**
  130. * Insert a line break
  131. *
  132. * @return text run representing this line break ('\n')
  133. */
  134. public XSLFTextRun addLineBreak(){
  135. CTTextLineBreak br = _p.addNewBr();
  136. CTTextCharacterProperties brProps = br.addNewRPr();
  137. if(_runs.size() > 0){
  138. // by default line break has the font size of the last text run
  139. CTTextCharacterProperties prevRun = _runs.get(_runs.size() - 1).getRPr();
  140. brProps.set(prevRun);
  141. }
  142. CTRegularTextRun r = CTRegularTextRun.Factory.newInstance();
  143. r.setRPr(brProps);
  144. r.setT("\n");
  145. XSLFTextRun run = new XSLFLineBreak(r, this, brProps);
  146. _runs.add(run);
  147. return run;
  148. }
  149. @Override
  150. public TextAlign getTextAlign(){
  151. ParagraphPropertyFetcher<TextAlign> fetcher = new ParagraphPropertyFetcher<TextAlign>(getIndentLevel()){
  152. public boolean fetch(CTTextParagraphProperties props){
  153. if(props.isSetAlgn()){
  154. TextAlign val = TextAlign.values()[props.getAlgn().intValue() - 1];
  155. setValue(val);
  156. return true;
  157. }
  158. return false;
  159. }
  160. };
  161. fetchParagraphProperty(fetcher);
  162. return fetcher.getValue();
  163. }
  164. @Override
  165. public void setTextAlign(TextAlign align) {
  166. CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr();
  167. if(align == null) {
  168. if(pr.isSetAlgn()) pr.unsetAlgn();
  169. } else {
  170. pr.setAlgn(STTextAlignType.Enum.forInt(align.ordinal() + 1));
  171. }
  172. }
  173. @Override
  174. public FontAlign getFontAlign(){
  175. ParagraphPropertyFetcher<FontAlign> fetcher = new ParagraphPropertyFetcher<FontAlign>(getIndentLevel()){
  176. public boolean fetch(CTTextParagraphProperties props){
  177. if(props.isSetFontAlgn()){
  178. FontAlign val = FontAlign.values()[props.getFontAlgn().intValue() - 1];
  179. setValue(val);
  180. return true;
  181. }
  182. return false;
  183. }
  184. };
  185. fetchParagraphProperty(fetcher);
  186. return fetcher.getValue();
  187. }
  188. /**
  189. * Specifies the font alignment that is to be applied to the paragraph.
  190. * Possible values for this include auto, top, center, baseline and bottom.
  191. * see {@link org.apache.poi.sl.usermodel.TextParagraph.FontAlign}.
  192. *
  193. * @param align font align
  194. */
  195. public void setFontAlign(FontAlign align){
  196. CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr();
  197. if(align == null) {
  198. if(pr.isSetFontAlgn()) pr.unsetFontAlgn();
  199. } else {
  200. pr.setFontAlgn(STTextFontAlignType.Enum.forInt(align.ordinal() + 1));
  201. }
  202. }
  203. /**
  204. * @return the font to be used on bullet characters within a given paragraph
  205. */
  206. public String getBulletFont(){
  207. ParagraphPropertyFetcher<String> fetcher = new ParagraphPropertyFetcher<String>(getIndentLevel()){
  208. public boolean fetch(CTTextParagraphProperties props){
  209. if(props.isSetBuFont()){
  210. setValue(props.getBuFont().getTypeface());
  211. return true;
  212. }
  213. return false;
  214. }
  215. };
  216. fetchParagraphProperty(fetcher);
  217. return fetcher.getValue();
  218. }
  219. public void setBulletFont(String typeface){
  220. CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr();
  221. CTTextFont font = pr.isSetBuFont() ? pr.getBuFont() : pr.addNewBuFont();
  222. font.setTypeface(typeface);
  223. }
  224. /**
  225. * @return the character to be used in place of the standard bullet point
  226. */
  227. public String getBulletCharacter(){
  228. ParagraphPropertyFetcher<String> fetcher = new ParagraphPropertyFetcher<String>(getIndentLevel()){
  229. public boolean fetch(CTTextParagraphProperties props){
  230. if(props.isSetBuChar()){
  231. setValue(props.getBuChar().getChar());
  232. return true;
  233. }
  234. return false;
  235. }
  236. };
  237. fetchParagraphProperty(fetcher);
  238. return fetcher.getValue();
  239. }
  240. public void setBulletCharacter(String str){
  241. CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr();
  242. CTTextCharBullet c = pr.isSetBuChar() ? pr.getBuChar() : pr.addNewBuChar();
  243. c.setChar(str);
  244. }
  245. /**
  246. *
  247. * @return the color of bullet characters within a given paragraph.
  248. * A <code>null</code> value means to use the text font color.
  249. */
  250. public PaintStyle getBulletFontColor(){
  251. final XSLFTheme theme = getParentShape().getSheet().getTheme();
  252. ParagraphPropertyFetcher<Color> fetcher = new ParagraphPropertyFetcher<Color>(getIndentLevel()){
  253. public boolean fetch(CTTextParagraphProperties props){
  254. if(props.isSetBuClr()){
  255. XSLFColor c = new XSLFColor(props.getBuClr(), theme, null);
  256. setValue(c.getColor());
  257. return true;
  258. }
  259. return false;
  260. }
  261. };
  262. fetchParagraphProperty(fetcher);
  263. Color col = fetcher.getValue();
  264. return (col == null) ? null : DrawPaint.createSolidPaint(col);
  265. }
  266. public void setBulletFontColor(Color color) {
  267. setBulletFontColor(DrawPaint.createSolidPaint(color));
  268. }
  269. /**
  270. * Set the color to be used on bullet characters within a given paragraph.
  271. *
  272. * @param color the bullet color
  273. */
  274. public void setBulletFontColor(PaintStyle color) {
  275. if (!(color instanceof SolidPaint)) {
  276. throw new IllegalArgumentException("Currently XSLF only supports SolidPaint");
  277. }
  278. // TODO: implement setting bullet color to null
  279. SolidPaint sp = (SolidPaint)color;
  280. Color col = DrawPaint.applyColorTransform(sp.getSolidColor());
  281. CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr();
  282. CTColor c = pr.isSetBuClr() ? pr.getBuClr() : pr.addNewBuClr();
  283. CTSRgbColor clr = c.isSetSrgbClr() ? c.getSrgbClr() : c.addNewSrgbClr();
  284. clr.setVal(new byte[]{(byte) col.getRed(), (byte) col.getGreen(), (byte) col.getBlue()});
  285. }
  286. /**
  287. * Returns the bullet size that is to be used within a paragraph.
  288. * This may be specified in two different ways, percentage spacing and font point spacing:
  289. * <p>
  290. * If bulletSize >= 0, then bulletSize is a percentage of the font size.
  291. * If bulletSize < 0, then it specifies the size in points
  292. * </p>
  293. *
  294. * @return the bullet size
  295. */
  296. public Double getBulletFontSize(){
  297. ParagraphPropertyFetcher<Double> fetcher = new ParagraphPropertyFetcher<Double>(getIndentLevel()){
  298. public boolean fetch(CTTextParagraphProperties props){
  299. if(props.isSetBuSzPct()){
  300. setValue(props.getBuSzPct().getVal() * 0.001);
  301. return true;
  302. }
  303. if(props.isSetBuSzPts()){
  304. setValue( - props.getBuSzPts().getVal() * 0.01);
  305. return true;
  306. }
  307. return false;
  308. }
  309. };
  310. fetchParagraphProperty(fetcher);
  311. return fetcher.getValue();
  312. }
  313. /**
  314. * Sets the bullet size that is to be used within a paragraph.
  315. * This may be specified in two different ways, percentage spacing and font point spacing:
  316. * <p>
  317. * If bulletSize >= 0, then bulletSize is a percentage of the font size.
  318. * If bulletSize < 0, then it specifies the size in points
  319. * </p>
  320. */
  321. public void setBulletFontSize(double bulletSize){
  322. CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr();
  323. if(bulletSize >= 0) {
  324. CTTextBulletSizePercent pt = pr.isSetBuSzPct() ? pr.getBuSzPct() : pr.addNewBuSzPct();
  325. pt.setVal((int)(bulletSize*1000));
  326. if(pr.isSetBuSzPts()) pr.unsetBuSzPts();
  327. } else {
  328. CTTextBulletSizePoint pt = pr.isSetBuSzPts() ? pr.getBuSzPts() : pr.addNewBuSzPts();
  329. pt.setVal((int)(-bulletSize*100));
  330. if(pr.isSetBuSzPct()) pr.unsetBuSzPct();
  331. }
  332. }
  333. /**
  334. * @return the auto numbering scheme, or null if not defined
  335. */
  336. public AutoNumberingScheme getAutoNumberingScheme() {
  337. ParagraphPropertyFetcher<AutoNumberingScheme> fetcher = new ParagraphPropertyFetcher<AutoNumberingScheme>(getIndentLevel()) {
  338. public boolean fetch(CTTextParagraphProperties props) {
  339. if (props.isSetBuAutoNum()) {
  340. AutoNumberingScheme ans = AutoNumberingScheme.forOoxmlID(props.getBuAutoNum().getType().intValue());
  341. if (ans != null) {
  342. setValue(ans);
  343. return true;
  344. }
  345. }
  346. return false;
  347. }
  348. };
  349. fetchParagraphProperty(fetcher);
  350. return fetcher.getValue();
  351. }
  352. /**
  353. * @return the auto numbering starting number, or null if not defined
  354. */
  355. public Integer getAutoNumberingStartAt() {
  356. ParagraphPropertyFetcher<Integer> fetcher = new ParagraphPropertyFetcher<Integer>(getIndentLevel()) {
  357. public boolean fetch(CTTextParagraphProperties props) {
  358. if (props.isSetBuAutoNum()) {
  359. if (props.getBuAutoNum().isSetStartAt()) {
  360. setValue(props.getBuAutoNum().getStartAt());
  361. return true;
  362. }
  363. }
  364. return false;
  365. }
  366. };
  367. fetchParagraphProperty(fetcher);
  368. return fetcher.getValue();
  369. }
  370. @Override
  371. public void setIndent(Double indent){
  372. if ((indent == null) && !_p.isSetPPr()) return;
  373. CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr();
  374. if(indent == null) {
  375. if(pr.isSetIndent()) pr.unsetIndent();
  376. } else {
  377. pr.setIndent(Units.toEMU(indent));
  378. }
  379. }
  380. @Override
  381. public Double getIndent() {
  382. ParagraphPropertyFetcher<Double> fetcher = new ParagraphPropertyFetcher<Double>(getIndentLevel()){
  383. public boolean fetch(CTTextParagraphProperties props){
  384. if(props.isSetIndent()){
  385. setValue(Units.toPoints(props.getIndent()));
  386. return true;
  387. }
  388. return false;
  389. }
  390. };
  391. fetchParagraphProperty(fetcher);
  392. return fetcher.getValue();
  393. }
  394. @Override
  395. public void setLeftMargin(Double leftMargin){
  396. if (leftMargin == null && !_p.isSetPPr()) return;
  397. CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr();
  398. if (leftMargin == null) {
  399. if(pr.isSetMarL()) pr.unsetMarL();
  400. } else {
  401. pr.setMarL(Units.toEMU(leftMargin));
  402. }
  403. }
  404. /**
  405. * @return the left margin (in points) of the paragraph, null if unset
  406. */
  407. @Override
  408. public Double getLeftMargin(){
  409. ParagraphPropertyFetcher<Double> fetcher = new ParagraphPropertyFetcher<Double>(getIndentLevel()){
  410. public boolean fetch(CTTextParagraphProperties props){
  411. if(props.isSetMarL()){
  412. double val = Units.toPoints(props.getMarL());
  413. setValue(val);
  414. return true;
  415. }
  416. return false;
  417. }
  418. };
  419. fetchParagraphProperty(fetcher);
  420. // if the marL attribute is omitted, then a value of 347663 is implied
  421. return fetcher.getValue();
  422. }
  423. @Override
  424. public void setRightMargin(Double rightMargin){
  425. if (rightMargin == null && !_p.isSetPPr()) return;
  426. CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr();
  427. if(rightMargin == null) {
  428. if(pr.isSetMarR()) pr.unsetMarR();
  429. } else {
  430. pr.setMarR(Units.toEMU(rightMargin));
  431. }
  432. }
  433. /**
  434. *
  435. * @return the right margin of the paragraph, null if unset
  436. */
  437. @Override
  438. public Double getRightMargin(){
  439. ParagraphPropertyFetcher<Double> fetcher = new ParagraphPropertyFetcher<Double>(getIndentLevel()){
  440. public boolean fetch(CTTextParagraphProperties props){
  441. if(props.isSetMarR()){
  442. double val = Units.toPoints(props.getMarR());
  443. setValue(val);
  444. return true;
  445. }
  446. return false;
  447. }
  448. };
  449. fetchParagraphProperty(fetcher);
  450. return fetcher.getValue();
  451. }
  452. @Override
  453. public Double getDefaultTabSize(){
  454. ParagraphPropertyFetcher<Double> fetcher = new ParagraphPropertyFetcher<Double>(getIndentLevel()){
  455. public boolean fetch(CTTextParagraphProperties props){
  456. if(props.isSetDefTabSz()){
  457. double val = Units.toPoints(props.getDefTabSz());
  458. setValue(val);
  459. return true;
  460. }
  461. return false;
  462. }
  463. };
  464. fetchParagraphProperty(fetcher);
  465. return fetcher.getValue();
  466. }
  467. public double getTabStop(final int idx){
  468. ParagraphPropertyFetcher<Double> fetcher = new ParagraphPropertyFetcher<Double>(getIndentLevel()){
  469. public boolean fetch(CTTextParagraphProperties props){
  470. if(props.isSetTabLst()){
  471. CTTextTabStopList tabStops = props.getTabLst();
  472. if(idx < tabStops.sizeOfTabArray() ) {
  473. CTTextTabStop ts = tabStops.getTabArray(idx);
  474. double val = Units.toPoints(ts.getPos());
  475. setValue(val);
  476. return true;
  477. }
  478. }
  479. return false;
  480. }
  481. };
  482. fetchParagraphProperty(fetcher);
  483. return fetcher.getValue() == null ? 0. : fetcher.getValue();
  484. }
  485. public void addTabStop(double value){
  486. CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr();
  487. CTTextTabStopList tabStops = pr.isSetTabLst() ? pr.getTabLst() : pr.addNewTabLst();
  488. tabStops.addNewTab().setPos(Units.toEMU(value));
  489. }
  490. @Override
  491. public void setLineSpacing(Double lineSpacing){
  492. if (lineSpacing == null && !_p.isSetPPr()) return;
  493. CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr();
  494. if(lineSpacing == null) {
  495. if (pr.isSetLnSpc()) pr.unsetLnSpc();
  496. } else {
  497. CTTextSpacing spc = (pr.isSetLnSpc()) ? pr.getLnSpc() : pr.addNewLnSpc();
  498. if (lineSpacing >= 0) {
  499. (spc.isSetSpcPct() ? spc.getSpcPct() : spc.addNewSpcPct()).setVal((int)(lineSpacing*1000));
  500. if (spc.isSetSpcPts()) spc.unsetSpcPts();
  501. } else {
  502. (spc.isSetSpcPts() ? spc.getSpcPts() : spc.addNewSpcPts()).setVal((int)(-lineSpacing*100));
  503. if (spc.isSetSpcPct()) spc.unsetSpcPct();
  504. }
  505. }
  506. }
  507. @Override
  508. public Double getLineSpacing(){
  509. ParagraphPropertyFetcher<Double> fetcher = new ParagraphPropertyFetcher<Double>(getIndentLevel()){
  510. public boolean fetch(CTTextParagraphProperties props){
  511. if(props.isSetLnSpc()){
  512. CTTextSpacing spc = props.getLnSpc();
  513. if(spc.isSetSpcPct()) setValue( spc.getSpcPct().getVal()*0.001 );
  514. else if (spc.isSetSpcPts()) setValue( -spc.getSpcPts().getVal()*0.01 );
  515. return true;
  516. }
  517. return false;
  518. }
  519. };
  520. fetchParagraphProperty(fetcher);
  521. Double lnSpc = fetcher.getValue();
  522. if (lnSpc != null && lnSpc > 0) {
  523. // check if the percentage value is scaled
  524. CTTextNormalAutofit normAutofit = getParentShape().getTextBodyPr().getNormAutofit();
  525. if(normAutofit != null) {
  526. double scale = 1 - (double)normAutofit.getLnSpcReduction() / 100000;
  527. lnSpc *= scale;
  528. }
  529. }
  530. return lnSpc;
  531. }
  532. @Override
  533. public void setSpaceBefore(Double spaceBefore){
  534. if (spaceBefore == null && !_p.isSetPPr()) {
  535. return;
  536. }
  537. // unset the space before on null input
  538. if (spaceBefore == null) {
  539. if(_p.getPPr().isSetSpcBef()) {
  540. _p.getPPr().unsetSpcBef();
  541. }
  542. return;
  543. }
  544. CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr();
  545. CTTextSpacing spc = CTTextSpacing.Factory.newInstance();
  546. if(spaceBefore >= 0) {
  547. spc.addNewSpcPct().setVal((int)(spaceBefore*1000));
  548. } else {
  549. spc.addNewSpcPts().setVal((int)(-spaceBefore*100));
  550. }
  551. pr.setSpcBef(spc);
  552. }
  553. @Override
  554. public Double getSpaceBefore(){
  555. ParagraphPropertyFetcher<Double> fetcher = new ParagraphPropertyFetcher<Double>(getIndentLevel()){
  556. public boolean fetch(CTTextParagraphProperties props){
  557. if(props.isSetSpcBef()){
  558. CTTextSpacing spc = props.getSpcBef();
  559. if(spc.isSetSpcPct()) setValue( spc.getSpcPct().getVal()*0.001 );
  560. else if (spc.isSetSpcPts()) setValue( -spc.getSpcPts().getVal()*0.01 );
  561. return true;
  562. }
  563. return false;
  564. }
  565. };
  566. fetchParagraphProperty(fetcher);
  567. return fetcher.getValue();
  568. }
  569. @Override
  570. public void setSpaceAfter(Double spaceAfter){
  571. if (spaceAfter == null && !_p.isSetPPr()) {
  572. return;
  573. }
  574. // unset the space before on null input
  575. if (spaceAfter == null) {
  576. if(_p.getPPr().isSetSpcAft()) {
  577. _p.getPPr().unsetSpcAft();
  578. }
  579. return;
  580. }
  581. CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr();
  582. CTTextSpacing spc = CTTextSpacing.Factory.newInstance();
  583. if(spaceAfter >= 0) {
  584. spc.addNewSpcPct().setVal((int)(spaceAfter*1000));
  585. } else {
  586. spc.addNewSpcPts().setVal((int)(-spaceAfter*100));
  587. }
  588. pr.setSpcAft(spc);
  589. }
  590. @Override
  591. public Double getSpaceAfter(){
  592. ParagraphPropertyFetcher<Double> fetcher = new ParagraphPropertyFetcher<Double>(getIndentLevel()){
  593. public boolean fetch(CTTextParagraphProperties props){
  594. if(props.isSetSpcAft()){
  595. CTTextSpacing spc = props.getSpcAft();
  596. if(spc.isSetSpcPct()) setValue( spc.getSpcPct().getVal()*0.001 );
  597. else if (spc.isSetSpcPts()) setValue( -spc.getSpcPts().getVal()*0.01 );
  598. return true;
  599. }
  600. return false;
  601. }
  602. };
  603. fetchParagraphProperty(fetcher);
  604. return fetcher.getValue();
  605. }
  606. @Override
  607. public void setIndentLevel(int level){
  608. CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr();
  609. pr.setLvl(level);
  610. }
  611. @Override
  612. public int getIndentLevel() {
  613. CTTextParagraphProperties pr = _p.getPPr();
  614. return (pr == null || !pr.isSetLvl()) ? 0 : pr.getLvl();
  615. }
  616. /**
  617. * Returns whether this paragraph has bullets
  618. */
  619. public boolean isBullet() {
  620. ParagraphPropertyFetcher<Boolean> fetcher = new ParagraphPropertyFetcher<Boolean>(getIndentLevel()){
  621. public boolean fetch(CTTextParagraphProperties props){
  622. if(props.isSetBuNone()) {
  623. setValue(false);
  624. return true;
  625. }
  626. if(props.isSetBuFont() || props.isSetBuChar()){
  627. setValue(true);
  628. return true;
  629. }
  630. return false;
  631. }
  632. };
  633. fetchParagraphProperty(fetcher);
  634. return fetcher.getValue() == null ? false : fetcher.getValue();
  635. }
  636. /**
  637. *
  638. * @param flag whether text in this paragraph has bullets
  639. */
  640. public void setBullet(boolean flag) {
  641. if(isBullet() == flag) return;
  642. CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr();
  643. if(flag) {
  644. pr.addNewBuFont().setTypeface("Arial");
  645. pr.addNewBuChar().setChar("\u2022");
  646. } else {
  647. if (pr.isSetBuFont()) pr.unsetBuFont();
  648. if (pr.isSetBuChar()) pr.unsetBuChar();
  649. if (pr.isSetBuAutoNum()) pr.unsetBuAutoNum();
  650. if (pr.isSetBuBlip()) pr.unsetBuBlip();
  651. if (pr.isSetBuClr()) pr.unsetBuClr();
  652. if (pr.isSetBuClrTx()) pr.unsetBuClrTx();
  653. if (pr.isSetBuFont()) pr.unsetBuFont();
  654. if (pr.isSetBuFontTx()) pr.unsetBuFontTx();
  655. if (pr.isSetBuSzPct()) pr.unsetBuSzPct();
  656. if (pr.isSetBuSzPts()) pr.unsetBuSzPts();
  657. if (pr.isSetBuSzTx()) pr.unsetBuSzTx();
  658. pr.addNewBuNone();
  659. }
  660. }
  661. /**
  662. * Specifies that automatic numbered bullet points should be applied to this paragraph
  663. *
  664. * @param scheme type of auto-numbering
  665. * @param startAt the number that will start number for a given sequence of automatically
  666. numbered bullets (1-based).
  667. */
  668. public void setBulletAutoNumber(AutoNumberingScheme scheme, int startAt) {
  669. if(startAt < 1) throw new IllegalArgumentException("Start Number must be greater or equal that 1") ;
  670. CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr();
  671. CTTextAutonumberBullet lst = pr.isSetBuAutoNum() ? pr.getBuAutoNum() : pr.addNewBuAutoNum();
  672. lst.setType(STTextAutonumberScheme.Enum.forInt(scheme.ooxmlId));
  673. lst.setStartAt(startAt);
  674. }
  675. @Override
  676. public String toString(){
  677. return "[" + getClass() + "]" + getText();
  678. }
  679. /* package */ CTTextParagraphProperties getDefaultMasterStyle(){
  680. CTPlaceholder ph = _shape.getCTPlaceholder();
  681. String defaultStyleSelector;
  682. switch(ph == null ? -1 : ph.getType().intValue()) {
  683. case STPlaceholderType.INT_TITLE:
  684. case STPlaceholderType.INT_CTR_TITLE:
  685. defaultStyleSelector = "titleStyle";
  686. break;
  687. case -1: // no placeholder means plain text box
  688. case STPlaceholderType.INT_FTR:
  689. case STPlaceholderType.INT_SLD_NUM:
  690. case STPlaceholderType.INT_DT:
  691. defaultStyleSelector = "otherStyle";
  692. break;
  693. default:
  694. defaultStyleSelector = "bodyStyle";
  695. break;
  696. }
  697. int level = getIndentLevel();
  698. // wind up and find the root master sheet which must be slide master
  699. final String nsDecl =
  700. "declare namespace p='http://schemas.openxmlformats.org/presentationml/2006/main' " +
  701. "declare namespace a='http://schemas.openxmlformats.org/drawingml/2006/main' ";
  702. final String xpaths[] = {
  703. nsDecl+".//p:txStyles/p:" + defaultStyleSelector +"/a:lvl" +(level+1)+ "pPr",
  704. nsDecl+".//p:notesStyle/a:lvl" +(level+1)+ "pPr"
  705. };
  706. XSLFSheet masterSheet = _shape.getSheet();
  707. for (XSLFSheet m = masterSheet; m != null; m = (XSLFSheet)m.getMasterSheet()) {
  708. masterSheet = m;
  709. XmlObject xo = masterSheet.getXmlObject();
  710. for (String xpath : xpaths) {
  711. XmlObject[] o = xo.selectPath(xpath);
  712. if (o.length == 1) {
  713. return (CTTextParagraphProperties)o[0];
  714. }
  715. }
  716. }
  717. // for (CTTextBody txBody : (CTTextBody[])xo.selectPath(nsDecl+".//p:txBody")) {
  718. // CTTextParagraphProperties defaultPr = null, lastPr = null;
  719. // boolean hasLvl = false;
  720. // for (CTTextParagraph p : txBody.getPArray()) {
  721. // CTTextParagraphProperties pr = p.getPPr();
  722. // if (pr.isSetLvl()) {
  723. // hasLvl |= true;
  724. // lastPr = pr;
  725. // if (pr.getLvl() == level) return pr;
  726. // } else {
  727. // defaultPr = pr;
  728. // }
  729. // }
  730. // if (!hasLvl) continue;
  731. // if (level == 0 && defaultPr != null) return defaultPr;
  732. // if (lastPr != null) return lastPr;
  733. // break;
  734. // }
  735. //
  736. // String err = "Failed to fetch default style for " + defaultStyleSelector + " and level=" + level;
  737. // throw new IllegalArgumentException(err);
  738. return null;
  739. }
  740. private <T> boolean fetchParagraphProperty(ParagraphPropertyFetcher<T> visitor){
  741. boolean ok = false;
  742. XSLFTextShape shape = getParentShape();
  743. XSLFSheet sheet = shape.getSheet();
  744. if(_p.isSetPPr()) ok = visitor.fetch(_p.getPPr());
  745. if (ok) return true;
  746. ok = shape.fetchShapeProperty(visitor);
  747. if (ok) return true;
  748. CTPlaceholder ph = shape.getCTPlaceholder();
  749. if(ph == null){
  750. // if it is a plain text box then take defaults from presentation.xml
  751. @SuppressWarnings("resource")
  752. XMLSlideShow ppt = sheet.getSlideShow();
  753. CTTextParagraphProperties themeProps = ppt.getDefaultParagraphStyle(getIndentLevel());
  754. if (themeProps != null) ok = visitor.fetch(themeProps);
  755. }
  756. if (ok) return true;
  757. // defaults for placeholders are defined in the slide master
  758. CTTextParagraphProperties defaultProps = getDefaultMasterStyle();
  759. // TODO: determine master shape
  760. if(defaultProps != null) ok = visitor.fetch(defaultProps);
  761. if (ok) return true;
  762. return false;
  763. }
  764. void copy(XSLFTextParagraph other){
  765. if (other == this) return;
  766. CTTextParagraph thisP = getXmlObject();
  767. CTTextParagraph otherP = other.getXmlObject();
  768. if (thisP.isSetPPr()) thisP.unsetPPr();
  769. if (thisP.isSetEndParaRPr()) thisP.unsetEndParaRPr();
  770. _runs.clear();
  771. for (int i=thisP.sizeOfBrArray(); i>0; i--) {
  772. thisP.removeBr(i-1);
  773. }
  774. for (int i=thisP.sizeOfRArray(); i>0; i--) {
  775. thisP.removeR(i-1);
  776. }
  777. for (int i=thisP.sizeOfFldArray(); i>0; i--) {
  778. thisP.removeFld(i-1);
  779. }
  780. XmlCursor thisC = thisP.newCursor();
  781. thisC.toEndToken();
  782. XmlCursor otherC = otherP.newCursor();
  783. otherC.copyXmlContents(thisC);
  784. otherC.dispose();
  785. thisC.dispose();
  786. List<XSLFTextRun> otherRs = other.getTextRuns();
  787. int i=0;
  788. for(CTRegularTextRun rtr : thisP.getRArray()) {
  789. XSLFTextRun run = new XSLFTextRun(rtr, this);
  790. run.copy(otherRs.get(i++));
  791. _runs.add(run);
  792. }
  793. // set properties again, in case we are based on a different
  794. // template
  795. TextAlign srcAlign = other.getTextAlign();
  796. if(srcAlign != getTextAlign()){
  797. setTextAlign(srcAlign);
  798. }
  799. boolean isBullet = other.isBullet();
  800. if(isBullet != isBullet()){
  801. setBullet(isBullet);
  802. if(isBullet) {
  803. String buFont = other.getBulletFont();
  804. if(buFont != null && !buFont.equals(getBulletFont())){
  805. setBulletFont(buFont);
  806. }
  807. String buChar = other.getBulletCharacter();
  808. if(buChar != null && !buChar.equals(getBulletCharacter())){
  809. setBulletCharacter(buChar);
  810. }
  811. PaintStyle buColor = other.getBulletFontColor();
  812. if(buColor != null && !buColor.equals(getBulletFontColor())){
  813. setBulletFontColor(buColor);
  814. }
  815. Double buSize = other.getBulletFontSize();
  816. if(!doubleEquals(buSize, getBulletFontSize())){
  817. setBulletFontSize(buSize);
  818. }
  819. }
  820. }
  821. Double leftMargin = other.getLeftMargin();
  822. if (!doubleEquals(leftMargin, getLeftMargin())){
  823. setLeftMargin(leftMargin);
  824. }
  825. Double indent = other.getIndent();
  826. if (!doubleEquals(indent, getIndent())) {
  827. setIndent(indent);
  828. }
  829. Double spaceAfter = other.getSpaceAfter();
  830. if (!doubleEquals(spaceAfter, getSpaceAfter())) {
  831. setSpaceAfter(spaceAfter);
  832. }
  833. Double spaceBefore = other.getSpaceBefore();
  834. if (!doubleEquals(spaceBefore, getSpaceBefore())) {
  835. setSpaceBefore(spaceBefore);
  836. }
  837. Double lineSpacing = other.getLineSpacing();
  838. if (!doubleEquals(lineSpacing, getLineSpacing())) {
  839. setLineSpacing(lineSpacing);
  840. }
  841. }
  842. private static boolean doubleEquals(Double d1, Double d2) {
  843. return (d1 == d2 || (d1 != null && d1.equals(d2)));
  844. }
  845. @Override
  846. public Double getDefaultFontSize() {
  847. CTTextCharacterProperties endPr = _p.getEndParaRPr();
  848. return (endPr == null || !endPr.isSetSz()) ? 12 : (endPr.getSz() / 100.);
  849. }
  850. @Override
  851. public String getDefaultFontFamily() {
  852. return (_runs.isEmpty() ? "Arial" : _runs.get(0).getFontFamily());
  853. }
  854. @Override
  855. public BulletStyle getBulletStyle() {
  856. if (!isBullet()) return null;
  857. return new BulletStyle(){
  858. @Override
  859. public String getBulletCharacter() {
  860. return XSLFTextParagraph.this.getBulletCharacter();
  861. }
  862. @Override
  863. public String getBulletFont() {
  864. return XSLFTextParagraph.this.getBulletFont();
  865. }
  866. @Override
  867. public Double getBulletFontSize() {
  868. return XSLFTextParagraph.this.getBulletFontSize();
  869. }
  870. @Override
  871. public PaintStyle getBulletFontColor() {
  872. return XSLFTextParagraph.this.getBulletFontColor();
  873. }
  874. @Override
  875. public void setBulletFontColor(Color color) {
  876. setBulletFontColor(DrawPaint.createSolidPaint(color));
  877. }
  878. @Override
  879. public void setBulletFontColor(PaintStyle color) {
  880. XSLFTextParagraph.this.setBulletFontColor(color);
  881. }
  882. @Override
  883. public AutoNumberingScheme getAutoNumberingScheme() {
  884. return XSLFTextParagraph.this.getAutoNumberingScheme();
  885. }
  886. @Override
  887. public Integer getAutoNumberingStartAt() {
  888. return XSLFTextParagraph.this.getAutoNumberingStartAt();
  889. }
  890. };
  891. }
  892. @Override
  893. public void setBulletStyle(Object... styles) {
  894. if (styles.length == 0) {
  895. setBullet(false);
  896. } else {
  897. setBullet(true);
  898. for (Object ostyle : styles) {
  899. if (ostyle instanceof Number) {
  900. setBulletFontSize(((Number)ostyle).doubleValue());
  901. } else if (ostyle instanceof Color) {
  902. setBulletFontColor((Color)ostyle);
  903. } else if (ostyle instanceof Character) {
  904. setBulletCharacter(ostyle.toString());
  905. } else if (ostyle instanceof String) {
  906. setBulletFont((String)ostyle);
  907. } else if (ostyle instanceof AutoNumberingScheme) {
  908. setBulletAutoNumber((AutoNumberingScheme)ostyle, 0);
  909. }
  910. }
  911. }
  912. }
  913. /**
  914. * Helper method for appending text and keeping paragraph and character properties.
  915. * The character properties are moved to the end paragraph marker
  916. */
  917. /* package */ void clearButKeepProperties() {
  918. CTTextParagraph thisP = getXmlObject();
  919. for (int i=thisP.sizeOfBrArray(); i>0; i--) {
  920. thisP.removeBr(i-1);
  921. }
  922. for (int i=thisP.sizeOfFldArray(); i>0; i--) {
  923. thisP.removeFld(i-1);
  924. }
  925. if (!_runs.isEmpty()) {
  926. int size = _runs.size();
  927. thisP.setEndParaRPr(_runs.get(size-1).getRPr());
  928. for (int i=size; i>0; i--) {
  929. thisP.removeR(i-1);
  930. }
  931. _runs.clear();
  932. }
  933. }
  934. @Override
  935. public boolean isHeaderOrFooter() {
  936. CTPlaceholder ph = _shape.getCTPlaceholder();
  937. int phId = (ph == null ? -1 : ph.getType().intValue());
  938. switch (phId) {
  939. case STPlaceholderType.INT_SLD_NUM:
  940. case STPlaceholderType.INT_DT:
  941. case STPlaceholderType.INT_FTR:
  942. case STPlaceholderType.INT_HDR:
  943. return true;
  944. default:
  945. return false;
  946. }
  947. }
  948. }