diff --git a/src/ooxml/java/org/apache/poi/xssf/model/ParagraphPropertyFetcher.java b/src/ooxml/java/org/apache/poi/xssf/model/ParagraphPropertyFetcher.java new file mode 100644 index 0000000000..00f40f790c --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xssf/model/ParagraphPropertyFetcher.java @@ -0,0 +1,68 @@ +/* + * ==================================================================== + * 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.xssf.model; + +import org.openxmlformats.schemas.drawingml.x2006.main.CTTextParagraphProperties; +import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTShape; +import org.apache.xmlbeans.XmlObject; +import org.apache.poi.util.Internal; + +/** + * Used internally to navigate the paragraph text style hierarchy within a shape and fetch properties +*/ +@Internal +public abstract class ParagraphPropertyFetcher { + + private T _value; + private int _level; + + public T getValue(){ + return _value; + } + + public void setValue(T val){ + _value = val; + } + + public ParagraphPropertyFetcher(int level) { + _level = level; + } + + /** + * + * @param shape the shape being examined + * @return true if the desired property was fetched + */ + public boolean fetch(CTShape shape) { + + XmlObject[] o = shape.selectPath( + "declare namespace xdr='http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing' " + + "declare namespace a='http://schemas.openxmlformats.org/drawingml/2006/main' " + + ".//xdr:txBody/a:lstStyle/a:lvl" + (_level + 1) + "pPr" + ); + if (o.length == 1) { + CTTextParagraphProperties props = (CTTextParagraphProperties) o[0]; + return fetch(props); + } + return false; + } + + public abstract boolean fetch(CTTextParagraphProperties props); +} \ No newline at end of file diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/ListAutoNumber.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/ListAutoNumber.java new file mode 100644 index 0000000000..5108813b28 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/ListAutoNumber.java @@ -0,0 +1,103 @@ +/* + * ==================================================================== + * 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.xssf.usermodel; + +/** + * Specifies type of automatic numbered bullet points that should be applied to a paragraph. + */ +public enum ListAutoNumber { + /** + * (a), (b), (c), ... + */ + ALPHA_LC_PARENT_BOTH, + /** + * (A), (B), (C), ... + */ + ALPHA_UC_PARENT_BOTH, + /** + * a), b), c), ... + */ + ALPHA_LC_PARENT_R, + /** + * A), B), C), ... + */ + ALPHA_UC_PARENT_R, + /** + * a., b., c., ... + */ + ALPHA_LC_PERIOD, + /** + * A., B., C., ... + */ + ALPHA_UC_PERIOD, + /** + * (1), (2), (3), ... + */ + ARABIC_PARENT_BOTH, + /** + * 1), 2), 3), ... + */ + ARABIC_PARENT_R, + + /** + * 1., 2., 3., ... + */ + ARABIC_PERIOD, + /** + * 1, 2, 3, ... + */ + ARABIC_PLAIN, + + /** + * (i), (ii), (iii), ... + */ + ROMAN_LC_PARENT_BOTH, + /** + * (I), (II), (III), ... + */ + ROMAN_UC_PARENT_BOTH, + /** + * i), ii), iii), ... + */ + ROMAN_LC_PARENT_R, + /** + * I), II), III), ... + */ + ROMAN_UC_PARENT_R, + /** + * i., ii., iii., ... + */ + ROMAN_LC_PERIOD , + /** + * I., II., III., ... + */ + ROMAN_UC_PERIOD, + /** + * Dbl-byte circle numbers + */ + CIRCLE_NUM_DB_PLAIN, + /** + * Wingdings black circle numbers + */ + CIRCLE_NUM_WD_BLACK_PLAIN, + /** + * Wingdings white circle numbers + */ + CIRCLE_NUM_WD_WHITE_PLAIN +} \ No newline at end of file diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/TextAlign.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/TextAlign.java new file mode 100644 index 0000000000..b93a885c20 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/TextAlign.java @@ -0,0 +1,48 @@ +/* + * ==================================================================== + * 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.xssf.usermodel; + +/** + * Specified a list of text alignment types + */ +public enum TextAlign { + /** + * Align text to the left margin. + */ + LEFT, + /** + * Align text in the center. + */ + CENTER, + + /** + * Align text to the right margin. + */ + RIGHT, + + /** + * Align text so that it is justified across the whole line. It + * is smart in the sense that it will not justify sentences + * which are short + */ + JUSTIFY, + JUSTIFY_LOW, + DIST, + THAI_DIST +} diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/TextAutofit.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/TextAutofit.java new file mode 100644 index 0000000000..6e01b134fd --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/TextAutofit.java @@ -0,0 +1,57 @@ +/* + * ==================================================================== + * 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.xssf.usermodel; + +/** + * Specifies a list of auto-fit types. + *

+ * Autofit 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 + *

+ */ +public enum TextAutofit { + /** + * Specifies that text within the text body should not be auto-fit to the bounding box. + * Auto-fitting is when text within a text box is scaled in order to remain inside + * the text box. + */ + NONE, + /** + * Specifies that text within the text body should be normally auto-fit to the bounding box. + * Autofitting is when text within a text box is scaled in order to remain inside the text box. + * + *

+ * Example: Consider the situation where a user is building a diagram and needs + * to have the text for each shape that they are using stay within the bounds of the shape. + * An easy way this might be done is by using NORMAL autofit + *

+ */ + NORMAL, + /** + * 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. + * + *

+ * Example: Consider the situation where a user is building a diagram and needs to have + * the text for each shape that they are using stay within the bounds of the shape. + * An easy way this might be done is by using SHAPE autofit + *

+ */ + SHAPE +} diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/TextCap.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/TextCap.java new file mode 100644 index 0000000000..9202e92b42 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/TextCap.java @@ -0,0 +1,30 @@ +/* + * ==================================================================== + * 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.xssf.usermodel; + +/** + * Text Capitalization that is to be applied to the text run. This is a render-only + * modification and does not affect the actual characters stored in the text run. + */ +public enum TextCap { + NONE, + SMALL, + ALL +} diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/TextDirection.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/TextDirection.java new file mode 100644 index 0000000000..111ab652fc --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/TextDirection.java @@ -0,0 +1,48 @@ +/* + * ==================================================================== + * 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.xssf.usermodel; + +/** + * Vertical Text Types + */ +public enum TextDirection { + /** + * Horizontal text. This should be default. + */ + HORIZONTAL, + /** + * Vertical orientation. + * (each line is 90 degrees rotated clockwise, so it goes + * from top to bottom; each next line is to the left from + * the previous one). + */ + VERTICAL, + /** + * Vertical orientation. + * (each line is 270 degrees rotated clockwise, so it goes + * from bottom to top; each next line is to the right from + * the previous one). + */ + VERTICAL_270, + /** + * Determines if all of the text is vertical + * ("one letter on top of another"). + */ + STACKED; +} diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/TextFontAlign.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/TextFontAlign.java new file mode 100644 index 0000000000..96417980c4 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/TextFontAlign.java @@ -0,0 +1,47 @@ +/* + * ==================================================================== + * 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.xssf.usermodel; + +/** + * Specified a list of text font alignment types + */ +public enum TextFontAlign { + /** + * Automatic alignment + */ + AUTO, + /** + * Align text to the top. + */ + TOP, + /** + * Align text in the center. + */ + CENTER, + + /** + * Align text to the baseline. + */ + BASELINE, + + /** + * Align text to the bottom. + */ + BOTTOM +} diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/TextHorizontalOverflow.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/TextHorizontalOverflow.java new file mode 100644 index 0000000000..b1fe0c18d3 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/TextHorizontalOverflow.java @@ -0,0 +1,36 @@ +/* + * ==================================================================== + * 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.xssf.usermodel; + +/** + * Text Horizontal Overflow + */ +public enum TextHorizontalOverflow { + /** + * When a big character does not fit into a line, allow a + * horizontal overflow. + */ + OVERFLOW, + + /** + * When a big character does not fit into a line, clip it at + * the proper horizontal overflow. + */ + CLIP +} diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/TextVerticalOverflow.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/TextVerticalOverflow.java new file mode 100644 index 0000000000..87d3e40189 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/TextVerticalOverflow.java @@ -0,0 +1,41 @@ +/* + * ==================================================================== + * 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.xssf.usermodel; + +/** + * Text Vertical Overflow + */ +public enum TextVerticalOverflow { + /** + * Overflow the text and pay no attention to top and bottom barriers. + */ + OVERFLOW, + + /** + * Pay attention to top and bottom barriers. Use an + * ellipsis to denote that there is text which is not visible. + */ + ELLIPSIS, + + /** + * Pay attention to top and bottom barriers. Provide no + * indication that there is text which is not visible. + */ + CLIP +} diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFLineBreak.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFLineBreak.java new file mode 100644 index 0000000000..99596992b9 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFLineBreak.java @@ -0,0 +1,45 @@ +/* + * ==================================================================== + * 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.xssf.usermodel; + +import org.openxmlformats.schemas.drawingml.x2006.main.CTRegularTextRun; +import org.openxmlformats.schemas.drawingml.x2006.main.CTTextCharacterProperties; + +class XSSFLineBreak extends XSSFTextRun { + private final CTTextCharacterProperties _brProps; + + XSSFLineBreak(CTRegularTextRun r, XSSFTextParagraph p, CTTextCharacterProperties brProps){ + super(r, p); + _brProps = brProps; + } + + @Override + protected CTTextCharacterProperties getRPr(){ + return _brProps; + } + + /** + * Always throws IllegalStateException. You cannot change text of a line break. + */ + public void setText(String text){ + throw new IllegalStateException("You cannot change text of a line break, it is always '\\n'"); + } + +} diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFTextParagraph.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFTextParagraph.java new file mode 100644 index 0000000000..28c52bdd23 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFTextParagraph.java @@ -0,0 +1,861 @@ +/* ==================================================================== + 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.xssf.usermodel; + + +import org.apache.poi.util.Internal; +import org.apache.poi.util.Units; +import org.apache.poi.xssf.usermodel.TextAlign; +import org.apache.poi.xssf.model.ParagraphPropertyFetcher; +import org.apache.xmlbeans.XmlObject; +import org.openxmlformats.schemas.drawingml.x2006.main.*; +import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTShape; + +import java.awt.Color; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +/** + * Represents a paragraph of text within the containing text body. + * The paragraph is the highest level text separation mechanism. + */ +public class XSSFTextParagraph implements Iterable{ + private final CTTextParagraph _p; + private final CTShape _shape; + private final List _runs; + + XSSFTextParagraph(CTTextParagraph p, CTShape ctShape){ + _p = p; + _shape = ctShape; + _runs = new ArrayList(); + + for(XmlObject ch : _p.selectPath("*")){ + if(ch instanceof CTRegularTextRun){ + CTRegularTextRun r = (CTRegularTextRun)ch; + _runs.add(new XSSFTextRun(r, this)); + } else if (ch instanceof CTTextLineBreak){ + CTTextLineBreak br = (CTTextLineBreak)ch; + CTRegularTextRun r = CTRegularTextRun.Factory.newInstance(); + r.setRPr(br.getRPr()); + r.setT("\n"); + _runs.add(new XSSFTextRun(r, this)); + } else if (ch instanceof CTTextField){ + CTTextField f = (CTTextField)ch; + CTRegularTextRun r = CTRegularTextRun.Factory.newInstance(); + r.setRPr(f.getRPr()); + r.setT(f.getT()); + _runs.add(new XSSFTextRun(r, this)); + } + } + } + + public String getText(){ + StringBuilder out = new StringBuilder(); + for (XSSFTextRun r : _runs) { + out.append(r.getText()); + } + return out.toString(); + } + + @Internal + public CTTextParagraph getXmlObject(){ + return _p; + } + + @Internal + public CTShape getParentShape(){ + return _shape; + } + + public List getTextRuns(){ + return _runs; + } + + public Iterator iterator(){ + return _runs.iterator(); + } + + /** + * Add a new run of text + * + * @return a new run of text + */ + public XSSFTextRun addNewTextRun(){ + CTRegularTextRun r = _p.addNewR(); + CTTextCharacterProperties rPr = r.addNewRPr(); + rPr.setLang("en-US"); + XSSFTextRun run = new XSSFTextRun(r, this); + _runs.add(run); + return run; + } + + /** + * Insert a line break + * + * @return text run representing this line break ('\n') + */ + public XSSFTextRun addLineBreak(){ + CTTextLineBreak br = _p.addNewBr(); + CTTextCharacterProperties brProps = br.addNewRPr(); + if(_runs.size() > 0){ + // by default line break has the font size of the last text run + CTTextCharacterProperties prevRun = _runs.get(_runs.size() - 1).getRPr(); + brProps.set(prevRun); + } + CTRegularTextRun r = CTRegularTextRun.Factory.newInstance(); + r.setRPr(brProps); + r.setT("\n"); + XSSFTextRun run = new XSSFLineBreak(r, this, brProps); + _runs.add(run); + return run; + } + + /** + * Returns the alignment that is applied to the paragraph. + * + * If this attribute is omitted, then a value of left is implied. + * @return alignment that is applied to the paragraph + */ + public TextAlign getTextAlign(){ + ParagraphPropertyFetcher fetcher = new ParagraphPropertyFetcher(getLevel()){ + public boolean fetch(CTTextParagraphProperties props){ + if(props.isSetAlgn()){ + TextAlign val = TextAlign.values()[props.getAlgn().intValue() - 1]; + setValue(val); + return true; + } + return false; + } + }; + fetchParagraphProperty(fetcher); + return fetcher.getValue() == null ? TextAlign.LEFT : fetcher.getValue(); + } + + /** + * Specifies the alignment that is to be applied to the paragraph. + * Possible values for this include left, right, centered, justified and distributed, + * see {@link org.apache.poi.xssf.usermodel.TextAlign}. + * + * @param align text align + */ + public void setTextAlign(TextAlign align){ + CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr(); + if(align == null) { + if(pr.isSetAlgn()) pr.unsetAlgn(); + } else { + pr.setAlgn(STTextAlignType.Enum.forInt(align.ordinal() + 1)); + } + } + + /** + * Returns where vertically on a line of text the actual words are positioned. This deals + * with vertical placement of the characters with respect to the baselines. + * + * If this attribute is omitted, then a value of baseline is implied. + * @return alignment that is applied to the paragraph + */ + public TextFontAlign getTextFontAlign(){ + ParagraphPropertyFetcher fetcher = new ParagraphPropertyFetcher(getLevel()){ + public boolean fetch(CTTextParagraphProperties props){ + if(props.isSetAlgn()){ + TextFontAlign val = TextFontAlign.values()[props.getFontAlgn().intValue() - 1]; + setValue(val); + return true; + } + return false; + } + }; + fetchParagraphProperty(fetcher); + return fetcher.getValue() == null ? TextFontAlign.BASELINE : fetcher.getValue(); + } + + /** + * Determines where vertically on a line of text the actual words are positioned. This deals + * with vertical placement of the characters with respect to the baselines. For instance + * having text anchored to the top baseline, anchored to the bottom baseline, centered in + * between, etc. + * + * @param align text font align + */ + public void setTextFontAlign(TextFontAlign align){ + CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr(); + if(align == null) { + if(pr.isSetFontAlgn()) pr.unsetFontAlgn(); + } else { + pr.setFontAlgn(STTextFontAlignType.Enum.forInt(align.ordinal() + 1)); + } + } + + /** + * @return the font to be used on bullet characters within a given paragraph + */ + public String getBulletFont(){ + ParagraphPropertyFetcher fetcher = new ParagraphPropertyFetcher(getLevel()){ + public boolean fetch(CTTextParagraphProperties props){ + if(props.isSetBuFont()){ + setValue(props.getBuFont().getTypeface()); + return true; + } + return false; + } + }; + fetchParagraphProperty(fetcher); + return fetcher.getValue(); + } + + public void setBulletFont(String typeface){ + CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr(); + CTTextFont font = pr.isSetBuFont() ? pr.getBuFont() : pr.addNewBuFont(); + font.setTypeface(typeface); + } + + /** + * @return the character to be used in place of the standard bullet point + */ + public String getBulletCharacter(){ + ParagraphPropertyFetcher fetcher = new ParagraphPropertyFetcher(getLevel()){ + public boolean fetch(CTTextParagraphProperties props){ + if(props.isSetBuChar()){ + setValue(props.getBuChar().getChar()); + return true; + } + return false; + } + }; + fetchParagraphProperty(fetcher); + return fetcher.getValue(); + } + + public void setBulletCharacter(String str){ + CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr(); + CTTextCharBullet c = pr.isSetBuChar() ? pr.getBuChar() : pr.addNewBuChar(); + c.setChar(str); + } + + /** + * + * @return the color of bullet characters within a given paragraph. + * A null value means to use the text font color. + */ + public Color getBulletFontColor(){ + ParagraphPropertyFetcher fetcher = new ParagraphPropertyFetcher(getLevel()){ + public boolean fetch(CTTextParagraphProperties props){ + if(props.isSetBuClr()){ + if(props.getBuClr().isSetSrgbClr()){ + CTSRgbColor clr = props.getBuClr().getSrgbClr(); + byte[] rgb = clr.getVal(); + setValue(new Color(0xFF & rgb[0], 0xFF & rgb[1], 0xFF & rgb[2])); + return true; + } + } + return false; + } + }; + fetchParagraphProperty(fetcher); + return fetcher.getValue(); + } + + /** + * Set the color to be used on bullet characters within a given paragraph. + * + * @param color the bullet color + */ + public void setBulletFontColor(Color color){ + CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr(); + CTColor c = pr.isSetBuClr() ? pr.getBuClr() : pr.addNewBuClr(); + CTSRgbColor clr = c.isSetSrgbClr() ? c.getSrgbClr() : c.addNewSrgbClr(); + clr.setVal(new byte[]{(byte) color.getRed(), (byte) color.getGreen(), (byte) color.getBlue()}); + } + + /** + * Returns the bullet size that is to be used within a paragraph. + * This may be specified in two different ways, percentage spacing and font point spacing: + *

+ * If bulletSize >= 0, then bulletSize is a percentage of the font size. + * If bulletSize < 0, then it specifies the size in points + *

+ * + * @return the bullet size + */ + public double getBulletFontSize(){ + ParagraphPropertyFetcher fetcher = new ParagraphPropertyFetcher(getLevel()){ + public boolean fetch(CTTextParagraphProperties props){ + if(props.isSetBuSzPct()){ + setValue(props.getBuSzPct().getVal() * 0.001); + return true; + } + if(props.isSetBuSzPts()){ + setValue( - props.getBuSzPts().getVal() * 0.01); + return true; + } + return false; + } + }; + fetchParagraphProperty(fetcher); + return fetcher.getValue() == null ? 100 : fetcher.getValue(); + } + + /** + * Sets the bullet size that is to be used within a paragraph. + * This may be specified in two different ways, percentage spacing and font point spacing: + *

+ * If bulletSize >= 0, then bulletSize is a percentage of the font size. + * If bulletSize < 0, then it specifies the size in points + *

+ */ + public void setBulletFontSize(double bulletSize){ + CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr(); + + if(bulletSize >= 0) { + CTTextBulletSizePercent pt = pr.isSetBuSzPct() ? pr.getBuSzPct() : pr.addNewBuSzPct(); + pt.setVal((int)(bulletSize*1000)); + if(pr.isSetBuSzPts()) pr.unsetBuSzPts(); + } else { + CTTextBulletSizePoint pt = pr.isSetBuSzPts() ? pr.getBuSzPts() : pr.addNewBuSzPts(); + pt.setVal((int)(-bulletSize*100)); + if(pr.isSetBuSzPct()) pr.unsetBuSzPct(); + } + } + + /** + * Specifies the indent size that will be applied to the first line of text in the paragraph. + * + * @param value the indent in points. + */ + public void setIndent(double value){ + CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr(); + if(value == -1) { + if(pr.isSetIndent()) pr.unsetIndent(); + } else { + pr.setIndent(Units.toEMU(value)); + } + } + + /** + * + * @return the indent applied to the first line of text in the paragraph. + */ + public double getIndent(){ + ParagraphPropertyFetcher fetcher = new ParagraphPropertyFetcher(getLevel()){ + public boolean fetch(CTTextParagraphProperties props){ + if(props.isSetIndent()){ + setValue(Units.toPoints(props.getIndent())); + return true; + } + return false; + } + }; + fetchParagraphProperty(fetcher); + + return fetcher.getValue() == null ? 0 : fetcher.getValue(); + } + + + + /** + * Specifies the left margin of the paragraph. This is specified in addition to the text body + * inset and applies only to this text paragraph. That is the text body inset and the LeftMargin + * attributes are additive with respect to the text position. + * + * @param value the left margin of the paragraph + */ + public void setLeftMargin(double value){ + CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr(); + if(value == -1) { + if(pr.isSetMarL()) pr.unsetMarL(); + } else { + pr.setMarL(Units.toEMU(value)); + } + + } + + /** + * + * @return the left margin of the paragraph + */ + public double getLeftMargin(){ + ParagraphPropertyFetcher fetcher = new ParagraphPropertyFetcher(getLevel()){ + public boolean fetch(CTTextParagraphProperties props){ + if(props.isSetMarL()){ + double val = Units.toPoints(props.getMarL()); + setValue(val); + return true; + } + return false; + } + }; + fetchParagraphProperty(fetcher); + // if the marL attribute is omitted, then a value of 347663 is implied + return fetcher.getValue() == null ? 0 : fetcher.getValue(); + } + + /** + * Specifies the right margin of the paragraph. This is specified in addition to the text body + * inset and applies only to this text paragraph. That is the text body inset and the marR + * attributes are additive with respect to the text position. + * + * @param value the right margin of the paragraph + */ + public void setRightMargin(double value){ + CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr(); + if(value == -1) { + if(pr.isSetMarR()) pr.unsetMarR(); + } else { + pr.setMarR(Units.toEMU(value)); + } + + } + + /** + * + * @return the right margin of the paragraph + */ + public double getRightMargin(){ + ParagraphPropertyFetcher fetcher = new ParagraphPropertyFetcher(getLevel()){ + public boolean fetch(CTTextParagraphProperties props){ + if(props.isSetMarR()){ + double val = Units.toPoints(props.getMarR()); + setValue(val); + return true; + } + return false; + } + }; + fetchParagraphProperty(fetcher); + // if the marL attribute is omitted, then a value of 347663 is implied + return fetcher.getValue() == null ? 0 : fetcher.getValue(); + } + + /** + * + * @return the default size for a tab character within this paragraph in points + */ + public double getDefaultTabSize(){ + ParagraphPropertyFetcher fetcher = new ParagraphPropertyFetcher(getLevel()){ + public boolean fetch(CTTextParagraphProperties props){ + if(props.isSetDefTabSz()){ + double val = Units.toPoints(props.getDefTabSz()); + setValue(val); + return true; + } + return false; + } + }; + fetchParagraphProperty(fetcher); + return fetcher.getValue() == null ? 0 : fetcher.getValue(); + } + + public double getTabStop(final int idx){ + ParagraphPropertyFetcher fetcher = new ParagraphPropertyFetcher(getLevel()){ + public boolean fetch(CTTextParagraphProperties props){ + if(props.isSetTabLst()){ + CTTextTabStopList tabStops = props.getTabLst(); + if(idx < tabStops.sizeOfTabArray() ) { + CTTextTabStop ts = tabStops.getTabArray(idx); + double val = Units.toPoints(ts.getPos()); + setValue(val); + return true; + } + } + return false; + } + }; + fetchParagraphProperty(fetcher); + return fetcher.getValue() == null ? 0. : fetcher.getValue(); + } + /** + * Add a single tab stop to be used on a line of text when there are one or more tab characters + * present within the text. + * + * @param value the position of the tab stop relative to the left margin + */ + public void addTabStop(double value){ + CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr(); + CTTextTabStopList tabStops = pr.isSetTabLst() ? pr.getTabLst() : pr.addNewTabLst(); + tabStops.addNewTab().setPos(Units.toEMU(value)); + } + + /** + * This element specifies the vertical line spacing that is to be used within a paragraph. + * This may be specified in two different ways, percentage spacing and font point spacing: + *

+ * If linespacing >= 0, then linespacing is a percentage of normal line height + * If linespacing < 0, the absolute value of linespacing is the spacing in points + *

+ * Examples: + *

+     *      // spacing will be 120% of the size of the largest text on each line
+     *      paragraph.setLineSpacing(120);
+     *
+     *      // spacing will be 200% of the size of the largest text on each line
+     *      paragraph.setLineSpacing(200);
+     *
+     *      // spacing will be 48 points
+     *      paragraph.setLineSpacing(-48.0);
+     * 
+ * + * @param linespacing the vertical line spacing + */ + public void setLineSpacing(double linespacing){ + CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr(); + CTTextSpacing spc = CTTextSpacing.Factory.newInstance(); + if(linespacing >= 0) spc.addNewSpcPct().setVal((int)(linespacing*1000)); + else spc.addNewSpcPts().setVal((int)(-linespacing*100)); + pr.setLnSpc(spc); + } + + /** + * Returns the vertical line spacing that is to be used within a paragraph. + * This may be specified in two different ways, percentage spacing and font point spacing: + *

+ * If linespacing >= 0, then linespacing is a percentage of normal line height. + * If linespacing < 0, the absolute value of linespacing is the spacing in points + *

+ * + * @return the vertical line spacing. + */ + public double getLineSpacing(){ + ParagraphPropertyFetcher fetcher = new ParagraphPropertyFetcher(getLevel()){ + public boolean fetch(CTTextParagraphProperties props){ + if(props.isSetLnSpc()){ + CTTextSpacing spc = props.getLnSpc(); + + if(spc.isSetSpcPct()) setValue( spc.getSpcPct().getVal()*0.001 ); + else if (spc.isSetSpcPts()) setValue( -spc.getSpcPts().getVal()*0.01 ); + return true; + } + return false; + } + }; + fetchParagraphProperty(fetcher); + + double lnSpc = fetcher.getValue() == null ? 100 : fetcher.getValue(); + if(lnSpc > 0) { + // check if the percentage value is scaled + CTTextNormalAutofit normAutofit = _shape.getTxBody().getBodyPr().getNormAutofit(); + if(normAutofit != null) { + double scale = 1 - (double)normAutofit.getLnSpcReduction() / 100000; + lnSpc *= scale; + } + } + + return lnSpc; + } + + /** + * Set the amount of vertical white space that will be present before the paragraph. + * This space is specified in either percentage or points: + *

+ * If spaceBefore >= 0, then space is a percentage of normal line height. + * If spaceBefore < 0, the absolute value of linespacing is the spacing in points + *

+ * Examples: + *

+     *      // The paragraph will be formatted to have a spacing before the paragraph text.
+     *      // The spacing will be 200% of the size of the largest text on each line
+     *      paragraph.setSpaceBefore(200);
+     *
+     *      // The spacing will be a size of 48 points
+     *      paragraph.setSpaceBefore(-48.0);
+     * 
+ * + * @param spaceBefore the vertical white space before the paragraph. + */ + public void setSpaceBefore(double spaceBefore){ + CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr(); + CTTextSpacing spc = CTTextSpacing.Factory.newInstance(); + if(spaceBefore >= 0) spc.addNewSpcPct().setVal((int)(spaceBefore*1000)); + else spc.addNewSpcPts().setVal((int)(-spaceBefore*100)); + pr.setSpcBef(spc); + } + + /** + * The amount of vertical white space before the paragraph + * This may be specified in two different ways, percentage spacing and font point spacing: + *

+ * If spaceBefore >= 0, then space is a percentage of normal line height. + * If spaceBefore < 0, the absolute value of linespacing is the spacing in points + *

+ * + * @return the vertical white space before the paragraph + */ + public double getSpaceBefore(){ + ParagraphPropertyFetcher fetcher = new ParagraphPropertyFetcher(getLevel()){ + public boolean fetch(CTTextParagraphProperties props){ + if(props.isSetSpcBef()){ + CTTextSpacing spc = props.getSpcBef(); + + if(spc.isSetSpcPct()) setValue( spc.getSpcPct().getVal()*0.001 ); + else if (spc.isSetSpcPts()) setValue( -spc.getSpcPts().getVal()*0.01 ); + return true; + } + return false; + } + }; + fetchParagraphProperty(fetcher); + + double spcBef = fetcher.getValue() == null ? 0 : fetcher.getValue(); + return spcBef; + } + + /** + * Set the amount of vertical white space that will be present after the paragraph. + * This space is specified in either percentage or points: + *

+ * If spaceAfter >= 0, then space is a percentage of normal line height. + * If spaceAfter < 0, the absolute value of linespacing is the spacing in points + *

+ * Examples: + *

+     *      // The paragraph will be formatted to have a spacing after the paragraph text.
+     *      // The spacing will be 200% of the size of the largest text on each line
+     *      paragraph.setSpaceAfter(200);
+     *
+     *      // The spacing will be a size of 48 points
+     *      paragraph.setSpaceAfter(-48.0);
+     * 
+ * + * @param spaceAfter the vertical white space after the paragraph. + */ + public void setSpaceAfter(double spaceAfter){ + CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr(); + CTTextSpacing spc = CTTextSpacing.Factory.newInstance(); + if(spaceAfter >= 0) spc.addNewSpcPct().setVal((int)(spaceAfter*1000)); + else spc.addNewSpcPts().setVal((int)(-spaceAfter*100)); + pr.setSpcAft(spc); + } + + /** + * The amount of vertical white space after the paragraph + * This may be specified in two different ways, percentage spacing and font point spacing: + *

+ * If spaceBefore >= 0, then space is a percentage of normal line height. + * If spaceBefore < 0, the absolute value of linespacing is the spacing in points + *

+ * + * @return the vertical white space after the paragraph + */ + public double getSpaceAfter(){ + ParagraphPropertyFetcher fetcher = new ParagraphPropertyFetcher(getLevel()){ + public boolean fetch(CTTextParagraphProperties props){ + if(props.isSetSpcAft()){ + CTTextSpacing spc = props.getSpcAft(); + + if(spc.isSetSpcPct()) setValue( spc.getSpcPct().getVal()*0.001 ); + else if (spc.isSetSpcPts()) setValue( -spc.getSpcPts().getVal()*0.01 ); + return true; + } + return false; + } + }; + fetchParagraphProperty(fetcher); + return fetcher.getValue() == null ? 0 : fetcher.getValue(); + } + + /** + * Specifies the particular level text properties that this paragraph will follow. + * The value for this attribute formats the text according to the corresponding level + * paragraph properties defined in the list of styles associated with the body of text + * that this paragraph belongs to (therefore in the parent shape). + *

+ * Note that the closest properties object to the text is used, therefore if there is + * a conflict between the text paragraph properties and the list style properties for + * this level then the text paragraph properties will take precedence. + *

+ * + * @param level the level (0 ... 4) + */ + public void setLevel(int level){ + CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr(); + + pr.setLvl(level); + } + + /** + * Returns the level of text properties that this paragraph will follow. + * + * @return the text level of this paragraph (0-based). Default is 0. + */ + public int getLevel(){ + CTTextParagraphProperties pr = _p.getPPr(); + if(pr == null) return 0; + + return pr.getLvl(); + } + + + /** + * Returns whether this paragraph has bullets + */ + public boolean isBullet() { + ParagraphPropertyFetcher fetcher = new ParagraphPropertyFetcher(getLevel()){ + public boolean fetch(CTTextParagraphProperties props){ + if(props.isSetBuNone()) { + setValue(false); + return true; + } + if(props.isSetBuFont() || props.isSetBuChar() || props.isSetBuAutoNum()){ + setValue(true); + return true; + } + return false; + } + }; + fetchParagraphProperty(fetcher); + return fetcher.getValue() == null ? false : fetcher.getValue(); + } + + /** + * Set or unset this paragraph as a bullet point + * + * @param flag whether text in this paragraph has bullets + */ + public void setBullet(boolean flag) { + if(isBullet() == flag) return; + + CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr(); + if(!flag) { + pr.addNewBuNone(); + + if(pr.isSetBuAutoNum()) pr.unsetBuAutoNum(); + if(pr.isSetBuBlip()) pr.unsetBuBlip(); + if(pr.isSetBuChar()) pr.unsetBuChar(); + if(pr.isSetBuClr()) pr.unsetBuClr(); + if(pr.isSetBuClrTx()) pr.unsetBuClrTx(); + if(pr.isSetBuFont()) pr.unsetBuFont(); + if(pr.isSetBuFontTx()) pr.unsetBuFontTx(); + if(pr.isSetBuSzPct()) pr.unsetBuSzPct(); + if(pr.isSetBuSzPts()) pr.unsetBuSzPts(); + if(pr.isSetBuSzTx()) pr.unsetBuSzTx(); + } else { + if(pr.isSetBuNone()) pr.unsetBuNone(); + if(!pr.isSetBuFont()) pr.addNewBuFont().setTypeface("Arial"); + if(!pr.isSetBuAutoNum()) pr.addNewBuChar().setChar("\u2022"); + } + } + + /** + * Set this paragraph as an automatic numbered bullet point + * + * @param scheme type of auto-numbering + * @param startAt the number that will start number for a given sequence of automatically + * numbered bullets (1-based). + */ + public void setBullet(ListAutoNumber scheme, int startAt) { + if(startAt < 1) throw new IllegalArgumentException("Start Number must be greater or equal that 1") ; + CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr(); + CTTextAutonumberBullet lst = pr.isSetBuAutoNum() ? pr.getBuAutoNum() : pr.addNewBuAutoNum(); + lst.setType(STTextAutonumberScheme.Enum.forInt(scheme.ordinal() + 1)); + lst.setStartAt(startAt); + + if(!pr.isSetBuFont()) pr.addNewBuFont().setTypeface("Arial"); + if(pr.isSetBuNone()) pr.unsetBuNone(); + // remove these elements if present as it results in invalid content when opening in Excel. + if(pr.isSetBuBlip()) pr.unsetBuBlip(); + if(pr.isSetBuChar()) pr.unsetBuChar(); + } + + /** + * Set this paragraph as an automatic numbered bullet point + * + * @param scheme type of auto-numbering + */ + public void setBullet(ListAutoNumber scheme) { + CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr(); + CTTextAutonumberBullet lst = pr.isSetBuAutoNum() ? pr.getBuAutoNum() : pr.addNewBuAutoNum(); + lst.setType(STTextAutonumberScheme.Enum.forInt(scheme.ordinal() + 1)); + + if(!pr.isSetBuFont()) pr.addNewBuFont().setTypeface("Arial"); + if(pr.isSetBuNone()) pr.unsetBuNone(); + // remove these elements if present as it results in invalid content when opening in Excel. + if(pr.isSetBuBlip()) pr.unsetBuBlip(); + if(pr.isSetBuChar()) pr.unsetBuChar(); + } + + /** + * Returns whether this paragraph has automatic numbered bullets + */ + public boolean isBulletAutoNumber() { + ParagraphPropertyFetcher fetcher = new ParagraphPropertyFetcher(getLevel()){ + public boolean fetch(CTTextParagraphProperties props){ + if(props.isSetBuAutoNum()) { + setValue(true); + return true; + } + return false; + } + }; + fetchParagraphProperty(fetcher); + return fetcher.getValue() == null ? false : fetcher.getValue(); + } + + /** + * Returns the starting number if this paragraph has automatic numbered bullets, otherwise returns 0 + */ + public int getBulletAutoNumberStart() { + ParagraphPropertyFetcher fetcher = new ParagraphPropertyFetcher(getLevel()){ + public boolean fetch(CTTextParagraphProperties props){ + if(props.isSetBuAutoNum() && props.getBuAutoNum().isSetStartAt()) { + setValue(props.getBuAutoNum().getStartAt()); + return true; + } + return false; + } + }; + fetchParagraphProperty(fetcher); + return fetcher.getValue() == null ? 0 : fetcher.getValue(); + } + + /** + * Returns the auto number scheme if this paragraph has automatic numbered bullets, otherwise returns ListAutoNumber.ARABIC_PLAIN + */ + public ListAutoNumber getBulletAutoNumberScheme() { + ParagraphPropertyFetcher fetcher = new ParagraphPropertyFetcher(getLevel()){ + public boolean fetch(CTTextParagraphProperties props){ + if(props.isSetBuAutoNum()) { + setValue(ListAutoNumber.values()[props.getBuAutoNum().getType().intValue() - 1]); + return true; + } + return false; + } + }; + fetchParagraphProperty(fetcher); + + // Note: documentation does not define a default, return ListAutoNumber.ARABIC_PLAIN (1,2,3...) + return fetcher.getValue() == null ? ListAutoNumber.ARABIC_PLAIN : fetcher.getValue(); + } + + + @SuppressWarnings("rawtypes") + private boolean fetchParagraphProperty(ParagraphPropertyFetcher visitor){ + boolean ok = false; + + if(_p.isSetPPr()) ok = visitor.fetch(_p.getPPr()); + + if(!ok) { + ok = visitor.fetch(_shape); + } + + return ok; + } + + @Override + public String toString(){ + return "[" + getClass() + "]" + getText(); + } +} diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFTextRun.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFTextRun.java new file mode 100644 index 0000000000..5b63e94f1e --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFTextRun.java @@ -0,0 +1,356 @@ +/* ==================================================================== + 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.xssf.usermodel; + +import org.openxmlformats.schemas.drawingml.x2006.main.CTRegularTextRun; +import org.openxmlformats.schemas.drawingml.x2006.main.CTSRgbColor; +import org.openxmlformats.schemas.drawingml.x2006.main.CTSolidColorFillProperties; +import org.openxmlformats.schemas.drawingml.x2006.main.CTTextCharacterProperties; +import org.openxmlformats.schemas.drawingml.x2006.main.CTTextFont; +import org.openxmlformats.schemas.drawingml.x2006.main.CTTextNormalAutofit; +import org.openxmlformats.schemas.drawingml.x2006.main.STTextStrikeType; +import org.openxmlformats.schemas.drawingml.x2006.main.STTextUnderlineType; + +import java.awt.Color; + +/** + * Represents a run of text within the containing text body. The run element is the + * lowest level text separation mechanism within a text body. + */ +public class XSSFTextRun { + private final CTRegularTextRun _r; + private final XSSFTextParagraph _p; + + XSSFTextRun(CTRegularTextRun r, XSSFTextParagraph p){ + _r = r; + _p = p; + } + + XSSFTextParagraph getParentParagraph(){ + return _p; + } + + public String getText(){ + return _r.getT(); + } + + public void setText(String text){ + _r.setT(text); + } + + public CTRegularTextRun getXmlObject(){ + return _r; + } + + public void setFontColor(Color color){ + CTTextCharacterProperties rPr = getRPr(); + CTSolidColorFillProperties fill = rPr.isSetSolidFill() ? rPr.getSolidFill() : rPr.addNewSolidFill(); + CTSRgbColor clr = fill.isSetSrgbClr() ? fill.getSrgbClr() : fill.addNewSrgbClr(); + clr.setVal(new byte[]{(byte)color.getRed(), (byte)color.getGreen(), (byte)color.getBlue()}); + + if(fill.isSetHslClr()) fill.unsetHslClr(); + if(fill.isSetPrstClr()) fill.unsetPrstClr(); + if(fill.isSetSchemeClr()) fill.unsetSchemeClr(); + if(fill.isSetScrgbClr()) fill.unsetScrgbClr(); + if(fill.isSetSysClr()) fill.unsetSysClr(); + + } + + public Color getFontColor(){ + + CTTextCharacterProperties rPr = getRPr(); + if(rPr.isSetSolidFill()){ + CTSolidColorFillProperties fill = rPr.getSolidFill(); + + if(fill.isSetSrgbClr()){ + CTSRgbColor clr = fill.getSrgbClr(); + byte[] rgb = clr.getVal(); + return new Color(0xFF & rgb[0], 0xFF & rgb[1], 0xFF & rgb[2]); + } + } + + return new Color(0, 0, 0); + } + + /** + * + * @param fontSize font size in points. + * The value of -1 unsets the Sz attribute from the underlying xml bean + */ + public void setFontSize(double fontSize){ + CTTextCharacterProperties rPr = getRPr(); + if(fontSize == -1.0) { + if(rPr.isSetSz()) rPr.unsetSz(); + } else { + if(fontSize < 1.0) { + throw new IllegalArgumentException("Minimum font size is 1pt but was " + fontSize); + } + + rPr.setSz((int)(100*fontSize)); + } + } + + /** + * @return font size in points or -1 if font size is not set. + */ + public double getFontSize(){ + double scale = 1; + double size = XSSFFont.DEFAULT_FONT_SIZE; // default font size + CTTextNormalAutofit afit = getParentParagraph().getParentShape().getTxBody().getBodyPr().getNormAutofit(); + if(afit != null) scale = (double)afit.getFontScale() / 100000; + + CTTextCharacterProperties rPr = getRPr(); + if(rPr.isSetSz()){ + size = rPr.getSz()*0.01; + } + + return size * scale; + } + + /** + * + * @return the spacing between characters within a text run, + * If this attribute is omitted then a value of 0 or no adjustment is assumed. + */ + public double getCharacterSpacing(){ + CTTextCharacterProperties rPr = getRPr(); + if(rPr.isSetSpc()){ + return rPr.getSpc()*0.01; + } + return 0; + } + + /** + * Set the spacing between characters within a text run. + *

+ * The spacing is specified in points. Positive values will cause the text to expand, + * negative values to condense. + *

+ * + * @param spc character spacing in points. + */ + public void setCharacterSpacing(double spc){ + CTTextCharacterProperties rPr = getRPr(); + if(spc == 0.0) { + if(rPr.isSetSpc()) rPr.unsetSpc(); + } else { + rPr.setSpc((int)(100*spc)); + } + } + + /** + * Specifies the typeface, or name of the font that is to be used for this text run. + * + * @param typeface the font to apply to this text run. + * The value of null unsets the Typeface attribute from the underlying xml. + */ + public void setFont(String typeface){ + setFontFamily(typeface, (byte)-1, (byte)-1, false); + } + + public void setFontFamily(String typeface, byte charset, byte pictAndFamily, boolean isSymbol){ + CTTextCharacterProperties rPr = getRPr(); + + if(typeface == null){ + if(rPr.isSetLatin()) rPr.unsetLatin(); + if(rPr.isSetCs()) rPr.unsetCs(); + if(rPr.isSetSym()) rPr.unsetSym(); + } else { + if(isSymbol){ + CTTextFont font = rPr.isSetSym() ? rPr.getSym() : rPr.addNewSym(); + font.setTypeface(typeface); + } else { + CTTextFont latin = rPr.isSetLatin() ? rPr.getLatin() : rPr.addNewLatin(); + latin.setTypeface(typeface); + if(charset != -1) latin.setCharset(charset); + if(pictAndFamily != -1) latin.setPitchFamily(pictAndFamily); + } + } + } + + /** + * @return font family or null if not set + */ + public String getFontFamily(){ + CTTextCharacterProperties rPr = getRPr(); + CTTextFont font = rPr.getLatin(); + if(font != null){ + return font.getTypeface(); + } + return XSSFFont.DEFAULT_FONT_NAME; + } + + public byte getPitchAndFamily(){ + CTTextCharacterProperties rPr = getRPr(); + CTTextFont font = rPr.getLatin(); + if(font != null){ + return font.getPitchFamily(); + } + return 0; + } + + /** + * Specifies whether a run of text will be formatted as strikethrough text. + * + * @param strike whether a run of text will be formatted as strikethrough text. + */ + public void setStrikethrough(boolean strike) { + getRPr().setStrike(strike ? STTextStrikeType.SNG_STRIKE : STTextStrikeType.NO_STRIKE); + } + + /** + * @return whether a run of text will be formatted as strikethrough text. Default is false. + */ + public boolean isStrikethrough() { + CTTextCharacterProperties rPr = getRPr(); + if(rPr.isSetStrike()){ + return rPr.getStrike() != STTextStrikeType.NO_STRIKE; + } + return false; + } + + /** + * @return whether a run of text will be formatted as a superscript text. Default is false. + */ + public boolean isSuperscript() { + CTTextCharacterProperties rPr = getRPr(); + if(rPr.isSetBaseline()){ + return rPr.getBaseline() > 0; + } + return false; + } + + /** + * Set the baseline for both the superscript and subscript fonts. + *

+ * The size is specified using a percentage. + * Positive values indicate superscript, negative values indicate subscript. + *

+ * + * @param baselineOffset + */ + public void setBaselineOffset(double baselineOffset){ + getRPr().setBaseline((int) baselineOffset * 1000); + } + + /** + * Set whether the text in this run is formatted as superscript. + * Default base line offset is 30% + * + * @see #setBaselineOffset(double) + */ + public void setSuperscript(boolean flag){ + setBaselineOffset(flag ? 30. : 0.); + } + + /** + * Set whether the text in this run is formatted as subscript. + * Default base line offset is -25%. + * + * @see #setBaselineOffset(double) + */ + public void setSubscript(boolean flag){ + setBaselineOffset(flag ? -25.0 : 0.); + } + + /** + * @return whether a run of text will be formatted as a superscript text. Default is false. + */ + public boolean isSubscript() { + CTTextCharacterProperties rPr = getRPr(); + if(rPr.isSetBaseline()){ + return rPr.getBaseline() < 0; + } + return false; + } + + /** + * @return whether a run of text will be formatted as a superscript text. Default is false. + */ + public TextCap getTextCap() { + CTTextCharacterProperties rPr = getRPr(); + if(rPr.isSetCap()){ + return TextCap.values()[rPr.getCap().intValue() - 1]; + } + return TextCap.NONE; + } + + /** + * Specifies whether this run of text will be formatted as bold text + * + * @param bold whether this run of text will be formatted as bold text + */ + public void setBold(boolean bold){ + getRPr().setB(bold); + } + + /** + * @return whether this run of text is formatted as bold text + */ + public boolean isBold(){ + CTTextCharacterProperties rPr = getRPr(); + if(rPr.isSetB()){ + return rPr.getB(); + } + return false; + } + + /** + * @param italic whether this run of text is formatted as italic text + */ + public void setItalic(boolean italic){ + getRPr().setI(italic); + } + + /** + * @return whether this run of text is formatted as italic text + */ + public boolean isItalic(){ + CTTextCharacterProperties rPr = getRPr(); + if(rPr.isSetI()){ + return rPr.getI(); + } + return false; + } + + /** + * @param underline whether this run of text is formatted as underlined text + */ + public void setUnderline(boolean underline) { + getRPr().setU(underline ? STTextUnderlineType.SNG : STTextUnderlineType.NONE); + } + + /** + * @return whether this run of text is formatted as underlined text + */ + public boolean isUnderline(){ + CTTextCharacterProperties rPr = getRPr(); + if(rPr.isSetU()){ + return rPr.getU() != STTextUnderlineType.NONE; + } + return false; + } + + protected CTTextCharacterProperties getRPr(){ + return _r.isSetRPr() ? _r.getRPr() : _r.addNewRPr(); + } + + @Override + public String toString(){ + return "[" + getClass() + "]" + getText(); + } +}