]> source.dussan.org Git - poi.git/commitdiff
POI-55292 with added files
authorTim Allison <tallison@apache.org>
Wed, 31 Jul 2013 00:21:04 +0000 (00:21 +0000)
committerTim Allison <tallison@apache.org>
Wed, 31 Jul 2013 00:21:04 +0000 (00:21 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1508692 13f79535-47bb-0310-9956-ffa450edef68

12 files changed:
src/ooxml/java/org/apache/poi/xssf/model/ParagraphPropertyFetcher.java [new file with mode: 0644]
src/ooxml/java/org/apache/poi/xssf/usermodel/ListAutoNumber.java [new file with mode: 0644]
src/ooxml/java/org/apache/poi/xssf/usermodel/TextAlign.java [new file with mode: 0644]
src/ooxml/java/org/apache/poi/xssf/usermodel/TextAutofit.java [new file with mode: 0644]
src/ooxml/java/org/apache/poi/xssf/usermodel/TextCap.java [new file with mode: 0644]
src/ooxml/java/org/apache/poi/xssf/usermodel/TextDirection.java [new file with mode: 0644]
src/ooxml/java/org/apache/poi/xssf/usermodel/TextFontAlign.java [new file with mode: 0644]
src/ooxml/java/org/apache/poi/xssf/usermodel/TextHorizontalOverflow.java [new file with mode: 0644]
src/ooxml/java/org/apache/poi/xssf/usermodel/TextVerticalOverflow.java [new file with mode: 0644]
src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFLineBreak.java [new file with mode: 0644]
src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFTextParagraph.java [new file with mode: 0644]
src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFTextRun.java [new file with mode: 0644]

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 (file)
index 0000000..00f40f7
--- /dev/null
@@ -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<T> {
+
+    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 (file)
index 0000000..5108813
--- /dev/null
@@ -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 (file)
index 0000000..b93a885
--- /dev/null
@@ -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 (file)
index 0000000..6e01b13
--- /dev/null
@@ -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.
+ * <p>
+ * 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
+ * </p>
+ */
+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.
+     *
+     * <p>
+     * <em>Example:</em> 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
+     * </p>
+     */
+    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.
+     *
+     * <p>
+     * <em>Example:</em> 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
+     * </p>
+     */
+    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 (file)
index 0000000..9202e92
--- /dev/null
@@ -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 (file)
index 0000000..111ab65
--- /dev/null
@@ -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 (file)
index 0000000..9641798
--- /dev/null
@@ -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 (file)
index 0000000..b1fe0c1
--- /dev/null
@@ -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 (file)
index 0000000..87d3e40
--- /dev/null
@@ -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 (file)
index 0000000..9959699
--- /dev/null
@@ -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 (file)
index 0000000..28c52bd
--- /dev/null
@@ -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<XSSFTextRun>{
+    private final CTTextParagraph _p;
+    private final CTShape _shape;
+    private final List<XSSFTextRun> _runs;
+
+    XSSFTextParagraph(CTTextParagraph p, CTShape ctShape){
+        _p = p;
+        _shape = ctShape;
+        _runs = new ArrayList<XSSFTextRun>();
+
+        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<XSSFTextRun> getTextRuns(){
+        return _runs;
+    }
+
+    public Iterator<XSSFTextRun> 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<TextAlign> fetcher = new ParagraphPropertyFetcher<TextAlign>(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<TextFontAlign> fetcher = new ParagraphPropertyFetcher<TextFontAlign>(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<String> fetcher = new ParagraphPropertyFetcher<String>(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<String> fetcher = new ParagraphPropertyFetcher<String>(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 <code>null</code> value means to use the text font color.
+     */
+    public Color getBulletFontColor(){
+        ParagraphPropertyFetcher<Color> fetcher = new ParagraphPropertyFetcher<Color>(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:
+     * <p>
+     * If bulletSize >= 0, then bulletSize is a percentage of the font size.
+     * If bulletSize < 0, then it specifies the size in points
+     * </p>
+     *
+     * @return the bullet size
+     */
+    public double getBulletFontSize(){
+        ParagraphPropertyFetcher<Double> fetcher = new ParagraphPropertyFetcher<Double>(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:
+     * <p>
+     * If bulletSize >= 0, then bulletSize is a percentage of the font size.
+     * If bulletSize < 0, then it specifies the size in points
+     * </p>
+     */
+    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<Double> fetcher = new ParagraphPropertyFetcher<Double>(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<Double> fetcher = new ParagraphPropertyFetcher<Double>(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<Double> fetcher = new ParagraphPropertyFetcher<Double>(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<Double> fetcher = new ParagraphPropertyFetcher<Double>(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<Double> fetcher = new ParagraphPropertyFetcher<Double>(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:
+     * <p>
+     * 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
+     * </p>
+     * Examples:
+     * <pre><code>
+     *      // 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);
+     * </code></pre>
+     * 
+     * @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:
+     * <p>
+     * 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
+     * </p>
+     *
+     * @return the vertical line spacing.
+     */
+    public double getLineSpacing(){
+        ParagraphPropertyFetcher<Double> fetcher = new ParagraphPropertyFetcher<Double>(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:
+     * <p>
+     * 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
+     * </p>
+     * Examples:
+     * <pre><code>
+     *      // 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);
+     * </code></pre>
+     *
+     * @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:
+     * <p>
+     * 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
+     * </p>
+     *
+     * @return the vertical white space before the paragraph
+     */
+    public double getSpaceBefore(){
+        ParagraphPropertyFetcher<Double> fetcher = new ParagraphPropertyFetcher<Double>(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:
+     * <p>
+     * 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
+     * </p>
+     * Examples:
+     * <pre><code>
+     *      // 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);
+     * </code></pre>
+     *
+     * @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:
+     * <p>
+     * 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
+     * </p>
+     *
+     * @return the vertical white space after the paragraph
+     */
+    public double getSpaceAfter(){
+        ParagraphPropertyFetcher<Double> fetcher = new ParagraphPropertyFetcher<Double>(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).
+     * <p>
+     * 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.
+     * </p>
+     *
+     * @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<Boolean> fetcher = new ParagraphPropertyFetcher<Boolean>(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<Boolean> fetcher = new ParagraphPropertyFetcher<Boolean>(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<Integer> fetcher = new ParagraphPropertyFetcher<Integer>(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<ListAutoNumber> fetcher = new ParagraphPropertyFetcher<ListAutoNumber>(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 (file)
index 0000000..5b63e94
--- /dev/null
@@ -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 <code>-1</code> 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.
+     * <p>
+     * The spacing is specified in points. Positive values will cause the text to expand,
+     * negative values to condense.
+     * </p>
+     *
+     * @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 <code>null</code> 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.
+     *  <p>
+     *     The size is specified using a percentage.
+     *     Positive values indicate superscript, negative values indicate subscript.
+     *  </p>
+     *
+     * @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();
+    }
+}