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

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