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.

HSLFTextShape.java 26KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772
  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.*;
  17. import java.awt.Rectangle;
  18. import java.awt.font.FontRenderContext;
  19. import java.awt.geom.Rectangle2D;
  20. import java.io.IOException;
  21. import java.util.*;
  22. import org.apache.poi.ddf.*;
  23. import org.apache.poi.hslf.exceptions.HSLFException;
  24. import org.apache.poi.hslf.record.*;
  25. import org.apache.poi.sl.draw.DrawFactory;
  26. import org.apache.poi.sl.draw.DrawTextShape;
  27. import org.apache.poi.sl.usermodel.*;
  28. import org.apache.poi.util.POILogger;
  29. /**
  30. * A common superclass of all shapes that can hold text.
  31. *
  32. * @author Yegor Kozlov
  33. */
  34. public abstract class HSLFTextShape extends HSLFSimpleShape implements TextShape<HSLFTextParagraph> {
  35. /**
  36. * How to anchor the text
  37. */
  38. /* package */ static final int AnchorTop = 0;
  39. /* package */ static final int AnchorMiddle = 1;
  40. /* package */ static final int AnchorBottom = 2;
  41. /* package */ static final int AnchorTopCentered = 3;
  42. /* package */ static final int AnchorMiddleCentered = 4;
  43. /* package */ static final int AnchorBottomCentered = 5;
  44. /* package */ static final int AnchorTopBaseline = 6;
  45. /* package */ static final int AnchorBottomBaseline = 7;
  46. /* package */ static final int AnchorTopCenteredBaseline = 8;
  47. /* package */ static final int AnchorBottomCenteredBaseline = 9;
  48. /**
  49. * How to wrap the text
  50. */
  51. public static final int WrapSquare = 0;
  52. public static final int WrapByPoints = 1;
  53. public static final int WrapNone = 2;
  54. public static final int WrapTopBottom = 3;
  55. public static final int WrapThrough = 4;
  56. /**
  57. * TextRun object which holds actual text and format data
  58. */
  59. protected List<HSLFTextParagraph> _paragraphs = new ArrayList<HSLFTextParagraph>();
  60. /**
  61. * Escher container which holds text attributes such as
  62. * TextHeaderAtom, TextBytesAtom ot TextCharsAtom, StyleTextPropAtom etc.
  63. */
  64. protected EscherTextboxWrapper _txtbox;
  65. /**
  66. * This setting is used for supporting a deprecated alignment
  67. *
  68. * @see <a href=""></a>
  69. */
  70. boolean alignToBaseline = false;
  71. /**
  72. * Used to calculate text bounds
  73. */
  74. protected static final FontRenderContext _frc = new FontRenderContext(null, true, true);
  75. /**
  76. * Create a TextBox object and initialize it from the supplied Record container.
  77. *
  78. * @param escherRecord <code>EscherSpContainer</code> container which holds information about this shape
  79. * @param parent the parent of the shape
  80. */
  81. protected HSLFTextShape(EscherContainerRecord escherRecord, ShapeContainer<HSLFShape> parent){
  82. super(escherRecord, parent);
  83. }
  84. /**
  85. * Create a new TextBox. This constructor is used when a new shape is created.
  86. *
  87. * @param parent the parent of this Shape. For example, if this text box is a cell
  88. * in a table then the parent is Table.
  89. */
  90. public HSLFTextShape(ShapeContainer<HSLFShape> parent){
  91. super(null, parent);
  92. _escherContainer = createSpContainer(parent instanceof HSLFGroupShape);
  93. }
  94. /**
  95. * Create a new TextBox. This constructor is used when a new shape is created.
  96. *
  97. */
  98. public HSLFTextShape(){
  99. this(null);
  100. }
  101. /**
  102. * Set default properties for the TextRun.
  103. * Depending on the text and shape type the defaults are different:
  104. * TextBox: align=left, valign=top
  105. * AutoShape: align=center, valign=middle
  106. *
  107. */
  108. protected void setDefaultTextProperties(HSLFTextParagraph _txtrun){
  109. }
  110. /**
  111. * When a textbox is added to a sheet we need to tell upper-level
  112. * <code>PPDrawing</code> about it.
  113. *
  114. * @param sh the sheet we are adding to
  115. */
  116. protected void afterInsert(HSLFSheet sh){
  117. super.afterInsert(sh);
  118. storeText();
  119. EscherTextboxWrapper _txtbox = getEscherTextboxWrapper();
  120. if(_txtbox != null){
  121. _escherContainer.addChildRecord(_txtbox.getEscherRecord());
  122. PPDrawing ppdrawing = sh.getPPDrawing();
  123. ppdrawing.addTextboxWrapper(_txtbox);
  124. // Ensure the escher layer knows about the added records
  125. try {
  126. _txtbox.writeOut(null);
  127. } catch (IOException e){
  128. throw new HSLFException(e);
  129. }
  130. if(getAnchor().equals(new Rectangle()) && !"".equals(getText())) resizeToFitText();
  131. }
  132. for (HSLFTextParagraph htp : _paragraphs) {
  133. htp.setShapeId(getShapeId());
  134. }
  135. sh.onAddTextShape(this);
  136. }
  137. protected EscherTextboxWrapper getEscherTextboxWrapper(){
  138. if(_txtbox != null) return _txtbox;
  139. EscherTextboxRecord textRecord = getEscherChild(EscherTextboxRecord.RECORD_ID);
  140. if (textRecord == null) return null;
  141. HSLFSheet sheet = getSheet();
  142. if (sheet != null) {
  143. PPDrawing drawing = sheet.getPPDrawing();
  144. if (drawing != null) {
  145. EscherTextboxWrapper wrappers[] = drawing.getTextboxWrappers();
  146. if (wrappers != null) {
  147. for (EscherTextboxWrapper w : wrappers) {
  148. // check for object identity
  149. if (textRecord == w.getEscherRecord()) {
  150. _txtbox = w;
  151. return _txtbox;
  152. }
  153. }
  154. }
  155. }
  156. }
  157. _txtbox = new EscherTextboxWrapper(textRecord);
  158. return _txtbox;
  159. }
  160. /**
  161. * Adjust the size of the shape so it encompasses the text inside it.
  162. *
  163. * @return a <code>Rectangle2D</code> that is the bounds of this shape.
  164. */
  165. public Rectangle2D resizeToFitText(){
  166. Rectangle2D anchor = getAnchor();
  167. if(anchor.getWidth() == 0.) {
  168. logger.log(POILogger.WARN, "Width of shape wasn't set. Defaulting to 200px");
  169. anchor = new Rectangle2D.Double(anchor.getX(), anchor.getY(), 200, anchor.getHeight());
  170. setAnchor(anchor);
  171. }
  172. double height = getTextHeight();
  173. height += 1; // add a pixel to compensate rounding errors
  174. anchor.setRect(anchor.getX(), anchor.getY(), anchor.getWidth(), height);
  175. setAnchor(anchor);
  176. return anchor;
  177. }
  178. /**
  179. * Returns the type of the text, from the TextHeaderAtom.
  180. * Possible values can be seen from TextHeaderAtom
  181. * @see org.apache.poi.hslf.record.TextHeaderAtom
  182. */
  183. public int getRunType() {
  184. getEscherTextboxWrapper();
  185. if (_txtbox == null) return -1;
  186. TextHeaderAtom headerAtom = (TextHeaderAtom)_txtbox.findFirstOfType(TextHeaderAtom.typeID);
  187. assert(headerAtom != null);
  188. return headerAtom.getTextType();
  189. }
  190. /**
  191. * Changes the type of the text. Values should be taken
  192. * from TextHeaderAtom. No checking is done to ensure you
  193. * set this to a valid value!
  194. * @see org.apache.poi.hslf.record.TextHeaderAtom
  195. */
  196. public void setRunType(int type) {
  197. getEscherTextboxWrapper();
  198. if (_txtbox == null) return;
  199. TextHeaderAtom headerAtom = (TextHeaderAtom)_txtbox.findFirstOfType(TextHeaderAtom.typeID);
  200. assert(headerAtom != null);
  201. headerAtom.setTextType(type);
  202. }
  203. /**
  204. * Returns the type of vertical alignment for the text.
  205. * One of the <code>Anchor*</code> constants defined in this class.
  206. *
  207. * @return the type of alignment
  208. */
  209. /* package */ int getAlignment(){
  210. EscherOptRecord opt = getEscherOptRecord();
  211. EscherSimpleProperty prop = getEscherProperty(opt, EscherProperties.TEXT__ANCHORTEXT);
  212. int align = HSLFTextShape.AnchorTop;
  213. if (prop == null){
  214. /**
  215. * If vertical alignment was not found in the shape properties then try to
  216. * fetch the master shape and search for the align property there.
  217. */
  218. int type = getRunType();
  219. if(getSheet() != null && getSheet().getMasterSheet() != null){
  220. HSLFMasterSheet master = getSheet().getMasterSheet();
  221. HSLFTextShape masterShape = master.getPlaceholderByTextType(type);
  222. if(masterShape != null) align = masterShape.getAlignment();
  223. } else {
  224. //not found in the master sheet. Use the hardcoded defaults.
  225. switch (type){
  226. case org.apache.poi.hslf.record.TextHeaderAtom.TITLE_TYPE:
  227. case org.apache.poi.hslf.record.TextHeaderAtom.CENTER_TITLE_TYPE:
  228. align = HSLFTextShape.AnchorMiddle;
  229. break;
  230. default:
  231. align = HSLFTextShape.AnchorTop;
  232. break;
  233. }
  234. }
  235. } else {
  236. align = prop.getPropertyValue();
  237. }
  238. alignToBaseline = (align == AnchorBottomBaseline || align == AnchorBottomCenteredBaseline
  239. || align == AnchorTopBaseline || align == AnchorTopCenteredBaseline);
  240. return align;
  241. }
  242. /**
  243. * Sets the type of alignment for the text.
  244. * One of the <code>Anchor*</code> constants defined in this class.
  245. *
  246. * @param align - the type of alignment
  247. */
  248. /* package */ void setAlignment(Boolean isCentered, VerticalAlignment vAlign) {
  249. int align[];
  250. switch (vAlign) {
  251. case TOP:
  252. align = new int[]{AnchorTop, AnchorTopCentered, AnchorTopBaseline, AnchorTopCenteredBaseline};
  253. break;
  254. default:
  255. case MIDDLE:
  256. align = new int[]{AnchorMiddle, AnchorMiddleCentered, AnchorMiddle, AnchorMiddleCentered};
  257. break;
  258. case BOTTOM:
  259. align = new int[]{AnchorBottom, AnchorBottomCentered, AnchorBottomBaseline, AnchorBottomCenteredBaseline};
  260. break;
  261. }
  262. int align2 = align[(isCentered ? 1 : 0)+(alignToBaseline ? 2 : 0)];
  263. setEscherProperty(EscherProperties.TEXT__ANCHORTEXT, align2);
  264. }
  265. @Override
  266. public VerticalAlignment getVerticalAlignment() {
  267. int va = getAlignment();
  268. switch (va) {
  269. case AnchorTop:
  270. case AnchorTopCentered:
  271. case AnchorTopBaseline:
  272. case AnchorTopCenteredBaseline: return VerticalAlignment.TOP;
  273. case AnchorBottom:
  274. case AnchorBottomCentered:
  275. case AnchorBottomBaseline:
  276. case AnchorBottomCenteredBaseline: return VerticalAlignment.BOTTOM;
  277. default:
  278. case AnchorMiddle:
  279. case AnchorMiddleCentered: return VerticalAlignment.MIDDLE;
  280. }
  281. }
  282. /**
  283. * @return true, if vertical alignment is relative to baseline
  284. * this is only used for older versions less equals Office 2003
  285. */
  286. public boolean isAlignToBaseline() {
  287. getAlignment();
  288. return alignToBaseline;
  289. }
  290. /**
  291. * Sets the vertical alignment relative to the baseline
  292. *
  293. * @param alignToBaseline if true, vertical alignment is relative to baseline
  294. */
  295. public void setAlignToBaseline(boolean alignToBaseline) {
  296. this.alignToBaseline = alignToBaseline;
  297. setAlignment(isHorizontalCentered(), getVerticalAlignment());
  298. }
  299. @Override
  300. public boolean isHorizontalCentered() {
  301. int va = getAlignment();
  302. switch (va) {
  303. case AnchorTopCentered:
  304. case AnchorTopCenteredBaseline:
  305. case AnchorBottomCentered:
  306. case AnchorBottomCenteredBaseline:
  307. case AnchorMiddleCentered:
  308. return true;
  309. default:
  310. return false;
  311. }
  312. }
  313. public void setVerticalAlignment(VerticalAlignment vAlign) {
  314. setAlignment(isHorizontalCentered(), vAlign);
  315. }
  316. /**
  317. * Sets if the paragraphs are horizontal centered
  318. *
  319. * @param isCentered true, if the paragraphs are horizontal centered
  320. * A {@code null} values unsets this property.
  321. *
  322. * @see TextShape#isHorizontalCentered()
  323. */
  324. public void setHorizontalCentered(Boolean isCentered){
  325. setAlignment(isCentered, getVerticalAlignment());
  326. }
  327. /**
  328. * Returns the distance (in points) between the bottom of the text frame
  329. * and the bottom of the inscribed rectangle of the shape that contains the text.
  330. * Default value is 1/20 inch.
  331. *
  332. * @return the botom margin
  333. */
  334. public double getBottomInset(){
  335. EscherOptRecord opt = getEscherOptRecord();
  336. EscherSimpleProperty prop = getEscherProperty(opt, EscherProperties.TEXT__TEXTBOTTOM);
  337. int val = prop == null ? EMU_PER_INCH/20 : prop.getPropertyValue();
  338. return val/EMU_PER_POINT;
  339. }
  340. /**
  341. * Sets the botom margin.
  342. * @see #getBottomInset()
  343. *
  344. * @param margin the bottom margin
  345. */
  346. public void setBottomInset(double margin){
  347. setEscherProperty(EscherProperties.TEXT__TEXTBOTTOM, (int)(margin*EMU_PER_POINT));
  348. }
  349. /**
  350. * Returns the distance (in points) between the left edge of the text frame
  351. * and the left edge of the inscribed rectangle of the shape that contains
  352. * the text.
  353. * Default value is 1/10 inch.
  354. *
  355. * @return the left margin
  356. */
  357. public double getLeftInset(){
  358. EscherOptRecord opt = getEscherOptRecord();
  359. EscherSimpleProperty prop = getEscherProperty(opt, EscherProperties.TEXT__TEXTLEFT);
  360. int val = prop == null ? EMU_PER_INCH/10 : prop.getPropertyValue();
  361. return val/EMU_PER_POINT;
  362. }
  363. /**
  364. * Sets the left margin.
  365. * @see #getLeftInset()
  366. *
  367. * @param margin the left margin
  368. */
  369. public void setLeftInset(double margin){
  370. setEscherProperty(EscherProperties.TEXT__TEXTLEFT, (int)(margin*EMU_PER_POINT));
  371. }
  372. /**
  373. * Returns the distance (in points) between the right edge of the
  374. * text frame and the right edge of the inscribed rectangle of the shape
  375. * that contains the text.
  376. * Default value is 1/10 inch.
  377. *
  378. * @return the right margin
  379. */
  380. public double getRightInset(){
  381. EscherOptRecord opt = getEscherOptRecord();
  382. EscherSimpleProperty prop = getEscherProperty(opt, EscherProperties.TEXT__TEXTRIGHT);
  383. int val = prop == null ? EMU_PER_INCH/10 : prop.getPropertyValue();
  384. return val/EMU_PER_POINT;
  385. }
  386. /**
  387. * Sets the right margin.
  388. * @see #getRightInset()
  389. *
  390. * @param margin the right margin
  391. */
  392. public void setRightInset(double margin){
  393. setEscherProperty(EscherProperties.TEXT__TEXTRIGHT, (int)(margin*EMU_PER_POINT));
  394. }
  395. /**
  396. * Returns the distance (in points) between the top of the text frame
  397. * and the top of the inscribed rectangle of the shape that contains the text.
  398. * Default value is 1/20 inch.
  399. *
  400. * @return the top margin
  401. */
  402. public double getTopInset(){
  403. EscherOptRecord opt = getEscherOptRecord();
  404. EscherSimpleProperty prop = getEscherProperty(opt, EscherProperties.TEXT__TEXTTOP);
  405. int val = prop == null ? EMU_PER_INCH/20 : prop.getPropertyValue();
  406. return val/EMU_PER_POINT;
  407. }
  408. /**
  409. * Sets the top margin.
  410. * @see #getTopInset()
  411. *
  412. * @param margin the top margin
  413. */
  414. public void setTopInset(double margin){
  415. setEscherProperty(EscherProperties.TEXT__TEXTTOP, (int)(margin*EMU_PER_POINT));
  416. }
  417. @Override
  418. public boolean getWordWrap(){
  419. int ww = getWordWrapEx();
  420. return (ww != WrapNone);
  421. }
  422. /**
  423. * Returns the value indicating word wrap.
  424. *
  425. * @return the value indicating word wrap.
  426. * Must be one of the <code>Wrap*</code> constants defined in this class.
  427. *
  428. * @see <a href="https://msdn.microsoft.com/en-us/library/dd948168(v=office.12).aspx">MSOWRAPMODE</a>
  429. */
  430. public int getWordWrapEx() {
  431. EscherOptRecord opt = getEscherOptRecord();
  432. EscherSimpleProperty prop = getEscherProperty(opt, EscherProperties.TEXT__WRAPTEXT);
  433. return prop == null ? WrapSquare : prop.getPropertyValue();
  434. }
  435. /**
  436. * Specifies how the text should be wrapped
  437. *
  438. * @param wrap the value indicating how the text should be wrapped.
  439. * Must be one of the <code>Wrap*</code> constants defined in this class.
  440. */
  441. public void setWordWrap(int wrap){
  442. setEscherProperty(EscherProperties.TEXT__WRAPTEXT, wrap);
  443. }
  444. /**
  445. * @return id for the text.
  446. */
  447. public int getTextId(){
  448. EscherOptRecord opt = getEscherOptRecord();
  449. EscherSimpleProperty prop = getEscherProperty(opt, EscherProperties.TEXT__TEXTID);
  450. return prop == null ? 0 : prop.getPropertyValue();
  451. }
  452. /**
  453. * Sets text ID
  454. *
  455. * @param id of the text
  456. */
  457. public void setTextId(int id){
  458. setEscherProperty(EscherProperties.TEXT__TEXTID, id);
  459. }
  460. /**
  461. * @return the TextRun object for this text box
  462. */
  463. public List<HSLFTextParagraph> getTextParagraphs(){
  464. if (!_paragraphs.isEmpty()) return _paragraphs;
  465. _txtbox = getEscherTextboxWrapper();
  466. if (_txtbox == null) {
  467. _paragraphs.addAll(HSLFTextParagraph.createEmptyParagraph());
  468. _txtbox = _paragraphs.get(0).getTextboxWrapper();
  469. } else {
  470. _paragraphs = HSLFTextParagraph.findTextParagraphs(_txtbox, getSheet());
  471. if (_paragraphs == null || _paragraphs.isEmpty()) {
  472. throw new RuntimeException("TextRecord didn't contained any text lines");
  473. }
  474. // initParagraphsFromSheetRecords();
  475. // if (_paragraphs.isEmpty()) {
  476. // List<List<HSLFTextParagraph>> llhtp = HSLFTextParagraph.findTextParagraphs(_txtbox);
  477. // if (!llhtp.isEmpty()) {
  478. // _paragraphs.addAll(llhtp.get(0));
  479. // }
  480. // }
  481. }
  482. for (HSLFTextParagraph p : _paragraphs) {
  483. p.setParentShape(this);
  484. }
  485. return _paragraphs;
  486. }
  487. public void setSheet(HSLFSheet sheet) {
  488. _sheet = sheet;
  489. // Initialize _txtrun object.
  490. // (We can't do it in the constructor because the sheet
  491. // is not assigned then, it's only built once we have
  492. // all the records)
  493. for (HSLFTextParagraph htp : getTextParagraphs()) {
  494. // Supply the sheet to our child RichTextRuns
  495. htp.supplySheet(_sheet);
  496. }
  497. }
  498. // protected void initParagraphsFromSheetRecords(){
  499. // EscherTextboxWrapper txtbox = getEscherTextboxWrapper();
  500. // HSLFSheet sheet = getSheet();
  501. //
  502. // if (sheet == null || txtbox == null) return;
  503. // List<List<HSLFTextParagraph>> sheetRuns = _sheet.getTextParagraphs();
  504. // if (sheetRuns == null) return;
  505. //
  506. // _paragraphs.clear();
  507. // OutlineTextRefAtom ota = (OutlineTextRefAtom)txtbox.findFirstOfType(OutlineTextRefAtom.typeID);
  508. //
  509. // if (ota != null) {
  510. // int idx = ota.getTextIndex();
  511. // for (List<HSLFTextParagraph> r : sheetRuns) {
  512. // if (r.isEmpty()) continue;
  513. // int ridx = r.get(0).getIndex();
  514. // if (ridx > idx) break;
  515. // if (ridx == idx) _paragraphs.addAll(r);
  516. // }
  517. // if(_paragraphs.isEmpty()) {
  518. // logger.log(POILogger.WARN, "text run not found for OutlineTextRefAtom.TextIndex=" + idx);
  519. // }
  520. // } else {
  521. // int shapeId = getShapeId();
  522. // for (List<HSLFTextParagraph> r : sheetRuns) {
  523. // if (r.isEmpty()) continue;
  524. // if (r.get(0).getShapeId() == shapeId) _paragraphs.addAll(r);
  525. // }
  526. // }
  527. //
  528. // // ensure the same references child records of TextRun - see #48916
  529. //// if(_txtrun != null) {
  530. //// for (int i = 0; i < child.length; i++) {
  531. //// for (Record r : _txtrun.getRecords()) {
  532. //// if (child[i].getRecordType() == r.getRecordType()) {
  533. //// child[i] = r;
  534. //// }
  535. //// }
  536. //// }
  537. //// }
  538. // }
  539. /*
  540. // 0xB acts like cariage return in page titles and like blank in the others
  541. char replChr;
  542. switch(tha == null ? -1 : tha.getTextType()) {
  543. case -1:
  544. case TextHeaderAtom.TITLE_TYPE:
  545. case TextHeaderAtom.CENTER_TITLE_TYPE:
  546. replChr = '\n';
  547. break;
  548. default:
  549. replChr = ' ';
  550. break;
  551. }
  552. // PowerPoint seems to store files with \r as the line break
  553. // The messes things up on everything but a Mac, so translate
  554. // them to \n
  555. String text = rawText.replace('\r','\n').replace('\u000b', replChr);
  556. */
  557. /**
  558. * Return <code>OEPlaceholderAtom</code>, the atom that describes a placeholder.
  559. *
  560. * @return <code>OEPlaceholderAtom</code> or <code>null</code> if not found
  561. */
  562. public OEPlaceholderAtom getPlaceholderAtom(){
  563. return getClientDataRecord(OEPlaceholderAtom.typeID);
  564. }
  565. /**
  566. *
  567. * Assigns a hyperlink to this text shape
  568. *
  569. * @param linkId id of the hyperlink, @see org.apache.poi.hslf.usermodel.SlideShow#addHyperlink(Hyperlink)
  570. * @param beginIndex the beginning index, inclusive.
  571. * @param endIndex the ending index, exclusive.
  572. * @see org.apache.poi.hslf.usermodel.HSLFSlideShow#addHyperlink(HSLFHyperlink)
  573. */
  574. public void setHyperlink(int linkId, int beginIndex, int endIndex){
  575. //TODO validate beginIndex and endIndex and throw IllegalArgumentException
  576. InteractiveInfo info = new InteractiveInfo();
  577. InteractiveInfoAtom infoAtom = info.getInteractiveInfoAtom();
  578. infoAtom.setAction(org.apache.poi.hslf.record.InteractiveInfoAtom.ACTION_HYPERLINK);
  579. infoAtom.setHyperlinkType(org.apache.poi.hslf.record.InteractiveInfoAtom.LINK_Url);
  580. infoAtom.setHyperlinkID(linkId);
  581. _txtbox.appendChildRecord(info);
  582. TxInteractiveInfoAtom txiatom = new TxInteractiveInfoAtom();
  583. txiatom.setStartIndex(beginIndex);
  584. txiatom.setEndIndex(endIndex);
  585. _txtbox.appendChildRecord(txiatom);
  586. }
  587. @Override
  588. public boolean isPlaceholder() {
  589. OEPlaceholderAtom oep = getPlaceholderAtom();
  590. if (oep != null) return true;
  591. //special case for files saved in Office 2007
  592. RoundTripHFPlaceholder12 hldr = getClientDataRecord(RoundTripHFPlaceholder12.typeID);
  593. if (hldr != null) return true;
  594. return false;
  595. }
  596. @Override
  597. public Iterator<HSLFTextParagraph> iterator() {
  598. return _paragraphs.iterator();
  599. }
  600. @Override
  601. public Insets2D getInsets() {
  602. Insets2D insets = new Insets2D(getTopInset(), getLeftInset(), getBottomInset(), getRightInset());
  603. return insets;
  604. }
  605. @Override
  606. public double getTextHeight(){
  607. DrawFactory drawFact = DrawFactory.getInstance(null);
  608. DrawTextShape<HSLFTextShape> dts = drawFact.getDrawable(this);
  609. return dts.getTextHeight();
  610. }
  611. @Override
  612. public TextDirection getTextDirection() {
  613. // TODO: determine vertical text setting
  614. return TextDirection.HORIZONTAL;
  615. }
  616. /**
  617. * Returns the raw text content of the shape. This hasn't had any
  618. * changes applied to it, and so is probably unlikely to print
  619. * out nicely.
  620. */
  621. public String getRawText() {
  622. return HSLFTextParagraph.getRawText(getTextParagraphs());
  623. }
  624. /**
  625. * Returns the text contained in this text frame, which has been made safe
  626. * for printing and other use.
  627. *
  628. * @return the text string for this textbox.
  629. */
  630. public String getText() {
  631. String rawText = getRawText();
  632. TextHeaderAtom _headerAtom = (TextHeaderAtom)_txtbox.findFirstOfType(TextHeaderAtom.typeID);
  633. int runType = (_headerAtom == null) ? -1 : _headerAtom.getTextType();
  634. return HSLFTextParagraph.toExternalString(rawText, runType);
  635. }
  636. // Update methods follow
  637. /**
  638. * Adds the supplied text onto the end of the TextParagraphs,
  639. * creating a new RichTextRun for it to sit in.
  640. *
  641. * @param text the text string used by this object.
  642. */
  643. public HSLFTextRun appendText(String text, boolean newParagraph) {
  644. // init paragraphs
  645. List<HSLFTextParagraph> paras = getTextParagraphs();
  646. return HSLFTextParagraph.appendText(paras, text, newParagraph);
  647. }
  648. /**
  649. * Sets (overwrites) the current text.
  650. * Uses the properties of the first paragraph / textrun
  651. *
  652. * @param text the text string used by this object.
  653. *
  654. * @return the last text run of the splitted text
  655. */
  656. public HSLFTextRun setText(String text) {
  657. // init paragraphs
  658. List<HSLFTextParagraph> paras = getTextParagraphs();
  659. HSLFTextRun htr = HSLFTextParagraph.setText(paras, text);
  660. setTextId(text.hashCode());
  661. return htr;
  662. }
  663. /**
  664. * Saves the modified paragraphs/textrun to the records.
  665. * Also updates the styles to the correct text length.
  666. */
  667. protected void storeText() {
  668. List<HSLFTextParagraph> paras = getTextParagraphs();
  669. HSLFTextParagraph.storeText(paras);
  670. }
  671. // Accesser methods follow
  672. /**
  673. * Returns the array of all hyperlinks in this text run
  674. *
  675. * @return the array of all hyperlinks in this text run or <code>null</code>
  676. * if not found.
  677. */
  678. public List<HSLFHyperlink> getHyperlinks() {
  679. return HSLFHyperlink.find(this);
  680. }
  681. }