Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

XSLFTextShape.java 19KB

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