PageRenderTime 35ms CodeModel.GetById 4ms RepoModel.GetById 0ms app.codeStats 0ms

/pptx/src/org/apache/poi/xslf/usermodel/XSLFTextShape.java

https://github.com/minstrelsy/POI-Android
Java | 617 lines | 356 code | 68 blank | 193 comment | 95 complexity | bf44107253290c5472d69b4baedf81f5 MD5 | raw file
  1. /*
  2. * ====================================================================
  3. * Licensed to the Apache Software Foundation (ASF) under one or more
  4. * contributor license agreements. See the NOTICE file distributed with
  5. * this work for additional information regarding copyright ownership.
  6. * The ASF licenses this file to You under the Apache License, Version 2.0
  7. * (the "License"); you may not use this file except in compliance with
  8. * the License. You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing, software
  13. * distributed under the License is distributed on an "AS IS" BASIS,
  14. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. * See the License for the specific language governing permissions and
  16. * limitations under the License.
  17. * ====================================================================
  18. */
  19. package org.apache.poi.xslf.usermodel;
  20. import java.util.ArrayList;
  21. import java.util.Iterator;
  22. import java.util.List;
  23. import net.pbdavey.awt.Graphics2D;
  24. import org.apache.poi.POIXMLException;
  25. import org.apache.poi.util.Beta;
  26. import org.apache.poi.util.Units;
  27. import org.apache.poi.xslf.model.PropertyFetcher;
  28. import org.apache.poi.xslf.model.TextBodyPropertyFetcher;
  29. import org.apache.xmlbeans.XmlObject;
  30. import org.openxmlformats.schemas.drawingml.x2006.main.CTTextBody;
  31. import org.openxmlformats.schemas.drawingml.x2006.main.CTTextBodyProperties;
  32. import org.openxmlformats.schemas.drawingml.x2006.main.CTTextParagraph;
  33. import org.openxmlformats.schemas.drawingml.x2006.main.STTextAnchoringType;
  34. import org.openxmlformats.schemas.drawingml.x2006.main.STTextVerticalType;
  35. //import org.openxmlformats.schemas.drawingml.x2006.main.STTextVerticalType;
  36. import org.openxmlformats.schemas.drawingml.x2006.main.STTextWrappingType;
  37. import org.openxmlformats.schemas.presentationml.x2006.main.CTApplicationNonVisualDrawingProps;
  38. import org.openxmlformats.schemas.presentationml.x2006.main.CTPlaceholder;
  39. import org.openxmlformats.schemas.presentationml.x2006.main.CTShape;
  40. import org.openxmlformats.schemas.presentationml.x2006.main.STPlaceholderType;
  41. import and.awt.BufferedImage;
  42. import and.awt.geom.Rectangle2D;
  43. import android.util.Log;
  44. /**
  45. * Represents a shape that can hold text.
  46. *
  47. * @author Yegor Kozlov
  48. */
  49. @Beta
  50. public abstract class XSLFTextShape extends XSLFSimpleShape implements Iterable<XSLFTextParagraph>{
  51. private final List<XSLFTextParagraph> _paragraphs;
  52. /**
  53. * whether the text was broken into lines.
  54. */
  55. private boolean _isTextBroken;
  56. /*package*/ XSLFTextShape(XmlObject shape, XSLFSheet sheet) {
  57. super(shape, sheet);
  58. _paragraphs = new ArrayList<XSLFTextParagraph>();
  59. CTTextBody txBody = getTextBody(false);
  60. if (txBody != null) {
  61. for (CTTextParagraph p : txBody.getPList()) {
  62. _paragraphs.add(new XSLFTextParagraph(p, this));
  63. }
  64. }
  65. }
  66. public Iterator<XSLFTextParagraph> iterator(){
  67. return _paragraphs.iterator();
  68. }
  69. /**
  70. *
  71. * @return text contained within this shape or empty string
  72. */
  73. public String getText() {
  74. StringBuilder out = new StringBuilder();
  75. for (XSLFTextParagraph p : _paragraphs) {
  76. if (out.length() > 0) out.append('\n');
  77. out.append(p.getText());
  78. }
  79. return out.toString();
  80. }
  81. /**
  82. * unset text from this shape
  83. */
  84. public void clearText(){
  85. _paragraphs.clear();
  86. CTTextBody txBody = getTextBody(true);
  87. txBody.setPArray(null); // remove any existing paragraphs
  88. }
  89. public void setText(String text){
  90. clearText();
  91. addNewTextParagraph().addNewTextRun().setText(text);
  92. }
  93. /**
  94. *
  95. * @return text paragraphs in this shape
  96. */
  97. public List<XSLFTextParagraph> getTextParagraphs() {
  98. return _paragraphs;
  99. }
  100. /**
  101. * add a new paragraph run to this shape
  102. *
  103. * @return created paragraph run
  104. */
  105. public XSLFTextParagraph addNewTextParagraph() {
  106. CTTextBody txBody = getTextBody(true);
  107. CTTextParagraph p = txBody.addNewP();
  108. XSLFTextParagraph paragraph = new XSLFTextParagraph(p, this);
  109. _paragraphs.add(paragraph);
  110. return paragraph;
  111. }
  112. /**
  113. * Sets the type of vertical alignment for the text.
  114. *
  115. * @param anchor - the type of alignment.
  116. * A <code>null</code> values unsets this property.
  117. */
  118. public void setVerticalAlignment(VerticalAlignment anchor){
  119. CTTextBodyProperties bodyPr = getTextBodyPr();
  120. if (bodyPr != null) {
  121. if(anchor == null) {
  122. if(bodyPr.isSetAnchor()) bodyPr.unsetAnchor();
  123. } else {
  124. bodyPr.setAnchor(STTextAnchoringType.Enum.forInt(anchor.ordinal() + 1));
  125. }
  126. }
  127. }
  128. /**
  129. * Returns the type of vertical alignment for the text.
  130. *
  131. * @return the type of vertical alignment
  132. */
  133. public VerticalAlignment getVerticalAlignment(){
  134. PropertyFetcher<VerticalAlignment> fetcher = new TextBodyPropertyFetcher<VerticalAlignment>(){
  135. public boolean fetch(CTTextBodyProperties props){
  136. if(props.isSetAnchor()){
  137. int val = props.getAnchor().intValue();
  138. setValue(VerticalAlignment.values()[val - 1]);
  139. return true;
  140. }
  141. return false;
  142. }
  143. };
  144. fetchShapeProperty(fetcher);
  145. return fetcher.getValue() == null ? VerticalAlignment.TOP : fetcher.getValue();
  146. }
  147. /**
  148. *
  149. * @param orientation vertical orientation of the text
  150. */
  151. public void setTextDirection(TextDirection orientation){
  152. CTTextBodyProperties bodyPr = getTextBodyPr();
  153. if (bodyPr != null) {
  154. if(orientation == null) {
  155. if(bodyPr.isSetVert()) bodyPr.unsetVert();
  156. } else {
  157. bodyPr.setVert(STTextVerticalType.Enum.forInt(orientation.ordinal() + 1));
  158. }
  159. }
  160. }
  161. /**
  162. * @return vertical orientation of the text
  163. */
  164. public TextDirection getTextDirection(){
  165. CTTextBodyProperties bodyPr = getTextBodyPr();
  166. if (bodyPr != null) {
  167. STTextVerticalType.Enum val = bodyPr.getVert();
  168. if(val != null){
  169. return TextDirection.values()[val.intValue() - 1];
  170. }
  171. }
  172. return TextDirection.HORIZONTAL;
  173. }
  174. /**
  175. * Returns the distance (in points) between the bottom of the text frame
  176. * and the bottom of the inscribed rectangle of the shape that contains the text.
  177. *
  178. * @return the bottom inset in points
  179. */
  180. public double getBottomInset(){
  181. PropertyFetcher<Double> fetcher = new TextBodyPropertyFetcher<Double>(){
  182. public boolean fetch(CTTextBodyProperties props){
  183. if(props.isSetBIns()){
  184. double val = Units.toPoints(props.getBIns());
  185. setValue(val);
  186. return true;
  187. }
  188. return false;
  189. }
  190. };
  191. fetchShapeProperty(fetcher);
  192. // If this attribute is omitted, then a value of 0.05 inches is implied
  193. return fetcher.getValue() == null ? 3.6 : fetcher.getValue();
  194. }
  195. /**
  196. * Returns the distance (in points) between the left edge of the text frame
  197. * and the left edge of the inscribed rectangle of the shape that contains
  198. * the text.
  199. *
  200. * @return the left inset in points
  201. */
  202. public double getLeftInset(){
  203. PropertyFetcher<Double> fetcher = new TextBodyPropertyFetcher<Double>(){
  204. public boolean fetch(CTTextBodyProperties props){
  205. if(props.isSetLIns()){
  206. double val = Units.toPoints(props.getLIns());
  207. setValue(val);
  208. return true;
  209. }
  210. return false;
  211. }
  212. };
  213. fetchShapeProperty(fetcher);
  214. // If this attribute is omitted, then a value of 0.1 inches is implied
  215. return fetcher.getValue() == null ? 7.2 : fetcher.getValue();
  216. }
  217. /**
  218. * Returns the distance (in points) between the right edge of the
  219. * text frame and the right edge of the inscribed rectangle of the shape
  220. * that contains the text.
  221. *
  222. * @return the right inset in points
  223. */
  224. public double getRightInset(){
  225. PropertyFetcher<Double> fetcher = new TextBodyPropertyFetcher<Double>(){
  226. public boolean fetch(CTTextBodyProperties props){
  227. if(props.isSetRIns()){
  228. double val = Units.toPoints(props.getRIns());
  229. setValue(val);
  230. return true;
  231. }
  232. return false;
  233. }
  234. };
  235. fetchShapeProperty(fetcher);
  236. // If this attribute is omitted, then a value of 0.1 inches is implied
  237. return fetcher.getValue() == null ? 7.2 : fetcher.getValue();
  238. }
  239. /**
  240. * Returns the distance (in points) between the top of the text frame
  241. * and the top of the inscribed rectangle of the shape that contains the text.
  242. *
  243. * @return the top inset in points
  244. */
  245. public double getTopInset(){
  246. PropertyFetcher<Double> fetcher = new TextBodyPropertyFetcher<Double>(){
  247. public boolean fetch(CTTextBodyProperties props){
  248. if(props.isSetTIns()){
  249. double val = Units.toPoints(props.getTIns());
  250. setValue(val);
  251. return true;
  252. }
  253. return false;
  254. }
  255. };
  256. fetchShapeProperty(fetcher);
  257. // If this attribute is omitted, then a value of 0.05 inches is implied
  258. return fetcher.getValue() == null ? 3.6 : fetcher.getValue();
  259. }
  260. /**
  261. * Sets the botom margin.
  262. * @see #getBottomInset()
  263. *
  264. * @param margin the bottom margin
  265. */
  266. public void setBottomInset(double margin){
  267. CTTextBodyProperties bodyPr = getTextBodyPr();
  268. if (bodyPr != null) {
  269. if(margin == -1) bodyPr.unsetBIns();
  270. else bodyPr.setBIns(Units.toEMU(margin));
  271. }
  272. }
  273. /**
  274. * Sets the left margin.
  275. * @see #getLeftInset()
  276. *
  277. * @param margin the left margin
  278. */
  279. public void setLeftInset(double margin){
  280. CTTextBodyProperties bodyPr = getTextBodyPr();
  281. if (bodyPr != null) {
  282. if(margin == -1) bodyPr.unsetLIns();
  283. else bodyPr.setLIns(Units.toEMU(margin));
  284. }
  285. }
  286. /**
  287. * Sets the right margin.
  288. * @see #getRightInset()
  289. *
  290. * @param margin the right margin
  291. */
  292. public void setRightInset(double margin){
  293. CTTextBodyProperties bodyPr = getTextBodyPr();
  294. if (bodyPr != null) {
  295. if(margin == -1) bodyPr.unsetRIns();
  296. else bodyPr.setRIns(Units.toEMU(margin));
  297. }
  298. }
  299. /**
  300. * Sets the top margin.
  301. * @see #getTopInset()
  302. *
  303. * @param margin the top margin
  304. */
  305. public void setTopInset(double margin){
  306. CTTextBodyProperties bodyPr = getTextBodyPr();
  307. if (bodyPr != null) {
  308. if(margin == -1) bodyPr.unsetTIns();
  309. else bodyPr.setTIns(Units.toEMU(margin));
  310. }
  311. }
  312. /**
  313. * @return whether to wrap words within the bounding rectangle
  314. */
  315. public boolean getWordWrap(){
  316. PropertyFetcher<Boolean> fetcher = new TextBodyPropertyFetcher<Boolean>(){
  317. public boolean fetch(CTTextBodyProperties props){
  318. if(props.isSetWrap()){
  319. setValue(props.getWrap() == STTextWrappingType.SQUARE);
  320. return true;
  321. }
  322. return false;
  323. }
  324. };
  325. fetchShapeProperty(fetcher);
  326. return fetcher.getValue() == null ? true : fetcher.getValue();
  327. }
  328. /**
  329. *
  330. * @param wrap whether to wrap words within the bounding rectangle
  331. */
  332. public void setWordWrap(boolean wrap){
  333. CTTextBodyProperties bodyPr = getTextBodyPr();
  334. if (bodyPr != null) {
  335. bodyPr.setWrap(wrap ? STTextWrappingType.SQUARE : STTextWrappingType.NONE);
  336. }
  337. }
  338. /**
  339. *
  340. * Specifies that a shape should be auto-fit to fully contain the text described within it.
  341. * Auto-fitting is when text within a shape is scaled in order to contain all the text inside
  342. *
  343. * @param value type of autofit
  344. */
  345. public void setTextAutofit(TextAutofit value){
  346. CTTextBodyProperties bodyPr = getTextBodyPr();
  347. if (bodyPr != null) {
  348. if(bodyPr.isSetSpAutoFit()) bodyPr.unsetSpAutoFit();
  349. if(bodyPr.isSetNoAutofit()) bodyPr.unsetNoAutofit();
  350. if(bodyPr.isSetNormAutofit()) bodyPr.unsetNormAutofit();
  351. switch(value){
  352. case NONE: bodyPr.addNewNoAutofit(); break;
  353. case NORMAL: bodyPr.addNewNormAutofit(); break;
  354. case SHAPE: bodyPr.addNewSpAutoFit(); break;
  355. }
  356. }
  357. }
  358. /**
  359. *
  360. * @return type of autofit
  361. */
  362. public TextAutofit getTextAutofit(){
  363. CTTextBodyProperties bodyPr = getTextBodyPr();
  364. if (bodyPr != null) {
  365. if(bodyPr.isSetNoAutofit()) return TextAutofit.NONE;
  366. else if (bodyPr.isSetNormAutofit()) return TextAutofit.NORMAL;
  367. else if (bodyPr.isSetSpAutoFit()) return TextAutofit.SHAPE;
  368. }
  369. return TextAutofit.NORMAL;
  370. }
  371. protected CTTextBodyProperties getTextBodyPr(){
  372. CTTextBody textBody = getTextBody(false);
  373. return textBody == null ? null : textBody.getBodyPr();
  374. }
  375. protected abstract CTTextBody getTextBody(boolean create);
  376. public Placeholder getTextType(){
  377. CTPlaceholder ph;
  378. XmlObject[] obj = getXmlObject().selectPath(
  379. "declare namespace p='http://schemas.openxmlformats.org/presentationml/2006/main' .//*/p:nvPr/p:ph");
  380. if(obj.length == 1){
  381. ph = (CTPlaceholder)obj[0];
  382. int val = ph.getType().intValue();
  383. return Placeholder.values()[val - 1];
  384. }
  385. else {
  386. return null;
  387. }
  388. }
  389. /**
  390. * Specifies that the corresponding shape should be represented by the generating application
  391. * as a placeholder. When a shape is considered a placeholder by the generating application
  392. * it can have special properties to alert the user that they may enter content into the shape.
  393. * Different types of placeholders are allowed and can be specified by using the placeholder
  394. * type attribute for this element
  395. *
  396. * @param placeholder
  397. */
  398. public void setPlaceholder(Placeholder placeholder){
  399. CTShape sh = (CTShape)getXmlObject();
  400. CTApplicationNonVisualDrawingProps nv = sh.getNvSpPr().getNvPr();
  401. if(placeholder == null) {
  402. if(nv.isSetPh()) nv.unsetPh();
  403. } else {
  404. nv.addNewPh().setType(STPlaceholderType.Enum.forInt(placeholder.ordinal() + 1));
  405. }
  406. }
  407. /**
  408. * Compute the cumulative height occupied by the text
  409. */
  410. public double getTextHeight(){
  411. // dry-run in a 1x1 image and return the vertical advance
  412. BufferedImage img = new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB);
  413. Graphics2D graphics = img.createGraphics();
  414. breakText(graphics);
  415. return drawParagraphs(graphics, 0, 0);
  416. }
  417. /**
  418. * Adjust the size of the shape so it encompasses the text inside it.
  419. *
  420. * @return a <code>Rectangle2D</code> that is the bounds of this shape.
  421. */
  422. public Rectangle2D resizeToFitText(){
  423. Rectangle2D anchor = getAnchor();
  424. if(anchor.getWidth() == 0.) throw new POIXMLException(
  425. "Anchor of the shape was not set.");
  426. double height = getTextHeight();
  427. height += 1; // add a pixel to compensate rounding errors
  428. anchor.setRect(anchor.getX(), anchor.getY(), anchor.getWidth(), height);
  429. setAnchor(anchor);
  430. return anchor;
  431. }
  432. /**
  433. * break the contained text into lines
  434. */
  435. private void breakText(Graphics2D graphics){
  436. if(!_isTextBroken) {
  437. for(XSLFTextParagraph p : _paragraphs) p.breakText(graphics);
  438. _isTextBroken = true;
  439. }
  440. }
  441. @Override
  442. public void drawContent(Graphics2D graphics) {
  443. breakText(graphics);
  444. RenderableShape rShape = new RenderableShape(this);
  445. Rectangle2D anchor = rShape.getAnchor(graphics);
  446. double x = anchor.getX() + getLeftInset();
  447. double y = anchor.getY();
  448. // first dry-run to calculate the total height of the text
  449. double textHeight = getTextHeight();
  450. Log.d("textInset", "drawContent x: " + x + ", y: " + y + ", textHeight: " + textHeight);
  451. switch (getVerticalAlignment()){
  452. case TOP:
  453. y += getTopInset();
  454. break;
  455. case BOTTOM:
  456. y += anchor.getHeight() - textHeight - getBottomInset();
  457. break;
  458. default:
  459. case MIDDLE:
  460. double delta = anchor.getHeight() - textHeight -
  461. getTopInset() - getBottomInset();
  462. y += getTopInset() + delta/2;
  463. break;
  464. }
  465. drawParagraphs(graphics, x, y);
  466. }
  467. /**
  468. * pain the paragraphs starting from top left (x,y)
  469. *
  470. * @return the vertical advance, i.e. the cumulative space occupied by the text
  471. */
  472. private double drawParagraphs(Graphics2D graphics, double x, double y) {
  473. double y0 = y;
  474. for(int i = 0; i < _paragraphs.size(); i++){
  475. XSLFTextParagraph p = _paragraphs.get(i);
  476. y += p.draw(graphics, x, y);
  477. }
  478. return y - y0;
  479. // double y0 = y;
  480. // for(int i = 0; i < _paragraphs.size(); i++){
  481. // XSLFTextParagraph p = _paragraphs.get(i);
  482. // List<TextFragment> lines = p.getTextLines();
  483. //
  484. // if(i > 0 && lines.size() > 0) {
  485. // // the amount of vertical white space before the paragraph
  486. // double spaceBefore = p.getSpaceBefore();
  487. // if(spaceBefore > 0) {
  488. // // positive value means percentage spacing of the height of the first line, e.g.
  489. // // the higher the first line, the bigger the space before the paragraph
  490. // y += spaceBefore*0.01*lines.get(0).getHeight();
  491. // } else {
  492. // // negative value means the absolute spacing in points
  493. // y += -spaceBefore;
  494. // }
  495. // }
  496. //
  497. // y += p.draw(graphics, x, y);
  498. //
  499. // if(i < _paragraphs.size() - 1) {
  500. // double spaceAfter = p.getSpaceAfter();
  501. // if(spaceAfter > 0) {
  502. // // positive value means percentage spacing of the height of the last line, e.g.
  503. // // the higher the last line, the bigger the space after the paragraph
  504. // y += spaceAfter*0.01*lines.get(lines.size() - 1).getHeight();
  505. // } else {
  506. // // negative value means the absolute spacing in points
  507. // y += -spaceAfter;
  508. // }
  509. // }
  510. // }
  511. // return y - y0;
  512. }
  513. @Override
  514. void copy(XSLFShape sh){
  515. super.copy(sh);
  516. XSLFTextShape tsh = (XSLFTextShape)sh;
  517. boolean srcWordWrap = tsh.getWordWrap();
  518. if(srcWordWrap != getWordWrap()){
  519. setWordWrap(srcWordWrap);
  520. }
  521. double leftInset = tsh.getLeftInset();
  522. if(leftInset != getLeftInset()) {
  523. setLeftInset(leftInset);
  524. }
  525. double rightInset = tsh.getRightInset();
  526. if(rightInset != getRightInset()) {
  527. setRightInset(rightInset);
  528. }
  529. double topInset = tsh.getTopInset();
  530. if(topInset != getTopInset()) {
  531. setTopInset(topInset);
  532. }
  533. double bottomInset = tsh.getBottomInset();
  534. if(bottomInset != getBottomInset()) {
  535. setBottomInset(bottomInset);
  536. }
  537. VerticalAlignment vAlign = tsh.getVerticalAlignment();
  538. if(vAlign != getVerticalAlignment()) {
  539. setVerticalAlignment(vAlign);
  540. }
  541. List<XSLFTextParagraph> srcP = tsh.getTextParagraphs();
  542. List<XSLFTextParagraph> tgtP = getTextParagraphs();
  543. for(int i = 0; i < srcP.size(); i++){
  544. XSLFTextParagraph p1 = srcP.get(i);
  545. XSLFTextParagraph p2 = tgtP.get(i);
  546. p2.copy(p1);
  547. }
  548. }
  549. }