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.

HSLFTextRun.java 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458
  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.usermodel.HSLFTextParagraph.getPropVal;
  17. import java.awt.Color;
  18. import org.apache.poi.hslf.exceptions.HSLFException;
  19. import org.apache.poi.hslf.model.textproperties.BitMaskTextProp;
  20. import org.apache.poi.hslf.model.textproperties.CharFlagsTextProp;
  21. import org.apache.poi.hslf.model.textproperties.TextProp;
  22. import org.apache.poi.hslf.model.textproperties.TextPropCollection;
  23. import org.apache.poi.hslf.model.textproperties.TextPropCollection.TextPropType;
  24. import org.apache.poi.hslf.record.ExHyperlink;
  25. import org.apache.poi.hslf.record.InteractiveInfo;
  26. import org.apache.poi.hslf.record.InteractiveInfoAtom;
  27. import org.apache.poi.hslf.record.Record;
  28. import org.apache.poi.sl.draw.DrawPaint;
  29. import org.apache.poi.sl.usermodel.PaintStyle;
  30. import org.apache.poi.sl.usermodel.PaintStyle.SolidPaint;
  31. import org.apache.poi.sl.usermodel.TextRun;
  32. import org.apache.poi.util.Internal;
  33. import org.apache.poi.util.POILogFactory;
  34. import org.apache.poi.util.POILogger;
  35. /**
  36. * Represents a run of text, all with the same style
  37. *
  38. */
  39. public final class HSLFTextRun implements TextRun {
  40. protected POILogger logger = POILogFactory.getLogger(this.getClass());
  41. /** The TextRun we belong to */
  42. private HSLFTextParagraph parentParagraph;
  43. private String _runText = "";
  44. private String _fontFamily;
  45. private int hyperlinkId = -1;
  46. /**
  47. * Our paragraph and character style.
  48. * Note - we may share these styles with other RichTextRuns
  49. */
  50. private TextPropCollection characterStyle = new TextPropCollection(1, TextPropType.character);
  51. /**
  52. * Create a new wrapper around a rich text string
  53. * @param parentParagraph the parent paragraph
  54. */
  55. public HSLFTextRun(HSLFTextParagraph parentParagraph) {
  56. this.parentParagraph = parentParagraph;
  57. }
  58. public TextPropCollection getCharacterStyle() {
  59. return characterStyle;
  60. }
  61. public void setCharacterStyle(TextPropCollection characterStyle) {
  62. this.characterStyle.copy(characterStyle);
  63. this.characterStyle.updateTextSize(_runText.length());
  64. }
  65. /**
  66. * Setting a master style reference
  67. *
  68. * @param characterStyle the master style reference
  69. *
  70. * @since POI 3.14-Beta1
  71. */
  72. @Internal
  73. /* package */ void setMasterStyleReference(TextPropCollection characterStyle) {
  74. this.characterStyle = characterStyle;
  75. }
  76. /**
  77. * Supply the SlideShow we belong to
  78. */
  79. public void updateSheet() {
  80. if (_fontFamily != null) {
  81. setFontFamily(_fontFamily);
  82. _fontFamily = null;
  83. }
  84. }
  85. /**
  86. * Get the length of the text
  87. */
  88. public int getLength() {
  89. return _runText.length();
  90. }
  91. /**
  92. * Fetch the text, in raw storage form
  93. */
  94. public String getRawText() {
  95. return _runText;
  96. }
  97. /**
  98. * Change the text
  99. */
  100. public void setText(String text) {
  101. if (text == null) {
  102. throw new HSLFException("text must not be null");
  103. }
  104. String newText = HSLFTextParagraph.toInternalString(text);
  105. if (!newText.equals(_runText)) {
  106. _runText = newText;
  107. if (HSLFSlideShow.getLoadSavePhase() == HSLFSlideShow.LoadSavePhase.LOADED) {
  108. parentParagraph.setDirty();
  109. }
  110. }
  111. }
  112. // --------------- Internal helpers on rich text properties -------
  113. /**
  114. * Fetch the value of the given flag in the CharFlagsTextProp.
  115. * Returns false if the CharFlagsTextProp isn't present, since the
  116. * text property won't be set if there's no CharFlagsTextProp.
  117. */
  118. private boolean isCharFlagsTextPropVal(int index) {
  119. return getFlag(index);
  120. }
  121. protected boolean getFlag(int index) {
  122. if (characterStyle == null) return false;
  123. BitMaskTextProp prop = (BitMaskTextProp)characterStyle.findByName(CharFlagsTextProp.NAME);
  124. if (prop == null || !prop.getSubPropMatches()[index]) {
  125. int txtype = parentParagraph.getRunType();
  126. HSLFSheet sheet = parentParagraph.getSheet();
  127. if (sheet != null) {
  128. HSLFMasterSheet master = sheet.getMasterSheet();
  129. if (master != null){
  130. prop = (BitMaskTextProp)master.getStyleAttribute(txtype, parentParagraph.getIndentLevel(), CharFlagsTextProp.NAME, true);
  131. }
  132. } else {
  133. logger.log(POILogger.WARN, "MasterSheet is not available");
  134. }
  135. }
  136. return prop == null ? false : prop.getSubValue(index);
  137. }
  138. /**
  139. * Set the value of the given flag in the CharFlagsTextProp, adding
  140. * it if required.
  141. */
  142. private void setCharFlagsTextPropVal(int index, boolean value) {
  143. // TODO: check if paragraph/chars can be handled the same ...
  144. if (getFlag(index) != value) {
  145. setFlag(index, value);
  146. parentParagraph.setDirty();
  147. }
  148. }
  149. /**
  150. * Sets the value of the given Paragraph TextProp, add if required
  151. * @param propName The name of the Paragraph TextProp
  152. * @param val The value to set for the TextProp
  153. */
  154. public void setCharTextPropVal(String propName, Integer val) {
  155. HSLFTextParagraph.setPropVal(characterStyle, propName, val);
  156. parentParagraph.setDirty();
  157. }
  158. // --------------- Friendly getters / setters on rich text properties -------
  159. @Override
  160. public boolean isBold() {
  161. return isCharFlagsTextPropVal(CharFlagsTextProp.BOLD_IDX);
  162. }
  163. @Override
  164. public void setBold(boolean bold) {
  165. setCharFlagsTextPropVal(CharFlagsTextProp.BOLD_IDX, bold);
  166. }
  167. @Override
  168. public boolean isItalic() {
  169. return isCharFlagsTextPropVal(CharFlagsTextProp.ITALIC_IDX);
  170. }
  171. @Override
  172. public void setItalic(boolean italic) {
  173. setCharFlagsTextPropVal(CharFlagsTextProp.ITALIC_IDX, italic);
  174. }
  175. @Override
  176. public boolean isUnderlined() {
  177. return isCharFlagsTextPropVal(CharFlagsTextProp.UNDERLINE_IDX);
  178. }
  179. @Override
  180. public void setUnderlined(boolean underlined) {
  181. setCharFlagsTextPropVal(CharFlagsTextProp.UNDERLINE_IDX, underlined);
  182. }
  183. /**
  184. * Does the text have a shadow?
  185. */
  186. public boolean isShadowed() {
  187. return isCharFlagsTextPropVal(CharFlagsTextProp.SHADOW_IDX);
  188. }
  189. /**
  190. * Does the text have a shadow?
  191. */
  192. public void setShadowed(boolean flag) {
  193. setCharFlagsTextPropVal(CharFlagsTextProp.SHADOW_IDX, flag);
  194. }
  195. /**
  196. * Is this text embossed?
  197. */
  198. public boolean isEmbossed() {
  199. return isCharFlagsTextPropVal(CharFlagsTextProp.RELIEF_IDX);
  200. }
  201. /**
  202. * Is this text embossed?
  203. */
  204. public void setEmbossed(boolean flag) {
  205. setCharFlagsTextPropVal(CharFlagsTextProp.RELIEF_IDX, flag);
  206. }
  207. @Override
  208. public boolean isStrikethrough() {
  209. return isCharFlagsTextPropVal(CharFlagsTextProp.STRIKETHROUGH_IDX);
  210. }
  211. @Override
  212. public void setStrikethrough(boolean flag) {
  213. setCharFlagsTextPropVal(CharFlagsTextProp.STRIKETHROUGH_IDX, flag);
  214. }
  215. /**
  216. * Gets the subscript/superscript option
  217. *
  218. * @return the percentage of the font size. If the value is positive, it is superscript, otherwise it is subscript
  219. */
  220. public int getSuperscript() {
  221. TextProp tp = getPropVal(characterStyle, "superscript", parentParagraph);
  222. return tp == null ? 0 : tp.getValue();
  223. }
  224. /**
  225. * Sets the subscript/superscript option
  226. *
  227. * @param val the percentage of the font size. If the value is positive, it is superscript, otherwise it is subscript
  228. */
  229. public void setSuperscript(int val) {
  230. setCharTextPropVal("superscript", val);
  231. }
  232. @Override
  233. public Double getFontSize() {
  234. TextProp tp = getPropVal(characterStyle, "font.size", parentParagraph);
  235. return tp == null ? null : (double)tp.getValue();
  236. }
  237. @Override
  238. public void setFontSize(Double fontSize) {
  239. Integer iFontSize = (fontSize == null) ? null : fontSize.intValue();
  240. setCharTextPropVal("font.size", iFontSize);
  241. }
  242. /**
  243. * Gets the font index
  244. */
  245. public int getFontIndex() {
  246. TextProp tp = getPropVal(characterStyle, "font.index", parentParagraph);
  247. return tp == null ? -1 : tp.getValue();
  248. }
  249. /**
  250. * Sets the font index
  251. */
  252. public void setFontIndex(int idx) {
  253. setCharTextPropVal("font.index", idx);
  254. }
  255. @Override
  256. public void setFontFamily(String fontFamily) {
  257. HSLFSheet sheet = parentParagraph.getSheet();
  258. @SuppressWarnings("resource")
  259. HSLFSlideShow slideShow = (sheet == null) ? null : sheet.getSlideShow();
  260. if (sheet == null || slideShow == null) {
  261. //we can't set font since slideshow is not assigned yet
  262. _fontFamily = fontFamily;
  263. return;
  264. }
  265. // Get the index for this font (adding if needed)
  266. Integer fontIdx = (fontFamily == null) ? null : slideShow.getFontCollection().addFont(fontFamily);
  267. setCharTextPropVal("font.index", fontIdx);
  268. }
  269. @Override
  270. public String getFontFamily() {
  271. HSLFSheet sheet = parentParagraph.getSheet();
  272. @SuppressWarnings("resource")
  273. HSLFSlideShow slideShow = (sheet == null) ? null : sheet.getSlideShow();
  274. if (sheet == null || slideShow == null) {
  275. return _fontFamily;
  276. }
  277. TextProp tp = getPropVal(characterStyle, "font.index,asian.font.index,ansi.font.index,symbol.font.index", parentParagraph);
  278. if (tp == null) { return null; }
  279. return slideShow.getFontCollection().getFontWithId(tp.getValue());
  280. }
  281. /**
  282. * @return font color as PaintStyle
  283. */
  284. @Override
  285. public SolidPaint getFontColor() {
  286. TextProp tp = getPropVal(characterStyle, "font.color", parentParagraph);
  287. if (tp == null) return null;
  288. Color color = HSLFTextParagraph.getColorFromColorIndexStruct(tp.getValue(), parentParagraph.getSheet());
  289. SolidPaint ps = DrawPaint.createSolidPaint(color);
  290. return ps;
  291. }
  292. /**
  293. * Sets color of the text, as a int bgr.
  294. * (PowerPoint stores as BlueGreenRed, not the more
  295. * usual RedGreenBlue)
  296. * @see java.awt.Color
  297. */
  298. public void setFontColor(int bgr) {
  299. setCharTextPropVal("font.color", bgr);
  300. }
  301. @Override
  302. public void setFontColor(Color color) {
  303. setFontColor(DrawPaint.createSolidPaint(color));
  304. }
  305. @Override
  306. public void setFontColor(PaintStyle color) {
  307. if (!(color instanceof SolidPaint)) {
  308. throw new IllegalArgumentException("HSLF only supports solid paint");
  309. }
  310. // In PowerPont RGB bytes are swapped, as BGR
  311. SolidPaint sp = (SolidPaint)color;
  312. Color c = DrawPaint.applyColorTransform(sp.getSolidColor());
  313. int rgb = new Color(c.getBlue(), c.getGreen(), c.getRed(), 254).getRGB();
  314. setFontColor(rgb);
  315. }
  316. protected void setFlag(int index, boolean value) {
  317. BitMaskTextProp prop = (BitMaskTextProp)characterStyle.addWithName(CharFlagsTextProp.NAME);
  318. prop.setSubValue(value, index);
  319. }
  320. public HSLFTextParagraph getTextParagraph() {
  321. return parentParagraph;
  322. }
  323. public TextCap getTextCap() {
  324. return TextCap.NONE;
  325. }
  326. @Override
  327. public boolean isSubscript() {
  328. return getSuperscript() < 0;
  329. }
  330. @Override
  331. public boolean isSuperscript() {
  332. return getSuperscript() > 0;
  333. }
  334. public byte getPitchAndFamily() {
  335. return 0;
  336. }
  337. /**
  338. * Sets the associated hyperlink id - currently this is only used while parsing and
  339. * can't be used for update a ppt
  340. *
  341. * @param hyperlink the id or -1 to unset it
  342. */
  343. public void setHyperlinkId(int hyperlinkId) {
  344. this.hyperlinkId = hyperlinkId;
  345. }
  346. /**
  347. * Returns the associated hyperlink id
  348. *
  349. * @return the hyperlink id
  350. */
  351. public int getHyperlinkId() {
  352. return hyperlinkId;
  353. }
  354. @Override
  355. public HSLFHyperlink getHyperlink() {
  356. if (hyperlinkId == -1) {
  357. return null;
  358. }
  359. ExHyperlink linkRecord = parentParagraph.getSheet().getSlideShow().getDocumentRecord().getExObjList().get(hyperlinkId);
  360. if (linkRecord == null) {
  361. return null;
  362. }
  363. InteractiveInfoAtom info = null;
  364. for (Record r : parentParagraph.getRecords()) {
  365. if (r instanceof InteractiveInfo) {
  366. InteractiveInfo ii = (InteractiveInfo)r;
  367. InteractiveInfoAtom iia = ii.getInteractiveInfoAtom();
  368. if (iia.getHyperlinkID() == hyperlinkId) {
  369. info = iia;
  370. break;
  371. }
  372. }
  373. }
  374. if (info == null) {
  375. return null;
  376. }
  377. // TODO: check previous/next sibling runs for same hyperlink id and return the whole string length
  378. int startIdx = parentParagraph.getStartIdxOfTextRun(this);
  379. HSLFHyperlink link = new HSLFHyperlink();
  380. link.setId(hyperlinkId);
  381. link.setType(info.getAction());
  382. link.setLabel(linkRecord.getLinkTitle());
  383. link.setAddress(linkRecord.getLinkURL());
  384. link.setStartIndex(startIdx);
  385. link.setEndIndex(startIdx+getLength());
  386. return link;
  387. }
  388. }