/pptx/src/org/apache/poi/xslf/usermodel/XSLFTextShape.java
Java | 617 lines | 356 code | 68 blank | 193 comment | 95 complexity | bf44107253290c5472d69b4baedf81f5 MD5 | raw file
- /*
- * ====================================================================
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * ====================================================================
- */
- package org.apache.poi.xslf.usermodel;
- import java.util.ArrayList;
- import java.util.Iterator;
- import java.util.List;
- import net.pbdavey.awt.Graphics2D;
- import org.apache.poi.POIXMLException;
- import org.apache.poi.util.Beta;
- import org.apache.poi.util.Units;
- import org.apache.poi.xslf.model.PropertyFetcher;
- import org.apache.poi.xslf.model.TextBodyPropertyFetcher;
- import org.apache.xmlbeans.XmlObject;
- import org.openxmlformats.schemas.drawingml.x2006.main.CTTextBody;
- import org.openxmlformats.schemas.drawingml.x2006.main.CTTextBodyProperties;
- import org.openxmlformats.schemas.drawingml.x2006.main.CTTextParagraph;
- import org.openxmlformats.schemas.drawingml.x2006.main.STTextAnchoringType;
- import org.openxmlformats.schemas.drawingml.x2006.main.STTextVerticalType;
- //import org.openxmlformats.schemas.drawingml.x2006.main.STTextVerticalType;
- import org.openxmlformats.schemas.drawingml.x2006.main.STTextWrappingType;
- import org.openxmlformats.schemas.presentationml.x2006.main.CTApplicationNonVisualDrawingProps;
- import org.openxmlformats.schemas.presentationml.x2006.main.CTPlaceholder;
- import org.openxmlformats.schemas.presentationml.x2006.main.CTShape;
- import org.openxmlformats.schemas.presentationml.x2006.main.STPlaceholderType;
- import and.awt.BufferedImage;
- import and.awt.geom.Rectangle2D;
- import android.util.Log;
- /**
- * Represents a shape that can hold text.
- *
- * @author Yegor Kozlov
- */
- @Beta
- public abstract class XSLFTextShape extends XSLFSimpleShape implements Iterable<XSLFTextParagraph>{
- private final List<XSLFTextParagraph> _paragraphs;
- /**
- * whether the text was broken into lines.
- */
- private boolean _isTextBroken;
- /*package*/ XSLFTextShape(XmlObject shape, XSLFSheet sheet) {
- super(shape, sheet);
- _paragraphs = new ArrayList<XSLFTextParagraph>();
- CTTextBody txBody = getTextBody(false);
- if (txBody != null) {
- for (CTTextParagraph p : txBody.getPList()) {
- _paragraphs.add(new XSLFTextParagraph(p, this));
- }
- }
- }
- public Iterator<XSLFTextParagraph> iterator(){
- return _paragraphs.iterator();
- }
- /**
- *
- * @return text contained within this shape or empty string
- */
- public String getText() {
- StringBuilder out = new StringBuilder();
- for (XSLFTextParagraph p : _paragraphs) {
- if (out.length() > 0) out.append('\n');
- out.append(p.getText());
- }
- return out.toString();
- }
- /**
- * unset text from this shape
- */
- public void clearText(){
- _paragraphs.clear();
- CTTextBody txBody = getTextBody(true);
- txBody.setPArray(null); // remove any existing paragraphs
- }
- public void setText(String text){
- clearText();
- addNewTextParagraph().addNewTextRun().setText(text);
- }
- /**
- *
- * @return text paragraphs in this shape
- */
- public List<XSLFTextParagraph> getTextParagraphs() {
- return _paragraphs;
- }
- /**
- * add a new paragraph run to this shape
- *
- * @return created paragraph run
- */
- public XSLFTextParagraph addNewTextParagraph() {
- CTTextBody txBody = getTextBody(true);
- CTTextParagraph p = txBody.addNewP();
- XSLFTextParagraph paragraph = new XSLFTextParagraph(p, this);
- _paragraphs.add(paragraph);
- return paragraph;
- }
- /**
- * Sets the type of vertical alignment for the text.
- *
- * @param anchor - the type of alignment.
- * A <code>null</code> values unsets this property.
- */
- public void setVerticalAlignment(VerticalAlignment anchor){
- CTTextBodyProperties bodyPr = getTextBodyPr();
- if (bodyPr != null) {
- if(anchor == null) {
- if(bodyPr.isSetAnchor()) bodyPr.unsetAnchor();
- } else {
- bodyPr.setAnchor(STTextAnchoringType.Enum.forInt(anchor.ordinal() + 1));
- }
- }
- }
- /**
- * Returns the type of vertical alignment for the text.
- *
- * @return the type of vertical alignment
- */
- public VerticalAlignment getVerticalAlignment(){
- PropertyFetcher<VerticalAlignment> fetcher = new TextBodyPropertyFetcher<VerticalAlignment>(){
- public boolean fetch(CTTextBodyProperties props){
- if(props.isSetAnchor()){
- int val = props.getAnchor().intValue();
- setValue(VerticalAlignment.values()[val - 1]);
- return true;
- }
- return false;
- }
- };
- fetchShapeProperty(fetcher);
- return fetcher.getValue() == null ? VerticalAlignment.TOP : fetcher.getValue();
- }
- /**
- *
- * @param orientation vertical orientation of the text
- */
- public void setTextDirection(TextDirection orientation){
- CTTextBodyProperties bodyPr = getTextBodyPr();
- if (bodyPr != null) {
- if(orientation == null) {
- if(bodyPr.isSetVert()) bodyPr.unsetVert();
- } else {
- bodyPr.setVert(STTextVerticalType.Enum.forInt(orientation.ordinal() + 1));
- }
- }
- }
- /**
- * @return vertical orientation of the text
- */
- public TextDirection getTextDirection(){
- CTTextBodyProperties bodyPr = getTextBodyPr();
- if (bodyPr != null) {
- STTextVerticalType.Enum val = bodyPr.getVert();
- if(val != null){
- return TextDirection.values()[val.intValue() - 1];
- }
- }
- return TextDirection.HORIZONTAL;
- }
- /**
- * Returns the distance (in points) between the bottom of the text frame
- * and the bottom of the inscribed rectangle of the shape that contains the text.
- *
- * @return the bottom inset in points
- */
- public double getBottomInset(){
- PropertyFetcher<Double> fetcher = new TextBodyPropertyFetcher<Double>(){
- public boolean fetch(CTTextBodyProperties props){
- if(props.isSetBIns()){
- double val = Units.toPoints(props.getBIns());
- setValue(val);
- return true;
- }
- return false;
- }
- };
- fetchShapeProperty(fetcher);
- // If this attribute is omitted, then a value of 0.05 inches is implied
- return fetcher.getValue() == null ? 3.6 : fetcher.getValue();
- }
- /**
- * Returns the distance (in points) between the left edge of the text frame
- * and the left edge of the inscribed rectangle of the shape that contains
- * the text.
- *
- * @return the left inset in points
- */
- public double getLeftInset(){
- PropertyFetcher<Double> fetcher = new TextBodyPropertyFetcher<Double>(){
- public boolean fetch(CTTextBodyProperties props){
- if(props.isSetLIns()){
- double val = Units.toPoints(props.getLIns());
- setValue(val);
- return true;
- }
- return false;
- }
- };
- fetchShapeProperty(fetcher);
- // If this attribute is omitted, then a value of 0.1 inches is implied
- return fetcher.getValue() == null ? 7.2 : fetcher.getValue();
- }
- /**
- * Returns the distance (in points) between the right edge of the
- * text frame and the right edge of the inscribed rectangle of the shape
- * that contains the text.
- *
- * @return the right inset in points
- */
- public double getRightInset(){
- PropertyFetcher<Double> fetcher = new TextBodyPropertyFetcher<Double>(){
- public boolean fetch(CTTextBodyProperties props){
- if(props.isSetRIns()){
- double val = Units.toPoints(props.getRIns());
- setValue(val);
- return true;
- }
- return false;
- }
- };
- fetchShapeProperty(fetcher);
- // If this attribute is omitted, then a value of 0.1 inches is implied
- return fetcher.getValue() == null ? 7.2 : fetcher.getValue();
- }
- /**
- * Returns the distance (in points) between the top of the text frame
- * and the top of the inscribed rectangle of the shape that contains the text.
- *
- * @return the top inset in points
- */
- public double getTopInset(){
- PropertyFetcher<Double> fetcher = new TextBodyPropertyFetcher<Double>(){
- public boolean fetch(CTTextBodyProperties props){
- if(props.isSetTIns()){
- double val = Units.toPoints(props.getTIns());
- setValue(val);
- return true;
- }
- return false;
- }
- };
- fetchShapeProperty(fetcher);
- // If this attribute is omitted, then a value of 0.05 inches is implied
- return fetcher.getValue() == null ? 3.6 : fetcher.getValue();
- }
- /**
- * Sets the botom margin.
- * @see #getBottomInset()
- *
- * @param margin the bottom margin
- */
- public void setBottomInset(double margin){
- CTTextBodyProperties bodyPr = getTextBodyPr();
- if (bodyPr != null) {
- if(margin == -1) bodyPr.unsetBIns();
- else bodyPr.setBIns(Units.toEMU(margin));
- }
- }
- /**
- * Sets the left margin.
- * @see #getLeftInset()
- *
- * @param margin the left margin
- */
- public void setLeftInset(double margin){
- CTTextBodyProperties bodyPr = getTextBodyPr();
- if (bodyPr != null) {
- if(margin == -1) bodyPr.unsetLIns();
- else bodyPr.setLIns(Units.toEMU(margin));
- }
- }
- /**
- * Sets the right margin.
- * @see #getRightInset()
- *
- * @param margin the right margin
- */
- public void setRightInset(double margin){
- CTTextBodyProperties bodyPr = getTextBodyPr();
- if (bodyPr != null) {
- if(margin == -1) bodyPr.unsetRIns();
- else bodyPr.setRIns(Units.toEMU(margin));
- }
- }
- /**
- * Sets the top margin.
- * @see #getTopInset()
- *
- * @param margin the top margin
- */
- public void setTopInset(double margin){
- CTTextBodyProperties bodyPr = getTextBodyPr();
- if (bodyPr != null) {
- if(margin == -1) bodyPr.unsetTIns();
- else bodyPr.setTIns(Units.toEMU(margin));
- }
- }
- /**
- * @return whether to wrap words within the bounding rectangle
- */
- public boolean getWordWrap(){
- PropertyFetcher<Boolean> fetcher = new TextBodyPropertyFetcher<Boolean>(){
- public boolean fetch(CTTextBodyProperties props){
- if(props.isSetWrap()){
- setValue(props.getWrap() == STTextWrappingType.SQUARE);
- return true;
- }
- return false;
- }
- };
- fetchShapeProperty(fetcher);
- return fetcher.getValue() == null ? true : fetcher.getValue();
- }
- /**
- *
- * @param wrap whether to wrap words within the bounding rectangle
- */
- public void setWordWrap(boolean wrap){
- CTTextBodyProperties bodyPr = getTextBodyPr();
- if (bodyPr != null) {
- bodyPr.setWrap(wrap ? STTextWrappingType.SQUARE : STTextWrappingType.NONE);
- }
- }
- /**
- *
- * Specifies that a shape should be auto-fit to fully contain the text described within it.
- * Auto-fitting is when text within a shape is scaled in order to contain all the text inside
- *
- * @param value type of autofit
- */
- public void setTextAutofit(TextAutofit value){
- CTTextBodyProperties bodyPr = getTextBodyPr();
- if (bodyPr != null) {
- if(bodyPr.isSetSpAutoFit()) bodyPr.unsetSpAutoFit();
- if(bodyPr.isSetNoAutofit()) bodyPr.unsetNoAutofit();
- if(bodyPr.isSetNormAutofit()) bodyPr.unsetNormAutofit();
- switch(value){
- case NONE: bodyPr.addNewNoAutofit(); break;
- case NORMAL: bodyPr.addNewNormAutofit(); break;
- case SHAPE: bodyPr.addNewSpAutoFit(); break;
- }
- }
- }
- /**
- *
- * @return type of autofit
- */
- public TextAutofit getTextAutofit(){
- CTTextBodyProperties bodyPr = getTextBodyPr();
- if (bodyPr != null) {
- if(bodyPr.isSetNoAutofit()) return TextAutofit.NONE;
- else if (bodyPr.isSetNormAutofit()) return TextAutofit.NORMAL;
- else if (bodyPr.isSetSpAutoFit()) return TextAutofit.SHAPE;
- }
- return TextAutofit.NORMAL;
- }
- protected CTTextBodyProperties getTextBodyPr(){
- CTTextBody textBody = getTextBody(false);
- return textBody == null ? null : textBody.getBodyPr();
- }
- protected abstract CTTextBody getTextBody(boolean create);
- public Placeholder getTextType(){
- CTPlaceholder ph;
- XmlObject[] obj = getXmlObject().selectPath(
- "declare namespace p='http://schemas.openxmlformats.org/presentationml/2006/main' .//*/p:nvPr/p:ph");
- if(obj.length == 1){
- ph = (CTPlaceholder)obj[0];
- int val = ph.getType().intValue();
- return Placeholder.values()[val - 1];
- }
- else {
- return null;
- }
- }
- /**
- * Specifies that the corresponding shape should be represented by the generating application
- * as a placeholder. When a shape is considered a placeholder by the generating application
- * it can have special properties to alert the user that they may enter content into the shape.
- * Different types of placeholders are allowed and can be specified by using the placeholder
- * type attribute for this element
- *
- * @param placeholder
- */
- public void setPlaceholder(Placeholder placeholder){
- CTShape sh = (CTShape)getXmlObject();
- CTApplicationNonVisualDrawingProps nv = sh.getNvSpPr().getNvPr();
- if(placeholder == null) {
- if(nv.isSetPh()) nv.unsetPh();
- } else {
- nv.addNewPh().setType(STPlaceholderType.Enum.forInt(placeholder.ordinal() + 1));
- }
- }
- /**
- * Compute the cumulative height occupied by the text
- */
- public double getTextHeight(){
- // dry-run in a 1x1 image and return the vertical advance
- BufferedImage img = new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB);
- Graphics2D graphics = img.createGraphics();
- breakText(graphics);
- return drawParagraphs(graphics, 0, 0);
- }
- /**
- * Adjust the size of the shape so it encompasses the text inside it.
- *
- * @return a <code>Rectangle2D</code> that is the bounds of this shape.
- */
- public Rectangle2D resizeToFitText(){
- Rectangle2D anchor = getAnchor();
- if(anchor.getWidth() == 0.) throw new POIXMLException(
- "Anchor of the shape was not set.");
- double height = getTextHeight();
- height += 1; // add a pixel to compensate rounding errors
-
- anchor.setRect(anchor.getX(), anchor.getY(), anchor.getWidth(), height);
- setAnchor(anchor);
-
- return anchor;
- }
-
- /**
- * break the contained text into lines
- */
- private void breakText(Graphics2D graphics){
- if(!_isTextBroken) {
- for(XSLFTextParagraph p : _paragraphs) p.breakText(graphics);
- _isTextBroken = true;
- }
- }
- @Override
- public void drawContent(Graphics2D graphics) {
- breakText(graphics);
- RenderableShape rShape = new RenderableShape(this);
- Rectangle2D anchor = rShape.getAnchor(graphics);
- double x = anchor.getX() + getLeftInset();
- double y = anchor.getY();
-
-
- // first dry-run to calculate the total height of the text
- double textHeight = getTextHeight();
-
- Log.d("textInset", "drawContent x: " + x + ", y: " + y + ", textHeight: " + textHeight);
- switch (getVerticalAlignment()){
- case TOP:
- y += getTopInset();
- break;
- case BOTTOM:
- y += anchor.getHeight() - textHeight - getBottomInset();
- break;
- default:
- case MIDDLE:
- double delta = anchor.getHeight() - textHeight -
- getTopInset() - getBottomInset();
- y += getTopInset() + delta/2;
- break;
- }
- drawParagraphs(graphics, x, y);
- }
- /**
- * pain the paragraphs starting from top left (x,y)
- *
- * @return the vertical advance, i.e. the cumulative space occupied by the text
- */
- private double drawParagraphs(Graphics2D graphics, double x, double y) {
- double y0 = y;
- for(int i = 0; i < _paragraphs.size(); i++){
- XSLFTextParagraph p = _paragraphs.get(i);
-
- y += p.draw(graphics, x, y);
- }
-
- return y - y0;
- // double y0 = y;
- // for(int i = 0; i < _paragraphs.size(); i++){
- // XSLFTextParagraph p = _paragraphs.get(i);
- // List<TextFragment> lines = p.getTextLines();
- //
- // if(i > 0 && lines.size() > 0) {
- // // the amount of vertical white space before the paragraph
- // double spaceBefore = p.getSpaceBefore();
- // if(spaceBefore > 0) {
- // // positive value means percentage spacing of the height of the first line, e.g.
- // // the higher the first line, the bigger the space before the paragraph
- // y += spaceBefore*0.01*lines.get(0).getHeight();
- // } else {
- // // negative value means the absolute spacing in points
- // y += -spaceBefore;
- // }
- // }
- //
- // y += p.draw(graphics, x, y);
- //
- // if(i < _paragraphs.size() - 1) {
- // double spaceAfter = p.getSpaceAfter();
- // if(spaceAfter > 0) {
- // // positive value means percentage spacing of the height of the last line, e.g.
- // // the higher the last line, the bigger the space after the paragraph
- // y += spaceAfter*0.01*lines.get(lines.size() - 1).getHeight();
- // } else {
- // // negative value means the absolute spacing in points
- // y += -spaceAfter;
- // }
- // }
- // }
- // return y - y0;
- }
- @Override
- void copy(XSLFShape sh){
- super.copy(sh);
- XSLFTextShape tsh = (XSLFTextShape)sh;
- boolean srcWordWrap = tsh.getWordWrap();
- if(srcWordWrap != getWordWrap()){
- setWordWrap(srcWordWrap);
- }
- double leftInset = tsh.getLeftInset();
- if(leftInset != getLeftInset()) {
- setLeftInset(leftInset);
- }
- double rightInset = tsh.getRightInset();
- if(rightInset != getRightInset()) {
- setRightInset(rightInset);
- }
- double topInset = tsh.getTopInset();
- if(topInset != getTopInset()) {
- setTopInset(topInset);
- }
- double bottomInset = tsh.getBottomInset();
- if(bottomInset != getBottomInset()) {
- setBottomInset(bottomInset);
- }
- VerticalAlignment vAlign = tsh.getVerticalAlignment();
- if(vAlign != getVerticalAlignment()) {
- setVerticalAlignment(vAlign);
- }
- List<XSLFTextParagraph> srcP = tsh.getTextParagraphs();
- List<XSLFTextParagraph> tgtP = getTextParagraphs();
- for(int i = 0; i < srcP.size(); i++){
- XSLFTextParagraph p1 = srcP.get(i);
- XSLFTextParagraph p2 = tgtP.get(i);
- p2.copy(p1);
- }
- }
- }