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.

HSLFTextParagraph.java 44KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236
  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.hslf.usermodel;
  16. import static org.apache.poi.hslf.record.RecordTypes.OutlineTextRefAtom;
  17. import java.awt.Color;
  18. import java.io.IOException;
  19. import java.util.*;
  20. import org.apache.poi.hslf.model.PPFont;
  21. import org.apache.poi.hslf.model.textproperties.*;
  22. import org.apache.poi.hslf.model.textproperties.TextPropCollection.TextPropType;
  23. import org.apache.poi.hslf.record.*;
  24. import org.apache.poi.sl.usermodel.TextParagraph;
  25. import org.apache.poi.util.*;
  26. /**
  27. * This class represents a run of text in a powerpoint document. That
  28. * run could be text on a sheet, or text in a note.
  29. * It is only a very basic class for now
  30. *
  31. * @author Nick Burch
  32. */
  33. public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
  34. protected static POILogger logger = POILogFactory.getLogger(HSLFTextParagraph.class);
  35. /**
  36. * How to align the text
  37. */
  38. /* package */ static final int AlignLeft = 0;
  39. /* package */ static final int AlignCenter = 1;
  40. /* package */ static final int AlignRight = 2;
  41. /* package */ static final int AlignJustify = 3;
  42. // Note: These fields are protected to help with unit testing
  43. // Other classes shouldn't really go playing with them!
  44. private final TextHeaderAtom _headerAtom;
  45. private TextBytesAtom _byteAtom;
  46. private TextCharsAtom _charAtom;
  47. private final TextPropCollection _paragraphStyle = new TextPropCollection(1, TextPropType.paragraph);
  48. protected TextRulerAtom _ruler;
  49. protected List<HSLFTextRun> _runs = new ArrayList<HSLFTextRun>();
  50. protected HSLFTextShape _parentShape;
  51. private HSLFSheet _sheet;
  52. private int shapeId;
  53. // private StyleTextPropAtom styleTextPropAtom;
  54. private StyleTextProp9Atom styleTextProp9Atom;
  55. /**
  56. * Constructs a Text Run from a Unicode text block.
  57. * Either a {@link TextCharsAtom} or a {@link TextBytesAtom} needs to be provided.
  58. *
  59. * @param tha the TextHeaderAtom that defines what's what
  60. * @param tba the TextBytesAtom containing the text or null if {@link TextCharsAtom} is provided
  61. * @param tca the TextCharsAtom containing the text or null if {@link TextBytesAtom} is provided
  62. */
  63. /* package */ HSLFTextParagraph(
  64. TextHeaderAtom tha,
  65. TextBytesAtom tba,
  66. TextCharsAtom tca
  67. ) {
  68. if (tha == null) {
  69. throw new IllegalArgumentException("TextHeaderAtom must be set.");
  70. }
  71. _headerAtom = tha;
  72. _byteAtom = tba;
  73. _charAtom = tca;
  74. }
  75. /* package */ HSLFTextParagraph(HSLFTextParagraph other) {
  76. _headerAtom = other._headerAtom;
  77. _byteAtom = other._byteAtom;
  78. _charAtom = other._charAtom;
  79. _parentShape = other._parentShape;
  80. _sheet = other._sheet;
  81. _ruler = other._ruler;
  82. shapeId = other.shapeId;
  83. _paragraphStyle.copy(other._paragraphStyle);
  84. }
  85. public void addTextRun(HSLFTextRun run) {
  86. _runs.add(run);
  87. }
  88. /**
  89. * Fetch the rich text runs (runs of text with the same styling) that
  90. * are contained within this block of text
  91. */
  92. public List<HSLFTextRun> getTextRuns() {
  93. return _runs;
  94. }
  95. public TextPropCollection getParagraphStyle() {
  96. return _paragraphStyle;
  97. }
  98. public void setParagraphStyle(TextPropCollection paragraphStyle) {
  99. _paragraphStyle.copy(paragraphStyle);
  100. }
  101. /**
  102. * Supply the Sheet we belong to, which might have an assigned SlideShow
  103. * Also passes it on to our child RichTextRuns
  104. */
  105. public void supplySheet(HSLFSheet sheet){
  106. this._sheet = sheet;
  107. if (_runs == null) return;
  108. for(HSLFTextRun rt : _runs) {
  109. rt.updateSheet();
  110. }
  111. }
  112. public HSLFSheet getSheet(){
  113. return this._sheet;
  114. }
  115. /**
  116. * @return Shape ID
  117. */
  118. protected int getShapeId(){
  119. return shapeId;
  120. }
  121. /**
  122. * @param id Shape ID
  123. */
  124. protected void setShapeId(int id){
  125. shapeId = id;
  126. }
  127. /**
  128. * @return 0-based index of the text run in the SLWT container
  129. */
  130. protected int getIndex(){
  131. return (_headerAtom != null) ? _headerAtom.getIndex() : -1;
  132. }
  133. /**
  134. * Sets the index of the paragraph in the SLWT container
  135. *
  136. * @param index
  137. */
  138. protected void setIndex(int index) {
  139. if (_headerAtom != null) _headerAtom.setIndex(index);
  140. }
  141. /**
  142. * Returns the type of the text, from the TextHeaderAtom.
  143. * Possible values can be seen from TextHeaderAtom
  144. * @see org.apache.poi.hslf.record.TextHeaderAtom
  145. */
  146. public int getRunType() {
  147. return (_headerAtom != null) ? _headerAtom.getTextType() : -1;
  148. }
  149. /**
  150. * Is this Text Run one from a {@link PPDrawing}, or is it
  151. * one from the {@link SlideListWithText}?
  152. */
  153. public boolean isDrawingBased() {
  154. return (getIndex() == -1);
  155. }
  156. public TextRulerAtom getTextRuler(){
  157. return _ruler;
  158. }
  159. public TextRulerAtom createTextRuler(){
  160. _ruler = getTextRuler();
  161. if (_ruler == null) {
  162. _ruler = TextRulerAtom.getParagraphInstance();
  163. Record childAfter = _byteAtom;
  164. if (childAfter == null) childAfter = _charAtom;
  165. if (childAfter == null) childAfter = _headerAtom;
  166. _headerAtom.getParentRecord().addChildAfter(_ruler, childAfter);
  167. }
  168. return _ruler;
  169. }
  170. /**
  171. * Returns records that make up the list of text paragraphs
  172. * (there can be misc InteractiveInfo, TxInteractiveInfo and other records)
  173. *
  174. * @return text run records
  175. */
  176. public Record[] getRecords(){
  177. Record r[] = _headerAtom.getParentRecord().getChildRecords();
  178. return getRecords(r, new int[]{0}, _headerAtom);
  179. }
  180. private static Record[] getRecords(Record[] records, int[] startIdx, TextHeaderAtom headerAtom) {
  181. if (records == null) {
  182. throw new NullPointerException("records need to be set.");
  183. }
  184. for (; startIdx[0] < records.length; startIdx[0]++) {
  185. Record r = records[startIdx[0]];
  186. if (r instanceof TextHeaderAtom && (headerAtom == null || r == headerAtom)) break;
  187. }
  188. if (startIdx[0] >= records.length) {
  189. logger.log(POILogger.INFO, "header atom wasn't found - container might contain only an OutlineTextRefAtom");
  190. return new Record[0];
  191. }
  192. int length;
  193. for (length = 1; startIdx[0]+length < records.length; length++) {
  194. if (records[startIdx[0]+length] instanceof TextHeaderAtom) break;
  195. }
  196. Record result[] = new Record[length];
  197. System.arraycopy(records, startIdx[0], result, 0, length);
  198. startIdx[0] += length;
  199. return result;
  200. }
  201. /** Numbered List info */
  202. public void setStyleTextProp9Atom(final StyleTextProp9Atom styleTextProp9Atom) {
  203. this.styleTextProp9Atom = styleTextProp9Atom;
  204. }
  205. /** Numbered List info */
  206. public StyleTextProp9Atom getStyleTextProp9Atom() {
  207. return this.styleTextProp9Atom;
  208. }
  209. /**
  210. * Fetch the value of the given Paragraph related TextProp.
  211. * Returns -1 if that TextProp isn't present.
  212. * If the TextProp isn't present, the value from the appropriate
  213. * Master Sheet will apply.
  214. */
  215. private int getParaTextPropVal(String propName) {
  216. TextProp prop = _paragraphStyle.findByName(propName);
  217. BitMaskTextProp maskProp = (BitMaskTextProp)_paragraphStyle.findByName(ParagraphFlagsTextProp.NAME);
  218. boolean hardAttribute = (maskProp != null && maskProp.getValue() == 0);
  219. if (prop == null && !hardAttribute){
  220. HSLFSheet sheet = getSheet();
  221. int txtype = getRunType();
  222. HSLFMasterSheet master = sheet.getMasterSheet();
  223. if (master != null)
  224. prop = master.getStyleAttribute(txtype, getIndentLevel(), propName, false);
  225. }
  226. return prop == null ? -1 : prop.getValue();
  227. }
  228. /**
  229. * Sets the value of the given Character TextProp, add if required
  230. * @param propName The name of the Character TextProp
  231. * @param val The value to set for the TextProp
  232. */
  233. public void setParaTextPropVal(String propName, int val) {
  234. // Ensure we have the StyleTextProp atom we're going to need
  235. assert(_paragraphStyle!=null);
  236. TextProp tp = fetchOrAddTextProp(_paragraphStyle, propName);
  237. tp.setValue(val);
  238. }
  239. @Override
  240. public Iterator<HSLFTextRun> iterator() {
  241. return _runs.iterator();
  242. }
  243. @Override
  244. public double getLeftMargin() {
  245. int val = getParaTextPropVal("text.offset");
  246. return val*HSLFShape.POINT_DPI/((double)HSLFShape.MASTER_DPI);
  247. }
  248. @Override
  249. public void setLeftMargin(double leftMargin) {
  250. int val = (int)(leftMargin*HSLFShape.MASTER_DPI/HSLFShape.POINT_DPI);
  251. setParaTextPropVal("text.offset", val);
  252. }
  253. @Override
  254. public double getRightMargin() {
  255. // TODO: find out, how to determine this value
  256. return 0;
  257. }
  258. @Override
  259. public void setRightMargin(double rightMargin) {
  260. // TODO: find out, how to set this value
  261. }
  262. @Override
  263. public double getIndent() {
  264. int val = getParaTextPropVal("bullet.offset");
  265. return val*HSLFShape.POINT_DPI/((double)HSLFShape.MASTER_DPI);
  266. }
  267. @Override
  268. public void setIndent(double intent) {
  269. int val = (int)(intent*HSLFShape.MASTER_DPI/HSLFShape.POINT_DPI);
  270. setParaTextPropVal("bullet.offset", val);
  271. }
  272. @Override
  273. public String getDefaultFontFamily() {
  274. return (_runs.isEmpty() ? "Arial" : _runs.get(0).getFontFamily());
  275. }
  276. @Override
  277. public double getDefaultFontSize() {
  278. return (_runs.isEmpty() ? 12 : _runs.get(0).getFontSize());
  279. }
  280. /**
  281. * Sets the type of horizontal alignment for the paragraph.
  282. *
  283. * @param align - the type of alignment
  284. */
  285. public void setAlignment(org.apache.poi.sl.usermodel.TextParagraph.TextAlign align) {
  286. int alignInt;
  287. switch (align) {
  288. default:
  289. case LEFT: alignInt = TextAlignmentProp.LEFT; break;
  290. case CENTER: alignInt = TextAlignmentProp.CENTER; break;
  291. case RIGHT: alignInt = TextAlignmentProp.RIGHT; break;
  292. case DIST: alignInt = TextAlignmentProp.DISTRIBUTED; break;
  293. case JUSTIFY: alignInt = TextAlignmentProp.JUSTIFY; break;
  294. case JUSTIFY_LOW: alignInt = TextAlignmentProp.JUSTIFYLOW; break;
  295. case THAI_DIST: alignInt = TextAlignmentProp.THAIDISTRIBUTED; break;
  296. }
  297. setParaTextPropVal("alignment", alignInt);
  298. }
  299. @Override
  300. public org.apache.poi.sl.usermodel.TextParagraph.TextAlign getTextAlign() {
  301. switch (getParaTextPropVal("alignment")) {
  302. default:
  303. case TextAlignmentProp.LEFT: return TextAlign.LEFT;
  304. case TextAlignmentProp.CENTER: return TextAlign.CENTER;
  305. case TextAlignmentProp.RIGHT: return TextAlign.RIGHT;
  306. case TextAlignmentProp.JUSTIFY: return TextAlign.JUSTIFY;
  307. case TextAlignmentProp.JUSTIFYLOW: return TextAlign.JUSTIFY_LOW;
  308. case TextAlignmentProp.DISTRIBUTED: return TextAlign.DIST;
  309. case TextAlignmentProp.THAIDISTRIBUTED: return TextAlign.THAI_DIST;
  310. }
  311. }
  312. @Override
  313. public FontAlign getFontAlign() {
  314. switch(getParaTextPropVal("fontAlign")) {
  315. default:
  316. case -1: return FontAlign.AUTO;
  317. case FontAlignmentProp.BASELINE: return FontAlign.BASELINE;
  318. case FontAlignmentProp.TOP: return FontAlign.TOP;
  319. case FontAlignmentProp.CENTER: return FontAlign.CENTER;
  320. case FontAlignmentProp.BOTTOM: return FontAlign.BOTTOM;
  321. }
  322. }
  323. @Override
  324. public BulletStyle getBulletStyle() {
  325. if (getBulletChar() == 0) return null;
  326. return new BulletStyle() {
  327. public String getBulletCharacter() {
  328. char chr = HSLFTextParagraph.this.getBulletChar();
  329. return (chr == 0 ? "" : ""+chr);
  330. }
  331. public String getBulletFont() {
  332. int fontIdx = HSLFTextParagraph.this.getBulletFont();
  333. if (fontIdx == -1) return getDefaultFontFamily();
  334. PPFont ppFont = getSheet().getSlideShow().getFont(fontIdx);
  335. return ppFont.getFontName();
  336. }
  337. public double getBulletFontSize() {
  338. return HSLFTextParagraph.this.getBulletSize();
  339. }
  340. public Color getBulletFontColor() {
  341. return HSLFTextParagraph.this.getBulletColor();
  342. }
  343. };
  344. }
  345. @Override
  346. public HSLFTextShape getParentShape() {
  347. return _parentShape;
  348. }
  349. public void setParentShape(HSLFTextShape parentShape) {
  350. _parentShape = parentShape;
  351. }
  352. /**
  353. *
  354. * @return indentation level
  355. */
  356. public int getIndentLevel() {
  357. return _paragraphStyle == null ? 0 : _paragraphStyle.getIndentLevel();
  358. }
  359. /**
  360. * Sets indentation level
  361. *
  362. * @param level indentation level. Must be in the range [0, 4]
  363. */
  364. public void setIndentLevel(int level) {
  365. if( _paragraphStyle != null ) _paragraphStyle.setIndentLevel((short)level);
  366. }
  367. /**
  368. * Sets whether this rich text run has bullets
  369. */
  370. public void setBullet(boolean flag) {
  371. setFlag(ParagraphFlagsTextProp.BULLET_IDX, flag);
  372. }
  373. /**
  374. * Returns whether this rich text run has bullets
  375. */
  376. public boolean isBullet() {
  377. return getFlag(ParagraphFlagsTextProp.BULLET_IDX);
  378. }
  379. /**
  380. * Returns whether this rich text run has bullets
  381. */
  382. public boolean isBulletHard() {
  383. return getFlag(ParagraphFlagsTextProp.BULLET_IDX);
  384. }
  385. /**
  386. * Sets the bullet character
  387. */
  388. public void setBulletChar(char c) {
  389. setParaTextPropVal("bullet.char", c);
  390. }
  391. /**
  392. * Returns the bullet character
  393. */
  394. public char getBulletChar() {
  395. int val = getParaTextPropVal("bullet.char");
  396. return (char)(val == -1 ? 0 : val);
  397. }
  398. /**
  399. * Sets the bullet size
  400. */
  401. public void setBulletSize(int size) {
  402. setParaTextPropVal("bullet.size", size);
  403. }
  404. /**
  405. * Returns the bullet size
  406. */
  407. public int getBulletSize() {
  408. return getParaTextPropVal("bullet.size");
  409. }
  410. /**
  411. * Sets the bullet color
  412. */
  413. public void setBulletColor(Color color) {
  414. int rgb = new Color(color.getBlue(), color.getGreen(), color.getRed(), 254).getRGB();
  415. setParaTextPropVal("bullet.color", rgb);
  416. }
  417. /**
  418. * Returns the bullet color
  419. */
  420. public Color getBulletColor() {
  421. int rgb = getParaTextPropVal("bullet.color");
  422. if (rgb == -1) {
  423. // if bullet color is undefined, return color of first run
  424. if (_runs.isEmpty()) return null;
  425. return _runs.get(0).getFontColor();
  426. }
  427. int cidx = rgb >> 24;
  428. if (rgb % 0x1000000 == 0){
  429. if (_sheet == null) return null;
  430. ColorSchemeAtom ca = _sheet.getColorScheme();
  431. if(cidx >= 0 && cidx <= 7) rgb = ca.getColor(cidx);
  432. }
  433. Color tmp = new Color(rgb, true);
  434. return new Color(tmp.getBlue(), tmp.getGreen(), tmp.getRed());
  435. }
  436. /**
  437. * Sets the bullet font
  438. */
  439. public void setBulletFont(int idx) {
  440. setParaTextPropVal("bullet.font", idx);
  441. setFlag(ParagraphFlagsTextProp.BULLET_HARDFONT_IDX, true);
  442. }
  443. /**
  444. * Returns the bullet font
  445. */
  446. public int getBulletFont() {
  447. return getParaTextPropVal("bullet.font");
  448. }
  449. @Override
  450. public void setLineSpacing(double lineSpacing) {
  451. // if lineSpacing < 0, we need to convert points (common interface) to master units (hslf)
  452. if (lineSpacing < 0) {
  453. lineSpacing = (lineSpacing*HSLFShape.MASTER_DPI/HSLFShape.POINT_DPI);
  454. }
  455. setParaTextPropVal("linespacing", (int)lineSpacing);
  456. }
  457. @Override
  458. public double getLineSpacing() {
  459. double val = getParaTextPropVal("linespacing");
  460. // if lineSpacing < 0, we need to convert master units (hslf) to points (common interface)
  461. if (val == -1) return 0;
  462. if (val < -1) val *= HSLFShape.POINT_DPI/((double)HSLFShape.MASTER_DPI);
  463. return val;
  464. }
  465. /**
  466. * Sets spacing before a paragraph.
  467. * <p>
  468. * If spacebefore >= 0, then spacebefore is a percentage of normal line height.
  469. * If spacebefore < 0, the absolute value of spacebefore is the spacing in master coordinates.
  470. * </p>
  471. */
  472. public void setSpaceBefore(int val) {
  473. setParaTextPropVal("spacebefore", val);
  474. }
  475. /**
  476. * Returns spacing before a paragraph
  477. * <p>
  478. * If spacebefore >= 0, then spacebefore is a percentage of normal line height.
  479. * If spacebefore < 0, the absolute value of spacebefore is the spacing in master coordinates.
  480. * </p>
  481. *
  482. * @return the spacing before a paragraph
  483. */
  484. @Override
  485. public double getSpaceBefore() {
  486. int val = getParaTextPropVal("spacebefore");
  487. return val == -1 ? 0 : val;
  488. }
  489. /**
  490. * Sets spacing after a paragraph.
  491. * <p>
  492. * If spaceafter >= 0, then spaceafter is a percentage of normal line height.
  493. * If spaceafter < 0, the absolute value of spaceafter is the spacing in master coordinates.
  494. * </p>
  495. */
  496. public void setSpaceAfter(int val) {
  497. setParaTextPropVal("spaceafter", val);
  498. }
  499. /**
  500. * Returns spacing after a paragraph
  501. * <p>
  502. * If spaceafter >= 0, then spaceafter is a percentage of normal line height.
  503. * If spaceafter < 0, the absolute value of spaceafter is the spacing in master coordinates.
  504. * </p>
  505. *
  506. * @return the spacing before a paragraph
  507. */
  508. @Override
  509. public double getSpaceAfter() {
  510. int val = getParaTextPropVal("spaceafter");
  511. return val == -1 ? 0 : val;
  512. }
  513. /**
  514. * Returns the named TextProp, either by fetching it (if it exists) or adding it
  515. * (if it didn't)
  516. * @param textPropCol The TextPropCollection to fetch from / add into
  517. * @param textPropName The name of the TextProp to fetch/add
  518. */
  519. protected static TextProp fetchOrAddTextProp(TextPropCollection textPropCol, String textPropName) {
  520. // Fetch / Add the TextProp
  521. return textPropCol.addWithName(textPropName);
  522. }
  523. protected boolean getFlag(int index) {
  524. if (_paragraphStyle == null) return false;
  525. BitMaskTextProp prop = (BitMaskTextProp) _paragraphStyle.findByName(ParagraphFlagsTextProp.NAME);
  526. if (prop == null) {
  527. if (_sheet != null) {
  528. int txtype = getRunType();
  529. HSLFMasterSheet master = _sheet.getMasterSheet();
  530. if (master != null) {
  531. prop = (BitMaskTextProp) master.getStyleAttribute(txtype, getIndentLevel(), ParagraphFlagsTextProp.NAME, false);
  532. }
  533. } else {
  534. logger.log(POILogger.WARN, "MasterSheet is not available");
  535. }
  536. }
  537. return prop == null ? false : prop.getSubValue(index);
  538. }
  539. protected void setFlag(int index, boolean value) {
  540. // Ensure we have the StyleTextProp atom we're going to need
  541. assert(_paragraphStyle!=null);
  542. BitMaskTextProp prop = (BitMaskTextProp) fetchOrAddTextProp(_paragraphStyle, ParagraphFlagsTextProp.NAME);
  543. prop.setSubValue(value,index);
  544. }
  545. /**
  546. * Check and add linebreaks to text runs leading other paragraphs
  547. *
  548. * @param paragraphs
  549. */
  550. protected static void fixLineEndings(List<HSLFTextParagraph> paragraphs) {
  551. HSLFTextRun lastRun = null;
  552. for (HSLFTextParagraph p : paragraphs) {
  553. if (lastRun != null && !lastRun.getRawText().endsWith("\r")) {
  554. lastRun.setText(lastRun.getRawText()+"\r");
  555. }
  556. List<HSLFTextRun> ltr = p.getTextRuns();
  557. if (ltr.isEmpty()) {
  558. throw new RuntimeException("paragraph without textruns found");
  559. }
  560. lastRun = ltr.get(ltr.size()-1);
  561. assert(lastRun.getRawText() != null);
  562. }
  563. }
  564. /**
  565. * Search for a StyleTextPropAtom is for this text header (list of paragraphs)
  566. *
  567. * @param header the header
  568. * @param textLen the length of the rawtext, or -1 if the length is not known
  569. */
  570. private static StyleTextPropAtom findStyleAtomPresent(TextHeaderAtom header, int textLen) {
  571. boolean afterHeader = false;
  572. StyleTextPropAtom style = null;
  573. for (Record record : header.getParentRecord().getChildRecords()) {
  574. long rt = record.getRecordType();
  575. if (afterHeader && rt == RecordTypes.TextHeaderAtom.typeID) {
  576. // already on the next header, quit searching
  577. break;
  578. }
  579. afterHeader |= (header == record);
  580. if (afterHeader && rt == RecordTypes.StyleTextPropAtom.typeID) {
  581. // found it
  582. style = (StyleTextPropAtom)record;
  583. }
  584. }
  585. if (style == null) {
  586. logger.log(POILogger.INFO, "styles atom doesn't exist. Creating dummy record for later saving.");
  587. style = new StyleTextPropAtom((textLen < 0) ? 1 : textLen);
  588. } else {
  589. if (textLen >= 0) {
  590. style.setParentTextSize(textLen);
  591. }
  592. }
  593. return style;
  594. }
  595. /**
  596. * Saves the modified paragraphs/textrun to the records.
  597. * Also updates the styles to the correct text length.
  598. */
  599. protected static void storeText(List<HSLFTextParagraph> paragraphs) {
  600. fixLineEndings(paragraphs);
  601. String rawText = toInternalString(getRawText(paragraphs));
  602. // Will it fit in a 8 bit atom?
  603. boolean isUnicode = StringUtil.hasMultibyte(rawText);
  604. // isUnicode = true;
  605. TextHeaderAtom headerAtom = paragraphs.get(0)._headerAtom;
  606. TextBytesAtom byteAtom = paragraphs.get(0)._byteAtom;
  607. TextCharsAtom charAtom = paragraphs.get(0)._charAtom;
  608. StyleTextPropAtom styleAtom = findStyleAtomPresent(headerAtom, rawText.length());
  609. // Store in the appropriate record
  610. Record oldRecord = null, newRecord = null;
  611. if (isUnicode) {
  612. if (byteAtom != null || charAtom == null) {
  613. oldRecord = byteAtom;
  614. charAtom = new TextCharsAtom();
  615. }
  616. newRecord = charAtom;
  617. charAtom.setText(rawText);
  618. } else {
  619. if (charAtom != null || byteAtom == null) {
  620. oldRecord = charAtom;
  621. byteAtom = new TextBytesAtom();
  622. }
  623. newRecord = byteAtom;
  624. byte[] byteText = new byte[rawText.length()];
  625. StringUtil.putCompressedUnicode(rawText,byteText,0);
  626. byteAtom.setText(byteText);
  627. }
  628. assert(newRecord != null);
  629. RecordContainer _txtbox = headerAtom.getParentRecord();
  630. Record[] cr = _txtbox.getChildRecords();
  631. int headerIdx = -1, textIdx = -1, styleIdx = -1;
  632. for (int i=0; i<cr.length; i++) {
  633. Record r = cr[i];
  634. if (r == headerAtom) headerIdx = i;
  635. else if (r == oldRecord || r == newRecord) textIdx = i;
  636. else if (r == styleAtom) styleIdx = i;
  637. }
  638. if (textIdx == -1) {
  639. // the old record was never registered, ignore it
  640. _txtbox.addChildAfter(newRecord, headerAtom);
  641. textIdx = headerIdx+1;
  642. } else {
  643. // swap not appropriated records - noop if unchanged
  644. cr[textIdx] = newRecord;
  645. }
  646. if (styleIdx == -1) {
  647. // Add the new StyleTextPropAtom after the TextCharsAtom / TextBytesAtom
  648. _txtbox.addChildAfter(styleAtom, newRecord);
  649. }
  650. for (HSLFTextParagraph p : paragraphs) {
  651. if (newRecord == byteAtom) {
  652. p._byteAtom = byteAtom;
  653. p._charAtom = null;
  654. } else {
  655. p._byteAtom = null;
  656. p._charAtom = charAtom;
  657. }
  658. }
  659. // Update the text length for its Paragraph and Character stylings
  660. // * reset the length, to the new string's length
  661. // * add on +1 if the last block
  662. styleAtom.clearStyles();
  663. TextPropCollection lastPTPC = null, lastRTPC = null, ptpc = null, rtpc = null;
  664. for (HSLFTextParagraph para : paragraphs) {
  665. ptpc = para.getParagraphStyle();
  666. ptpc.updateTextSize(0);
  667. if (!ptpc.equals(lastPTPC)) {
  668. lastPTPC = styleAtom.addParagraphTextPropCollection(0);
  669. lastPTPC.copy(ptpc);
  670. }
  671. for (HSLFTextRun tr : para.getTextRuns()) {
  672. rtpc = tr.getCharacterStyle();
  673. rtpc.updateTextSize(0);
  674. if (!rtpc.equals(lastRTPC)) {
  675. lastRTPC = styleAtom.addCharacterTextPropCollection(0);
  676. lastRTPC.copy(rtpc);
  677. }
  678. int len = tr.getLength();
  679. ptpc.updateTextSize(ptpc.getCharactersCovered()+len);
  680. rtpc.updateTextSize(len);
  681. lastPTPC.updateTextSize(lastPTPC.getCharactersCovered()+len);
  682. lastRTPC.updateTextSize(lastRTPC.getCharactersCovered()+len);
  683. }
  684. }
  685. assert(lastPTPC != null && lastRTPC != null && ptpc != null && rtpc != null);
  686. ptpc.updateTextSize(ptpc.getCharactersCovered()+1);
  687. rtpc.updateTextSize(rtpc.getCharactersCovered()+1);
  688. lastPTPC.updateTextSize(lastPTPC.getCharactersCovered()+1);
  689. lastRTPC.updateTextSize(lastRTPC.getCharactersCovered()+1);
  690. /**
  691. * If TextSpecInfoAtom is present, we must update the text size in it,
  692. * otherwise the ppt will be corrupted
  693. */
  694. for (Record r : paragraphs.get(0).getRecords()) {
  695. if (r instanceof TextSpecInfoAtom) {
  696. ((TextSpecInfoAtom)r).setParentSize(rawText.length()+1);
  697. break;
  698. }
  699. }
  700. if (_txtbox instanceof EscherTextboxWrapper) {
  701. try {
  702. ((EscherTextboxWrapper)_txtbox).writeOut(null);
  703. } catch (IOException e) {
  704. throw new RuntimeException("failed dummy write", e);
  705. }
  706. }
  707. }
  708. /**
  709. * Adds the supplied text onto the end of the TextParagraphs,
  710. * creating a new RichTextRun for it to sit in.
  711. *
  712. * @param text the text string used by this object.
  713. */
  714. protected static HSLFTextRun appendText(List<HSLFTextParagraph> paragraphs, String text, boolean newParagraph) {
  715. text = toInternalString(text);
  716. // check paragraphs
  717. assert(!paragraphs.isEmpty() && !paragraphs.get(0).getTextRuns().isEmpty());
  718. HSLFTextParagraph htp = paragraphs.get(paragraphs.size()-1);
  719. HSLFTextRun htr = htp.getTextRuns().get(htp.getTextRuns().size()-1);
  720. boolean isFirst = !newParagraph;
  721. for (String rawText : text.split("(?<=\r)")) {
  722. if (!isFirst) {
  723. TextPropCollection tpc = htp.getParagraphStyle();
  724. HSLFTextParagraph prevHtp = htp;
  725. htp = new HSLFTextParagraph(htp._headerAtom, htp._byteAtom, htp._charAtom);
  726. htp.getParagraphStyle().copy(tpc);
  727. htp.setParentShape(prevHtp.getParentShape());
  728. htp.setShapeId(prevHtp.getShapeId());
  729. htp.supplySheet(prevHtp.getSheet());
  730. paragraphs.add(htp);
  731. }
  732. isFirst = false;
  733. TextPropCollection tpc = htr.getCharacterStyle();
  734. // special case, last text run is empty, we will reuse it
  735. if (htr.getLength() > 0) {
  736. htr = new HSLFTextRun(htp);
  737. htr.getCharacterStyle().copy(tpc);
  738. htp.addTextRun(htr);
  739. }
  740. htr.setText(rawText);
  741. }
  742. storeText(paragraphs);
  743. return htr;
  744. }
  745. /**
  746. * Sets (overwrites) the current text.
  747. * Uses the properties of the first paragraph / textrun
  748. *
  749. * @param text the text string used by this object.
  750. */
  751. public static HSLFTextRun setText(List<HSLFTextParagraph> paragraphs, String text) {
  752. // check paragraphs
  753. assert(!paragraphs.isEmpty() && !paragraphs.get(0).getTextRuns().isEmpty());
  754. Iterator<HSLFTextParagraph> paraIter = paragraphs.iterator();
  755. HSLFTextParagraph htp = paraIter.next(); // keep first
  756. assert(htp != null);
  757. while (paraIter.hasNext()) {
  758. paraIter.next();
  759. paraIter.remove();
  760. }
  761. Iterator<HSLFTextRun> runIter = htp.getTextRuns().iterator();
  762. HSLFTextRun htr = runIter.next();
  763. htr.setText("");
  764. assert(htr != null);
  765. while (runIter.hasNext()) {
  766. runIter.next();
  767. runIter.remove();
  768. }
  769. return appendText(paragraphs, text, false);
  770. }
  771. public static String getText(List<HSLFTextParagraph> paragraphs) {
  772. assert(!paragraphs.isEmpty());
  773. String rawText = getRawText(paragraphs);
  774. return toExternalString(rawText, paragraphs.get(0).getRunType());
  775. }
  776. public static String getRawText(List<HSLFTextParagraph> paragraphs) {
  777. StringBuilder sb = new StringBuilder();
  778. for (HSLFTextParagraph p : paragraphs) {
  779. for (HSLFTextRun r : p.getTextRuns()) {
  780. sb.append(r.getRawText());
  781. }
  782. }
  783. return sb.toString();
  784. }
  785. /**
  786. * Returns a new string with line breaks converted into internal ppt
  787. * representation
  788. */
  789. protected static String toInternalString(String s) {
  790. String ns = s.replaceAll("\\r?\\n", "\r");
  791. return ns;
  792. }
  793. /**
  794. * Converts raw text from the text paragraphs to a formatted string,
  795. * i.e. it converts certain control characters used in the raw txt
  796. *
  797. * @param rawText the raw text
  798. * @param runType the run type of the shape, paragraph or headerAtom.
  799. * use -1 if unknown
  800. * @return the formatted string
  801. */
  802. public static String toExternalString(String rawText, int runType) {
  803. // PowerPoint seems to store files with \r as the line break
  804. // The messes things up on everything but a Mac, so translate
  805. // them to \n
  806. String text = rawText.replace('\r', '\n');
  807. switch (runType) {
  808. // 0xB acts like cariage return in page titles and like blank in the
  809. // others
  810. case -1:
  811. case org.apache.poi.hslf.record.TextHeaderAtom.TITLE_TYPE:
  812. case org.apache.poi.hslf.record.TextHeaderAtom.CENTER_TITLE_TYPE:
  813. text = text.replace((char) 0x0B, '\n');
  814. break;
  815. default:
  816. text = text.replace((char) 0x0B, ' ');
  817. break;
  818. }
  819. return text;
  820. }
  821. /**
  822. * For a given PPDrawing, grab all the TextRuns
  823. */
  824. public static List<List<HSLFTextParagraph>> findTextParagraphs(PPDrawing ppdrawing, HSLFSheet sheet) {
  825. List<List<HSLFTextParagraph>> runsV = new ArrayList<List<HSLFTextParagraph>>();
  826. for (EscherTextboxWrapper wrapper : ppdrawing.getTextboxWrappers()) {
  827. runsV.add(findTextParagraphs(wrapper, sheet));
  828. }
  829. return runsV;
  830. }
  831. /**
  832. * Scans through the supplied record array, looking for
  833. * a TextHeaderAtom followed by one of a TextBytesAtom or
  834. * a TextCharsAtom. Builds up TextRuns from these
  835. *
  836. * @param wrapper an EscherTextboxWrapper
  837. */
  838. protected static List<HSLFTextParagraph> findTextParagraphs(EscherTextboxWrapper wrapper, HSLFSheet sheet) {
  839. // propagate parents to parent-aware records
  840. RecordContainer.handleParentAwareRecords(wrapper);
  841. int shapeId = wrapper.getShapeId();
  842. List<HSLFTextParagraph> rv = null;
  843. OutlineTextRefAtom ota = (OutlineTextRefAtom)wrapper.findFirstOfType(OutlineTextRefAtom.typeID);
  844. if (ota != null) {
  845. // if we are based on an outline, there are no further records to be parsed from the wrapper
  846. if (sheet == null) {
  847. throw new RuntimeException("Outline atom reference can't be solved without a sheet record");
  848. }
  849. List<List<HSLFTextParagraph>> sheetRuns = sheet.getTextParagraphs();
  850. assert(sheetRuns != null);
  851. int idx = ota.getTextIndex();
  852. for (List<HSLFTextParagraph> r : sheetRuns) {
  853. if (r.isEmpty()) continue;
  854. int ridx = r.get(0).getIndex();
  855. if (ridx > idx) break;
  856. if (ridx == idx) {
  857. if (rv == null) {
  858. rv = r;
  859. } else {
  860. // create a new container
  861. // TODO: ... is this case really happening?
  862. rv = new ArrayList<HSLFTextParagraph>(rv);
  863. rv.addAll(r);
  864. }
  865. }
  866. }
  867. if(rv == null || rv.isEmpty()) {
  868. logger.log(POILogger.WARN, "text run not found for OutlineTextRefAtom.TextIndex=" + idx);
  869. }
  870. } else {
  871. if (sheet != null) {
  872. // check sheet runs first, so we get exactly the same paragraph list
  873. List<List<HSLFTextParagraph>> sheetRuns = sheet.getTextParagraphs();
  874. assert(sheetRuns != null);
  875. for (List<HSLFTextParagraph> paras : sheetRuns) {
  876. if (!paras.isEmpty() && paras.get(0)._headerAtom.getParentRecord() == wrapper) {
  877. rv = paras;
  878. break;
  879. }
  880. }
  881. }
  882. if (rv == null) {
  883. // if we haven't found the wrapper in the sheet runs, create a new paragraph list from its record
  884. List<List<HSLFTextParagraph>> rvl = findTextParagraphs(wrapper.getChildRecords());
  885. switch (rvl.size()) {
  886. case 0: break; // nothing found
  887. case 1: rv = rvl.get(0); break; // normal case
  888. default:
  889. throw new RuntimeException("TextBox contains more than one list of paragraphs.");
  890. }
  891. }
  892. }
  893. if (rv != null) {
  894. StyleTextProp9Atom styleTextProp9Atom = wrapper.getStyleTextProp9Atom();
  895. for (HSLFTextParagraph htp : rv) {
  896. htp.setShapeId(shapeId);
  897. htp.setStyleTextProp9Atom(styleTextProp9Atom);
  898. }
  899. }
  900. return rv;
  901. }
  902. /**
  903. * Scans through the supplied record array, looking for
  904. * a TextHeaderAtom followed by one of a TextBytesAtom or
  905. * a TextCharsAtom. Builds up TextRuns from these
  906. *
  907. * @param records the records to build from
  908. */
  909. protected static List<List<HSLFTextParagraph>> findTextParagraphs(Record[] records) {
  910. List<List<HSLFTextParagraph>> paragraphCollection = new ArrayList<List<HSLFTextParagraph>>();
  911. int[] recordIdx = {0};
  912. for (int slwtIndex = 0; recordIdx[0] < records.length; slwtIndex++) {
  913. TextHeaderAtom header = null;
  914. TextBytesAtom tbytes = null;
  915. TextCharsAtom tchars = null;
  916. TextRulerAtom ruler = null;
  917. MasterTextPropAtom indents = null;
  918. for (Record r : getRecords(records, recordIdx, null)) {
  919. long rt = r.getRecordType();
  920. if (RecordTypes.TextHeaderAtom.typeID == rt) {
  921. header = (TextHeaderAtom)r;
  922. } else if (RecordTypes.TextBytesAtom.typeID == rt) {
  923. tbytes = (TextBytesAtom)r;
  924. } else if (RecordTypes.TextCharsAtom.typeID == rt) {
  925. tchars = (TextCharsAtom)r;
  926. } else if (RecordTypes.TextRulerAtom.typeID == rt) {
  927. ruler = (TextRulerAtom)r;
  928. } else if (RecordTypes.MasterTextPropAtom.typeID == rt) {
  929. indents = (MasterTextPropAtom)r;
  930. }
  931. // don't search for RecordTypes.StyleTextPropAtom.typeID here ... see findStyleAtomPresent below
  932. }
  933. if (header == null) break;
  934. if (header.getParentRecord() instanceof SlideListWithText) {
  935. // runs found in PPDrawing are not linked with SlideListWithTexts
  936. header.setIndex(slwtIndex);
  937. }
  938. if (tbytes == null && tchars == null) {
  939. tbytes = new TextBytesAtom();
  940. // don't add record yet - set it in storeText
  941. logger.log(POILogger.INFO, "bytes nor chars atom doesn't exist. Creating dummy record for later saving.");
  942. }
  943. String rawText = (tchars != null) ? tchars.getText() : tbytes.getText();
  944. StyleTextPropAtom styles = findStyleAtomPresent(header, rawText.length());
  945. List<HSLFTextParagraph> paragraphs = new ArrayList<HSLFTextParagraph>();
  946. paragraphCollection.add(paragraphs);
  947. // split, but keep delimiter
  948. for (String para : rawText.split("(?<=\r)")) {
  949. HSLFTextParagraph tpara = new HSLFTextParagraph(header, tbytes, tchars);
  950. paragraphs.add(tpara);
  951. tpara._ruler = ruler;
  952. tpara.getParagraphStyle().updateTextSize(para.length());
  953. HSLFTextRun trun = new HSLFTextRun(tpara);
  954. tpara.addTextRun(trun);
  955. trun.setText(para);
  956. }
  957. applyCharacterStyles(paragraphs, styles.getCharacterStyles());
  958. applyParagraphStyles(paragraphs, styles.getParagraphStyles());
  959. if (indents != null) {
  960. applyParagraphIndents(paragraphs, indents.getIndents());
  961. }
  962. }
  963. if (paragraphCollection.isEmpty()) {
  964. logger.log(POILogger.DEBUG, "No text records found.");
  965. }
  966. return paragraphCollection;
  967. }
  968. protected static void applyCharacterStyles(List<HSLFTextParagraph> paragraphs, List<TextPropCollection> charStyles) {
  969. int paraIdx = 0, runIdx = 0;
  970. HSLFTextRun trun;
  971. for (int csIdx=0; csIdx<charStyles.size(); csIdx++) {
  972. TextPropCollection p = charStyles.get(csIdx);
  973. for (int ccRun = 0, ccStyle = p.getCharactersCovered(); ccRun < ccStyle; ) {
  974. HSLFTextParagraph para = paragraphs.get(paraIdx);
  975. List<HSLFTextRun> runs = para.getTextRuns();
  976. trun = runs.get(runIdx);
  977. int len = trun.getLength();
  978. if (ccRun+len <= ccStyle) {
  979. ccRun += len;
  980. } else {
  981. String text = trun.getRawText();
  982. trun.setText(text.substring(0,ccStyle-ccRun));
  983. HSLFTextRun nextRun = new HSLFTextRun(para);
  984. nextRun.setText(text.substring(ccStyle-ccRun));
  985. runs.add(runIdx+1, nextRun);
  986. ccRun += ccStyle-ccRun;
  987. }
  988. TextPropCollection pCopy = new TextPropCollection(0, TextPropType.character);
  989. pCopy.copy(p);
  990. trun.setCharacterStyle(pCopy);
  991. len = trun.getLength();
  992. if (paraIdx == paragraphs.size()-1 && runIdx == runs.size()-1) {
  993. if (csIdx < charStyles.size()-1) {
  994. // special case, empty trailing text run
  995. HSLFTextRun nextRun = new HSLFTextRun(para);
  996. nextRun.setText("");
  997. runs.add(nextRun);
  998. } else {
  999. // need to add +1 to the last run of the last paragraph
  1000. len++;
  1001. ccRun++;
  1002. }
  1003. }
  1004. pCopy.updateTextSize(len);
  1005. // need to compare it again, in case a run has been added after
  1006. if (++runIdx == runs.size()) {
  1007. paraIdx++;
  1008. runIdx = 0;
  1009. }
  1010. }
  1011. }
  1012. }
  1013. protected static void applyParagraphStyles(List<HSLFTextParagraph> paragraphs, List<TextPropCollection> paraStyles) {
  1014. int paraIdx = 0;
  1015. for (TextPropCollection p : paraStyles) {
  1016. for (int ccPara = 0, ccStyle = p.getCharactersCovered(); ccPara < ccStyle; paraIdx++) {
  1017. if (paraIdx >= paragraphs.size() || ccPara >= ccStyle-1) return;
  1018. HSLFTextParagraph htp = paragraphs.get(paraIdx);
  1019. TextPropCollection pCopy = new TextPropCollection(0, TextPropType.paragraph);
  1020. pCopy.copy(p);
  1021. htp.setParagraphStyle(pCopy);
  1022. int len = 0;
  1023. for (HSLFTextRun trun : htp.getTextRuns()) {
  1024. len += trun.getLength();
  1025. }
  1026. if (paraIdx == paragraphs.size()-1) len++;
  1027. pCopy.updateTextSize(len);
  1028. ccPara += len;
  1029. }
  1030. }
  1031. }
  1032. protected static void applyParagraphIndents(List<HSLFTextParagraph> paragraphs, List<IndentProp> paraStyles) {
  1033. int paraIdx = 0;
  1034. for (IndentProp p : paraStyles) {
  1035. for (int ccPara = 0, ccStyle = p.getCharactersCovered(); ccPara < ccStyle; paraIdx++) {
  1036. HSLFTextParagraph para = paragraphs.get(paraIdx);
  1037. int len = 0;
  1038. for (HSLFTextRun trun : para.getTextRuns()) {
  1039. len += trun.getLength();
  1040. }
  1041. para.setIndentLevel(p.getIndentLevel());
  1042. ccPara += len+1;
  1043. }
  1044. }
  1045. }
  1046. protected static List<HSLFTextParagraph> createEmptyParagraph() {
  1047. EscherTextboxWrapper wrapper = new EscherTextboxWrapper();
  1048. TextHeaderAtom tha = new TextHeaderAtom();
  1049. tha.setParentRecord(wrapper);
  1050. wrapper.appendChildRecord(tha);
  1051. TextBytesAtom tba = new TextBytesAtom();
  1052. tba.setText("".getBytes());
  1053. wrapper.appendChildRecord(tba);
  1054. StyleTextPropAtom sta = new StyleTextPropAtom(1);
  1055. TextPropCollection paraStyle = sta.addParagraphTextPropCollection(1);
  1056. TextPropCollection charStyle = sta.addCharacterTextPropCollection(1);
  1057. wrapper.appendChildRecord(sta);
  1058. HSLFTextParagraph htp = new HSLFTextParagraph(tha, tba, null);
  1059. htp.setParagraphStyle(paraStyle);
  1060. HSLFTextRun htr = new HSLFTextRun(htp);
  1061. htr.setCharacterStyle(charStyle);
  1062. htr.setText("");
  1063. htp.addTextRun(htr);
  1064. return Arrays.asList(htp);
  1065. }
  1066. public EscherTextboxWrapper getTextboxWrapper() {
  1067. return (EscherTextboxWrapper)_headerAtom.getParentRecord();
  1068. }
  1069. }